]> scripts.mit.edu Git - wizard.git/blob - wizard/command/upgrade.py
Improve upgrade dry run, improve error messages.
[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 def main(argv, baton):
17     options, args = parse_args(argv)
18     dir = args[0]
19     command.chdir(dir)
20     if not os.path.isdir(".git"):
21         raise NotAutoinstallError()
22     try:
23         d = deploy.Deployment(".")
24     except IOError as e:
25         if e.errno == errno.ENOENT:
26             raise NotAutoinstallError()
27         else: raise e
28     repo = d.application.repository
29     # begin the command line process
30     sh = shell.Shell()
31     # setup environment
32     util.set_git_env()
33     # commit their changes
34     if not options.dry_run:
35         pre_upgrade_commit(sh)
36     # perform fetch to update repository state
37     sh.call("git", "fetch", repo)
38     # clone their website to a temporary directory
39     temp_dir = tempfile.mkdtemp()
40     temp_wc_dir = os.path.join(temp_dir, "repo")
41     logging.info("Using temporary directory: " + temp_wc_dir)
42     sh.call("git", "clone", "--shared", ".", temp_wc_dir)
43     with util.ChangeDirectory(temp_wc_dir):
44         if options.dry_run:
45             pre_upgrade_commit(sh)
46         # reconfigure the repository path
47         sh.call("git", "remote", "add", "scripts", repo)
48         sh.call("git", "fetch", "scripts")
49         # perform the merge
50         version, _ = sh.call("git", "--git-dir="+repo, "describe", "--tags", "master")
51         try:
52             message = "Upgraded autoinstall in %s to %s.\n\n%s" % (util.get_dir_owner(), version, util.get_git_footer())
53             try:
54                 message += "\nUpgraded-by: " + util.get_operator_git()
55             except util.NoOperatorInfo:
56                 pass
57             # naive merge algorithm:
58             sh.call("git", "merge", "-m", message, "scripts/master")
59             # crazy merge algorithm:
60             #original_scripts_tag = d # something here
61             #sh.call("git", "checkout", original_scripts_tag)
62         except shell.CallError:
63             print temp_wc_dir
64             raise MergeFailed
65     if options.dry_run:
66         logging.info("Dry run, bailing")
67         return
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 pre_upgrade_commit(sh):
80     try:
81         message = "Pre-commit of %s locker before autoinstall upgrade.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
82         try:
83             message += "\nPre-commit-by: " + util.get_operator_git()
84         except util.NoOperatorInfo:
85             pass
86         sh.call("git", "commit", "-a", "-m", message)
87     except shell.CallError:
88         logging.info("No changes detected")
89         pass
90
91 def parse_args(argv):
92     usage = """usage: %prog upgrade [ARGS] DIR
93
94 Upgrades an autoinstall to the latest version.  This involves
95 updating files and running .scripts/update.
96
97 WARNING: This is still experimental."""
98     parser = command.WizardOptionParser(usage)
99     parser.add_option("--dry-run", dest="dry_run", action="store_true",
100             default=False, help="Prints would would be run without changing anything")
101     options, args = parser.parse_all(argv)
102     if len(args) > 1:
103         parser.error("too many arguments")
104     elif not args:
105         parser.error("must specify directory")
106     return options, args
107
108 class Error(command.Error):
109     """Base exception for all exceptions raised by upgrade"""
110     pass
111
112 class NotAutoinstallError(Error):
113     def __str__(self):
114         return """
115
116 ERROR: Could not find .git file. Are you sure
117 this is an autoinstalled application? Did you remember
118 to migrate it?
119 """
120
121 class MergeFailed(Error):
122     def __str__(self):
123         return """
124
125 ERROR: Merge failed.  Change directory to the temporary
126 directory and manually resolve the merge.
127 """