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