]> scripts.mit.edu Git - wizard.git/blob - wizard/command/migrate.py
Remove rollback code (could destroy useful state), add warnings about
[wizard.git] / wizard / command / migrate.py
1 import optparse
2 import sys
3 import os
4 import shutil
5 import logging.handlers
6 import errno
7
8 from wizard import deploy
9 from wizard import shell
10 from wizard.command import _base
11
12 class Error(_base.Error):
13     """Base exception for all exceptions raised by migrate"""
14     pass
15
16 class AlreadyMigratedError(Error):
17     def __init__(self, dir):
18         self.dir = dir
19     def __str__(self):
20         return """
21
22 ERROR: Directory already contains a .git and/or
23 .scripts directory.  Did you already migrate it?
24 """
25
26 class NotAutoinstallError(Error):
27     def __init__(self, dir):
28         self.dir = dir
29     def __str__(self):
30         return """
31
32 ERROR: Could not find .scripts-version file. Are you sure
33 this is an autoinstalled application?
34 """
35
36 class NoRepositoryError(Error):
37     def __init__(self, app):
38         self.app = app
39     def __str__(self):
40         return """
41
42 ERROR: Could not find repository for this application. Have
43 you converted the repository over? Is the name %s
44 the same as the name of the .git folder?
45 """ % self.app
46
47 class NoTagError(Error):
48     def __init__(self, version):
49         self.version = version
50     def __str__(self):
51         return """
52
53 ERROR: Could not find tag v%s-scripts in repository
54 for %s.  Double check and make sure
55 the repository was prepared with all necessary tags!
56 """ % (self.version.version, self.version.application.name)
57
58 def main(argv, global_options, logger = None):
59     usage = """usage: %prog migrate [ARGS] DIR
60
61 Migrates a directory to our Git-based autoinstall format.
62 Performs basic sanity checking and intelligently determines
63 what repository and tag to use.
64
65 This command is meant to be run as the owner of the install
66 it is upgrading (see the scripts AFS kernel patch).  Do
67 NOT run this command as root."""
68     parser = _base.WizardOptionParser(usage)
69     parser.add_option("--dry-run", dest="dry_run", action="store_true",
70             default=False, help="Prints would would be run without changing anything")
71     parser.add_option("--force", "-f", dest="force", action="store_true",
72             default=False, help="If .git or .scripts directory already exists, delete them and migrate")
73     options, args, logger = parser.parse_all(argv, logger)
74     if len(args) > 1:
75         parser.error("too many arguments")
76     elif not args:
77         parser.error("must specify directory")
78     logger.debug("uid is %d" % os.getuid())
79     dir = args[0]
80     try:
81         os.chdir(dir)
82     except OSError as e:
83         if e.errno == errno.EACCES:
84             raise _base.PermissionsError(dir)
85         elif e.errno == errno.ENOENT:
86             raise _base.NoSuchDirectoryError(dir)
87         else: raise e
88     if os.path.isdir(".git") or os.path.isdir(".scripts"):
89         if not options.force:
90             raise AlreadyMigratedError(dir)
91         else:
92             if os.path.isdir(".git"):
93                 logger.warning("Force removing .git directory")
94                 if not options.dry_run: shutil.rmtree(".git")
95             if os.path.isdir(".scripts"):
96                 logger.warning("Force removing .scripts directory")
97                 if not options.dry_run: shutil.rmtree(".scripts")
98     try:
99         d = deploy.Deployment.fromDir(".")
100         version = d.getAppVersion()
101     except IOError as e:
102         if e.errno == errno.ENOENT:
103             raise NotAutoinstallError(dir)
104         else: raise e
105     # calculate the repository we'll be pulling out of
106     application = version.application
107     app = application.name
108     repo = os.path.join("/afs/athena.mit.edu/contrib/scripts/wizard/srv", app + ".git")
109     if not os.path.isdir(repo):
110         raise NoRepositoryError(app)
111     # begin the command line process
112     sh = shell.Shell(logger, options.dry_run)
113     # check if the version we're trying to convert exists. We assume
114     # a convention here, namely, v1.2.3-scripts is what we want. If
115     # you broke the convention... shame on you.
116     try:
117         tag = "v%s-scripts" % version.version
118         sh.call("git", "--git-dir", repo, "rev-parse", tag)
119     except shell.CallError:
120         raise NoTagError(version)
121     did_git_init = False
122     did_git_checkout_scripts = False
123     # create repository
124     sh.call("git", "init")
125     did_git_init = True
126     # configure our alternates (to save space and make this quick)
127     data = os.path.join(repo, "objects")
128     file = ".git/objects/info/alternates"
129     if not options.dry_run:
130         alternates = open(file, "w")
131         alternates.write(data)
132         alternates.close()
133     else:
134         logger.info("# create %s containing \"%s\"" % (file, data))
135     # configure our remote
136     # (later on, we won't actually use these ourselves, since we
137     #  might want to use an arbitrary repository)
138     sh.call("git", "remote", "add", "origin", repo)
139     # configure what would normally be set up on a 'git clone' for consistency
140     sh.call("git", "config", "branch.master.remote", "origin")
141     sh.call("git", "config", "branch.master.merge", "refs/heads/master")
142     # perform the initial fetch
143     sh.call("git", "fetch", "origin")
144     # soft reset to our tag
145     sh.call("git", "reset", tag, "--")
146     # checkout the .scripts directory
147     sh.call("git", "checkout", ".scripts")
148     did_git_checkout_scripts = True
149     # XXX: setup .scripts/version???
150     # for verbose purposes, give us a git status and git diff
151     if options.verbose:
152         try:
153             sh.call("git", "status")
154         except shell.CallError:
155             pass
156         try:
157             sh.call("git", "diff")
158         except shell.CallError:
159             pass