]> scripts.mit.edu Git - wizard.git/blob - wizard/command/migrate.py
Implement Wordpress installer for 2.0.2 w/ sqlalchemy.
[wizard.git] / wizard / command / migrate.py
1 import os
2 import shutil
3 import logging
4
5 from wizard import command, deploy, shell, util
6
7 def main(argv, baton):
8     options, args = parse_args(argv, baton)
9     dir = args[0]
10
11     shell.drop_priviledges(dir, options.log_file)
12
13     util.chdir(dir)
14     sh = shell.Shell(options.dry_run)
15
16     logging.info("Migrating %s" % dir)
17     logging.debug("uid is %d" % os.getuid())
18
19     deployment = deploy.ProductionCopy(".")
20
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")
24
25     os.unsetenv("GIT_DIR") # prevent some perverse errors
26
27     try:
28         deployment.verify()
29         raise AlreadyMigratedError(deployment.location)
30     except deploy.NotMigratedError:
31         pass
32     except (deploy.CorruptedAutoinstallError, AlreadyMigratedError):
33         if not options.force:
34             raise
35
36     deployment.verifyTag(options.srv_path)
37
38     if options.force_version:
39         version = deployment.application.makeVersion(options.force_version)
40     else:
41         deployment.verifyVersion()
42         version = deployment.app_version
43     repo = version.application.repository(options.srv_path)
44     tag = version.scripts_tag
45
46     with util.LockDirectory(".scripts-migrate-lock"):
47         try:
48             if options.force:
49                 perform_force(options)
50             make_repository(sh, options, repo, tag)
51             check_variables(deployment, options)
52         except KeyboardInterrupt:
53             # revert it; barring zany race conditions this is safe
54             if os.path.exists(".scripts"):
55                 shutil.rmtree(".scripts")
56             if os.path.exists(".git"):
57                 shutil.rmtree(".git")
58
59 def parse_args(argv, baton):
60     usage = """usage: %prog migrate [ARGS] DIR
61
62 Migrates a directory to our Git-based autoinstall format.
63 Performs basic sanity checking and intelligently determines
64 what repository and tag to use.
65
66 This command is meant to be run as the owner of the install
67 it is upgrading (see the scripts AFS kernel patch).  Do
68 NOT run this command as root."""
69     parser = command.WizardOptionParser(usage)
70     baton.push(parser, "srv_path")
71     parser.add_option("--dry-run", dest="dry_run", action="store_true",
72             default=False, help="Prints would would be run without changing anything")
73     parser.add_option("--force", "-f", dest="force", action="store_true",
74             default=False, help="If .git or .scripts directory already exists,"
75             "delete them and migrate")
76     parser.add_option("--force-version", dest="force_version",
77             default=None, help="If .scripts-version tells lies, explicitly specify"
78             "a version to migrate to.")
79     options, args = parser.parse_all(argv)
80     if len(args) > 1:
81         parser.error("too many arguments")
82     elif not args:
83         parser.error("must specify directory")
84     return (options, args)
85
86 def perform_force(options):
87     has_git = os.path.isdir(".git")
88     has_scripts = os.path.isdir(".scripts")
89
90     if has_git:
91         logging.warning("Force removing .git directory")
92         if not options.dry_run: backup = util.safe_unlink(".git")
93         logging.info(".git backed up to %s" % backup)
94     if has_scripts:
95         logging.warning("Force removing .scripts directory")
96         if not options.dry_run: backup = util.safe_unlink(".scripts")
97         logging.info(".scripts backed up to %s" % backup)
98
99 def make_repository(sh, options, repo, tag):
100     sh.call("git", "init") # create repository
101     # configure our alternates (to save space and make this quick)
102     data = os.path.join(repo, "objects")
103     file = ".git/objects/info/alternates"
104     if not options.dry_run:
105         alternates = open(file, "w")
106         alternates.write(data)
107         alternates.close()
108         htaccess = open(".git/.htaccess", "w")
109         htaccess.write("Deny from all\n")
110         htaccess.close()
111     else:
112         logging.info("# create %s containing \"%s\"" % (file, data))
113         logging.info('# create .htaccess containing "Deny from all"')
114     # configure our remote (this is merely for convenience; wizard scripts
115     # will not rely on this)
116     sh.call("git", "remote", "add", "origin", repo)
117     # configure what would normally be set up on a 'git clone' for consistency
118     sh.call("git", "config", "branch.master.remote", "origin")
119     sh.call("git", "config", "branch.master.merge", "refs/heads/master")
120     # perform the initial fetch
121     sh.call("git", "fetch", "origin")
122     # soft reset to our tag
123     sh.call("git", "reset", tag, "--")
124     # checkout the .scripts directory
125     sh.call("git", "checkout", ".scripts")
126     logging.info("Diffstat:\n" + sh.eval("git", "diff", "--stat"))
127     # commit user local changes
128     message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
129     util.set_git_env()
130     try:
131         message += "\nMigrated-by: " + util.get_operator_git()
132     except util.NoOperatorInfo:
133         pass
134     sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
135
136 def check_variables(d, options):
137     """Attempt to extract variables and complain if some are missing."""
138     variables = d.extract()
139     for k,v in variables.items():
140         if v is None and k not in d.application.deprecated_keys:
141             logging.warning("Variable %s not found" % k)
142         else:
143             logging.debug("Variable %s is %s" % (k,v))
144
145 class Error(command.Error):
146     """Base exception for all exceptions raised by migrate"""
147     pass
148
149 class AlreadyMigratedError(Error):
150     def __init__(self, dir):
151         self.dir = dir
152     def __str__(self):
153         return """
154
155 ERROR: Directory already contains a .git and
156 .scripts directory.  If you force this migration,
157 both of these directories will be removed.
158 """
159