def test_repo_dir_load(self): patch_dir = os.path.join(self.tmp_dir_path, 'patch1') os.mkdir(patch_dir) with open(os.path.join(patch_dir, 'depends_on'), 'wb') as fp: fp.write('patch2\n') fp.write('patch7\n') with open(os.path.join(patch_dir, 'upgrade.sql'), 'wb') as fp: fp.write('SELECT 1\n') with open(os.path.join(patch_dir, 'downgrade.sql'), 'wb') as fp: fp.write('SELECT 2\n') patch_dir = os.path.join(self.tmp_dir_path, 'patch2') os.mkdir(patch_dir) with open(os.path.join(patch_dir, 'upgrade.sql'), 'wb') as fp: fp.write('SELECT 3\n') with open(os.path.join(patch_dir, 'downgrade.sql'), 'wb') as fp: fp.write('SELECT 4\n') patch_dir = os.path.join(self.tmp_dir_path, 'patch7') os.mkdir(patch_dir) with open(os.path.join(patch_dir, 'depends_on'), 'wb') as fp: fp.write('patch2\n') with open(os.path.join(patch_dir, 'upgrade.sql'), 'wb') as fp: fp.write('SELECT 5\n') open(os.path.join(self.tmp_dir_path, 'unrelated_file'), 'wb').close() repo_loader = DirPatchRepositoryLoader(patch_loader=DirPatchLoader()) repo = repo_loader.load_repo(self.tmp_dir_path) eq_(set(repo.patches.keys()), set(['patch1', 'patch2', 'patch7'])) patch = repo.patches['patch7'] eq_(patch.name, 'patch7') eq_(set(patch.depends_on_names), set([('patch2', False)])) eq_(patch.upgrade_sql, 'SELECT 5\n') eq_(patch.downgrade_sql, None) repo.resolve_dependencies() eq_(set(patch.depends_on), set([repo.patches['patch2']]))
def main(): parser = argparse.ArgumentParser( description='Migrate SQL schemas (and data) from one set of SQL ' 'patches to another set.') cmd_parser = parser.add_subparsers(title='migration commands', description='the list of migration operations that can be ' 'performed') init_parser = cmd_parser.add_parser('init', help='initialise the ' 'repository with the book-keeping tables needed for migration') patch_group = init_parser.add_mutually_exclusive_group() patch_group.add_argument('--patches', metavar='PATCH', nargs='+', help='list of patches that are assumed to be applied already', default=[]) patch_group.add_argument('--all-patches', help='assume that all currently ' 'known patches were applied already', action='store_true', default=False) init_parser.set_defaults(cmd_func=cmd_init) convert_init_parser = cmd_parser.add_parser('convert-init', help='convert a repository from the pre-0.15 format to the ' \ 'current repository format, detecting all applied patches') convert_init_parser.set_defaults(cmd_func=cmd_convert_init) uninit_parser = cmd_parser.add_parser('uninit', help='uninitialise the ' 'repository by removing the book-keeping tables') uninit_parser.set_defaults(cmd_func=cmd_uninit) status_parser = cmd_parser.add_parser('status', help='list which patches '\ 'are currently applied to the repository') status_parser.set_defaults(cmd_func=cmd_status) upgrade_parser = cmd_parser.add_parser('upgrade', help='upgrade the ' 'repository by applying additional SQL patches') upgrade_parser.add_argument('patches', metavar='PATCH', nargs='*', help='list of patches that will be applied (defaults to all ' 'missing patches)', default=[]) upgrade_parser.add_argument('--skip-sql', help='only modify the metadata ' 'but do not execute the SQL of the patches', action='store_true', default=False) upgrade_parser.set_defaults(cmd_func=cmd_upgrade) test_parser = cmd_parser.add_parser('test', help='test the ' 'specified SQL patch') test_parser.add_argument('patches', metavar='PATCH', nargs='*', help='list of patches that will be tested (defaults to all ' 'missing patches)', default=[]) test_parser.set_defaults(cmd_func=cmd_test) downgrade_parser = cmd_parser.add_parser('downgrade', help='downgrade the ' 'repository by reverting SQL patches') downgrade_parser.add_argument('patches', metavar='PATCH', nargs='*', help='list of patches to remove (defaults to all applied patches)', default=[]) downgrade_parser.add_argument('--skip-sql', help='only modify the metadata ' 'but do not execute the SQL of the patches', action='store_true', default=False) downgrade_parser.set_defaults(cmd_func=cmd_downgrade) renew_parser = cmd_parser.add_parser('renew', help='renew the ' 'repository by reverting and then reapplying the specified SQL ' 'patches. Additional patches that needed to be reverted will be ' 're-applied too.') renew_parser.add_argument('patches', metavar='PATCH', nargs='+', help='list of patches that will be reverted and then reapplied', default=[]) renew_parser.set_defaults(cmd_func=cmd_renew) calc_minimal_parser = cmd_parser.add_parser('calc-minimal', help='calculcate the minimal set of patches necessary to cause the ' 'listed set of patches to be applied') calc_minimal_parser.add_argument('patches', metavar='PATCH', nargs='+', help='list of patches for which the minimal set will be determined') calc_minimal_parser.set_defaults(cmd_func=cmd_calc_minimal) parser.add_argument('--add-repo', help='additional repository of patches ' 'to query', metavar='REPO', dest='repo_paths', action='append', default=[]) parser.add_argument('--simulate', help='rollback all changes afterwards', action='store_true', default=False) parser.add_argument('url', help='SQL database connection URL') options = parser.parse_args() engine = open_engine(options.url) Session = sessionmaker(bind=engine, autocommit=False) sess = Session() repo_loader = DirPatchRepositoryLoader(patch_loader=DirPatchLoader()) repo = repo_loader.load_repo(PATCH_REPO_PATH) for repo_path in options.repo_paths: override_repo = repo_loader.load_repo(repo_path) repo.patches.update(override_repo.patches) repo.resolve_dependencies() try: driver = Driver(sess, repo) options.cmd_func(options=options, repo=repo, driver=driver) if options.simulate: print >>sys.stderr, "notice: simulation option set, rolling back "\ "any changes." sess.rollback() else: sess.commit() except PatchFailedException, ex: print >>sys.stderr, "error: %s" % (ex.args[0]) if ex.details is not None: for detail in ex.details: print >>sys.stderr, "error: details: %s" % (detail) print >>sys.stderr, "notice: rolling back any changes to the database." sess.rollback() sys.exit(1)