def prompt_save_config(config, path): # Offer to save the current configuration for future runs # Don't cache anything in batch mode (because we can't prompt to find the # user's preference). if utils.confirm("Save migration configuration to {}?\n" "This is saved in plain text and " "contains your database password.\n\n" "Answering 'y' means you do not have to specify " "the migration source or database connection " "for future runs".format(path)): save_config(config, path)
def upgrade_legacy_config(args, config, sources): for dir in reversed(sources): path = os.path.join(dir, LEGACY_CONFIG_FILENAME) if not os.path.isfile(path): continue legacy_config = read_config(path) def transfer_setting(oldname, newname, transform=None, section="DEFAULT"): try: config.get(section, newname) except configparser.NoOptionError: try: value = legacy_config.get(section, oldname) except configparser.NoOptionError: pass else: if transform: value = transform(value) config.set(section, newname, value) transfer_setting("dburi", "database") transfer_setting( "migration_table", "migration_table", lambda v: (default_migration_table if v == "None" else v), ) config_path = args.config or CONFIG_FILENAME if not args.batch_mode: if utils.confirm( "Move legacy configuration in {!r} to {!r}?".format( path, config_path)): save_config(config, config_path) try: if utils.confirm( "Delete legacy configuration file {!r}".format( path)): os.unlink(path) except OSError: logger.warn( "Could not remove %r. Manually remove this file " "to avoid future warnings", path, ) return True else: logger.warn( "Found legacy configuration in %r. Run " "yoyo in interactive mode to update your " "configuration files", path, ) try: args.database = args.database or legacy_config.get( "DEFAULT", "dburi") except configparser.NoOptionError: pass try: args.migration_table = (args.migration_table or legacy_config.get( "DEFAULT", "migration_table")) except configparser.NoOptionError: pass return False
def get_migrations(args, backend): sources = args.sources dburi = args.database if not sources: raise InvalidArgument("Please specify the migration source directory") migrations = read_migrations(*sources) if args.match: migrations = migrations.filter( lambda m: re.search(args.match, m.id) is not None ) if args.revision: targets = [m for m in migrations if args.revision in m.id] if len(targets) == 0: raise InvalidArgument( "'{}' doesn't match any revisions.".format(args.revision) ) if len(targets) > 1: raise InvalidArgument( "'{}' matches multiple revisions. " "Please specify one of {}.".format( args.revision, ", ".join(m.id for m in targets) ) ) target = targets[0] # apply: apply target an all its dependencies if args.func in {mark, apply}: deps = ancestors(target, migrations) target_plus_deps = deps | {target} migrations = migrations.filter(lambda m: m in target_plus_deps) # rollback/reapply: rollback target and everything that depends on it else: deps = descendants(target, migrations) target_plus_deps = deps | {target} migrations = migrations.filter(lambda m: m in target_plus_deps) else: if args.func in {apply, mark}: migrations = backend.to_apply(migrations) elif args.func in {rollback, reapply, unmark}: migrations = backend.to_rollback(migrations) if not args.batch_mode and not args.revision: migrations = prompt_migrations(backend, migrations, args.command_name) if ( args.batch_mode and not args.revision and not args.all and args.func is rollback ): if len(migrations) > 1: warnings.warn( "Only rolling back a single migration." "To roll back multiple migrations, " "either use interactive mode or use " "--revision or --all" ) migrations = migrations[:1] if not args.batch_mode and migrations: print("") print( "Selected", utils.plural(len(migrations), "%d migration:", "%d migrations:"), ) for m in migrations: print(" [{m.id}]".format(m=m)) prompt = "{} {} to {}".format( args.command_name.title(), utils.plural( len(migrations), "this migration", "these %d migrations" ), dburi, ) if not utils.confirm(prompt, default="y"): return migrations.replace([]) return migrations