forked from jensl/critic
-
Notifications
You must be signed in to change notification settings - Fork 2
/
upgrade.py
230 lines (187 loc) · 7.31 KB
/
upgrade.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# -*- mode: python; encoding: utf-8 -*-
#
# Copyright 2012 Jens Lindström, Opera Software ASA
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
import os
import sys
import json
import traceback
import os.path
import argparse
import installation
def convertUTF8(text):
# Check if it's already valid UTF-8 and return it unchanged if so.
try:
text.decode('utf-8')
return text
except: pass
# Try to decode as latin-1.
try: return text.decode('latin-1').encode('utf-8')
except: pass
# Fallback: just replace all non-ASCII characters with '?'.
return re.sub("[\x80-\xff]", "?", text)
parser = argparse.ArgumentParser(description="Critic upgrade script")
parser.add_argument("--etc-dir", default="/etc/critic", help="directory where the Critic system configuration is stored", action="store")
parser.add_argument("--identity", "-i", default="main", help="system identity to upgrade", action="store")
parser.add_argument("--dry-run", "-n", help="produce output but don't modify the system at all", action="store_true")
for module in installation.modules:
if hasattr(module, "add_arguments"):
module.add_arguments("install", parser)
arguments = parser.parse_args()
if os.getuid() != 0:
print """
ERROR: This script must be run as root.
"""
sys.exit(1)
def abort():
print
print "ERROR: Upgrade aborted."
print
for module in reversed(installation.modules):
try:
if hasattr(module, "undo"):
module.undo()
except:
print >>sys.stderr, "FAILED: %s.undo()" % module.__name__
traceback.print_exc()
sys.exit(1)
etc_path = os.path.join(arguments.etc_dir, arguments.identity)
if not os.path.isdir(etc_path):
print """\
%s: no such directory
Make sure the --etc-dir[=%s] and --identity[=%s] options
correctly define where the installed system's configuration is stored.""" % (etc_path, arguments.etc_dir, arguments.identity)
sys.exit(1)
sys.path.insert(0, etc_path)
try: import configuration
except ImportError:
print """\
Failed to import 'configuration' module.
Make sure the --etc-dir[=%s] and --identity[=%s] options
correctly define where the installed system's configuration is stored.""" % (etc_path, arguments.etc_dir, arguments.identity)
sys.exit(1)
install_data_path = os.path.join(configuration.paths.INSTALL_DIR, ".install.data")
if not os.path.isfile(install_data_path):
print """\
%s: no such file
This installation of Critic appears to be incomplete or corrupt.""" % install_data_path
sys.exit(1)
def deunicode(v):
if type(v) == unicode: return v.encode("utf-8")
elif type(v) == list: return map(deunicode, v)
elif type(v) == dict: return dict([(deunicode(a), deunicode(b)) for a, b in v.items()])
else: return v
try:
with open(install_data_path, "r") as install_data:
data = deunicode(json.load(install_data))
if not isinstance(data, dict): raise ValueError
except ValueError:
print """\
%s: failed to parse JSON object
This installation of Critic appears to be incomplete or corrupt.""" % install_data_path
sys.exit(1)
print """
Critic Upgrade
==============
"""
if "sha1" not in data:
try: guess_sha1 = installation.process.check_output([data["installation.prereqs.git"], "rev-parse", "HEAD@{1}"],
cwd=os.path.dirname(os.path.abspath(__file__)))
except: guess_sha1 = None
print """
The SHA-1 of the commit you initially installed was not recorded. This
means you installed a version before the install.py script was changed
to record the SHA-1 currently checked out."""
if guess_sha1:
print """
A reasonable guess is HEAD@{1}, or "where HEAD was before the last
operation that changed HEAD". Otherwise, please figure out what you
installed. If you need to guess, guessing on something too old (i.e.
a commit that is an ancestor of the actual commit) is safer than
guessing on something too recent."""
default = "HEAD@{1}"
else:
print """
Please figure out what you installed. If you need to guess, guessing
on something too old (i.e. a commit that is an ancestor of the actual
commit) is safer than guessing on something too recent."""
default = None
print """
The commit can be specified as a SHA-1 or any symbolic ref understood
by "git rev-parse".
"""
def revparse(value):
return installation.process.check_output([data["installation.prereqs.git"], "rev-parse", "--verify", value],
cwd=os.path.dirname(os.path.abspath(__file__))).strip()
def valid_commit(value):
try: sha1 = revparse(value)
except: return "not a valid ref (checked with \"git rev-parse --verify\")"
try: installation.process.check_output([data["installation.prereqs.git"], "cat-file", "commit", sha1],
cwd=os.path.dirname(os.path.abspath(__file__)))
except: return "not a commit"
sha1 = revparse(installation.input.string(prompt="What commit was originally installed?",
default=default,
check=valid_commit))
data["sha1"] = sha1
git = data["installation.prereqs.git"]
if installation.process.check_output([git, "status", "--porcelain"]).strip():
print """
ERROR: This Git repository has local modifications.
Installing from a Git repository with local changes is not supported.
Please commit or stash the changes and then try again.
"""
sys.exit(1)
try:
for module in installation.modules:
try:
if hasattr(module, "prepare") and not module.prepare("upgrade", arguments, data):
abort()
except KeyboardInterrupt:
abort()
except SystemExit:
raise
except:
print >>sys.stderr, "FAILED: %s.upgrade()" % module.__name__
traceback.print_exc()
abort()
for module in installation.modules:
try:
if hasattr(module, "upgrade") and not module.upgrade(arguments, data):
abort()
except KeyboardInterrupt:
abort()
except SystemExit:
raise
except:
print >>sys.stderr, "FAILED: %s.upgrade()" % module.__name__
traceback.print_exc()
abort()
if not arguments.dry_run:
with open(install_data_path, "w") as install_data:
json.dump(data, install_data)
for module in installation.modules:
try:
if hasattr(module, "finish"):
module.finish()
except:
print >>sys.stderr, "WARNING: %s.finish() failed" % module.__name__
traceback.print_exc()
print
print "SUCCESS: Upgrade complete!"
print
except SystemExit:
raise
except:
traceback.print_exc()
abort()