8 from wizard import command, deploy, shell, util
10 def main(argv, baton):
11 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.Deployment(".")
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)
44 deployment.verifyVersion()
45 version = deployment.app_version
46 repo = version.application.repository(options.srv_path)
47 tag = version.scripts_tag
49 # XXX: turn this into a context
53 os.open(".scripts-migrate-lock", os.O_CREAT | os.O_EXCL)
55 if e.errno == errno.EEXIST:
56 raise DirectoryLockedError
57 elif e.errno == errno.EACCES:
58 raise command.PermissionsError(dir)
60 if options.force: perform_force(options)
61 make_repository(sh, options, repo, tag)
62 check_variables(deployment, options)
63 except KeyboardInterrupt:
64 # revert it; barring a particularly zany race condition
66 if os.path.exists(".scripts"):
67 shutil.rmtree(".scripts")
68 if os.path.exists(".git"):
72 os.unlink(".scripts-migrate-lock")
76 def parse_args(argv, baton):
77 usage = """usage: %prog migrate [ARGS] DIR
79 Migrates a directory to our Git-based autoinstall format.
80 Performs basic sanity checking and intelligently determines
81 what repository and tag to use.
83 This command is meant to be run as the owner of the install
84 it is upgrading (see the scripts AFS kernel patch). Do
85 NOT run this command as root."""
86 parser = command.WizardOptionParser(usage)
87 baton.push(parser, "srv_path")
88 parser.add_option("--dry-run", dest="dry_run", action="store_true",
89 default=False, help="Prints would would be run without changing anything")
90 parser.add_option("--force", "-f", dest="force", action="store_true",
91 default=False, help="If .git or .scripts directory already exists,"
92 "delete them and migrate")
93 parser.add_option("--force-version", dest="force_version",
94 default=None, help="If .scripts-version tells lies, explicitly specify"
95 "a version to migrate to.")
96 options, args = parser.parse_all(argv)
98 parser.error("too many arguments")
100 parser.error("must specify directory")
101 return (options, args)
103 def perform_force(options):
104 has_git = os.path.isdir(".git")
105 has_scripts = os.path.isdir(".scripts")
108 logging.warning("Force removing .git directory")
109 if not options.dry_run: backup = util.safe_unlink(".git")
110 logging.info(".git backed up to %s" % backup)
112 logging.warning("Force removing .scripts directory")
113 if not options.dry_run: backup = util.safe_unlink(".scripts")
114 logging.info(".scripts backed up to %s" % backup)
116 def make_repository(sh, options, repo, tag):
117 sh.call("git", "init") # create repository
118 # configure our alternates (to save space and make this quick)
119 data = os.path.join(repo, "objects")
120 file = ".git/objects/info/alternates"
121 if not options.dry_run:
122 alternates = open(file, "w")
123 alternates.write(data)
125 htaccess = open(".git/.htaccess", "w")
126 htaccess.write("Deny from all\n")
129 logging.info("# create %s containing \"%s\"" % (file, data))
130 logging.info('# create .htaccess containing "Deny from all"')
131 # configure our remote (this is merely for convenience; wizard scripts
132 # will not rely on this)
133 sh.call("git", "remote", "add", "origin", repo)
134 # configure what would normally be set up on a 'git clone' for consistency
135 sh.call("git", "config", "branch.master.remote", "origin")
136 sh.call("git", "config", "branch.master.merge", "refs/heads/master")
137 # perform the initial fetch
138 sh.call("git", "fetch", "origin")
139 # soft reset to our tag
140 sh.call("git", "reset", tag, "--")
141 # checkout the .scripts directory
142 sh.call("git", "checkout", ".scripts")
143 logging.info("Diffstat:\n" + sh.eval("git", "diff", "--stat"))
144 # commit user local changes
145 message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
148 message += "\nMigrated-by: " + util.get_operator_git()
149 except util.NoOperatorInfo:
151 sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
153 def check_variables(d, options):
154 """Attempt to extract variables and complain if some are missing."""
155 variables = d.extract()
156 for k,v in variables.items():
157 if v is None and k not in d.application.deprecated_keys:
158 logging.warning("Variable %s not found" % k)
160 logging.debug("Variable %s is %s" % (k,v))
162 class Error(command.Error):
163 """Base exception for all exceptions raised by migrate"""
166 class AlreadyMigratedError(Error):
167 def __init__(self, dir):
172 ERROR: Directory already contains a .git and
173 .scripts directory. If you force this migration,
174 both of these directories will be removed.
177 class DirectoryLockedError(Error):
178 def __init__(self, dir):
183 ERROR: Could not acquire lock on directory. Maybe there is
184 another migration process running?