class Promotion::Evolver
The Evolver
class evolves the database by executing migration scripts from the evolve folder of the project being promoted
This class may be invoked via promote or via evolve or devolve commands
Public Class Methods
Creates a new Evolver
# File lib/promotion/evolver.rb, line 9 def initialize(appname, evolve=true, targetVersion=nil) @appname = appname @evolve = (evolve == true) @target = targetVersion.to_i() @currentVersion = nil @spec = get_spec() db = @spec.elements["Database"] @dbms = db.text() @database = db.attributes["database"] || "" end
Public Instance Methods
Returns the database to an earlier schema version:
-
find all of the relevant schema migration files in the
devolve
folder -
execute in sequence all migrations from the current version down to the target version
-
update the version file with the new version number
# File lib/promotion/evolver.rb, line 95 def devolve() $log.info("\n#{'_'*40}\nDevolving the database #{@database}\n") devolveFolder = File.expand_path("#{@appname}/devolve", Folders::Staging) Dir.chdir(devolveFolder) migrations = Dir["*.sql"].collect { |f| f.sub(".sql","").to_i() }.sort() migrations.reject! { |v| v > @currentVersion } migrations.reject! { |v| v <= @target } # after devolving we are at the previous version migrations.reverse! if @target < @currentVersion print("Devolving the database") else puts("Already at version #{@currentVersion}.") exit 0 end completed = @currentVersion migrations.each { |v| success = system("#{@dbms} #{@database} < #{v}.sql") if success $log.info("Devolved the database from version #{v}") completed = v else $log.error("Failed to devolve the database from version #{v}.") break end } return(completed-1) # after devolving we are at the previous version end
Starts the database evolution:
-
find all of the relevant schema migration files in the
evolve
folder -
execute in sequence all migrations after the current version, updating the version file with the latest successful version number
# File lib/promotion/evolver.rb, line 64 def evolve() $log.info("\n#{'_'*40}\nEvolving the database #{@database}\n") evolveFolder = File.expand_path("#{@appname}/evolve", Folders::Staging) Dir.chdir(evolveFolder) migrations = Dir["*.sql"].collect { |f| f.sub(".sql","").to_i() }.sort() migrations.reject! { |v| v <= @currentVersion } migrations.reject! { |v| v > @target } unless @target == 0 @target = migrations.last.to_i if @target <= @currentVersion puts("Already at version #{@currentVersion}.") exit 0 end completed = @currentVersion migrations.each { |v| success = system("#{@dbms} #{@database} < #{v}.sql") if success $log.info("Evolved the database to version #{v}") completed = v else $log.error("Failed to evolve the database to version #{v}.") break end } return(completed) end
The deployment descriptor for an application should contain a single Database element in order to make use of the evolve
command. SQLite3 also needs a database
attribute to specify the file to operate on.
<Database>/usr/bin/mysql</Database> <Database database="/var/myapp/myapp.db">/usr/bin/sqlite3</Database>
# File lib/promotion/evolver.rb, line 25 def get_spec() appFolder = File.expand_path(@appname, Folders::Staging) Dir.chdir(appFolder) specfile = File.expand_path(Files::Spec, appFolder) unless File.exist?(specfile) puts("\nSpecification file #{specfile} does not exist.\n" ) exit 1 end doc = REXML::Document.new(File.new(specfile)) doc.root end
Gets the current version from the version file and calls evolve or devolve as required
# File lib/promotion/evolver.rb, line 38 def start() versionFilename = File.expand_path("@version.#{@appname}", Folders::Staging) if !File.exist?(versionFilename) puts("We expect a version file at #{versionFilename} containing a single line") puts("with the current version of the database (eg. 1001)") exit 1 end versionFile = File.new(versionFilename, 'r') @currentVersion = versionFile.gets.chomp().to_i() versionFile.close() completed = @currentVersion if @evolve completed = evolve() else completed = devolve() end versionFile = File.new(versionFilename, 'w') versionFile.puts(completed) versionFile.close() puts("Evolved the database to version #{completed} ") end