]> scripts.mit.edu Git - wizard.git/blob - wizard/command/migrate.py
Add list-errors, fix exceptions, and fix logging bug.
[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 _command
11
12 def main(argv, baton):
13     options, args = parse_args(argv)
14     dir = args[0]
15
16     logging.debug("uid is %d" % os.getuid())
17
18     _command.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 = _command.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         htaccess = open(".git/.htaccess", "w")
95         htaccess.write("Deny from all\n")
96         htaccess.close()
97     else:
98         logging.info("# create %s containing \"%s\"" % (file, data))
99         logging.info('# create .htaccess containing "Deny from all"')
100     # configure our remote (this is merely for convenience; wizard scripts
101     # will not rely on this)
102     sh.call("git", "remote", "add", "origin", repo)
103     # configure what would normally be set up on a 'git clone' for consistency
104     sh.call("git", "config", "branch.master.remote", "origin")
105     sh.call("git", "config", "branch.master.merge", "refs/heads/master")
106     # perform the initial fetch
107     sh.call("git", "fetch", "origin")
108     # soft reset to our tag
109     sh.call("git", "reset", tag, "--")
110     # checkout the .scripts directory
111     sh.call("git", "checkout", ".scripts")
112     # commit user local changes
113     message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
114     util.set_git_env()
115     try:
116         message += "\nMigrated-by: " + util.get_operator_git()
117     except util.NoOperatorInfo:
118         pass
119     sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
120     # for verbose purposes, give us a git status and git diff
121     if options.verbose:
122         try:
123             sh.call("git", "status")
124         except shell.CallError:
125             pass
126         try:
127             sh.call("git", "diff")
128         except shell.CallError:
129             pass
130
131 class Error(_command.Error):
132     """Base exception for all exceptions raised by migrate"""
133     pass
134
135 class AlreadyMigratedError(Error):
136     def __init__(self, dir):
137         self.dir = dir
138     def __str__(self):
139         return """
140
141 ERROR: Directory already contains a .git and/or
142 .scripts directory.  Did you already migrate it?
143 """
144
145 class NotAutoinstallError(Error):
146     def __init__(self, dir):
147         self.dir = dir
148     def __str__(self):
149         return """
150
151 ERROR: Could not find .scripts-version file. Are you sure
152 this is an autoinstalled application?
153 """
154
155 class NoTagError(Error):
156     def __init__(self, version):
157         self.version = version
158     def __str__(self):
159         return """
160
161 ERROR: Could not find tag v%s-scripts in repository
162 for %s.  Double check and make sure
163 the repository was prepared with all necessary tags!
164 """ % (self.version.version, self.version.application.name)