forked from mozilla-b2g/gonk-misc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
add-revision.py
executable file
·156 lines (141 loc) · 6.25 KB
/
add-revision.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
#!/usr/bin/env python
# This simple script adds a rev attr to an android manifest file
import xml.dom.minidom
import subprocess
import sys
import os
from optparse import OptionParser
class MissingRepositoryException(Exception):
pass
def cmd(args, cwd, bufsize=8192):
"""Execute a command based on the args and current
working directory. Read up to the first 8K of output
and return the output as a strip()'d string"""
#XXX This function sucks.
proc = subprocess.Popen(args=args, cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output=proc.stdout.read(bufsize)
proc.wait()
return output.strip()
def git_op(args, path):
"""Call a command using some git specific logic, like
checking whether the directory the command is to be run
in exists. Raises a MissingRepositoryException if the
repository does not exist"""
if not os.path.isdir(path):
print >> sys.stderr, "%s (%s) was not found" % (path, os.path.abspath(path))
raise MissingRepositoryException("Missing the %s repository" % path)
return cmd(args, cwd=path)
def find_tag(path, only_annotated=False):
"""Given a path, use Git to figure out if there is a local
or annotated tag for HEAD and return it as a string"""
# Should verify that describe will only ever print the tag to stdout
cmd = ['git', 'describe', '--exact-match']
if not only_annotated:
cmd.append('--tags')
cmd.append('HEAD')
p_tag = git_op(cmd, path=path)
if len(p_tag) > 0:
return p_tag
else:
return None
def find_rev(path):
"""Given a path, use Git to figure out what the commit id is
for HEAD and return it as a string"""
# Should verify that rev-parse will only ever print the rev to stdout
return git_op(['git', 'rev-parse', 'HEAD'], path=path)
def find_ref(path, only_annotated=False):
"""Given a repository's path, return the newest tag for HEAD if the commit has
been tagged, or the commit id if it hasn't"""
tag=find_tag(path, only_annotated)
rev=find_rev(path)
if tag:
return tag
else:
return rev
def parse_manifest(man_filename):
"""Parse a manifest XML file and return a 'Document' from it.
Any <include name="another.xml"/> elements are replaced with the contents
if the specified file."""
doc = xml.dom.minidom.parse(man_filename)
for inc_node in doc.getElementsByTagName("include"):
inc_filename = inc_node.getAttribute('name')
inc_directory = os.path.dirname(os.path.realpath(man_filename));
inc_path = os.path.join(inc_directory, inc_filename)
# parse the included xml
inc_doc = parse_manifest(inc_path)
# add all the elements found in the included manifest
m = inc_doc.getElementsByTagName("manifest")[0]
for c in m.childNodes:
# prevent insertBefore() from deleting c from its parent because
# that modifies m.childNodes and the loop gets screwed up
c.parentNode = None
# insert right before the <include .../> element
inc_node.parentNode.insertBefore(c, inc_node)
# remove the <include .../> element itself
inc_node.parentNode.removeChild(inc_node)
return doc
def add_revision(man_filename, b2g_root, output, force=False, tags=False, only_annotated=False):
"""Take a string that is a filename to the source manifest, the root
of the repository tree and write a copy of the xml manifest to the file
'output' that has revisions. If tags is set to true, prepend each project
node with a comment node that contains the name of the repository and the
tag for that repository. Specifying force=True will cause revisions in the
original manifest to be overwritten with computed ones"""
doc = parse_manifest(man_filename)
for project in doc.getElementsByTagName("project"):
if project.getAttribute('revision') and not force:
pass
else:
manifest_path = project.getAttribute('path')
fs_path = os.path.join(b2g_root, manifest_path)
commit_id = find_rev(fs_path)
project.setAttribute('revision', commit_id)
parentNode = project.parentNode
tag = find_ref(fs_path, only_annotated)
if tags and tag != commit_id:
comment = " Information: %s is tagged with %s " % (project.getAttribute('name'), tag)
parentNode.insertBefore(doc.createComment(comment), project)
if hasattr(output, 'write'):
doc.writexml(output)
else:
with open(output, 'w+b') as of:
doc.writexml(of)
def main():
parser = OptionParser()
parser.add_option("--b2g-path", dest="b2g_path",
help="path to root of the b2g repository tree",
default=os.getcwd())
parser.add_option("--force", dest="force",
help="force changes to revision attributes",
action="store_true")
parser.add_option("-o", "--output", dest="output",
help="output file to write new manifest to",
default=None)
parser.add_option("--stdio", dest="stdio",
help="print new manifest to stdio instead of file",
action="store_true")
parser.add_option("-t", "--tags", dest="tags",
help="attempt to resolve commit ids to tags",
action="store_true")
parser.add_option("--only-annotated", dest="only_annotated",
help="only use annotated tags when resolving tags",
action="store_true")
(options, args) = parser.parse_args()
if options.output and options.stdio:
parser.error("use one of --output, -o or --stdio")
elif not options.output and not options.stdio:
parser.error("must specify --output or --stdio")
if not os.path.isdir(options.b2g_path):
parser.error("b2g path is not a valid and existing directory")
if len(args) != 1:
parser.error("specify one manifest")
add_revision(args[0],
options.b2g_path,
sys.stdout if options.stdio else options.output,
options.force,
options.tags,
only_annotated=options.only_annotated)
if __name__=="__main__":
main()