X-Git-Url: https://scripts.mit.edu/gitweb/wizard.git/blobdiff_plain/64a47dc1d57c700fadc1d428f02cec01faabeddb..9f2c4a526259a93972135cf1e8ddc22aa2631da3:/wizard/command/migrate.py diff --git a/wizard/command/migrate.py b/wizard/command/migrate.py index e564d22..259d2e7 100644 --- a/wizard/command/migrate.py +++ b/wizard/command/migrate.py @@ -1,40 +1,71 @@ import os import shutil import logging -import errno -import sys from wizard import command, deploy, shell, util def main(argv, baton): options, args = parse_args(argv, baton) - dir = args[0] - command.chdir(dir) + if args: + dir = args[0] + else: + dir = os.getcwd() + + shell.drop_priviledges(dir, options.log_file) - shell.drop_priviledges() + util.chdir(dir) + sh = shell.Shell(options.dry_run) + logging.info("Migrating %s" % dir) logging.debug("uid is %d" % os.getuid()) - check_if_already_migrated(options) + deployment = deploy.ProductionCopy(".") - deployment = make_deployment() # uses chdir - version = deployment.app_version - repo = version.application.repository(options.srv_path) - tag = version.scripts_tag + # deal with old-style migration, remove this later + if os.path.isfile(".scripts/old-version") and not os.path.isfile(".scripts-version"): + os.rename(".scripts/old-version", ".scripts-version") os.unsetenv("GIT_DIR") # prevent some perverse errors - sh = shell.Shell(options.dry_run) - check_if_tag_exists(sh, repo, tag) - make_repository(sh, options, repo, tag) - check_variables(deployment, options) + try: + deployment.verify() + raise AlreadyMigratedError(deployment.location) + except deploy.NotMigratedError: + pass + except (deploy.CorruptedAutoinstallError, AlreadyMigratedError): + if not options.force: + raise - if not options.dry_run: - deployment.scriptsifyVersion() - os.rename(".scripts-version", ".scripts/old-version") # archive + deployment.verifyTag(options.srv_path) + + if options.force_version: + version = deployment.application.makeVersion(options.force_version) else: - logging.info("# create .scripts/version containing \"%s-%s-scripts\"" % (deployment.application.name, deployment.version)) - logging.info("mv .scripts-version .scripts/old-version") + try: + deployment.verifyVersion() + version = deployment.app_version + except deploy.VersionMismatchError as e: + # well, we'll use that then + version = deployment.application.makeVersion(str(e.real_version)) + repo = version.application.repository(options.srv_path) + tag = version.scripts_tag + try: + sh.call("git", "--git-dir=%s" % repo, "rev-parse", tag) + except shell.CallError: + raise UnsupportedVersion(version.version) + + with util.LockDirectory(".scripts-migrate-lock"): + try: + if options.force: + perform_force(options) + make_repository(sh, options, repo, tag) + check_variables(deployment, options) + except KeyboardInterrupt: + # revert it; barring zany race conditions this is safe + if os.path.exists(".scripts"): + shutil.rmtree(".scripts") + if os.path.exists(".git"): + shutil.rmtree(".git") def parse_args(argv, baton): usage = """usage: %prog migrate [ARGS] DIR @@ -51,43 +82,28 @@ NOT run this command as root.""" parser.add_option("--dry-run", dest="dry_run", action="store_true", default=False, help="Prints would would be run without changing anything") parser.add_option("--force", "-f", dest="force", action="store_true", - default=False, help="If .git or .scripts directory already exists, delete them and migrate") + default=False, help="If .git or .scripts directory already exists," + "delete them and migrate") + parser.add_option("--force-version", dest="force_version", + default=None, help="If .scripts-version tells lies, explicitly specify" + "a version to migrate to.") options, args = parser.parse_all(argv) if len(args) > 1: parser.error("too many arguments") - elif not args: - parser.error("must specify directory") return (options, args) -def check_if_already_migrated(options): - # XXX: duplicates some logic with Deployment.migrated - if os.path.isdir(".git") or os.path.isdir(".scripts"): - if not options.force: - raise AlreadyMigratedError(dir) - else: - if os.path.isdir(".git"): - logging.warning("Force removing .git directory") - if not options.dry_run: shutil.rmtree(".git") - if os.path.isdir(".scripts"): - logging.warning("Force removing .scripts directory") - if not options.dry_run: shutil.rmtree(".scripts") - -def make_deployment(): - try: - return deploy.Deployment(".") - except IOError as e: - if e.errno == errno.ENOENT: - raise NotAutoinstallError(dir) - else: raise e - -def check_if_tag_exists(sh, repo, tag): - # check if the version we're trying to convert exists. We assume - # a convention here, namely, v1.2.3-scripts is what we want. If - # you broke the convention... shame on you. - try: - sh.call("git", "--git-dir", repo, "rev-parse", tag) - except shell.CallError: - raise NoTagError(version) +def perform_force(options): + has_git = os.path.isdir(".git") + has_scripts = os.path.isdir(".scripts") + + if has_git: + logging.warning("Force removing .git directory") + if not options.dry_run: backup = util.safe_unlink(".git") + logging.info(".git backed up to %s" % backup) + if has_scripts: + logging.warning("Force removing .scripts directory") + if not options.dry_run: backup = util.safe_unlink(".scripts") + logging.info(".scripts backed up to %s" % backup) def make_repository(sh, options, repo, tag): sh.call("git", "init") # create repository @@ -116,6 +132,7 @@ def make_repository(sh, options, repo, tag): sh.call("git", "reset", tag, "--") # checkout the .scripts directory sh.call("git", "checkout", ".scripts") + logging.info("Diffstat:\n" + sh.eval("git", "diff", "--stat")) # commit user local changes message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer()) util.set_git_env() @@ -124,16 +141,6 @@ def make_repository(sh, options, repo, tag): except util.NoOperatorInfo: pass sh.call("git", "commit", "--allow-empty", "-a", "-m", message) - # for verbose purposes, give us a git status and git diff - if options.verbose: - try: - sh.call("git", "status") - except shell.CallError: - pass - try: - sh.call("git", "diff") - except shell.CallError: - pass def check_variables(d, options): """Attempt to extract variables and complain if some are missing.""" @@ -149,32 +156,25 @@ class Error(command.Error): pass class AlreadyMigratedError(Error): + quiet = True def __init__(self, dir): self.dir = dir def __str__(self): return """ -ERROR: Directory already contains a .git and/or -.scripts directory. Did you already migrate it? -""" - -class NotAutoinstallError(Error): - def __init__(self, dir): - self.dir = dir - def __str__(self): - return """ - -ERROR: Could not find .scripts-version file. Are you sure -this is an autoinstalled application? +This autoinstall is already migrated; move along, nothing to +see here. (If you really want to, you can force a re-migration +with --force, but this will blow away the existing .git and +.scripts directories (i.e. your history and Wizard configuration).) """ -class NoTagError(Error): +class UnsupportedVersion(Error): def __init__(self, version): self.version = version def __str__(self): return """ -ERROR: Could not find tag v%s-scripts in repository -for %s. Double check and make sure -the repository was prepared with all necessary tags! -""" % (self.version.version, self.version.application.name) +ERROR: This autoinstall is presently on %s, which is unsupported by +Wizard. Please manually upgrade it to one that is supported, +and then retry the migration; usually the latest version is supported. +""" % self.version