]> scripts.mit.edu Git - wizard.git/blob - wizard/command/migrate.py
Move a bunch of summary items to full class commands.
[wizard.git] / wizard / command / migrate.py
1 import os
2 import shutil
3 import logging
4 import errno
5 import sys
6
7 from wizard import command, deploy, shell, util
8
9 def main(argv, baton):
10     options, args = parse_args(argv)
11     dir = args[0]
12
13     logging.debug("uid is %d" % os.getuid())
14
15     command.chdir(dir)
16     check_if_already_migrated(options)
17
18     deployment = make_deployment() # uses chdir
19     version = deployment.app_version
20     repo    = version.application.repository
21     tag     = version.scripts_tag
22
23     os.unsetenv("GIT_DIR") # prevent some perverse errors
24
25     sh = shell.Shell(options.dry_run)
26     check_if_tag_exists(sh, repo, tag)
27     make_repository(sh, options, repo, tag)
28     make_variables(deployment, options)
29
30     if not options.dry_run:
31         deployment.updateVersion()
32         os.rename(".scripts-version", ".scripts/old-version") # archive
33     else:
34         logging.info("# create .scripts/version containing \"%s-%s\"" % (deployment.application.name, deployment.version))
35         logging.info("mv .scripts-version .scripts/old-version")
36
37 def parse_args(argv):
38     usage = """usage: %prog migrate [ARGS] DIR
39
40 Migrates a directory to our Git-based autoinstall format.
41 Performs basic sanity checking and intelligently determines
42 what repository and tag to use.
43
44 This command is meant to be run as the owner of the install
45 it is upgrading (see the scripts AFS kernel patch).  Do
46 NOT run this command as root."""
47     parser = command.WizardOptionParser(usage)
48     parser.add_option("--dry-run", dest="dry_run", action="store_true",
49             default=False, help="Prints would would be run without changing anything")
50     parser.add_option("--force", "-f", dest="force", action="store_true",
51             default=False, help="If .git or .scripts directory already exists, delete them and migrate")
52     options, args = parser.parse_all(argv)
53     if len(args) > 1:
54         parser.error("too many arguments")
55     elif not args:
56         parser.error("must specify directory")
57     return (options, args)
58
59 def check_if_already_migrated(options):
60     if os.path.isdir(".git") or os.path.isdir(".scripts"):
61         if not options.force:
62             raise AlreadyMigratedError(dir)
63         else:
64             if os.path.isdir(".git"):
65                 logging.warning("Force removing .git directory")
66                 if not options.dry_run: shutil.rmtree(".git")
67             if os.path.isdir(".scripts"):
68                 logging.warning("Force removing .scripts directory")
69                 if not options.dry_run: shutil.rmtree(".scripts")
70
71 def make_deployment():
72     try:
73         return deploy.Deployment(".")
74     except IOError as e:
75         if e.errno == errno.ENOENT:
76             raise NotAutoinstallError(dir)
77         else: raise e
78
79 def check_if_tag_exists(sh, repo, tag):
80     # check if the version we're trying to convert exists. We assume
81     # a convention here, namely, v1.2.3-scripts is what we want. If
82     # you broke the convention... shame on you.
83     try:
84         sh.call("git", "--git-dir", repo, "rev-parse", tag)
85     except shell.CallError:
86         raise NoTagError(version)
87
88 def make_repository(sh, options, repo, tag):
89     sh.call("git", "init") # create repository
90     # configure our alternates (to save space and make this quick)
91     data = os.path.join(repo, "objects")
92     file = ".git/objects/info/alternates"
93     if not options.dry_run:
94         alternates = open(file, "w")
95         alternates.write(data)
96         alternates.close()
97         htaccess = open(".git/.htaccess", "w")
98         htaccess.write("Deny from all\n")
99         htaccess.close()
100     else:
101         logging.info("# create %s containing \"%s\"" % (file, data))
102         logging.info('# create .htaccess containing "Deny from all"')
103     # configure our remote (this is merely for convenience; wizard scripts
104     # will not rely on this)
105     sh.call("git", "remote", "add", "origin", repo)
106     # configure what would normally be set up on a 'git clone' for consistency
107     sh.call("git", "config", "branch.master.remote", "origin")
108     sh.call("git", "config", "branch.master.merge", "refs/heads/master")
109     # perform the initial fetch
110     sh.call("git", "fetch", "origin")
111     # soft reset to our tag
112     sh.call("git", "reset", tag, "--")
113     # checkout the .scripts directory
114     sh.call("git", "checkout", ".scripts")
115     # commit user local changes
116     message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
117     util.set_git_env()
118     try:
119         message += "\nMigrated-by: " + util.get_operator_git()
120     except util.NoOperatorInfo:
121         pass
122     sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
123     # for verbose purposes, give us a git status and git diff
124     if options.verbose:
125         try:
126             sh.call("git", "status")
127         except shell.CallError:
128             pass
129         try:
130             sh.call("git", "diff")
131         except shell.CallError:
132             pass
133
134 def make_variables(d, options):
135     """Make .scripts/variables which contains variables based off of
136     what was regexed out of existing configuration files."""
137     variables = d.extract()
138     if not options.dry_run: f = open(".scripts/variables", "w")
139     for k,v in variables.items():
140         if v is None:
141             # once we get everything on the same version, you should
142             # actually start paying attention to these warnings
143             logging.warning("Variable %s not found" % k)
144         else:
145             logging.debug("Variable %s is %s" % (k,v))
146             if not options.dry_run:
147                 f.write("%s %s\n" % (k,v))
148             else:
149                 logging.info('# write line "%s %s" to .scripts/variables' % (k,v))
150     if not options.dry_run: f.close()
151
152 class Error(command.Error):
153     """Base exception for all exceptions raised by migrate"""
154     pass
155
156 class AlreadyMigratedError(Error):
157     def __init__(self, dir):
158         self.dir = dir
159     def __str__(self):
160         return """
161
162 ERROR: Directory already contains a .git and/or
163 .scripts directory.  Did you already migrate it?
164 """
165
166 class NotAutoinstallError(Error):
167     def __init__(self, dir):
168         self.dir = dir
169     def __str__(self):
170         return """
171
172 ERROR: Could not find .scripts-version file. Are you sure
173 this is an autoinstalled application?
174 """
175
176 class NoTagError(Error):
177     def __init__(self, version):
178         self.version = version
179     def __str__(self):
180         return """
181
182 ERROR: Could not find tag v%s-scripts in repository
183 for %s.  Double check and make sure
184 the repository was prepared with all necessary tags!
185 """ % (self.version.version, self.version.application.name)