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