6 from wizard import command, deploy, shell, util
9 options, args = parse_args(argv, baton)
10 dir = os.path.abspath(args[0]) if args else os.getcwd()
11 shell.drop_priviledges(dir, options.log_file)
14 sh = shell.Shell(options.dry_run)
16 logging.info("Migrating %s" % dir)
17 logging.debug("uid is %d" % os.getuid())
19 deployment = deploy.ProductionCopy(".")
21 # deal with old-style migration, remove this later
22 if os.path.isfile(".scripts/old-version") and not os.path.isfile(".scripts-version"):
23 os.rename(".scripts/old-version", ".scripts-version")
25 os.unsetenv("GIT_DIR") # prevent some perverse errors
29 raise AlreadyMigratedError(deployment.location)
30 except deploy.NotMigratedError:
32 except (deploy.CorruptedAutoinstallError, AlreadyMigratedError):
36 deployment.verifyTag(options.srv_path)
38 if options.force_version:
39 version = deployment.application.makeVersion(options.force_version)
42 deployment.verifyVersion()
43 version = deployment.app_version
44 except deploy.VersionMismatchError as e:
45 # well, we'll use that then
46 version = deployment.application.makeVersion(str(e.real_version))
47 repo = version.application.repository(options.srv_path)
48 tag = version.scripts_tag
50 sh.call("git", "--git-dir=%s" % repo, "rev-parse", tag)
51 except shell.CallError:
52 raise UnsupportedVersion(version.version)
54 with util.LockDirectory(".scripts-migrate-lock"):
57 perform_force(options)
58 make_repository(sh, options, repo, tag)
59 check_variables(deployment, options)
60 except KeyboardInterrupt:
61 # revert it; barring zany race conditions this is safe
62 if os.path.exists(".scripts"):
63 shutil.rmtree(".scripts")
64 if os.path.exists(".git"):
67 def parse_args(argv, baton):
68 usage = """usage: %prog migrate [ARGS] DIR
70 Migrates a directory to our Git-based autoinstall format.
71 Performs basic sanity checking and intelligently determines
72 what repository and tag to use.
74 This command is meant to be run as the owner of the install
75 it is upgrading (see the scripts AFS kernel patch). Do
76 NOT run this command as root."""
77 parser = command.WizardOptionParser(usage)
78 baton.push(parser, "srv_path")
79 parser.add_option("--dry-run", dest="dry_run", action="store_true",
80 default=False, help="Prints would would be run without changing anything")
81 parser.add_option("--force", "-f", dest="force", action="store_true",
82 default=False, help="If .git or .scripts directory already exists,"
83 "delete them and migrate")
84 parser.add_option("--force-version", dest="force_version",
85 default=None, help="If .scripts-version tells lies, explicitly specify"
86 "a version to migrate to.")
87 options, args = parser.parse_all(argv)
89 parser.error("too many arguments")
90 return (options, args)
92 def perform_force(options):
93 has_git = os.path.isdir(".git")
94 has_scripts = os.path.isdir(".scripts")
97 logging.warning("Force removing .git directory")
98 if not options.dry_run: backup = util.safe_unlink(".git")
99 logging.info(".git backed up to %s" % backup)
101 logging.warning("Force removing .scripts directory")
102 if not options.dry_run: backup = util.safe_unlink(".scripts")
103 logging.info(".scripts backed up to %s" % backup)
105 def make_repository(sh, options, repo, tag):
106 sh.call("git", "init") # create repository
107 # configure our alternates (to save space and make this quick)
108 data = os.path.join(repo, "objects")
109 file = ".git/objects/info/alternates"
110 if not options.dry_run:
111 alternates = open(file, "w")
112 alternates.write(data)
114 htaccess = open(".git/.htaccess", "w")
115 htaccess.write("Deny from all\n")
118 logging.info("# create %s containing \"%s\"" % (file, data))
119 logging.info('# create .htaccess containing "Deny from all"')
120 # configure our remote (this is merely for convenience; wizard scripts
121 # will not rely on this)
122 sh.call("git", "remote", "add", "origin", repo)
123 # configure what would normally be set up on a 'git clone' for consistency
124 sh.call("git", "config", "branch.master.remote", "origin")
125 sh.call("git", "config", "branch.master.merge", "refs/heads/master")
126 # perform the initial fetch
127 sh.call("git", "fetch", "origin")
128 # soft reset to our tag
129 sh.call("git", "reset", tag, "--")
130 # checkout the .scripts directory
131 sh.call("git", "checkout", ".scripts")
132 logging.info("Diffstat:\n" + sh.eval("git", "diff", "--stat"))
133 # commit user local changes
134 message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
137 message += "\nMigrated-by: " + util.get_operator_git()
138 except util.NoOperatorInfo:
140 sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
142 def check_variables(d, options):
143 """Attempt to extract variables and complain if some are missing."""
144 variables = d.extract()
145 for k,v in variables.items():
146 if v is None and k not in d.application.deprecated_keys:
147 logging.warning("Variable %s not found" % k)
149 logging.debug("Variable %s is %s" % (k,v))
151 class Error(command.Error):
152 """Base exception for all exceptions raised by migrate"""
155 class AlreadyMigratedError(Error):
157 def __init__(self, dir):
162 This autoinstall is already migrated; move along, nothing to
163 see here. (If you really want to, you can force a re-migration
164 with --force, but this will blow away the existing .git and
165 .scripts directories (i.e. your history and Wizard configuration).)
168 class UnsupportedVersion(Error):
169 def __init__(self, version):
170 self.version = version
174 ERROR: This autoinstall is presently on %s, which is unsupported by
175 Wizard. Please manually upgrade it to one that is supported,
176 and then retry the migration; usually the latest version is supported.