import optparse import sys import os import shutil import logging.handlers import errno from wizard import deploy from wizard import shell from wizard.command import _base 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) def main(argv, global_options, logger = None): 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.""" 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, logger = parser.parse_all(argv, logger) if len(args) > 1: parser.error("too many arguments") elif not args: parser.error("must specify directory") dir = args[0] try: os.chdir(dir) except OSError as e: if e.errno == errno.EACCES: raise _base.PermissionsError(dir) elif e.errno == errno.ENOENT: raise _base.NoSuchDirectoryError(dir) else: raise e if os.path.isdir(".git") or os.path.isdir(".scripts"): if not options.force: raise AlreadyMigratedError(dir) else: if os.path.isdir(".git"): logger.warning("Force removing .git directory") if not options.dry_run: shutil.rmtree(".git") if os.path.isdir(".scripts"): logger.warning("Force removing .scripts directory") if not options.dry_run: shutil.rmtree(".scripts") try: d = deploy.Deployment.fromDir(".") version = d.getAppVersion() except IOError as e: if e.errno == errno.ENOENT: raise NotAutoinstallError(dir) else: raise e # calculate the repository we'll be pulling out of application = version.application app = application.name repo = os.path.join("/afs/athena.mit.edu/contrib/scripts/wizard/srv", app + ".git") if not os.path.isdir(repo): raise NoRepositoryError(app) # begin the command line process sh = shell.Shell(logger, options.dry_run) # 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: tag = "v%s-scripts" % version.version sh.call("git", "--git-dir", repo, "rev-parse", tag) except shell.CallError: raise NoTagError(version) did_git_init = False did_git_checkout_scripts = False try: # create repository sh.call("git", "--git-dir=.git", "init") did_git_init = True # 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: logger.info("# create %s containing \"%s\"" % (file, data)) # configure our remote 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") did_git_checkout_scripts = True # XXX: setup .scripts/version??? # 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 except: # this... is pretty bad logger.critical("ERROR: Exception detected! Rolling back...") if did_git_init: sh.call("rm", "-Rf", ".git") if did_git_checkout_scripts: sh.call("rm", "-Rf", ".scripts") raise