Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(554)

Side by Side Diff: tools/push-to-trunk/common_includes.py

Issue 49653002: Add push-to-trunk python port. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2013 the V8 project authors. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following
11 # disclaimer in the documentation and/or other materials provided
12 # with the distribution.
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived
15 # from this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 import os
30 import re
31 import subprocess
32 import sys
33
34 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME"
35 TEMP_BRANCH = "TEMP_BRANCH"
36 BRANCHNAME = "BRANCHNAME"
37 DOT_GIT_LOCATION = "DOT_GIT_LOCATION"
38 VERSION_FILE = "VERSION_FILE"
39 CHANGELOG_FILE = "CHANGELOG_FILE"
40 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE"
41 COMMITMSG_FILE = "COMMITMSG_FILE"
42 PATCH_FILE = "PATCH_FILE"
43
44
45 def TextToFile(text, file_name):
46 with open(file_name, "w") as f:
47 f.write(text)
48
49
50 def AppendToFile(text, file_name):
51 with open(file_name, "a") as f:
52 f.write(text)
53
54
55 def LinesInFile(file_name):
56 with open(file_name) as f:
57 for line in f:
58 yield line
59
60
61 def FileToText(file_name):
62 with open(file_name) as f:
63 return f.read()
64
65
66 def MSub(rexp, replacement, text):
67 return re.sub(rexp, replacement, text, flags=re.MULTILINE)
68
69
70 # Some commands don't like the pipe, e.g. calling vi from within the script or
71 # from subscripts like git cl upload.
72 def Command(cmd, args="", prefix="", pipe=True):
73 stdout = subprocess.PIPE if pipe else None
74 p = subprocess.Popen("%s %s %s" % (prefix, cmd, args),
Jakob Kummerow 2013/11/06 16:37:23 Have you tried using subprocess.check_output()? Th
Michael Achenbach 2013/11/07 15:56:46 Same result. Try: python -c "import subprocess; su
75 shell=True,
76 stdout=stdout)
77 p.wait()
78 if p.returncode != 0:
79 return None
80 return p.stdout.read() if pipe else 0
81
82
83 # Wrapper for side effects.
84 class SideEffectsHandler(object):
85 def Command(self, cmd, args="", prefix="", pipe=True):
86 return Command(cmd, args, prefix, pipe)
87
88 def ReadLine():
89 return sys.stdin.readline().strip()
90
91 DEFAULT_SIDE_EFFECTS_HANDLER = SideEffectsHandler()
92
93
94 class Step(object):
95 def __init__(self, text="", requires=None):
96 self._text = text
97 self._number = -1
98 self._requires = requires
99 self._side_effects_hanlder = DEFAULT_SIDE_EFFECTS_HANDLER
Jakob Kummerow 2013/11/06 16:37:23 nit: typo
Michael Achenbach 2013/11/07 15:56:46 Done.
100
101 def SetNumber(self, number):
102 self._number = number
103
104 def SetConfig(self, config):
105 self._config = config
106
107 def SetState(self, state):
108 self._state = state
109
110 def SetOptions(self, options):
111 self._options = options
112
113 def SetSideEffectHandler(self, handler):
Jakob Kummerow 2013/11/06 16:37:23 nit: please use the same spelling: if the field is
Michael Achenbach 2013/11/07 15:56:46 Done.
114 self._side_effects_hanlder = handler
115
116 def Config(self, key):
117 return self._config[key]
118
119 def Run(self):
120 assert self._number >= 0
121 assert self._config is not None
122 assert self._state is not None
123 assert self._side_effects_hanlder is not None
124 if self._requires:
125 self.RestoreIfUnset(self._requires)
126 if not self._state[self._requires]:
127 return
128 print ">>> Step %d: %s" % (self._number, self._text)
129 self.RunStep()
130
131 def RunStep(self):
Jakob Kummerow 2013/11/06 16:37:23 maybe "raise NotImplementedError" here to clarify
Michael Achenbach 2013/11/07 15:56:46 Done.
132 pass
133
134 def ReadLine(self):
135 return self._side_effects_hanlder.ReadLine()
136
137 def CommandSE(self, cmd, args="", prefix="", pipe=True):
Jakob Kummerow 2013/11/06 16:37:23 Hm... I'm not too happy with the -SE postfix. How
Michael Achenbach 2013/11/07 15:56:46 Done.
Jakob Kummerow 2013/11/08 10:43:19 Sorry if I didn't express that clearly enough; my
Michael Achenbach 2013/11/08 13:08:51 Done.
138 return self._side_effects_hanlder.Command(cmd, args, prefix, pipe)
139
140 def Git(self, args="", prefix="", pipe=True):
141 return self.CommandSE("git", args, prefix, pipe)
142
143 def Editor(self, args):
144 return self.CommandSE(os.environ["EDITOR"],
145 args, pipe=False)
Jakob Kummerow 2013/11/06 16:37:23 nit: fits on last line
Michael Achenbach 2013/11/07 15:56:46 Done.
146
147 def Die(self, msg=""):
148 if msg != "":
149 print "Error: %s" % msg
150 print "Exiting"
151 raise Exception(msg)
152
153 def Confirm(self, msg):
154 print "%s [Y/n] " % msg
Jakob Kummerow 2013/11/06 16:37:23 pro tip: if you add a comma: print "%s [Y/n] " %
Michael Achenbach 2013/11/07 15:56:46 Good to know. I'll also replace all the sys.stdout
155 answer = self.ReadLine()
156 return answer == "" or answer == "Y" or answer == "y"
157
158 def DeleteBranch(self, name):
159 git_result = self.Git("branch").strip()
160 for line in git_result.splitlines():
161 if re.match(r".*\s+%s$" % name, line):
162 msg = "Branch %s exists, do you want to delete it?" % name
163 if self.Confirm(msg):
164 if self.Git("branch -D %s" % name) is None:
165 self.Die("Deleting branch '%s' failed." % name)
166 print "Branch %s deleted." % name
167 else:
168 msg = "Can't continue. Please delete branch %s and try again." % name
169 self.Die(msg)
170
171 def Persist(self, var_name, value):
Jakob Kummerow 2013/11/06 16:37:23 Per-variable persist/restore is fine for now if yo
Michael Achenbach 2013/11/07 15:56:46 Separate CL.
172 value = value or "__EMPTY__"
Jakob Kummerow 2013/11/06 16:37:23 IIRC __EMPTY__ was a hack around the fact that Bas
Michael Achenbach 2013/11/07 15:56:46 Separate CL.
173 TextToFile(value, "%s-%s" % (self._config[PERSISTFILE_BASENAME], var_name))
174
175 def Restore(self, var_name):
176 value = FileToText("%s-%s" % (self._config[PERSISTFILE_BASENAME], var_name))
177 value = value or self.Die( "Variable '%s' could not be restored." % var_name )
Jakob Kummerow 2013/11/06 16:37:23 nit: 80col. It's probably enough to delete the sup
Michael Achenbach 2013/11/07 15:56:46 Done.
178 return "" if value == "__EMPTY__" else value
179
180 def RestoreIfUnset(self, var_name):
181 if self._state.get(var_name) is None:
182 self._state[var_name] = self.Restore(var_name)
183
184 def InitialEnvironmentChecks(self):
185 # Cancel if this is not a git checkout.
186 if not os.path.exists(self._config[DOT_GIT_LOCATION]):
187 self.Die("This is not a git checkout, this script won't work for you.")
188
189 # Cancel if EDITOR is unset or not executable.
190 if (not os.environ.get("EDITOR") or
191 Command("which", os.environ["EDITOR"]) is None):
192 self.Die("Please set your EDITOR environment variable, you'll need it.")
193
194 def CommonPrepare(self):
195 # Check for a clean workdir.
196 if self.Git("status -s -uno").strip() != "":
197 self.Die("Workspace is not clean. Please commit or undo your changes.")
198
199 # Persist current branch.
200 current_branch = ""
201 git_result = self.Git("status -s -b -uno").strip()
202 for line in git_result.splitlines():
203 if re.match(r"^##.*", line):
Jakob Kummerow 2013/11/06 16:37:23 Let's match only once here. match = re.match(r"^#
Michael Achenbach 2013/11/07 15:56:46 Done.
204 current_branch = re.match(r"^## (.+)", line).group(1)
205 break
206 self.Persist("current_branch", current_branch)
207
208 # Fetch unfetched revisions.
209 if self.Git("svn fetch") is None:
210 self.Die("'git svn fetch' failed.")
211
212 # Get ahold of a safe temporary branch and check it out.
213 if current_branch != self._config[TEMP_BRANCH]:
214 self.DeleteBranch(self._config[TEMP_BRANCH])
215 self.Git("checkout -b %s" % self._config[TEMP_BRANCH])
216
217 # Delete the branch that will be created later if it exists already.
218 self.DeleteBranch(self._config[BRANCHNAME])
219
220 def CommonCleanup(self):
221 self.RestoreIfUnset("current_branch")
222 self.Git("checkout -f %s" % self._state["current_branch"])
223 if self._config[TEMP_BRANCH] != self._state["current_branch"]:
224 self.Git("branch -D %s" % self._config[TEMP_BRANCH])
225 if self._config[BRANCHNAME] != self._state["current_branch"]:
226 self.Git("branch -D %s" % self._config[BRANCHNAME])
227
228 # Clean up all temporary files.
229 Command("rm", "-f %s*" % self._config[PERSISTFILE_BASENAME])
230
231 def ReadAndPersistVersion(self, prefix=""):
232 def ReadAndPersist(var_name, def_name):
233 if re.match(r"^#define %s\s+\d*" % def_name, line):
Jakob Kummerow 2013/11/06 16:37:23 same "match only once" comment applies here.
Michael Achenbach 2013/11/07 15:56:46 Done.
234 major = re.match(r"^#define %s\s+(\d*)" % def_name, line).group(1)
235 self.Persist("%s%s" % (prefix, var_name), major)
236 self._state["%s%s" % (prefix, var_name)] = major
237 for line in LinesInFile(self._config[VERSION_FILE]):
238 for (var_name, def_name) in [("major", "MAJOR_VERSION"),
239 ("minor", "MINOR_VERSION"),
240 ("build", "BUILD_NUMBER"),
241 ("patch", "PATCH_LEVEL")]:
242 ReadAndPersist(var_name, def_name)
243
244 def RestoreVersionIfUnset(self, prefix=""):
245 for v in ["major", "minor", "build", "patch"]:
246 self.RestoreIfUnset("%s%s" % (prefix, v))
247
248 def WaitForLGTM(self):
249 print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit "
250 "your change. (If you need to iterate on the patch or double check "
251 "that it's sane, do so in another shell, but remember to not "
252 "change the headline of the uploaded CL.")
253 answer = ""
254 while answer != "LGTM":
255 sys.stdout.write("> ")
256 answer = self.ReadLine()
257 if answer != "LGTM":
258 print "That was not 'LGTM'."
259
260 def WaitForResolvingConflicts(self, patch_file):
261 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", "
262 "or resolve the conflicts, stage *all* touched files with "
263 "'git add', and type \"RESOLVED<Return>\"")
264 answer = ""
265 while answer != "RESOLVED":
266 if answer == "ABORT":
267 self.Die("Applying the patch failed.")
268 if answer != "":
269 print "That was not 'RESOLVED' or 'ABORT'."
270 sys.stdout.write("> ")
271 answer = self.ReadLine()
272
273 # Takes a file containing the patch to apply as first argument.
274 def ApplyPatch(self, patch_file, reverse_patch=""):
275 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file)
276 if self.Git(args) is None:
277 self.WaitForResolvingConflicts(patch_file)
278
279
280 class UploadStep(Step):
281 def __init__(self):
282 Step.__init__(self, "Upload for code review.")
283
284 def RunStep(self):
285 print "Please enter the email address of a V8 reviewer for your patch: "
286 reviewer = self.ReadLine()
287 args = "cl upload -r \"%s\" --send-mail" % reviewer
288 if self.Git(args,pipe=False) is None:
289 self.Die("'git cl upload' failed, please try again.")
OLDNEW
« no previous file with comments | « no previous file | tools/push-to-trunk/push_to_trunk.py » ('j') | tools/push-to-trunk/push_to_trunk.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698