5 import logging.handlers
10 from wizard import command, deploy, shell, util
12 # XXX: WARNING EXPERIMENTAL DANGER DANGER WILL ROBINSON
14 # need errors for checking DAG integrity (if the user is on a completely
15 # different history tree, stuff is problems)
17 def main(argv, baton):
18 options, args = parse_args(argv)
21 if not os.path.isdir(".git"):
22 raise NotAutoinstallError()
24 d = deploy.Deployment(".")
26 if e.errno == errno.ENOENT:
27 raise NotAutoinstallError()
29 repo = d.application.repository
30 # begin the command line process
34 # commit their changes
35 if not options.dry_run:
36 pre_upgrade_commit(sh)
37 # perform fetch to update repository state
38 sh.call("git", "fetch", repo)
39 # clone their website to a temporary directory
40 temp_dir = tempfile.mkdtemp(prefix="wizard")
41 temp_wc_dir = os.path.join(temp_dir, "repo")
42 logging.info("Using temporary directory: " + temp_wc_dir)
43 sh.call("git", "clone", "--shared", ".", temp_wc_dir)
44 with util.ChangeDirectory(temp_wc_dir):
46 pre_upgrade_commit(sh)
47 # reconfigure the repository path
48 sh.call("git", "remote", "add", "scripts", repo)
49 sh.call("git", "fetch", "scripts")
51 version = sh.call("git", "--git-dir="+repo, "describe", "--tags", "master")[0].rstrip()
52 user_commit = sh.call("git", "rev-parse", "HEAD")[0].rstrip()
53 next_commit = sh.call("git", "rev-parse", version)[0].rstrip()
54 message = "Upgraded autoinstall in %s to %s.\n\n%s" % (util.get_dir_owner(), version, util.get_git_footer())
56 message += "\nUpgraded-by: " + util.get_operator_git()
57 except util.NoOperatorInfo:
60 # naive merge algorithm:
61 # sh.call("git", "merge", "-m", message, "scripts/master")
62 # crazy merge algorithm:
63 def make_virtual_commit(tag, parents = []):
64 """WARNING: Changes state of Git repository"""
65 sh.call("git", "checkout", tag, "--")
66 d.parametrize(temp_wc_dir)
67 for file in d.application.parametrized_files:
69 sh.call("git", "add", "--", file)
70 except shell.CallError:
72 virtual_tree = sh.call("git", "write-tree")[0].rstrip()
73 parent_args = itertools.chain(*(["-p", p] for p in parents))
74 virtual_commit = sh.call("git", "commit-tree", virtual_tree, *parent_args, input="")[0].rstrip()
75 sh.call("git", "reset", "--hard")
77 user_tree = sh.call("git", "rev-parse", "HEAD^{tree}")[0].rstrip()
78 base_virtual_commit = make_virtual_commit("v" + str(d.version))
79 next_virtual_commit = make_virtual_commit(version, [base_virtual_commit])
80 user_virtual_commit = sh.call("git", "commit-tree", user_tree, "-p", base_virtual_commit, input="")[0].rstrip()
81 sh.call("git", "checkout", user_virtual_commit, "--")
82 sh.call("git", "merge", next_virtual_commit) # XXX
83 except shell.CallError:
86 # Make it possible to resume here
87 new_tree = sh.call("git", "rev-parse", "HEAD^{tree}")[0].rstrip()
88 final_commit = sh.call("git", "commit-tree", new_tree, "-p", user_commit, "-p", next_commit, input=message)[0].rstrip()
89 sh.call("git", "checkout", "master")
90 sh.call("git", "reset", "--hard", final_commit)
91 # Till now, all of our operations were in a tmp sandbox.
93 logging.info("Dry run, bailing. See results at %s" % temp_wc_dir)
95 # XXX: frob .htaccess to make site inaccessible
96 # git merge (which performs a fast forward)
97 # - merge could fail (race)
98 sh.call("git", "pull", temp_wc_dir, "master")
100 sh.call(".scripts/update")
101 # XXX: frob .htaccess to make site accessible
102 # XXX: - check if .htaccess changed, first. Upgrade
103 # process might have frobbed it. Don't be
104 # particularly worried if the segment dissappeared
106 def pre_upgrade_commit(sh):
108 message = "Pre-commit of %s locker before autoinstall upgrade.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
110 message += "\nPre-commit-by: " + util.get_operator_git()
111 except util.NoOperatorInfo:
113 sh.call("git", "commit", "-a", "-m", message)
114 except shell.CallError:
115 logging.info("No changes detected")
118 def parse_args(argv):
119 usage = """usage: %prog upgrade [ARGS] DIR
121 Upgrades an autoinstall to the latest version. This involves
122 updating files and running .scripts/update.
124 WARNING: This is still experimental."""
125 parser = command.WizardOptionParser(usage)
126 parser.add_option("--dry-run", dest="dry_run", action="store_true",
127 default=False, help="Prints would would be run without changing anything")
128 options, args = parser.parse_all(argv)
130 parser.error("too many arguments")
132 parser.error("must specify directory")
135 class Error(command.Error):
136 """Base exception for all exceptions raised by upgrade"""
139 class NotAutoinstallError(Error):
143 ERROR: Could not find .git file. Are you sure
144 this is an autoinstalled application? Did you remember
148 class MergeFailed(Error):
152 ERROR: Merge failed. Change directory to the temporary
153 directory and manually resolve the merge.