]> scripts.mit.edu Git - wizard.git/blob - wizard/command/migrate.py
Comments, refinements and extra debugging statements.
[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     parser = _base.WizardOptionParser(usage)
65     parser.add_option("--dry-run", dest="dry_run", action="store_true",
66             default=False, help="Prints would would be run without changing anything")
67     parser.add_option("--force", "-f", dest="force", action="store_true",
68             default=False, help="If .git or .scripts directory already exists, delete them and migrate")
69     options, args, logger = parser.parse_all(argv, logger)
70     if len(args) > 1:
71         parser.error("too many arguments")
72     elif not args:
73         parser.error("must specify directory")
74     logger.debug("uid is %d" % os.getuid())
75     dir = args[0]
76     try:
77         os.chdir(dir)
78     except OSError as e:
79         if e.errno == errno.EACCES:
80             raise _base.PermissionsError(dir)
81         elif e.errno == errno.ENOENT:
82             raise _base.NoSuchDirectoryError(dir)
83         else: raise e
84     if os.path.isdir(".git") or os.path.isdir(".scripts"):
85         if not options.force:
86             raise AlreadyMigratedError(dir)
87         else:
88             if os.path.isdir(".git"):
89                 logger.warning("Force removing .git directory")
90                 if not options.dry_run: shutil.rmtree(".git")
91             if os.path.isdir(".scripts"):
92                 logger.warning("Force removing .scripts directory")
93                 if not options.dry_run: shutil.rmtree(".scripts")
94     try:
95         d = deploy.Deployment.fromDir(".")
96         version = d.getAppVersion()
97     except IOError as e:
98         if e.errno == errno.ENOENT:
99             raise NotAutoinstallError(dir)
100         else: raise e
101     # calculate the repository we'll be pulling out of
102     application = version.application
103     app = application.name
104     repo = os.path.join("/afs/athena.mit.edu/contrib/scripts/wizard/srv", app + ".git")
105     if not os.path.isdir(repo):
106         raise NoRepositoryError(app)
107     # begin the command line process
108     sh = shell.Shell(logger, options.dry_run)
109     # check if the version we're trying to convert exists. We assume
110     # a convention here, namely, v1.2.3-scripts is what we want. If
111     # you broke the convention... shame on you.
112     try:
113         tag = "v%s-scripts" % version.version
114         sh.call("git", "--git-dir", repo, "rev-parse", tag)
115     except shell.CallError:
116         raise NoTagError(version)
117     did_git_init = False
118     did_git_checkout_scripts = False
119     try:
120         # create repository
121         sh.call("git", "--git-dir=.git", "init")
122         did_git_init = True
123         # configure our alternates (to save space and make this quick)
124         data = os.path.join(repo, "objects")
125         file = ".git/objects/info/alternates"
126         if not options.dry_run:
127             alternates = open(file, "w")
128             alternates.write(data)
129             alternates.close()
130         else:
131             logger.info("# create %s containing \"%s\"" % (file, data))
132         # configure our remote
133         sh.call("git", "remote", "add", "origin", repo)
134         # configure what would normally be set up on a 'git clone' for consistency
135         sh.call("git", "config", "branch.master.remote", "origin")
136         sh.call("git", "config", "branch.master.merge", "refs/heads/master")
137         # perform the initial fetch
138         sh.call("git", "fetch", "origin")
139         # soft reset to our tag
140         sh.call("git", "reset", tag, "--")
141         # checkout the .scripts directory
142         sh.call("git", "checkout", ".scripts")
143         did_git_checkout_scripts = True
144         # XXX: setup .scripts/version???
145         # for verbose purposes, give us a git status and git diff
146         if options.verbose:
147             try:
148                 sh.call("git", "status")
149             except shell.CallError:
150                 pass
151             try:
152                 sh.call("git", "diff")
153             except shell.CallError:
154                 pass
155     except:
156         # this... is pretty bad
157         logger.critical("ERROR: Exception detected! Rolling back...")
158         if did_git_init:
159             sh.call("rm", "-Rf", ".git")
160         if did_git_checkout_scripts:
161             sh.call("rm", "-Rf", ".scripts")
162         raise