Skip to content

Commit fe97917

Browse files
committed
When the reject file is simple, replace it by rpmdev-bumpspec
1 parent 4bbce1b commit fe97917

6 files changed

Lines changed: 3599 additions & 9 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ ferrypick
33

44
Apply patches from Fedora dist git to different components.
55

6-
This simple tool does 3 steps:
6+
This simple tool does these steps:
77

88
1. download patch file from src.fedoraproject.org
99
2. replaces package name with current dist-git work dir package name
1010
3. runs `git am --reject` on the product
11+
4. if the rejected hunks only touch `Release` and add `%changelog` in spec
12+
files, ignore the rejects and run rpmdev-bumpspec instead
1113

1214
Usage:
1315

ferrypick.py

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import subprocess
44
import sys
55
import urllib.request
6+
import shlex
7+
from pathlib import Path
68

79
COMMIT_RE = re.compile(r"^https://src\.fedoraproject\.org/\S+/([^/\s]+)/c/([0-9a-f]+)")
810
PR_RE = re.compile(r"^https://src\.fedoraproject\.org/\S+/([^/\s]+)/pull-request/\d+")
@@ -62,9 +64,9 @@ def stdout(cmd):
6264
return subprocess.check_output(cmd, shell=True, text=True).rstrip()
6365

6466

65-
def execute(cmd):
66-
proc = subprocess.run(cmd, shell=True, text=True)
67-
return proc.returncode
67+
def execute(*cmd, **kwargs):
68+
print(f"$ {' '.join(shlex.quote(str(c)) for c in cmd)}")
69+
return subprocess.run(cmd, text=True, **kwargs)
6870

6971

7072
def parse_args():
@@ -95,15 +97,84 @@ def get_patch_content(link):
9597
return (content, original_name)
9698

9799

100+
def handle_reject(filename):
101+
"""If the .rej file given in `filename` is "simple", run rmpdev-bumpspec
102+
103+
Simple means roughly that only Release lines are touched and
104+
%changelog lines are added.
105+
106+
Removes the reject file if successful.
107+
"""
108+
changelog = None
109+
path = Path(filename)
110+
author = None
111+
with path.open() as f:
112+
for line in f:
113+
# Find first hunk header
114+
if line.startswith('@'):
115+
break
116+
for line in f:
117+
marker = line[:1]
118+
print(line.rstrip())
119+
if marker == '@':
120+
# Hunk header
121+
continue
122+
elif marker == ' ':
123+
# Context
124+
if line.strip() == '%changelog':
125+
changelog = []
126+
continue
127+
elif marker in ('+', '-'):
128+
if changelog is not None:
129+
if marker == '-':
130+
# Removing existing changelog - bad
131+
return
132+
if match := re.match(
133+
r'\*\s+(\S+\s+){4}(?P<author>[^>]+>)',
134+
line[1:]
135+
):
136+
author = match['author']
137+
else:
138+
changelog.append(line[1:])
139+
elif line[1:].startswith('Release:'):
140+
continue
141+
else:
142+
# Adding/removing something else - bad
143+
return
144+
else:
145+
# Unknown line - bad
146+
return
147+
if author is None:
148+
print('No author found in reject')
149+
return
150+
print(f'Rejects in {filename} look harmless')
151+
execute(
152+
'rpmdev-bumpspec',
153+
'-u', author,
154+
'-c', ''.join(changelog).strip(),
155+
path.with_suffix(''),
156+
check=True,
157+
)
158+
path.unlink()
159+
160+
98161
def apply_patch(filename):
99-
cmd = f"git am --reject {filename}"
100-
print(f"$ {cmd}")
101-
exitcode = execute(cmd)
162+
exitcode = execute("git", "am", "--reject", filename).returncode
102163
if exitcode:
103164
print(file=sys.stderr)
104-
print(f"{cmd} failed with exit code {exitcode}", file=sys.stderr)
165+
print(f"git am failed with exit code {exitcode}", file=sys.stderr)
105166
print(f"Patch stored as: {filename}", file=sys.stderr)
106-
sys.exit(exitcode)
167+
168+
for spec_rej in Path().glob(f'**/*.spec.rej'):
169+
print(f'Processing rejects in {spec_rej}')
170+
handle_reject(spec_rej)
171+
if not any(Path().glob(f'**/*.rej')):
172+
for spec in Path().glob(f'**/*.spec'):
173+
execute("git", "add", spec.relative_to(Path()), check=True)
174+
exitcode = execute("git", "am", "--continue").returncode
175+
176+
if exitcode:
177+
sys.exit(exitcode)
107178

108179

109180
def main():

0 commit comments

Comments
 (0)