diff --git a/migrate/versioning/api.py b/migrate/versioning/api.py index 3bca613..3e96d56 100644 --- a/migrate/versioning/api.py +++ b/migrate/versioning/api.py @@ -11,8 +11,8 @@ __all__=[ 'help', 'create', 'script', +'script_sql', 'make_update_script_for_model', -'commit', 'version', 'source', 'version_control', @@ -61,57 +61,42 @@ def create(repository,name,**opts): except exceptions.PathFoundError,e: raise exceptions.KnownError("The path %s already exists"%e.args[0]) -def script(path,**opts): - """%prog script PATH +def script(description,repository=None,**opts): + """%prog script [--repository=REPOSITORY_PATH] DESCRIPTION - Create an empty change script at the specified path. + Create an empty change script using the next unused version number appended with the given description. + For instance, manage.py script "Add initial tables" creates: repository/versions/001_Add_initial_tables.py """ try: - cls_script_python.create(path,**opts) + if repository is None: + raise exceptions.UsageError("A repository must be specified") + repos = cls_repository(repository) + repos.create_script(description,**opts) except exceptions.PathFoundError,e: raise exceptions.KnownError("The path %s already exists"%e.args[0]) -def commit(script,repository,database=None,operation=None,version=None,**opts): - """%prog commit SCRIPT_PATH.py REPOSITORY_PATH [VERSION] +def script_sql(database,repository=None,**opts): + """%prog script_sql [--repository=REPOSITORY_PATH] DATABASE - %prog commit SCRIPT_PATH.sql REPOSITORY_PATH DATABASE OPERATION [VERSION] - - Commit a script to this repository. The committed script is added to the - repository, and the file disappears. - - Once a script has been committed, you can use it to upgrade a database with - the 'upgrade' command. - - If a version is given, that version will be replaced instead of creating a - new version. - - Normally, when writing change scripts in Python, you'll use the first form - of this command (DATABASE and OPERATION aren't specified). If you write - change scripts as .sql files, you'll need to specify DATABASE ('postgres', - 'mysql', 'oracle', 'sqlite'...) and OPERATION ('upgrade' or 'downgrade'). - You may commit multiple .sql files under the same version to complete - functionality for a particular version:: - - %prog commit upgrade.postgres.sql /repository/path postgres upgrade 1 - %prog commit downgrade.postgres.sql /repository/path postgres downgrade 1 - %prog commit upgrade.sqlite.sql /repository/path sqlite upgrade 1 - %prog commit downgrade.sqlite.sql /repository/path sqlite downgrade 1 - [etc...] + Create empty change SQL scripts for given DATABASE, where DATABASE is either specific ('postgres', 'mysql', + 'oracle', 'sqlite', etc.) or generic ('default'). + For instance, manage.py script_sql postgres creates: + repository/versions/001_upgrade_postgres.py and repository/versions/001_downgrade_postgres.py """ - if (database is not None) and (operation is None) and (version is None): - # Version was supplied as a positional - version = database - database = None - - repos = cls_repository(repository) - repos.commit(script,version,database=database,operation=operation) + try: + if repository is None: + raise exceptions.UsageError("A repository must be specified") + repos = cls_repository(repository) + repos.create_script_sql(database,**opts) + except exceptions.PathFoundError,e: + raise exceptions.KnownError("The path %s already exists"%e.args[0]) -def test(script,repository,url=None,**opts): - """%prog test SCRIPT_PATH REPOSITORY_PATH URL [VERSION] +def test(repository,url=None,**opts): + """%prog test REPOSITORY_PATH URL [VERSION] """ engine=create_engine(url) - schema = cls_schema(engine,repository) - script = cls_script_python(script) + repos=cls_repository(repository) + script = repos.version(None).script() # Upgrade print "Upgrading...", try: diff --git a/migrate/versioning/genmodel.py b/migrate/versioning/genmodel.py index 1437c0b..39714b6 100644 --- a/migrate/versioning/genmodel.py +++ b/migrate/versioning/genmodel.py @@ -126,9 +126,6 @@ class ModelGenerator(object): for modelTable in self.diff.tablesWithDiff: modelTable = modelTable.tometadata(meta) dbTable = self.diff.reflected_model.tables[modelTable.name] - #print 'TODO DEBUG.cols1', [x.name for x in dbTable.columns] - #dbTable = dbTable.tometadata(meta) - #print 'TODO DEBUG.cols2', [x.name for x in dbTable.columns] tableName = modelTable.name missingInDatabase, missingInModel, diffDecl = self.diff.colDiffs[tableName] if dbCanHandleThisChange(missingInDatabase, missingInModel, diffDecl): diff --git a/migrate/versioning/migrate_repository.py b/migrate/versioning/migrate_repository.py new file mode 100644 index 0000000..77da77d --- /dev/null +++ b/migrate/versioning/migrate_repository.py @@ -0,0 +1,35 @@ +''' Script to migrate repository. This shouldn't use any other migrate modules, so that it can work in any version. ''' + +import os, sys + + +def usage(): + + print '''Usage: %(prog)s repository-to-migrate + +Upgrade your repository to the new flat format. + +NOTE: You should probably make a backup before running this. +''' % {'prog': sys.argv[0]} + + raise SystemExit(1) + + +def migrate_repository(repos): + print 'Migrating repository at: %s to new format' % repos + versions = '%s/versions' % repos + dirs = os.listdir(versions) + numdirs = [ int(dir) for dir in dirs if dir.isdigit() ] # Only use int's in list. + numdirs.sort() # Sort list. + for dir in numdirs: + origdir = '%s/%s' % (versions, dir) + print ' Working on directory: %s' % origdir + files = os.listdir(origdir) + pass # finish TODO xxx + + +if __name__ == '__main__': + if len(sys.argv) != 2: + usage() + migrate_repository(sys.argv[1]) + diff --git a/migrate/versioning/repository.py b/migrate/versioning/repository.py index c4cd7eb..47e2302 100644 --- a/migrate/versioning/repository.py +++ b/migrate/versioning/repository.py @@ -113,9 +113,11 @@ class Repository(pathed.Pathed): log.error("There was an error creating your repository") return cls(path) - def commit(self,*p,**k): - reqd = self.config.get('db_settings','required_dbs') - return self.versions.commit(required=reqd,*p,**k) + def create_script(self,description,**k): + self.versions.createNewVersion(description,**k) + + def create_script_sql(self,database,**k): + self.versions.createNewSQLVersion(database,**k) latest=property(lambda self: self.versions.latest) version_table=property(lambda self: self.config.get('db_settings','version_table')) diff --git a/migrate/versioning/shell.py b/migrate/versioning/shell.py index ee6afab..aa52bec 100644 --- a/migrate/versioning/shell.py +++ b/migrate/versioning/shell.py @@ -8,7 +8,6 @@ import inspect alias = dict( s=api.script, - ci=api.commit, vc=api.version_control, dbv=api.db_version, v=api.version, diff --git a/migrate/versioning/version.py b/migrate/versioning/version.py index 9b68df9..75a7a4f 100644 --- a/migrate/versioning/version.py +++ b/migrate/versioning/version.py @@ -1,5 +1,5 @@ from migrate.versioning import exceptions,pathed,script -import os,shutil +import os,re,shutil @@ -31,21 +31,38 @@ class VerNum(object): return int(self)-int(value) +def strToFilename(s): + s = s.replace(' ', '_').replace('"', '_').replace("'", '_') + while '__' in s: + s = s.replace('__', '_') + return s + + class Collection(pathed.Pathed): """A collection of versioning scripts in a repository""" + FILENAME_WITH_VERSION = re.compile(r'^(\d+).*') def __init__(self,path): super(Collection,self).__init__(path) - self.versions=dict() + + # Create temporary list of files, allowing skipped version numbers. + files = os.listdir(path) + if '1' in files: + raise Exception('It looks like you have a repository in the old format (with directories for each version). Please convert repository before proceeding.') + tempVersions = dict() + for filename in files: + match = self.FILENAME_WITH_VERSION.match(filename) + if match: + num = int(match.group(1)) + tempVersions.setdefault(num, []).append(filename) + else: + pass # Must be a helper file or something, let's ignore it. + + # Create the versions member where the keys are VerNum's and the values are Version's. + self.versions=dict() + for num, files in tempVersions.items(): + self.versions[VerNum(num)] = Version(num, path, files) + self.latest = max([VerNum(0)] + self.versions.keys()) # calculate latest version - ver=self.latest=VerNum(1) - vers=os.listdir(path) - # This runs up to the latest *complete* version; stops when one's missing - while str(ver) in vers: - verpath=self.version_path(ver) - self.versions[ver]=Version(verpath) - ver+=1 - self.latest=ver-1 - def version_path(self,ver): return os.path.join(self.path,str(ver)) @@ -54,47 +71,52 @@ class Collection(pathed.Pathed): vernum = self.latest return self.versions[VerNum(vernum)] - def commit(self,path,ver=None,*p,**k): - """Commit a script to this collection of scripts - """ - maxver = self.latest+1 - if ver is None: - ver = maxver - # Ver must be valid: can't upgrade past the next version + def getNewVersion(self): + ver = self.latest+1 # No change scripts exist for 0 (even though it's a valid version) - if ver > maxver or ver == 0: + if ver <= 0: raise exceptions.InvalidVersionError() - verpath = self.version_path(ver) - tmpname = None - try: - # If replacing an old version, copy it in case it gets trashed - if os.path.exists(verpath): - tmpname = os.path.join(os.path.split(verpath)[0],"%s_tmp"%ver) - shutil.copytree(verpath,tmpname) - version = Version(verpath) - else: - # Create version folder - version = Version.create(verpath) - self.versions[ver] = version - # Commit the individual script - script = version.commit(path,*p,**k) - except: - # Rollback everything we did in the try before dying, and reraise - # Remove the created version folder - shutil.rmtree(verpath) - # Rollback if a version already existed above - if tmpname is not None: - shutil.move(tmpname,verpath) - raise - # Success: mark latest; delete old version - if tmpname is not None: - shutil.rmtree(tmpname) self.latest = ver + return ver + def createNewVersion(self, description, **k): + ver = self.getNewVersion() + extra = strToFilename(description) + if extra: + if extra == '_': + extra = '' + elif not extra.startswith('_'): + extra = '_%s' % extra + filename = '%03d%s.py' % (ver, extra) + filepath = self.version_path(filename) + if os.path.exists(filepath): + raise Exception('Script already exists: %s' % filepath) + else: + script.PythonScript.create(filepath) + self.versions[ver] = Version(ver, self.path, [filename]) + + def createNewSQLVersion(self, database, **k): + # Determine version number to use. + if (not self.versions) or self.versions[self.latest].python: + # First version or current version already contains python script, so create a new version. + ver = self.getNewVersion() + self.versions[ver] = Version(ver, self.path, []) + else: + ver = self.latest + + # Create new files. + for op in ('upgrade', 'downgrade'): + filename = '%03d_%s_%s.sql' % (ver, database, op) + filepath = self.version_path(filename) + if os.path.exists(filepath): + raise Exception('Script already exists: %s' % filepath) + else: + open(filepath, "w").close() + self.versions[ver]._add_script(filepath) + @classmethod def clear(cls): super(Collection,cls).clear() - Version.clear() class extensions: @@ -103,28 +125,25 @@ class extensions: sql='sql' -class Version(pathed.Pathed): +class Version(object): # formerly inherit from: (pathed.Pathed): """A single version in a repository """ - def __init__(self,path): - super(Version,self).__init__(path) + def __init__(self,vernum,path,filelist): # Version must be numeric try: - self.version=VerNum(os.path.basename(path)) + self.version=VerNum(vernum) except: - raise exceptions.InvalidVersionError(path) + raise exceptions.InvalidVersionError(vernum) # Collect scripts in this folder self.sql = dict() self.python = None - try: - for script in os.listdir(path): - # skip __init__.py, because we assume that it's - # just there to mark the package - if script == '__init__.py': - continue - self._add_script(os.path.join(path,script)) - except: - raise exceptions.InvalidVersionError(path) + + for script in filelist: + # skip __init__.py, because we assume that it's + # just there to mark the package + if script == '__init__.py': + continue + self._add_script(os.path.join(path,script)) def script(self,database=None,operation=None): #if database is None and operation is None: @@ -163,10 +182,13 @@ class Version(pathed.Pathed): self._add_script_py(path) elif path.endswith(extensions.sql): self._add_script_sql(path) + + SQL_FILENAME = re.compile(r'^(\d+)_([^_]+)_([^_]+).sql') def _add_script_sql(self,path): - try: - version,dbms,op,ext=os.path.basename(path).split('.',3) - except: + match = self.SQL_FILENAME.match(os.path.basename(path)) + if match: + version, dbms, op = match.group(1), match.group(2), match.group(3) + else: raise exceptions.ScriptError("Invalid sql script name %s"%path) # File the script into a dictionary @@ -176,6 +198,8 @@ class Version(pathed.Pathed): ops = dbmses[dbms] ops[op] = script.SqlScript(path) def _add_script_py(self,path): + if self.python is not None: + raise Exception('You can only have one Python script per version, but you have: %s and %s' % (self.python, path)) self.python = script.PythonScript(path) def _rm_ignore(self,path): @@ -185,29 +209,3 @@ class Version(pathed.Pathed): except OSError: pass - def commit(self,path,database=None,operation=None,required=None): - if (database is not None) and (operation is not None): - return self._commit_sql(path,database,operation) - return self._commit_py(path,required) - def _commit_sql(self,path,database,operation): - if not path.endswith(extensions.sql): - msg = "Bad file extension: should end with %s"%extensions.sql - raise exceptions.ScriptError(msg) - dest=os.path.join(self.path,'%s.%s.%s.%s'%( - str(self.version),str(database),str(operation),extensions.sql)) - # Move the committed py script to this version's folder - shutil.move(path,dest) - self._add_script(dest) - - def _commit_py(self,path_py,required=None): - if (not os.path.exists(path_py)) or (not os.path.isfile(path_py)): - raise exceptions.InvalidVersionError(path_py) - dest = os.path.join(self.path,'%s.%s'%(str(self.version),extensions.py)) - - # Move the committed py script to this version's folder - shutil.move(path_py,dest) - self._add_script(dest) - # Also delete the .pyc file, if it exists - path_pyc = path_py+'c' - if os.path.exists(path_pyc): - self._rm_ignore(path_pyc) diff --git a/test/versioning/test_repository.py b/test/versioning/test_repository.py index a86a910..a9b4630 100644 --- a/test/versioning/test_repository.py +++ b/test/versioning/test_repository.py @@ -53,28 +53,11 @@ class TestVersionedRepository(fixture.Pathed): def setUp(self): Repository.clear() self.path_repos=self.tmp_repos() - self.path_script=self.tmp_py() # Create repository, script Repository.create(self.path_repos,'repository_name') - def test_commit(self): - """Commit scripts to a repository and detect repository version""" - # Load repository; commit script by pathname; script should go away - self.script_cls.create(self.path_script) - repos=Repository(self.path_repos) - self.assert_(os.path.exists(self.path_script)) - repos.commit(self.path_script) - self.assert_(not os.path.exists(self.path_script)) - # .pyc file from the committed script shouldn't exist either - self.assert_(not os.path.exists(self.path_script+'c')) - version = repos.versions.version() - self.assert_(os.path.exists(os.path.join(version.path, - "%s.py" % version.version))) - self.assert_(os.path.exists(os.path.join(version.path, - "__init__.py"))) def test_version(self): """We should correctly detect the version of a repository""" - self.script_cls.create(self.path_script) repos=Repository(self.path_repos) # Get latest version, or detect if a specified version exists self.assertEquals(repos.latest,0) @@ -82,15 +65,14 @@ class TestVersionedRepository(fixture.Pathed): # (so we can't just assume the following tests are correct) self.assert_(repos.latest>=0) self.assert_(repos.latest<1) - # Commit a script and test again - repos.commit(self.path_script) + # Create a script and test again + repos.create_script('') self.assertEquals(repos.latest,1) self.assert_(repos.latest>=0) self.assert_(repos.latest>=1) self.assert_(repos.latest<2) - # Commit a new script and test again - self.script_cls.create(self.path_script) - repos.commit(self.path_script) + # Create a new script and test again + repos.create_script('') self.assertEquals(repos.latest,2) self.assert_(repos.latest>=0) self.assert_(repos.latest>=1) @@ -98,62 +80,27 @@ class TestVersionedRepository(fixture.Pathed): self.assert_(repos.latest<3) def test_source(self): """Get a script object by version number and view its source""" - self.script_cls.create(self.path_script) # Load repository and commit script repos=Repository(self.path_repos) - repos.commit(self.path_script) + repos.create_script('') # Get script object source=repos.version(1).script().source() # Source is valid: script must have an upgrade function # (not a very thorough test, but should be plenty) self.assert_(source.find('def upgrade')>=0) def test_latestversion(self): - self.script_cls.create(self.path_script) """Repository.version() (no params) returns the latest version""" repos=Repository(self.path_repos) - repos.commit(self.path_script) + repos.create_script('') self.assert_(repos.version(repos.latest) is repos.version()) self.assert_(repos.version() is not None) - def xtest_commit_fail(self): - """Failed commits shouldn't corrupt the repository - Test disabled - logsql ran the script on commit; now that that's gone, - the content of the script is not checked before commit - """ - repos=Repository(self.path_repos) - path_script=self.tmp_py() - text_script = """ - from sqlalchemy import * - from migrate import * - - # Upgrade is not declared; commit should fail - #def upgrade(): - # raise Exception() - - def downgrade(): - raise Exception() - """.replace("\n ","\n") - fd=open(path_script,'w') - fd.write(text_script) - fd.close() - - # Record current state, and commit - ver_pre = os.listdir(repos.versions.path) - repos_pre = os.listdir(repos.path) - self.assertRaises(Exception,repos.commit,path_script) - # Version is unchanged - self.assertEquals(repos.latest,0) - # No new files created; committed script not moved - self.assert_(os.path.exists(path_script)) - self.assertEquals(os.listdir(repos.versions.path),ver_pre) - self.assertEquals(os.listdir(repos.path),repos_pre) def test_changeset(self): """Repositories can create changesets properly""" # Create a nonzero-version repository of empty scripts repos=Repository(self.path_repos) for i in range(10): - self.script_cls.create(self.path_script) - repos.commit(self.path_script) + repos.create_script('') def check_changeset(params,length): """Creates and verifies a changeset""" diff --git a/test/versioning/test_runchangeset.py b/test/versioning/test_runchangeset.py index 2238916..192ecc0 100644 --- a/test/versioning/test_runchangeset.py +++ b/test/versioning/test_runchangeset.py @@ -8,7 +8,6 @@ class TestRunChangeset(fixture.Pathed,fixture.DB): def setUp(self): Repository.clear() self.path_repos=self.tmp_repos() - self.path_script=self.tmp_py() # Create repository, script Repository.create(self.path_repos,'repository_name') @@ -17,8 +16,7 @@ class TestRunChangeset(fixture.Pathed,fixture.DB): """Running a changeset against a repository gives expected results""" repos=Repository(self.path_repos) for i in range(10): - script.PythonScript.create(self.path_script) - repos.commit(self.path_script) + repos.create_script('') try: ControlledSchema(self.engine,repos).drop() except: diff --git a/test/versioning/test_schema.py b/test/versioning/test_schema.py index 3607f11..c5e96ac 100644 --- a/test/versioning/test_schema.py +++ b/test/versioning/test_schema.py @@ -58,11 +58,9 @@ class TestControlledSchema(fixture.Pathed,fixture.DB): dbcontrol.drop() # Now try it with a nonzero value - script_path = self.tmp_py() version=10 for i in range(version): - script.PythonScript.create(script_path) - self.repos.commit(script_path) + self.repos.create_script('') self.assertEquals(self.repos.latest,version) # Test with some mid-range value diff --git a/test/versioning/test_shell.py b/test/versioning/test_shell.py index 7f1366f..eb566ad 100644 --- a/test/versioning/test_shell.py +++ b/test/versioning/test_shell.py @@ -120,17 +120,24 @@ class TestShellCommands(Shell): def test_script(self): """We can create a migration script via the command line""" - script=self.tmp_py() - # Creating a file that doesn't exist should succeed - self.assertSuccess(self.cmd('script',script)) - self.assert_(os.path.exists(script)) + repos=self.tmp_repos() + self.assertSuccess(self.cmd('create',repos,'repository_name')) + self.assertSuccess(self.cmd('script', '--repository=%s' % repos, 'Desc')) + self.assert_(os.path.exists('%s/versions/001_Desc.py' % repos)) # 's' instead of 'script' should work too - os.remove(script) - self.assert_(not os.path.exists(script)) - self.assertSuccess(self.cmd('s',script)) - self.assert_(os.path.exists(script)) + self.assertSuccess(self.cmd('script', '--repository=%s' % repos, 'More')) + self.assert_(os.path.exists('%s/versions/002_More.py' % repos)) + + def test_script_sql(self): + """We can create a migration sql script via the command line""" + repos=self.tmp_repos() + self.assertSuccess(self.cmd('create',repos,'repository_name')) + self.assertSuccess(self.cmd('script_sql', '--repository=%s' % repos, 'mydb')) + self.assert_(os.path.exists('%s/versions/001_mydb_upgrade.sql' % repos)) + self.assert_(os.path.exists('%s/versions/001_mydb_downgrade.sql' % repos)) + # Can't create it again: it already exists - self.assertFailure(self.cmd('script',script)) + self.assertFailure(self.cmd('script_sql', '--repository=%s' % repos, 'mydb')) def test_manage(self): """Create a project management script""" @@ -145,20 +152,8 @@ class TestShellRepository(Shell): def setUp(self): """Create repository, python change script""" self.path_repos=repos=self.tmp_repos() - self.path_script=script=self.tmp_py() self.assertSuccess(self.cmd('create',repos,'repository_name')) - self.assertSuccess(self.cmd('script',script)) - def test_commit_1(self): - """Commits should work correctly; script should vanish after commit""" - self.assert_(os.path.exists(self.path_script)) - self.assertSuccess(self.cmd('commit',self.path_script,self.path_repos)) - self.assert_(not os.path.exists(self.path_script)) - def test_commit_2(self): - """Commits should work correctly with repository as a keyword param""" - self.assert_(os.path.exists(self.path_script)) - self.assertSuccess(self.cmd('commit',self.path_script,'--repository=%s'%self.path_repos)) - self.assert_(not os.path.exists(self.path_script)) def test_version(self): """Correctly detect repository version""" # Version: 0 (no scripts yet); successful execution @@ -169,17 +164,17 @@ class TestShellRepository(Shell): fd=self.execute(self.cmd('version',self.path_repos)) self.assertEquals(fd.read().strip(),"0") self.assertSuccess(fd) - # Commit a script and version should increment - self.assertSuccess(self.cmd('commit',self.path_script,'--repository=%s'%self.path_repos)) + # Create a script and version should increment + self.assertSuccess(self.cmd('script', '--repository=%s' % self.path_repos, 'Desc')) fd=self.execute(self.cmd('version',self.path_repos)) self.assertEquals(fd.read().strip(),"1") self.assertSuccess(fd) def test_source(self): """Correctly fetch a script's source""" - source=open(self.path_script).read() + self.assertSuccess(self.cmd('script', '--repository=%s' % self.path_repos, 'Desc')) + filename='%s/versions/001_Desc.py' % self.path_repos + source=open(filename).read() self.assert_(source.find('def upgrade')>=0) - self.assertSuccess(self.cmd('commit',self.path_script,'--repository=%s'%self.path_repos)) - # Later, we'll want to make repos optional somehow # Version is now 1 fd=self.execute(self.cmd('version',self.path_repos)) self.assert_(fd.read().strip()=="1") @@ -190,50 +185,11 @@ class TestShellRepository(Shell): self.assertSuccess(fd) self.assert_(result.strip()==source.strip()) # We can also send the source to a file... test that too - self.assertSuccess(self.cmd('source',1,self.path_script,'--repository=%s'%self.path_repos)) - self.assert_(os.path.exists(self.path_script)) - fd=open(self.path_script) + self.assertSuccess(self.cmd('source',1,filename,'--repository=%s'%self.path_repos)) + self.assert_(os.path.exists(filename)) + fd=open(filename) result=fd.read() self.assert_(result.strip()==source.strip()) - def test_commit_replace(self): - """Commit can replace a specified version""" - # Commit the default script - self.assertSuccess(self.cmd('commit',self.path_script,self.path_repos)) - self.assertEquals(self.cmd_version(self.path_repos),1) - # Read the default script's text - fd=self.execute(self.cmd('source',1,'--repository=%s'%self.path_repos)) - script_src_1 = fd.read() - self.assertSuccess(fd) - - # Commit a new script - script_text=""" - from sqlalchemy import * - from migrate import * - - # Our test is just that the source is different; so we don't have to - # do anything useful in here. - - def upgrade(): - pass - - def downgrade(): - pass - """.replace('\n ','\n') - fd=open(self.path_script,'w') - fd.write(script_text) - fd.close() - self.assertSuccess(self.cmd('commit',self.path_script,self.path_repos,1)) - # We specified a version above - it should replace that, not create new - self.assertEquals(self.cmd_version(self.path_repos),1) - # Source should change - fd=self.execute(self.cmd('source',1,'--repository=%s'%self.path_repos)) - script_src_2 = fd.read() - self.assertSuccess(fd) - self.assertNotEquals(script_src_1,script_src_2) - # source should be reasonable - self.assertEquals(script_src_2.strip(),script_text.strip()) - self.assert_(script_src_1.count('from migrate import')) - self.assert_(script_src_1.count('from sqlalchemy import')) class TestShellDatabase(Shell,fixture.DB): """Commands associated with a particular database""" @@ -263,8 +219,7 @@ class TestShellDatabase(Shell,fixture.DB): path_script = self.tmp_py() version=1 for i in range(version): - self.assertSuccess(self.cmd('script',path_script)) - self.assertSuccess(self.cmd('commit',path_script,path_repos)) + self.assertSuccess(self.cmd('script', '--repository=%s' % path_repos, 'Desc')) # Repository version is correct fd=self.execute(self.cmd('version',path_repos)) self.assertEquals(fd.read().strip(),str(version)) @@ -284,7 +239,6 @@ class TestShellDatabase(Shell,fixture.DB): # Create a repository repos_name = 'repos_name' repos_path = self.tmp() - script_path = self.tmp_py() self.assertSuccess(self.cmd('create',repos_path,repos_name)) self.assertEquals(self.cmd_version(repos_path),0) # Version the DB @@ -301,8 +255,7 @@ class TestShellDatabase(Shell,fixture.DB): self.assertFailure(self.cmd('upgrade',self.url,repos_path,-1)) # Add a script to the repository; upgrade the db - self.assertSuccess(self.cmd('script',script_path)) - self.assertSuccess(self.cmd('commit',script_path,repos_path)) + self.assertSuccess(self.cmd('script', '--repository=%s' % repos_path, 'Desc')) self.assertEquals(self.cmd_version(repos_path),1) self.assertEquals(self.cmd_db_version(self.url,repos_path),0) @@ -321,14 +274,6 @@ class TestShellDatabase(Shell,fixture.DB): self.assertSuccess(self.cmd('drop_version_control',self.url,repos_path)) def _run_test_sqlfile(self,upgrade_script,downgrade_script): - upgrade_path = self.tmp_sql() - downgrade_path = self.tmp_sql() - upgrade = (upgrade_path,upgrade_script) - downgrade = (downgrade_path,downgrade_script) - for file_path,file_text in (upgrade,downgrade): - fd = open(file_path,'w') - fd.write(file_text) - fd.close() repos_path = self.tmp() repos_name = 'repos' @@ -338,15 +283,12 @@ class TestShellDatabase(Shell,fixture.DB): self.assertEquals(self.cmd_version(repos_path),0) self.assertEquals(self.cmd_db_version(self.url,repos_path),0) - self.assertSuccess(self.cmd('commit',upgrade_path,repos_path,'postgres','upgrade')) + beforeCount = len(os.listdir(os.path.join(repos_path,'versions'))) # hmm, this number changes sometimes based on running from svn + self.assertSuccess(self.cmd('script_sql', '--repository=%s' % repos_path, 'postgres')) self.assertEquals(self.cmd_version(repos_path),1) - self.assertEquals(len(os.listdir(os.path.join(repos_path,'versions','1'))),2) - - # Add, not replace - self.assertSuccess(self.cmd('commit',downgrade_path,repos_path,'postgres','downgrade','--version=1')) - self.assertEquals(len(os.listdir(os.path.join(repos_path,'versions','1'))),3) - self.assertEquals(self.cmd_version(repos_path),1) - + self.assertEquals(len(os.listdir(os.path.join(repos_path,'versions'))), beforeCount + 2) + open('%s/versions/001_postgres_upgrade.sql' % repos_path, 'a').write(upgrade_script) + open('%s/versions/001_postgres_downgrade.sql' % repos_path, 'a').write(downgrade_script) self.assertEquals(self.cmd_db_version(self.url,repos_path),0) self.assertRaises(Exception,self.engine.text('select * from t_table').execute) @@ -392,7 +334,6 @@ class TestShellDatabase(Shell,fixture.DB): def test_test(self): repos_name = 'repos_name' repos_path = self.tmp() - script_path = self.tmp_py() self.assertSuccess(self.cmd('create',repos_path,repos_name)) self.exitcode(self.cmd('drop_version_control',self.url,repos_path)) @@ -401,9 +342,9 @@ class TestShellDatabase(Shell,fixture.DB): self.assertEquals(self.cmd_db_version(self.url,repos_path),0) # Empty script should succeed - self.assertSuccess(self.cmd('script',script_path)) - self.assertSuccess(self.cmd('test',script_path,repos_path,self.url)) - self.assertEquals(self.cmd_version(repos_path),0) + self.assertSuccess(self.cmd('script', '--repository=%s' % repos_path, 'Desc')) + self.assertSuccess(self.cmd('test',repos_path,self.url)) + self.assertEquals(self.cmd_version(repos_path),1) self.assertEquals(self.cmd_db_version(self.url,repos_path),0) # Error script should fail @@ -423,8 +364,8 @@ class TestShellDatabase(Shell,fixture.DB): file=open(script_path,'w') file.write(script_text) file.close() - self.assertFailure(self.cmd('test',script_path,repos_path,self.url)) - self.assertEquals(self.cmd_version(repos_path),0) + self.assertFailure(self.cmd('test',repos_path,self.url,'blah blah')) + self.assertEquals(self.cmd_version(repos_path),1) self.assertEquals(self.cmd_db_version(self.url,repos_path),0) # Nonempty script using migrate_engine should succeed @@ -451,8 +392,8 @@ class TestShellDatabase(Shell,fixture.DB): file=open(script_path,'w') file.write(script_text) file.close() - self.assertSuccess(self.cmd('test',script_path,repos_path,self.url)) - self.assertEquals(self.cmd_version(repos_path),0) + self.assertSuccess(self.cmd('test',repos_path,self.url)) + self.assertEquals(self.cmd_version(repos_path),1) self.assertEquals(self.cmd_db_version(self.url,repos_path),0) @fixture.usedb() @@ -507,7 +448,7 @@ class TestShellDatabase(Shell,fixture.DB): output, exitcode = self.output_and_exitcode('python %s update_db_from_model' % script_path) self.assertEquals(output, "") self.assertEquals(self.cmd_version(repos_path),0) - self.assertEquals(self.cmd_db_version(self.url,repos_path),0) # version did not get bumped yet because new version not yet committed + self.assertEquals(self.cmd_db_version(self.url,repos_path),0) # version did not get bumped yet because new version not yet created output, exitcode = self.output_and_exitcode('python %s compare_model_to_db' % script_path) self.assertEquals(output, "No schema diffs") output, exitcode = self.output_and_exitcode('python %s create_model' % script_path) @@ -545,13 +486,12 @@ class TestShellDatabase(Shell,fixture.DB): tmp_account_rundiffs.drop() """) - # Commit the change. - upgrade_script_path = self.tmp_named('upgrade_script.py') + # Save the upgrade script. + self.assertSuccess(self.cmd('script', '--repository=%s' % repos_path, 'Desc')) + upgrade_script_path = '%s/versions/001_Desc.py' % repos_path open(upgrade_script_path, 'w').write(output) #output, exitcode = self.output_and_exitcode('python %s test %s' % (script_path, upgrade_script_path)) # no, we already upgraded the db above #self.assertEquals(output, "") - output, exitcode = self.output_and_exitcode('python %s commit %s' % (script_path, upgrade_script_path)) - self.assertEquals(output, "") output, exitcode = self.output_and_exitcode('python %s update_db_from_model' % script_path) # bump the db_version self.assertEquals(output, "") self.assertEquals(self.cmd_version(repos_path),1) diff --git a/test/versioning/test_version.py b/test/versioning/test_version.py index 1c24065..686e080 100644 --- a/test/versioning/test_version.py +++ b/test/versioning/test_version.py @@ -42,3 +42,13 @@ class TestVerNum(fixture.Base): self.assert_(VerNum(1)>=1) self.assert_(not VerNum(1)>=2) self.assert_(VerNum(2)>=1) + +class TestDescriptionNaming(fixture.Base): + def test_names(self): + self.assertEquals(strToFilename(''), '') + self.assertEquals(strToFilename('a'), 'a') + self.assertEquals(strToFilename('Abc Def'), 'Abc_Def') + self.assertEquals(strToFilename('Abc "D" Ef'), 'Abc_D_Ef') + self.assertEquals(strToFilename("Abc's Stuff"), 'Abc_s_Stuff') + self.assertEquals(strToFilename("a b"), 'a_b') +