import os import shutil import logging import errno import sys from wizard import deploy from wizard import shell from wizard import util from wizard.command import _base def main(argv, global_options): options, args = parse_args(argv) dir = args[0] logging.debug("uid is %d" % os.getuid()) _base.chdir(dir) check_if_already_migrated(options) version = calculate_version() repo = version.application.getRepository() tag = version.getScriptsTag() 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) os.rename(".scripts-version", ".scripts/old-version") # archive def parse_args(argv): usage = """usage: %prog migrate [ARGS] DIR Migrates a directory to our Git-based autoinstall format. Performs basic sanity checking and intelligently determines what repository and tag to use. This command is meant to be run as the owner of the install it is upgrading (see the scripts AFS kernel patch). Do NOT run this command as root.""" parser = _base.WizardOptionParser(usage) 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") 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): 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 calculate_version(): try: d = deploy.Deployment.fromDir(".") return d.getAppVersion() 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 make_repository(sh, options, repo, tag): sh.call("git", "init") # create repository # configure our alternates (to save space and make this quick) data = os.path.join(repo, "objects") file = ".git/objects/info/alternates" if not options.dry_run: alternates = open(file, "w") alternates.write(data) alternates.close() else: logging.info("# create %s containing \"%s\"" % (file, data)) # configure our remote (this is merely for convenience; wizard scripts # will not rely on this) sh.call("git", "remote", "add", "origin", repo) # configure what would normally be set up on a 'git clone' for consistency sh.call("git", "config", "branch.master.remote", "origin") sh.call("git", "config", "branch.master.merge", "refs/heads/master") # perform the initial fetch sh.call("git", "fetch", "origin") # soft reset to our tag sh.call("git", "reset", tag, "--") # checkout the .scripts directory sh.call("git", "checkout", ".scripts") # commit user local changes lines = ["Initial commit after migration." ,"" ,"Wizard-revision: %s" % util.get_revision() ,"Wizard-args: %s" % " ".join(sys.argv) ] try: lines.append("Migrated-by: " + util.get_operator_git()) # maybe this should go in massmigrate op_realname, op_email = util.get_operator_info() os.putenv("GIT_COMMITTER_NAME", op_realname) os.putenv("GIT_COMMITTER_EMAIL", op_email) except util.NoOperatorInfo: pass try: lockername = util.get_dir_owner(".") os.putenv("GIT_AUTHOR_NAME", "%s locker" % lockername) os.putenv("GIT_AUTHOR_EMAIL", "%s@scripts.mit.edu" % lockername) except KeyError: pass sh.call("git", "commit", "--allow-empty", "-a", "-m", "\n".join(lines)) # 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 class Error(_base.Error): """Base exception for all exceptions raised by migrate""" pass class AlreadyMigratedError(Error): 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? """ class NoRepositoryError(Error): def __init__(self, app): self.app = app def __str__(self): return """ ERROR: Could not find repository for this application. Have you converted the repository over? Is the name %s the same as the name of the .git folder? """ % self.app class NoTagError(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)