Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
uses: fedora-python/tox-github-action@master
with:
tox_env: ${{ matrix.tox_env }}
dnf_install: /usr/bin/rpmdev-bumpspec
Comment thread
hroncok marked this conversation as resolved.
strategy:
matrix:
tox_env: [py38, py39]
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ ferrypick

Apply patches from Fedora dist git to different components.

This simple tool does 3 steps:
This simple tool does these steps:

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

Requires the `rpmdev-bumpspec` tool from [rpmdevtools].

[rpmdevtools]: https://pagure.io/rpmdevtools

Usage:

Expand Down
90 changes: 82 additions & 8 deletions ferrypick.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import subprocess
import sys
import urllib.request
import shlex
from pathlib import Path

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


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


def parse_args():
Expand Down Expand Up @@ -97,15 +99,87 @@ def get_patch_content(link):
return (content, original_name)


def handle_reject(filename):
"""If the .rej file given in `filename` is "simple", run rmpdev-bumpspec

Simple means roughly that only Release lines are touched and
%changelog lines are added.

Removes the reject file if successful.
"""
changelog = None
path = Path(filename)
author = None
with path.open() as f:
for line in f:
# Find first hunk header
if line.startswith('@'):
break
for line in f:
marker = line[:1]
print(line.rstrip())
if marker == '@':
# Hunk header
continue
elif marker == ' ':
# Context
if line.strip() == '%changelog':
changelog = []
continue
elif marker in ('+', '-'):
if changelog is not None:
if marker == '-':
# Removing existing changelog - bad
return
if match := re.match(
r'\*\s+(\S+\s+){4}(?P<author>[^>]+>)',
line[1:]
):
author = match['author']
else:
changelog.append(line[1:])
elif line[1:].startswith('Release:'):
continue
else:
# Adding/removing something else - bad
return
else:
# Unknown line - bad
return
if author is None:
print('No author found in reject')
return
print(f'Rejects in {filename} look harmless')
execute(
'rpmdev-bumpspec',
'-u', author,
'-c', ''.join(changelog).strip(),
path.with_suffix(''),
check=True,
)
path.unlink()


def apply_patch(filename):
cmd = f"git am --committer-date-is-author-date --reject {filename}"
print(f"$ {cmd}")
exitcode = execute(cmd)
args = [
"git", "am", "--committer-date-is-author-date", "--reject", filename,
]
exitcode = execute(*args).returncode
if exitcode:
print(file=sys.stderr)
print(f"{cmd} failed with exit code {exitcode}", file=sys.stderr)
print(f"git am failed with exit code {exitcode}", file=sys.stderr)
print(f"Patch stored as: {filename}", file=sys.stderr)
sys.exit(exitcode)

for spec_rej in Path().glob(f'**/*.spec.rej'):
Comment thread
hroncok marked this conversation as resolved.
print(f'Processing rejects in {spec_rej}')
handle_reject(spec_rej)
if not any(Path().glob(f'**/*.rej')):
for spec in Path().glob(f'**/*.spec'):
execute("git", "add", spec.relative_to(Path()), check=True)
exitcode = execute("git", "am", "--continue").returncode

if exitcode:
sys.exit(exitcode)


def main():
Expand Down
Loading