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