/
git_tarballs
executable file
·211 lines (167 loc) · 7.26 KB
/
git_tarballs
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
#!/usr/bin/env python
#
# Copyright 2012 SUSE Linux
#
# 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 argparse
from datetime import datetime
import glob
import os
import re
import sys
import tarfile
import urllib
COMMIT_HASH_SIZE = 7
def download_tarball(url, filename):
"""Download an upstream tarball
:url: remote source of the tarball
:filename: where to save the downloaded tarball
"""
try:
urllib.urlretrieve(url, filename)
except IOError as e:
sys.exit(e)
def get_changelog_from_tarball(tar_name):
with tarfile.open(tar_name) as tar:
changelog_file = next(f for f in tar.getnames()
if f.endswith('ChangeLog'))
t = tar.getmember(changelog_file)
changelog = tar.extractfile(t).read()
return changelog
def get_parent_dir_and_version_from_tarball(tar_name):
with tarfile.open(tar_name) as tar:
try:
parent_dir = tar.firstmember.name
version = parent_dir.rsplit('-', 1)[1]
return (parent_dir, version)
except IndexError:
sys.exit("Could not figure out version from directory "
"inside tarball: " + tar.firstmember.name)
def get_upstream_commit(changelog):
try:
return re.search(r'^commit (.*?)$', changelog, re.MULTILINE).group(1)
except AttributeError:
sys.exit("Could not parse ChangeLog file.")
def parse_changelog(changelog):
"""Parse a git ChangeLog file returning an iterable of dicts
:changelog: string with the contents of the file
"""
try:
# FIXME: we can actually ignore Merge commits altogether.
return re.finditer(r'^commit (?P<commit>.*?)$'
'.*?'
'^Author:\s+(?P<author>.*?)$'
'.*?'
'^Date:\s+(?P<date>.*?)$'
'.*?'
'\n\n\s+(?P<message>.*?)$'
'(?:\n\n|\n.*?)',
changelog, re.MULTILINE | re.DOTALL)
except AttributeError:
sys.exit("Could not parse ChangeLog file.")
def get_commit_from_spec(package):
with open(package + '.spec') as f:
try:
return re.search(r'^Version:\s+.*\+git\.\d+\.(.*?)(\s+#)?$',
f.read(), flags=re.MULTILINE).group(1)
except AttributeError:
sys.exit("Could not parse version from spec file")
def package_version(upstream_version, upstream_commit):
return '%s+git.%s.%s' % (upstream_version,
datetime.utcnow().strftime('%s'),
upstream_commit[:COMMIT_HASH_SIZE])
def update_spec_file(package_version, tarball_parent_dir, filename):
for specfile in glob.glob('./*.spec'):
with open(specfile, 'r+') as f:
contents = f.read()
f.seek(0)
f.truncate()
contents = re.sub(r'\n((Version:\s+).*)\n',
r'\n\g<2>%s\n' % package_version,
contents, count=1)
contents = re.sub(r'\n((%setup\s+).*)\n',
r'\n\g<2>-q -n %s\n' % tarball_parent_dir,
contents, count=1)
contents = re.sub(r'\n((Source:\s+).*)\n',
r'\n\g<2>%s\n' % filename,
contents, count=1)
f.write(contents)
def diff_changes(changes_list, package_commit):
"""Return a list of dict changes newer than the ones in package_version
:changes_list: a list of dicts from the ChangeLog file
:package_commit: a git commit hash of the current version from the spec file
Returns an empty list if there are no newer commits.
"""
new_changes = []
for change in changes_list:
change = change.groupdict()
if change['commit'].startswith(package_commit):
break
new_changes.append(change)
return new_changes
def create_changes(changes_list, package_version, package_commit, email):
"""Return a string with the new changes for the .changes file
:changes_list: a list of dicts from the ChangeLog file
:package_version: release version string for the .changes file entry
:package_commit: a git commit hash of the current version from the spec file
:email: email address used for the .changes file entry
"""
changes_diff = diff_changes(changes_list, package_commit)
if not changes_diff:
sys.exit("There are no new changes.")
timestamp = datetime.utcnow().strftime('%a %b %e %T UTC %Y')
#commit_hash = changes_diff[0]['commit'][:COMMIT_HASH_SIZE]
# XXX Merge commits should be skipped when parsing the changelog
commits = " + " + "\n + ".join(c['message'] for c in changes_diff
if not c['message'].startswith('Merge "'))
change = (
'--------------------------------------------------------------------\n'
'%(timestamp)s - %(email)s\n'
'\n'
'- Update to version %(package_version)s:\n'
'%(commits)s\n'
'\n' % locals())
return change
def update_changes_file(package, changes):
for changes_file in glob.glob('./*.changes'):
with open(changes_file, 'r+') as f:
contents = f.read()
f.seek(0)
f.write(changes)
f.write(contents)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Git Tarballs')
parser.add_argument('--url', required=True,
help='upstream tarball URL to download')
parser.add_argument('--filename',
help='where to save the downloaded tarball')
parser.add_argument('--package',
help='the OBS package name')
parser.add_argument('--email', required=True,
help='email of the commit author (for the .changes file)')
parser.add_argument('--outdir', help='osc service parameter that does nothing')
args = parser.parse_args()
if not args.filename:
args.filename = args.url.rsplit("/", 1)[1]
if not args.package:
args.package = os.getcwd().rsplit("/", 1)[1]
download_tarball(args.url, args.filename)
changelog = get_changelog_from_tarball(args.filename)
changes_list = parse_changelog(changelog)
upstream_commit = get_upstream_commit(changelog)
tarball_parent_dir, upstream_version = get_parent_dir_and_version_from_tarball(args.filename)
package_commit = get_commit_from_spec(args.package)
package_version = package_version(upstream_version, upstream_commit)
changes = create_changes(changes_list, package_version, package_commit, args.email)
update_changes_file(args.package, changes)
update_spec_file(package_version, tarball_parent_dir, args.filename)