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