Skip to content

Commit

Permalink
Initial project
Browse files Browse the repository at this point in the history
  • Loading branch information
hroncok committed Jun 1, 2020
1 parent 743d5bd commit 4e122ab
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 0 deletions.
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
ferrypicker
===========

Apply patches from Fedora dist git to different components.

This simple tool does 3 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

Usage:

```shell
[python36 (f32 %)]$ git switch -c f32-backport
Switched to a new branch 'f32-backport'

[python36 (f32-backport %)]$ ferrypick https://src.fedoraproject.org/rpms/python3.6/pull-request/2
Downloading https://src.fedoraproject.org/rpms/python3.6/pull-request/2.patch
$ git am --reject /tmp/tmp7pa062j6.patch
Applying: Fix python3-config --configdir
Checking patch 00102-lib64.patch...
.git/rebase-apply/patch:26: new blank line at EOF.
+
Checking patch 00205-make-libpl-respect-lib64.patch...
Checking patch python36.spec...
error: while searching for:
#global prerel ...
%global upstream_version %{general_version}%{?prerel}
Version: %{general_version}%{?prerel:~%{prerel}}
Release: 4%{?dist}
License: Python



error: patch failed: python36.spec:17
error: while searching for:
# ======================================================

%changelog
* Wed May 06 2020 Miro Hrončok <[email protected]> - 3.6.10-4
- Rename from python36 to python3.6


error: patch failed: python36.spec:1535
Applied patch 00102-lib64.patch cleanly.
Applied patch 00205-make-libpl-respect-lib64.patch cleanly.
Applying patch python36.spec with 2 rejects...
Rejected hunk #1.
Hunk #2 applied cleanly.
Hunk #3 applied cleanly.
Rejected hunk #4.
Patch failed at 0001 Fix python3-config --configdir
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
```
Enjoy.
76 changes: 76 additions & 0 deletions ferrypick.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import contextlib
import os.path
import re
import shutil
import subprocess
import sys
import tempfile
import urllib.request

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+")
REPLACE_SUFFIXES = "spec", "rpmlintrc"


def parse_link(link):
"""
For a given pagure link, return package name and the patch link.
Raise ValueError if not recognized.
"""
for regex in COMMIT_RE, PR_RE:
if match := regex.match(link):
return match.group(1), match.group(0) + ".patch"
raise ValueError("Unrecognized link")


def rename(bytesline, original_name, current_name):
"""
On a given bytes line, naïvely replace original package name with current package name.
Works on pkgname.spec and pkgname.rpmlintrc only (as defined in REPLACE_SUFFIXES).
"""
if original_name != current_name:
for suffix in REPLACE_SUFFIXES:
for prefix in "a", "b": # this is what git does
original = f"{prefix}/{original_name}.{suffix}".encode("utf-8")
current = f"{prefix}/{current_name}.{suffix}".encode("utf-8")
bytesline = bytesline.replace(original, current)
return bytesline


@contextlib.contextmanager
def patch(link, original_name, current_name):
print(f"Downloading {link}")
with tempfile.NamedTemporaryFile(suffix=".patch") as tmp_file:
with urllib.request.urlopen(link) as response:
for line in response:
tmp_file.write(rename(line, original_name, current_name))
tmp_file.flush()
yield tmp_file.name


def stdout(cmd):
return subprocess.check_output(cmd, shell=True, text=True).rstrip()


def execute(cmd):
return subprocess.run(cmd, shell=True, text=True)


def main():
# TODO?: Add more sophisticated argument parsing
if len(sys.argv) < 2:
sys.exit(f"Usage: {sys.argv[0]} COMMIT_OR_PR_LINK [CURRENT_PKGNAME]")
link = sys.argv[1]
try:
current_name = sys.argv[2]
except IndexError:
current_name = os.path.basename(stdout("git rev-parse --show-toplevel"))

original_name, patch_link = parse_link(link)
with patch(patch_link, original_name, current_name) as p:
print(f"$ git am --reject {p}")
execute(f"git am --reject {p}")


if __name__ == "__main__":
main()
42 changes: 42 additions & 0 deletions tests/a0928446.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
From a0928446f85cfccf3c2fe26a2726502931b91f76 Mon Sep 17 00:00:00 2001
From: Tomas Orsava <[email protected]>
Date: May 26 2020 17:16:15 +0000
Subject: rpmlint: Small fixes


---

diff --git a/python3.8.rpmlintrc b/python3.8.rpmlintrc
index 013f1e9..b97e76f 100644
--- a/python3.8.rpmlintrc
+++ b/python3.8.rpmlintrc
@@ -4,7 +4,7 @@ addFilter(r'crypto-policy-non-compliance-openssl')


# TESTS:
-addFilter(r'(zero-length|pem-certificate|uncompressed-zip) /usr/lib(64)?/python3.\d+/test')
+addFilter(r'(zero-length|pem-certificate|uncompressed-zip) /usr/lib(64)?/python3\.\d+/test')


# OTHER DELIBERATES:
@@ -65,7 +65,7 @@ addFilter(r'^python3(\.\d+)?-debuginfo\.[^:]+: (E|W): useless-provides debuginfo
addFilter(r'library-without-ldconfig-post')

# debug package contains devel and non-devel files
-addFilter(r'python3(\.\d+)?-debug.[^:]+: (E|W): (non-)?devel-file-in-(non-)?devel-package')
+addFilter(r'python3(\.\d+)?-debug\.[^:]+: (E|W): (non-)?devel-file-in-(non-)?devel-package')

# this goes to other subpackage, hence not actually dangling, the read error is bogus
addFilter(r'dangling-relative-symlink /usr/lib(64)?/pkgconfig/python-3\.\d+dm?(-embed)?\.pc python-3\.\d+(-embed)?\.pc')
@@ -80,8 +80,8 @@ addFilter(r'macro-in-comment %\{_pyconfig(32|64)_h\}')

# Python modules don't need to be linked against libc
# Since 3.8 they are no longer linked against libpython3.8.so.1.0
-addFilter(r'E: library-not-linked-against-libc /usr/lib(64)?/python3.\d+/lib-dynload/')
-addFilter(r'E: shared-lib-without-dependency-information /usr/lib(64)?/python3.\d+/lib-dynload/')
+addFilter(r'E: library-not-linked-against-libc /usr/lib(64)?/python3\.\d+/lib-dynload/')
+addFilter(r'E: shared-lib-without-dependency-information /usr/lib(64)?/python3\.\d+/lib-dynload/')

# SPELLING ERRORS
addFilter(r'spelling-error .* en_US (bytecode|pyc|filename|tkinter|namespaces|pytest) ')

59 changes: 59 additions & 0 deletions tests/test_ferrypick.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pytest
from pathlib import Path

import ferrypick


U = "https://src.fedoraproject.org"


def test_parse_link_pr():
n, p = ferrypick.parse_link(f"{U}/rpms/python-rpm-macros/pull-request/62")
assert p == f"{U}/rpms/python-rpm-macros/pull-request/62.patch"
assert n == "python-rpm-macros"


def test_parse_link_commit():
hash = "f54cef86717adf4f5374820c3d5314f75b340b8b"
n, p = ferrypick.parse_link(f"{U}/rpms/python3.7/c/{hash}?branch=master")
assert p == f"{U}/rpms/python3.7/c/{hash}.patch"
assert n == "python3.7"


def test_parse_link_commit_from_pr():
hash = "6697c4ae608728bce1025ef45af"
n, p = ferrypick.parse_link(f"{U}/fork/ca/rpms/python3.7/c/{hash}?branch=rename")
assert p == f"{U}/fork/ca/rpms/python3.7/c/{hash}.patch"
assert n == "python3.7"


def test_parse_link_bad():
with pytest.raises(ValueError):
ferrypick.parse_link(f"{U}/fork/ca/rpms/python3.7/commits/rename")


def test_rename_git_diff_spec():
line = b"diff --git a/python3.7.spec b/python3.7.spec"
new = ferrypick.rename(line, "python3.7", "python37")
assert new == b"diff --git a/python37.spec b/python37.spec"


def test_rename_git_diff_rpmlintrc():
line = b"+++ b/python3.rpmlintrc"
new = ferrypick.rename(line, "python3", "python3.9")
assert new == b"+++ b/python3.9.rpmlintrc"


def test_rename_git_diff_random_occurrence():
line = b" # remember to update the python3-docs package as well"
new = ferrypick.rename(line, "python3-docs", "python-docs")
assert new == line


def test_patch_real():
link = f"{U}/rpms/python3.9/c/a0928446.patch"
original_name = "python3.9"
current_name = "python3.8"
expected = Path(__file__).parent / "a0928446.patch"
with ferrypick.patch(link, original_name, current_name) as patch:
assert Path(patch).read_text() == expected.read_text()

0 comments on commit 4e122ab

Please sign in to comment.