-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfptool
executable file
·255 lines (209 loc) · 10.7 KB
/
fptool
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#!/usr/bin/python3
import os, sys, glob
import functools
import subprocess
import argparse
import pipes
check_output = functools.partial(subprocess.check_output, universal_newlines=True)
def warning(*args, **kargs):
print("[warning]", *args, file=sys.stderr, **kargs)
def error(*args, **kargs):
print(file=sys.stderr)
print(*args, file=sys.stderr, **kargs)
print(file=sys.stderr)
if error:
sys.exit(1)
class Application:
def __init__(self):
self._commit = []
self._rollback = []
def notice(self, *args, **kargs):
if self.options.verbose:
print("[notice]", *args, file=sys.stderr, **kargs);
def run(self, command, method=subprocess.check_call):
if self.options.verbose:
print("[run] command={}".format(command), file=sys.stderr)
return method(command, universal_newlines=True)
def plan(self, command, rollback=False):
command = [arg for arg in command if arg is not None]
self.notice("{} command: {}".format("rollback" if rollback else "transaction", command))
commands = self._rollback if rollback else self._commit
commands.append(command)
def commit(self):
if self.options.pretend:
self.notice("Plan not commited due to '--pretend' option.")
self.notice("Printing commands...")
print('--------------------')
else:
self.notice("Commiting plan...")
while self._commit:
command = self._commit[0]
if self.options.pretend:
print(" ".join(pipes.quote(arg) for arg in command))
else:
self.run(command)
del self._commit[0]
if self.options.pretend:
print('--------------------')
self.notice("Printing rollback commands...")
print('--------------------')
for command in self._rollback:
print(" ".join(pipes.quote(arg) for arg in command))
print('--------------------')
self.notice("Done.")
def rollback(self):
self.notice("Rolling back.")
self.notice("Couldn't finish the following commands:")
self.notice("-"*79)
for command in self._commit:
self.notice(" ".join(pipes.quote(arg) for arg in command))
self.notice("-"*79)
for command in self._rollback:
try:
self.run(command)
except subprocess.CalledProcessError:
pass
def main(self):
parser = argparse.ArgumentParser(description="Perform common fedora packaging tasks.")
parser.add_argument("--pretend", "-p", action="store_true", help="Do not perform any changes.")
parser.add_argument("--quiet", "-q", dest="verbose", action="store_false", help="Do not print notices")
parser.add_argument("--fedpkg", default="fedpkg", help="Path to fedpkg tool or an alternative.")
parser.add_argument("--packages", "--package", default="", help="Perform changes on packages.")
parser.add_argument("--master", "-m", action="store_true", help="Perform changes on the master branch.")
parser.add_argument("--branches", "--branch", default="", help="Perform changes on branches.")
parser.add_argument("--rhbug", help="Bugzilla bug identifier.")
parser.add_argument("--dist", help="Override '--dist' argument passed to 'fedpkg'.")
parser.add_argument("--private-branch", action="store_true", help="Check out a private branch.")
parser.add_argument("--bump", nargs='?', const='', help="Bump release and create changelog entry. Optionally also switch upstream version.")
parser.add_argument("--scratch-build", action="store_true", help="Perform a scratch build using a locally built source RPM.")
parser.add_argument("--commit", action="store_true", help="Create a commit.")
parser.add_argument("--merge", action="store_true", help="Merge commits from master to branches.")
parser.add_argument("--push", action="store_true", help="Push a commit.")
parser.add_argument("--build", action="store_true", help="Create a build.")
parser.add_argument("--update", action="store_true", help="Create an update.")
parser.add_argument("--stable", action="store_true", help="Request update stabilization.")
parser.add_argument("--stay", dest="switch_back", action="store_false", help="Don't switch back to master.")
# update types
parser.add_argument("--type", default="enhancement", help="Type of update (enhancement, bugfix, security).")
parser.add_argument("--comment", help="Bump or pdate comment.")
self.options = parser.parse_args()
# select package and enter its directory
self.orig_pwd = os.getcwd()
self.packages = [package.strip() for package in self.options.packages.split(",")]
self.packages = [package for package in self.packages if package]
print(self.packages)
if self.packages:
package = self.packages[0]
os.chdir(os.path.join(os.environ["HOME"], "fedora", package))
# check the environment and postprocess arguments
self.notice("Current directory: {!r}.".format(os.getcwd()))
specfiles = glob.glob("*.spec")
if len(specfiles) == 0:
error("Specfile not found.")
elif len(specfiles) == 1:
self.specfile = specfiles[0]
self.notice("Specfile found: {}".format(self.specfile))
else:
error("More than one specfiles found.")
# Usually only needed for packages that are not yet in Fedora
self.module_name = os.path.split(os.getcwd())[-1]
if not os.path.isdir(".git"):
self.notice("Git working directory not detected.")
self.orig_branch = self.run(['git', 'symbolic-ref', '--short', 'HEAD'], method=check_output).strip()
self.branches = [branch.strip() for branch in self.options.branches.split(",")]
self.branches = [branch for branch in self.branches if branch]
if not self.options.master and not self.branches:
error("You need to specify a branch using '--master' or '--branches'.")
# Plan commands on master branch
if self.options.master:
self.process_branch(None)
# Plan commands on branches
if self.branches:
for branch in self.branches:
self.process_branch(branch)
self.run(['git', 'checkout', self.orig_branch])
if self.options.switch_back:
self.plan(['git', 'checkout', self.orig_branch])
self.plan(['git', 'checkout', self.orig_branch], rollback=True)
try:
self.commit()
except:
self.rollback()
finally:
os.chdir(self.orig_pwd)
def pkg(self, command):
result = [self.options.fedpkg]
if self.module_name:
result.append('--module-name=' + self.module_name)
result += command
return result
def process_branch(self, branch):
# We used to use `fedpkg switch-branch` but it doesn't cope with
# situations that `git checkout` handles like a small change
# unintentionally performed on another branch.
self.plan(['git', 'checkout', branch or 'master'])
dist = self.options.dist or branch or 'master'
try:
verrel = self.run(self.pkg(["--dist="+dist, "verrel"]), method=check_output).strip()
self.notice("Package name and version reported by fedpkg is '{}'.".format(verrel))
except subprocess.CalledProcessError:
warning("Failed to run 'fedpkg verrel'. Make sure your working directory is a Fedora package directory and that you installed all necessary tools.")
if self.options.private_branch:
if not self.options.rhbug:
error("Cannot use '--private-branch' without '--rhbug'.")
private = "private-{}-rh{}".format(branch, self.options.rhbug)
if self.run(['git', 'show-ref', 'heads/'+private], method=subprocess.call) == 0:
error("Branch already exists. You can remove it using the following command.\n\n git branch --delete --force {}".format(private))
self.plan(['git', 'checkout', branch], rollback=True)
self.plan(['git', 'branch', '--delete', '--force', private], rollback=True)
self.plan(['git', 'checkout', '-b', private])
# From now on we need to perform all actions on the private branch.
branch = private
if self.options.bump is not None:
args = ['rpmdev-bumpspec', self.specfile]
if self.options.bump:
args += ['--new', self.options.bump, '--comment', 'New version {}'.format(self.options.bump)]
elif self.options.private_branch:
args.append('--rightmost')
self.plan(['sed', '-r', '-i', 's/^(Release:[^%]*)(.rh{0})?%/\\1.rh{0}%/'.format(self.options.rhbug), self.specfile])
if self.options.comment:
args += ['--comment', self.options.comment]
self.plan(args)
# Use the newly written specfile to download sources
if self.options.bump:
self.commit()
lines = self.run(['spectool', '-l', self.specfile], method=subprocess.check_output).split('\n')
paths = [line.split('/')[-1] for line in lines if '/' in line]
if self.options.scratch_build:
self.plan(self.pkg(['--dist='+dist, 'build', '--scratch', '--srpm']))
if self.options.commit:
self.plan(self.pkg(['commit', '-c']))
if self.options.merge:
self.plan(['git', 'merge', '--ff-only', 'master'])
if self.options.push:
self.plan(['git', 'push', '--force', '--set-upstream', 'origin', branch])
if self.options.build:
self.plan(self.pkg(['build']))
if branch is not None:
if self.options.update:
default_comment = {
'enhancement': "Enhancement update.",
'bugfix': "Bug fix update.",
'security': "Security update.",
}
self.plan([
'bodhi',
'--new',
'--type={}'.format(self.options.type),
'--bugs={}'.format(self.options.rhbug or ""),
'--notes={}'.format(self.options.comment or default_comment[self.options.type]),
'--close-bugs',
verrel])
if self.options.stable:
self.plan([
'bodhi',
verrel,
'--request',
'stable'])
if __name__ == '__main__':
Application().main()