]> scripts.mit.edu Git - wizard.git/blob - wizard/command/upgrade.py
Refactor to get rid of _package.py using __import__ magic.
[wizard.git] / wizard / command / upgrade.py
1 import optparse
2 import sys
3 import os
4 import shutil
5 import logging.handlers
6 import errno
7 import tempfile
8
9 from wizard import command, deploy, shell, util
10
11 # XXX: WARNING EXPERIMENTAL DANGER DANGER WILL ROBINSON
12
13 # need errors for checking DAG integrity (if the user is on a completely
14 # different history tree, stuff is problems)
15
16 # we need an option that specifies the person making the update. Since n-b
17 # doesn't actually have a way to do this, username is probably what we want.
18 def main(argv, baton):
19     options, args = parse_args(argv)
20     dir = args[0]
21     command.chdir(dir)
22     if not os.path.isdir(".git"):
23         raise NotAutoinstallError()
24     try:
25         d = deploy.Deployment.fromDir(".")
26     except IOError as e:
27         if e.errno == errno.ENOENT:
28             raise NotAutoinstallError()
29         else: raise e
30     repo = d.application.repository
31     # begin the command line process
32     sh = shell.Shell(options.dry_run)
33     # setup environment
34     util.set_git_env()
35     # commit their changes
36     message = "Pre-commit of %s locker before autoinstall upgrade.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
37     try:
38         message += "\nPre-commit-by: " + util.get_operator_git()
39     except util.NoOperatorInfo:
40         pass
41     try:
42         sh.call("git", "commit", "-a", "-m", "Pre-upgrade commit.")
43     except shell.CallError:
44         logging.info("No changes detected")
45         pass
46     # perform fetch to update repository state
47     sh.call("git", "fetch", repo)
48     # clone their website to a temporary directory
49     temp_dir = tempfile.mkdtemp()
50     temp_wc_dir = os.path.join(temp_dir, "repo")
51     logging.info("Using temporary directory: " + temp_wc_dir)
52     sh.call("git", "clone", "--shared", ".", temp_wc_dir)
53     with util.ChangeDirectory(temp_wc_dir):
54         # reconfigure the repository path
55         sh.call("git", "remote", "add", "scripts", repo)
56         sh.call("git", "fetch", "scripts")
57         # perform the merge
58         version, _ = sh.call(["git", "--git-dir="+repo, "describe", "--tags", "master"])
59         try:
60             message = "Upgraded autoinstall in %s to %s.\n\n%s" % (util.get_dir_owner(), version, util.get_git_footer())
61             try:
62                 message += "\nUpgraded-by: " + util.get_operator_git()
63             except util.NoOperatorInfo:
64                 pass
65             sh.call("git", "merge", "-m", message, "scripts/master")
66         except shell.CallError:
67             raise MergeFailed
68     # XXX: frob .htaccess to make site inaccessible
69     # git merge (which performs a fast forward)
70     #   - merge could fail (race)
71     sh.call("git", "pull", temp_wc_dir, "master")
72     # run update script
73     sh.call(".scripts/update")
74     # XXX: frob .htaccess to make site accessible
75     # XXX:  - check if .htaccess changed, first.  Upgrade
76     #       process might have frobbed it.  Don't be
77     #       particularly worried if the segment dissappeared
78
79 def parse_args(argv):
80     usage = """usage: %prog upgrade [ARGS] DIR
81
82 Upgrades an autoinstall to the latest version.  This involves
83 updating files and running .scripts/update.
84
85 WARNING: This is still experimental."""
86     parser = command.WizardOptionParser(usage)
87     parser.add_option("--dry-run", dest="dry_run", action="store_true",
88             default=False, help="Prints would would be run without changing anything")
89     options, args = parser.parse_all(argv)
90     if len(args) > 1:
91         parser.error("too many arguments")
92     elif not args:
93         parser.error("must specify directory")
94     return options, args
95
96 class Error(command.Error):
97     """Base exception for all exceptions raised by upgrade"""
98     pass
99
100 class NotAutoinstallError(Error):
101     def __str__(self):
102         return """
103
104 ERROR: Could not find .git file. Are you sure
105 this is an autoinstalled application? Did you remember
106 to migrate it?
107 """
108
109 class MergeFailed(Error):
110     pass