mirror of
https://github.com/funkemunky/KDE-x86_64-v4-Fedora.git
synced 2026-05-31 09:01:56 +00:00
Adding srpm build process
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
FEDORA_BRANCH ?= f43
|
||||||
|
FEDORA_DIST ?= .fc43
|
||||||
|
FEDORA_NAMESPACE ?= rpms
|
||||||
|
LOOKASIDE_BASEURL ?= https://src.fedoraproject.org/repo/pkgs
|
||||||
|
PYTHON ?= python3
|
||||||
|
REPO_ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..)
|
||||||
|
SPEC_REF := $(strip $(if $(spec),$(spec),$(COPR_PACKAGE)))
|
||||||
|
|
||||||
|
.PHONY: srpm
|
||||||
|
|
||||||
|
srpm:
|
||||||
|
test -n "$(SPEC_REF)"
|
||||||
|
test -n "$(outdir)"
|
||||||
|
mkdir -p "$(outdir)"
|
||||||
|
cd "$(REPO_ROOT)" && "$(PYTHON)" ci/copr-distgit-make-srpm.py \
|
||||||
|
--spec-ref "$(SPEC_REF)" \
|
||||||
|
--outdir "$(outdir)" \
|
||||||
|
--branch "$(FEDORA_BRANCH)" \
|
||||||
|
--dist "$(FEDORA_DIST)" \
|
||||||
|
--namespace "$(FEDORA_NAMESPACE)" \
|
||||||
|
--lookaside-baseurl "$(LOOKASIDE_BASEURL)"
|
||||||
@@ -1,359 +1,136 @@
|
|||||||
name: Build Fedora x86_64-v3 RPMs (Gitea)
|
name: Validate Fedora x86_64-v3 Copr SRPMs (Gitea)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
package_input:
|
||||||
|
description: "Optional comma, space, or newline separated package list. Leave blank to use packages.txt."
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'packages.txt'
|
- '.copr/Makefile'
|
||||||
- 'ci/assemble-dnf-repo.py'
|
|
||||||
- '.gitea/workflows/build-v3-rpms.yml'
|
- '.gitea/workflows/build-v3-rpms.yml'
|
||||||
|
- 'ci/copr-distgit-make-srpm.py'
|
||||||
|
- 'packages.txt'
|
||||||
|
- 'packaging/copr-rpm-macros-x86-64-v3.spec'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
prepare:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
outputs:
|
||||||
image: ghcr.io/funkemunky/kde-x86_64-v4-fedora-rpm-builder:latest
|
matrix: ${{ steps.select.outputs.matrix }}
|
||||||
strategy:
|
package_count: ${{ steps.select.outputs.package_count }}
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
arch: [x86-64-v3]
|
|
||||||
shard:
|
|
||||||
- "001"
|
|
||||||
- "002"
|
|
||||||
- "003"
|
|
||||||
- "004"
|
|
||||||
- "005"
|
|
||||||
- "006"
|
|
||||||
- "007"
|
|
||||||
- "008"
|
|
||||||
- "009"
|
|
||||||
- "010"
|
|
||||||
- "011"
|
|
||||||
- "012"
|
|
||||||
- "013"
|
|
||||||
- "014"
|
|
||||||
- "015"
|
|
||||||
- "016"
|
|
||||||
- "017"
|
|
||||||
- "018"
|
|
||||||
- "019"
|
|
||||||
- "020"
|
|
||||||
- "021"
|
|
||||||
- "022"
|
|
||||||
- "023"
|
|
||||||
- "024"
|
|
||||||
- "025"
|
|
||||||
- "026"
|
|
||||||
- "027"
|
|
||||||
- "028"
|
|
||||||
- "029"
|
|
||||||
- "030"
|
|
||||||
- "031"
|
|
||||||
- "032"
|
|
||||||
- "033"
|
|
||||||
- "034"
|
|
||||||
- "035"
|
|
||||||
- "036"
|
|
||||||
- "037"
|
|
||||||
- "038"
|
|
||||||
- "039"
|
|
||||||
- "040"
|
|
||||||
- "041"
|
|
||||||
- "042"
|
|
||||||
- "043"
|
|
||||||
- "044"
|
|
||||||
- "045"
|
|
||||||
- "046"
|
|
||||||
- "047"
|
|
||||||
- "048"
|
|
||||||
- "049"
|
|
||||||
- "050"
|
|
||||||
- "051"
|
|
||||||
- "052"
|
|
||||||
- "053"
|
|
||||||
- "054"
|
|
||||||
- "055"
|
|
||||||
- "056"
|
|
||||||
- "057"
|
|
||||||
- "058"
|
|
||||||
- "059"
|
|
||||||
- "060"
|
|
||||||
- "061"
|
|
||||||
- "062"
|
|
||||||
- "063"
|
|
||||||
- "064"
|
|
||||||
- "065"
|
|
||||||
- "066"
|
|
||||||
- "067"
|
|
||||||
- "068"
|
|
||||||
- "069"
|
|
||||||
- "070"
|
|
||||||
- "071"
|
|
||||||
- "072"
|
|
||||||
- "073"
|
|
||||||
- "074"
|
|
||||||
- "075"
|
|
||||||
- "076"
|
|
||||||
- "077"
|
|
||||||
- "078"
|
|
||||||
- "079"
|
|
||||||
- "080"
|
|
||||||
- "081"
|
|
||||||
- "082"
|
|
||||||
- "083"
|
|
||||||
- "084"
|
|
||||||
- "085"
|
|
||||||
- "086"
|
|
||||||
- "087"
|
|
||||||
- "088"
|
|
||||||
- "089"
|
|
||||||
- "090"
|
|
||||||
- "091"
|
|
||||||
- "092"
|
|
||||||
- "093"
|
|
||||||
- "094"
|
|
||||||
- "095"
|
|
||||||
- "096"
|
|
||||||
- "097"
|
|
||||||
- "098"
|
|
||||||
- "099"
|
|
||||||
- "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"
|
|
||||||
steps:
|
steps:
|
||||||
- name: Pre-install Node.js and Git
|
|
||||||
run: |
|
|
||||||
if command -v node >/dev/null 2>&1 && command -v git >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
echo "Node.js and Git already present; skipping install."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p /etc/dnf/dnf.conf.d
|
|
||||||
printf '%s\n' 'keepcache=True' 'max_parallel_downloads=10' > /etc/dnf/dnf.conf.d/99-ci-cache.conf
|
|
||||||
dnf -y install nodejs git
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install build tools
|
|
||||||
run: |
|
|
||||||
if command -v fedpkg >/dev/null 2>&1 \
|
|
||||||
&& command -v rpmbuild >/dev/null 2>&1 \
|
|
||||||
&& command -v rpmdev-setuptree >/dev/null 2>&1 \
|
|
||||||
&& command -v git >/dev/null 2>&1 \
|
|
||||||
&& command -v python3 >/dev/null 2>&1 \
|
|
||||||
&& rpm -q dnf-plugins-core >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
echo "Build tools already present; skipping install."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p /etc/dnf/dnf.conf.d
|
- id: select
|
||||||
printf '%s\n' 'keepcache=True' 'max_parallel_downloads=10' > /etc/dnf/dnf.conf.d/99-ci-cache.conf
|
|
||||||
dnf -y install 'dnf-command(builddep)' fedpkg rpm-build rpmdevtools git python3
|
|
||||||
|
|
||||||
- name: Build shard packages
|
|
||||||
env:
|
env:
|
||||||
SHARD_INDEX: ${{ matrix.shard }}
|
PACKAGE_INPUT: ${{ inputs.package_input }}
|
||||||
FEDORA_BRANCH: f43
|
|
||||||
TARGET_MARCH: ${{ matrix.arch }}
|
|
||||||
run: |
|
run: |
|
||||||
python3 <<'PY'
|
python3 <<'PY'
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import re
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
workspace = Path(os.environ["GITHUB_WORKSPACE"])
|
packages_file = Path("packages.txt")
|
||||||
shard_index = int(os.environ["SHARD_INDEX"])
|
package_input = os.environ.get("PACKAGE_INPUT", "")
|
||||||
fedora_branch = os.environ["FEDORA_BRANCH"]
|
if not package_input.strip() and not packages_file.exists():
|
||||||
target_march = os.environ["TARGET_MARCH"]
|
|
||||||
packages_file = workspace / "packages.txt"
|
|
||||||
|
|
||||||
if not packages_file.exists():
|
|
||||||
print("packages.txt not found")
|
print("packages.txt not found")
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
all_packages = [
|
if package_input.strip():
|
||||||
|
packages = [
|
||||||
|
entry
|
||||||
|
for entry in re.split(r"[\s,]+", package_input.strip())
|
||||||
|
if entry
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
packages = [
|
||||||
line.strip()
|
line.strip()
|
||||||
for line in packages_file.read_text(encoding="utf-8").splitlines()
|
for line in packages_file.read_text(encoding="utf-8").splitlines()
|
||||||
if line.strip() and not line.startswith("#")
|
if line.strip() and not line.startswith("#")
|
||||||
]
|
]
|
||||||
|
|
||||||
max_shards = 128
|
max_matrix = 128
|
||||||
shard_count = min(len(all_packages), max_shards)
|
matrix = []
|
||||||
if shard_count == 0 or shard_index > shard_count:
|
if packages:
|
||||||
print(
|
shard_size = max(1, -(-len(packages) // max_matrix))
|
||||||
f"Skipping shard {shard_index:03d} for {target_march}; "
|
shards = [
|
||||||
f"package_count={len(all_packages)} shard_count={shard_count}"
|
packages[index:index + shard_size]
|
||||||
)
|
for index in range(0, len(packages), shard_size)
|
||||||
raise SystemExit(0)
|
]
|
||||||
|
matrix = [
|
||||||
|
{
|
||||||
|
"shard_label": f"{shard_index + 1:03d}",
|
||||||
|
"packages": shard,
|
||||||
|
}
|
||||||
|
for shard_index, shard in enumerate(shards)
|
||||||
|
]
|
||||||
|
|
||||||
shard_size = max(1, -(-len(all_packages) // shard_count))
|
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as handle:
|
||||||
start_index = (shard_index - 1) * shard_size
|
handle.write(f"matrix={json.dumps(matrix)}\n")
|
||||||
end_index = start_index + shard_size
|
handle.write(f"package_count={len(packages)}\n")
|
||||||
packages = all_packages[start_index:end_index]
|
|
||||||
|
|
||||||
# Standard Fedora optflags with march override
|
|
||||||
# We'll use a simplified version of the override from the other workflow
|
|
||||||
optflags_override = (
|
|
||||||
f"-O2 -flto=auto -ffat-lto-objects -fexceptions -g "
|
|
||||||
"-grecord-gcc-switches -pipe -Wall -Werror=format-security "
|
|
||||||
"-Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 "
|
|
||||||
"-Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 "
|
|
||||||
"-fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 "
|
|
||||||
f"-m64 -march={target_march} -mtune=generic -fasynchronous-unwind-tables "
|
|
||||||
"-fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 "
|
|
||||||
"-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"
|
|
||||||
)
|
|
||||||
|
|
||||||
shard_root = workspace / "artifacts" / f"{target_march}-shard-{shard_index:03d}"
|
|
||||||
shard_root.mkdir(parents=True, exist_ok=True)
|
|
||||||
rpmbuild_root = workspace / ".rpmbuild"
|
|
||||||
rpmbuild_root.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
def run_with_retry(command, *, cwd=None, cleanup_path=None, attempts=3):
|
|
||||||
for attempt in range(1, attempts + 1):
|
|
||||||
if cleanup_path is not None and cleanup_path.exists():
|
|
||||||
shutil.rmtree(cleanup_path)
|
|
||||||
try:
|
|
||||||
subprocess.run(command, cwd=cwd, check=True)
|
|
||||||
return
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
if attempt == attempts:
|
|
||||||
raise
|
|
||||||
time.sleep(5 * attempt)
|
|
||||||
|
|
||||||
for package_name in packages:
|
|
||||||
package_dir = workspace / package_name
|
|
||||||
run_with_retry(
|
|
||||||
[
|
|
||||||
"git", "clone", "--depth", "1", "--branch", fedora_branch,
|
|
||||||
f"https://src.fedoraproject.org/rpms/{package_name}.git",
|
|
||||||
str(package_dir),
|
|
||||||
],
|
|
||||||
cleanup_path=package_dir,
|
|
||||||
)
|
|
||||||
|
|
||||||
run_with_retry(["fedpkg", "sources"], cwd=package_dir)
|
|
||||||
|
|
||||||
spec_file = next(package_dir.glob("*.spec"), None)
|
|
||||||
if not spec_file:
|
|
||||||
print(f"No spec file for {package_name}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
subprocess.run(["dnf", "-y", "builddep", str(spec_file)], check=True)
|
|
||||||
|
|
||||||
topdir = rpmbuild_root / package_name
|
|
||||||
for subdir in ("BUILD", "BUILDROOT", "RPMS", "SOURCES", "SPECS", "SRPMS"):
|
|
||||||
(topdir / subdir).mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
subprocess.run(
|
|
||||||
[
|
|
||||||
"rpmbuild", "-ba", str(spec_file),
|
|
||||||
"--define", f"optflags {optflags_override}",
|
|
||||||
"--define", f"_topdir {topdir}",
|
|
||||||
"--define", f"_builddir {topdir / 'BUILD'}",
|
|
||||||
"--define", f"_buildrootdir {topdir / 'BUILDROOT'}",
|
|
||||||
"--define", f"_rpmdir {topdir / 'RPMS'}",
|
|
||||||
"--define", f"_srcrpmdir {topdir / 'SRPMS'}",
|
|
||||||
"--define", f"_sourcedir {package_dir}",
|
|
||||||
"--define", f"_specdir {package_dir}",
|
|
||||||
],
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
for rpm_path in (topdir / "RPMS").rglob("*.rpm"):
|
|
||||||
shutil.copy2(rpm_path, shard_root / rpm_path.name)
|
|
||||||
for src_path in (topdir / "SRPMS").rglob("*.src.rpm"):
|
|
||||||
shutil.copy2(src_path, shard_root / src_path.name)
|
|
||||||
print(f"Built {package_name}")
|
|
||||||
PY
|
PY
|
||||||
|
|
||||||
- name: Upload RPM artifacts
|
validate-srpms:
|
||||||
uses: actions/upload-artifact@v3
|
needs: prepare
|
||||||
with:
|
if: ${{ needs.prepare.outputs.package_count != '0' }}
|
||||||
name: rpm-${{ matrix.arch }}-shard-${{ matrix.shard }}
|
|
||||||
path: artifacts/${{ matrix.arch }}-shard-${{ matrix.shard }}/
|
|
||||||
if-no-files-found: ignore
|
|
||||||
|
|
||||||
assemble-repo:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/funkemunky/kde-x86_64-v4-fedora-rpm-builder:latest
|
image: fedora:43
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include: ${{ fromJSON(needs.prepare.outputs.matrix) }}
|
||||||
- arch: x86-64-v3
|
|
||||||
repo_id: fedora43-kde-x86-64-v3
|
|
||||||
- arch: x86-64-v4
|
|
||||||
repo_id: fedora43-kde-x86-64-v4
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Download RPM shard artifacts
|
- name: Install SRPM validation tools
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
path: downloaded-rpms
|
|
||||||
|
|
||||||
- name: Stage matching shard artifacts
|
|
||||||
run: |
|
run: |
|
||||||
mkdir -p filtered-rpms
|
dnf -y install git make python3 rpm-build ca-certificates curl
|
||||||
shopt -s nullglob
|
|
||||||
for artifact_dir in downloaded-rpms/rpm-${{ matrix.arch }}-shard-*; do
|
|
||||||
cp -a "$artifact_dir"/. filtered-rpms/
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Install repo assembly tools
|
- name: Generate shard SRPMs
|
||||||
|
env:
|
||||||
|
PACKAGES_JSON: ${{ toJSON(matrix.packages) }}
|
||||||
|
SHARD_LABEL: ${{ matrix.shard_label }}
|
||||||
run: |
|
run: |
|
||||||
if command -v createrepo_c >/dev/null 2>&1
|
python3 <<'PY'
|
||||||
then
|
import json
|
||||||
echo "createrepo_c already present; skipping install."
|
import os
|
||||||
exit 0
|
import subprocess
|
||||||
fi
|
from pathlib import Path
|
||||||
|
|
||||||
mkdir -p /etc/dnf/dnf.conf.d
|
workspace = Path(os.environ["GITHUB_WORKSPACE"])
|
||||||
printf '%s\n' 'keepcache=True' 'max_parallel_downloads=10' > /etc/dnf/dnf.conf.d/99-ci-cache.conf
|
outdir = workspace / "artifacts" / f"srpm-shard-{os.environ['SHARD_LABEL']}"
|
||||||
dnf -y install createrepo_c
|
outdir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
- name: Assemble DNF repository
|
subprocess.run(
|
||||||
run: |
|
[
|
||||||
python3 ci/assemble-dnf-repo.py \
|
"make",
|
||||||
--input-dir filtered-rpms \
|
"-f",
|
||||||
--output-dir repo/${{ matrix.arch }} \
|
".copr/Makefile",
|
||||||
--repo-id ${{ matrix.repo_id }} \
|
"srpm",
|
||||||
--repo-name 'Fedora 43 KDE ${{ matrix.arch }}'
|
f"outdir={outdir}",
|
||||||
|
"spec=packaging/copr-rpm-macros-x86-64-v3.spec",
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
cwd=workspace,
|
||||||
|
)
|
||||||
|
|
||||||
- name: Upload DNF repository artifact
|
for package_name in json.loads(os.environ["PACKAGES_JSON"]):
|
||||||
uses: actions/upload-artifact@v3
|
subprocess.run(
|
||||||
with:
|
[
|
||||||
name: dnf-repo-${{ matrix.arch }}
|
"make",
|
||||||
path: repo/${{ matrix.arch }}/
|
"-f",
|
||||||
if-no-files-found: error
|
".copr/Makefile",
|
||||||
|
"srpm",
|
||||||
|
f"outdir={outdir}",
|
||||||
|
f"spec={package_name}",
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
cwd=workspace,
|
||||||
|
)
|
||||||
|
PY
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Build Fedora x86_64-v3 RPMs
|
name: Validate Fedora x86_64-v3 Copr SRPMs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -8,11 +8,29 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
type: string
|
type: string
|
||||||
|
copr_project:
|
||||||
|
description: "Optional Copr project in owner/project form for syncing SCM package definitions."
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
submit_to_copr:
|
||||||
|
description: "Sync package definitions to Copr after validation."
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
submit_package_builds:
|
||||||
|
description: "Queue package builds after the x86_64-v3 macro package build succeeds."
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'packages.txt'
|
- '.copr/Makefile'
|
||||||
- 'ci/assemble-dnf-repo.py'
|
|
||||||
- '.github/workflows/build-v3-rpms.yml'
|
- '.github/workflows/build-v3-rpms.yml'
|
||||||
|
- 'ci/copr-distgit-make-srpm.py'
|
||||||
|
- 'ci/sync-copr-packages.py'
|
||||||
|
- 'packages.txt'
|
||||||
|
- 'packaging/copr-rpm-macros-x86-64-v3.spec'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -40,7 +58,7 @@ jobs:
|
|||||||
package_input = os.environ.get("PACKAGE_INPUT", "")
|
package_input = os.environ.get("PACKAGE_INPUT", "")
|
||||||
if not package_input.strip() and not packages_file.exists():
|
if not package_input.strip() and not packages_file.exists():
|
||||||
print("::error::packages.txt not found")
|
print("::error::packages.txt not found")
|
||||||
exit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
if package_input.strip():
|
if package_input.strip():
|
||||||
packages = [
|
packages = [
|
||||||
@@ -55,8 +73,7 @@ jobs:
|
|||||||
if line.strip() and not line.startswith("#")
|
if line.strip() and not line.startswith("#")
|
||||||
]
|
]
|
||||||
|
|
||||||
# Limit matrix size to avoid GitHub Actions limits
|
max_matrix = 128
|
||||||
max_matrix = 256
|
|
||||||
matrix = []
|
matrix = []
|
||||||
if packages:
|
if packages:
|
||||||
shard_size = max(1, -(-len(packages) // max_matrix))
|
shard_size = max(1, -(-len(packages) // max_matrix))
|
||||||
@@ -73,201 +90,117 @@ jobs:
|
|||||||
for shard_index, shard in enumerate(shards)
|
for shard_index, shard in enumerate(shards)
|
||||||
]
|
]
|
||||||
|
|
||||||
with open(os.environ["GITHUB_OUTPUT"], "a") as fh:
|
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as handle:
|
||||||
fh.write(f"matrix={json.dumps(matrix)}\n")
|
handle.write(f"matrix={json.dumps(matrix)}\n")
|
||||||
fh.write(f"package_count={len(packages)}\n")
|
handle.write(f"package_count={len(packages)}\n")
|
||||||
PY
|
PY
|
||||||
|
|
||||||
build:
|
validate-srpms:
|
||||||
needs: prepare
|
needs: prepare
|
||||||
if: ${{ needs.prepare.outputs.package_count != '0' }}
|
if: ${{ needs.prepare.outputs.package_count != '0' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/funkemunky/kde-x86_64-v4-fedora-rpm-builder:latest
|
image: fedora:43
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include: ${{ fromJSON(needs.prepare.outputs.matrix) }}
|
include: ${{ fromJSON(needs.prepare.outputs.matrix) }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Restore DNF cache
|
|
||||||
uses: actions/cache@v4
|
- name: Install SRPM validation tools
|
||||||
with:
|
|
||||||
path: /var/cache/dnf
|
|
||||||
key: fedora43-dnf-${{ hashFiles('.github/workflows/build-v3-rpms.yml', 'ci/fedora-rpm-builder.Containerfile') }}
|
|
||||||
restore-keys: |
|
|
||||||
fedora43-dnf-
|
|
||||||
- name: Install build tools
|
|
||||||
run: |
|
run: |
|
||||||
if command -v fedpkg >/dev/null 2>&1 \
|
dnf -y install git make python3 rpm-build ca-certificates curl
|
||||||
&& command -v rpmbuild >/dev/null 2>&1 \
|
|
||||||
&& command -v rpmdev-setuptree >/dev/null 2>&1 \
|
|
||||||
&& command -v git >/dev/null 2>&1 \
|
|
||||||
&& command -v python3 >/dev/null 2>&1 \
|
|
||||||
&& rpm -q dnf-plugins-core >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
echo "Build tools already present; skipping install."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p /etc/dnf/dnf.conf.d
|
- name: Generate shard SRPMs
|
||||||
printf '%s\n' 'keepcache=True' 'max_parallel_downloads=10' > /etc/dnf/dnf.conf.d/99-ci-cache.conf
|
|
||||||
dnf -y install 'dnf-command(builddep)' fedpkg rpm-build rpmdevtools git python3
|
|
||||||
|
|
||||||
- name: Build shard packages
|
|
||||||
env:
|
env:
|
||||||
SHARD_INDEX: ${{ matrix.shard }}
|
|
||||||
PACKAGES_JSON: ${{ toJSON(matrix.packages) }}
|
PACKAGES_JSON: ${{ toJSON(matrix.packages) }}
|
||||||
FEDORA_BRANCH: f43
|
SHARD_LABEL: ${{ matrix.shard_label }}
|
||||||
TARGET_MARCH: x86-64-v3
|
|
||||||
run: |
|
run: |
|
||||||
python3 <<'PY'
|
python3 <<'PY'
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
workspace = Path(os.environ["GITHUB_WORKSPACE"])
|
workspace = Path(os.environ["GITHUB_WORKSPACE"])
|
||||||
shard_index = int(os.environ["SHARD_INDEX"])
|
outdir = workspace / "artifacts" / f"srpm-shard-{os.environ['SHARD_LABEL']}"
|
||||||
packages = json.loads(os.environ["PACKAGES_JSON"])
|
outdir.mkdir(parents=True, exist_ok=True)
|
||||||
fedora_branch = os.environ["FEDORA_BRANCH"]
|
|
||||||
target_march = os.environ["TARGET_MARCH"]
|
|
||||||
|
|
||||||
# Standard Fedora optflags with march override
|
|
||||||
# We'll use a simplified version of the override from the other workflow
|
|
||||||
optflags_override = (
|
|
||||||
f"-O2 -flto=auto -ffat-lto-objects -fexceptions -g "
|
|
||||||
"-grecord-gcc-switches -pipe -Wall -Werror=format-security "
|
|
||||||
"-Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 "
|
|
||||||
"-Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 "
|
|
||||||
"-fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 "
|
|
||||||
f"-m64 -march={target_march} -mtune=generic -fasynchronous-unwind-tables "
|
|
||||||
"-fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 "
|
|
||||||
"-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"
|
|
||||||
)
|
|
||||||
|
|
||||||
shard_root = workspace / "artifacts" / f"shard-{shard_index:03d}"
|
|
||||||
shard_root.mkdir(parents=True, exist_ok=True)
|
|
||||||
rpmbuild_root = workspace / ".rpmbuild"
|
|
||||||
rpmbuild_root.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
def run_with_retry(command, *, cwd=None, cleanup_path=None, attempts=3):
|
|
||||||
for attempt in range(1, attempts + 1):
|
|
||||||
if cleanup_path is not None and cleanup_path.exists():
|
|
||||||
shutil.rmtree(cleanup_path)
|
|
||||||
try:
|
|
||||||
subprocess.run(command, cwd=cwd, check=True)
|
|
||||||
return
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
if attempt == attempts:
|
|
||||||
raise
|
|
||||||
time.sleep(5 * attempt)
|
|
||||||
|
|
||||||
for package_name in packages:
|
|
||||||
package_dir = workspace / package_name
|
|
||||||
run_with_retry(
|
|
||||||
[
|
|
||||||
"git", "clone", "--depth", "1", "--branch", fedora_branch,
|
|
||||||
f"https://src.fedoraproject.org/rpms/{package_name}.git",
|
|
||||||
str(package_dir),
|
|
||||||
],
|
|
||||||
cleanup_path=package_dir,
|
|
||||||
)
|
|
||||||
|
|
||||||
run_with_retry(["fedpkg", "sources"], cwd=package_dir)
|
|
||||||
|
|
||||||
spec_file = next(package_dir.glob("*.spec"), None)
|
|
||||||
if not spec_file:
|
|
||||||
print(f"No spec file for {package_name}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
subprocess.run(["dnf", "-y", "builddep", str(spec_file)], check=True)
|
|
||||||
|
|
||||||
topdir = rpmbuild_root / package_name
|
|
||||||
for subdir in ("BUILD", "BUILDROOT", "RPMS", "SOURCES", "SPECS", "SRPMS"):
|
|
||||||
(topdir / subdir).mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
"rpmbuild", "-ba", str(spec_file),
|
"make",
|
||||||
"--define", f"optflags {optflags_override}",
|
"-f",
|
||||||
"--define", f"_topdir {topdir}",
|
".copr/Makefile",
|
||||||
"--define", f"_builddir {topdir / 'BUILD'}",
|
"srpm",
|
||||||
"--define", f"_buildrootdir {topdir / 'BUILDROOT'}",
|
f"outdir={outdir}",
|
||||||
"--define", f"_rpmdir {topdir / 'RPMS'}",
|
"spec=packaging/copr-rpm-macros-x86-64-v3.spec",
|
||||||
"--define", f"_srcrpmdir {topdir / 'SRPMS'}",
|
|
||||||
"--define", f"_sourcedir {package_dir}",
|
|
||||||
"--define", f"_specdir {package_dir}",
|
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
|
cwd=workspace,
|
||||||
)
|
)
|
||||||
|
|
||||||
for rpm_path in (topdir / "RPMS").rglob("*.rpm"):
|
for package_name in json.loads(os.environ["PACKAGES_JSON"]):
|
||||||
shutil.copy2(rpm_path, shard_root / rpm_path.name)
|
subprocess.run(
|
||||||
for src_path in (topdir / "SRPMS").rglob("*.src.rpm"):
|
[
|
||||||
shutil.copy2(src_path, shard_root / src_path.name)
|
"make",
|
||||||
print(f"Built {package_name}")
|
"-f",
|
||||||
|
".copr/Makefile",
|
||||||
|
"srpm",
|
||||||
|
f"outdir={outdir}",
|
||||||
|
f"spec={package_name}",
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
cwd=workspace,
|
||||||
|
)
|
||||||
PY
|
PY
|
||||||
|
|
||||||
- name: Upload RPM artifacts
|
- name: Upload SRPM artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: rpm-shard-${{ matrix.shard_label }}
|
name: srpm-shard-${{ matrix.shard_label }}
|
||||||
path: artifacts/shard-${{ matrix.shard_label }}/
|
path: artifacts/srpm-shard-${{ matrix.shard_label }}/
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
assemble-repo:
|
sync-copr:
|
||||||
needs: build
|
needs:
|
||||||
|
- prepare
|
||||||
|
- validate-srpms
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' && inputs.submit_to_copr && inputs.copr_project != '' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/funkemunky/kde-x86_64-v4-fedora-rpm-builder:latest
|
image: fedora:43
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Download RPM shard artifacts
|
- name: Install Copr sync tools
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
pattern: rpm-shard-*
|
|
||||||
path: downloaded-rpms
|
|
||||||
|
|
||||||
- name: Install repo assembly tools
|
|
||||||
run: |
|
run: |
|
||||||
if command -v createrepo_c >/dev/null 2>&1
|
dnf -y install copr-cli git make python3 rpm-build ca-certificates curl
|
||||||
then
|
|
||||||
echo "createrepo_c already present; skipping install."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p /etc/dnf/dnf.conf.d
|
- name: Configure copr-cli
|
||||||
printf '%s\n' 'keepcache=True' 'max_parallel_downloads=10' > /etc/dnf/dnf.conf.d/99-ci-cache.conf
|
|
||||||
dnf -y install createrepo_c
|
|
||||||
|
|
||||||
- name: Assemble DNF repository
|
|
||||||
run: |
|
|
||||||
python3 ci/assemble-dnf-repo.py \
|
|
||||||
--input-dir downloaded-rpms \
|
|
||||||
--output-dir repo/fedora43-x86-64-v3 \
|
|
||||||
--repo-id fedora43-kde-x86-64-v3 \
|
|
||||||
--repo-name 'Fedora 43 KDE x86_64-v3'
|
|
||||||
|
|
||||||
- name: Upload RPMs to Nexus YUM repository
|
|
||||||
env:
|
env:
|
||||||
NEXUS_BASE_URL: https://nexus.funkemunky.cc
|
COPR_CONFIG: ${{ secrets.COPR_CONFIG }}
|
||||||
NEXUS_REPOSITORY: dnf-repo-x86-64-v3
|
|
||||||
NEXUS_USER: ${{ secrets.nexusUser }}
|
|
||||||
NEXUS_PASSWORD: ${{ secrets.nexusPassword }}
|
|
||||||
run: |
|
run: |
|
||||||
test -n "${NEXUS_USER}"
|
test -n "${COPR_CONFIG}"
|
||||||
test -n "${NEXUS_PASSWORD}"
|
mkdir -p ~/.config
|
||||||
PACKAGE_ROOT="repo/fedora43-x86-64-v3/packages"
|
printf '%s\n' "${COPR_CONFIG}" > ~/.config/copr
|
||||||
find "${PACKAGE_ROOT}" -type f -name '*.rpm' | while read -r FILEPATH; do
|
chmod 0600 ~/.config/copr
|
||||||
FILENAME="$(basename "${FILEPATH}")"
|
|
||||||
curl --fail --show-error --silent \
|
- name: Sync Copr SCM package definitions
|
||||||
--user "${NEXUS_USER}:${NEXUS_PASSWORD}" \
|
env:
|
||||||
-X POST "${NEXUS_BASE_URL}/service/rest/v1/components?repository=${NEXUS_REPOSITORY}" \
|
COPR_PROJECT: ${{ inputs.copr_project }}
|
||||||
-H "Content-Type: multipart/form-data" \
|
PACKAGE_INPUT: ${{ inputs.package_input }}
|
||||||
-F "yum.asset=@${FILEPATH};type=application/x-rpm" \
|
SUBMIT_PACKAGE_BUILDS: ${{ inputs.submit_package_builds }}
|
||||||
-F "yum.asset.filename=${FILENAME}"
|
run: |
|
||||||
done
|
set -euo pipefail
|
||||||
|
ARGS=(
|
||||||
|
--project "${COPR_PROJECT}"
|
||||||
|
--clone-url "https://github.com/${GITHUB_REPOSITORY}.git"
|
||||||
|
--commit "${GITHUB_SHA}"
|
||||||
|
--package-input "${PACKAGE_INPUT}"
|
||||||
|
--submit-macro-build
|
||||||
|
)
|
||||||
|
if [ "${SUBMIT_PACKAGE_BUILDS}" = "true" ]; then
|
||||||
|
ARGS+=(--submit-package-builds --nowait-package-builds)
|
||||||
|
fi
|
||||||
|
python3 ci/sync-copr-packages.py "${ARGS[@]}"
|
||||||
|
|||||||
-305
@@ -1,305 +0,0 @@
|
|||||||
.rpmbuild/
|
|
||||||
*.src.rpm
|
|
||||||
__pycache__/
|
|
||||||
.dnf/
|
|
||||||
scripts/__pycache__/
|
|
||||||
|
|
||||||
# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,maven,java,intellij,eclipse,netbeans
|
|
||||||
# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,maven,java,intellij,eclipse,netbeans
|
|
||||||
|
|
||||||
### Eclipse ###
|
|
||||||
.metadata
|
|
||||||
bin/
|
|
||||||
tmp/
|
|
||||||
*.tmp
|
|
||||||
*.bak
|
|
||||||
*.swp
|
|
||||||
*~.nib
|
|
||||||
local.properties
|
|
||||||
.settings/
|
|
||||||
.loadpath
|
|
||||||
.recommenders
|
|
||||||
*.iml
|
|
||||||
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# External tool builders
|
|
||||||
.externalToolBuilders/
|
|
||||||
|
|
||||||
# Locally stored "Eclipse launch configurations"
|
|
||||||
*.launch
|
|
||||||
|
|
||||||
# PyDev specific (Python IDE for Eclipse)
|
|
||||||
*.pydevproject
|
|
||||||
|
|
||||||
# CDT-specific (C/C++ Development Tooling)
|
|
||||||
.cproject
|
|
||||||
|
|
||||||
# CDT- autotools
|
|
||||||
.autotools
|
|
||||||
|
|
||||||
# Java annotation processor (APT)
|
|
||||||
.factorypath
|
|
||||||
|
|
||||||
# PDT-specific (PHP Development Tools)
|
|
||||||
.buildpath
|
|
||||||
|
|
||||||
# sbteclipse plugin
|
|
||||||
.target
|
|
||||||
|
|
||||||
# Tern plugin
|
|
||||||
.tern-project
|
|
||||||
|
|
||||||
# TeXlipse plugin
|
|
||||||
.texlipse
|
|
||||||
|
|
||||||
# STS (Spring Tool Suite)
|
|
||||||
.springBeans
|
|
||||||
|
|
||||||
# Code Recommenders
|
|
||||||
.recommenders/
|
|
||||||
|
|
||||||
# Annotation Processing
|
|
||||||
.apt_generated/
|
|
||||||
.apt_generated_test/
|
|
||||||
|
|
||||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
|
||||||
.cache-main
|
|
||||||
.scala_dependencies
|
|
||||||
.worksheet
|
|
||||||
|
|
||||||
# Uncomment this line if you wish to ignore the project description file.
|
|
||||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
|
||||||
#.project
|
|
||||||
|
|
||||||
### Eclipse Patch ###
|
|
||||||
# Spring Boot Tooling
|
|
||||||
.sts4-cache/
|
|
||||||
|
|
||||||
### Intellij ###
|
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
|
||||||
|
|
||||||
# User-specific stuff
|
|
||||||
.idea/**/workspace.xml
|
|
||||||
.idea/**/tasks.xml
|
|
||||||
.idea/**/usage.statistics.xml
|
|
||||||
.idea/**/dictionaries
|
|
||||||
.idea/**/shelf
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
.idea/**/contentModel.xml
|
|
||||||
|
|
||||||
# Sensitive or high-churn files
|
|
||||||
.idea/**/dataSources/
|
|
||||||
.idea/**/dataSources.ids
|
|
||||||
.idea/**/dataSources.local.xml
|
|
||||||
.idea/**/sqlDataSources.xml
|
|
||||||
.idea/**/dynamic.xml
|
|
||||||
.idea/**/uiDesigner.xml
|
|
||||||
.idea/**/dbnavigator.xml
|
|
||||||
|
|
||||||
# Gradle
|
|
||||||
.idea/**/gradle.xml
|
|
||||||
.idea/**/libraries
|
|
||||||
|
|
||||||
# Gradle and Maven with auto-import
|
|
||||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
|
||||||
# since they will be recreated, and may cause churn. Uncomment if using
|
|
||||||
# auto-import.
|
|
||||||
# .idea/artifacts
|
|
||||||
# .idea/compiler.xml
|
|
||||||
# .idea/jarRepositories.xml
|
|
||||||
# .idea/modules.xml
|
|
||||||
# .idea/*.iml
|
|
||||||
# .idea/modules
|
|
||||||
# *.iml
|
|
||||||
# *.ipr
|
|
||||||
|
|
||||||
# CMake
|
|
||||||
cmake-build-*/
|
|
||||||
|
|
||||||
# Mongo Explorer plugin
|
|
||||||
.idea/**/mongoSettings.xml
|
|
||||||
|
|
||||||
# File-based project format
|
|
||||||
*.iws
|
|
||||||
|
|
||||||
# IntelliJ
|
|
||||||
out/
|
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin
|
|
||||||
.idea_modules/
|
|
||||||
|
|
||||||
# JIRA plugin
|
|
||||||
atlassian-ide-plugin.xml
|
|
||||||
|
|
||||||
# Cursive Clojure plugin
|
|
||||||
.idea/replstate.xml
|
|
||||||
|
|
||||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
||||||
com_crashlytics_export_strings.xml
|
|
||||||
crashlytics.properties
|
|
||||||
crashlytics-build.properties
|
|
||||||
fabric.properties
|
|
||||||
|
|
||||||
# Editor-based Rest Client
|
|
||||||
.idea/httpRequests
|
|
||||||
|
|
||||||
# Android studio 3.1+ serialized cache file
|
|
||||||
.idea/caches/build_file_checksums.ser
|
|
||||||
|
|
||||||
### Intellij Patch ###
|
|
||||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
|
||||||
|
|
||||||
# *.iml
|
|
||||||
# modules.xml
|
|
||||||
# .idea/misc.xml
|
|
||||||
# *.ipr
|
|
||||||
|
|
||||||
# Sonarlint plugin
|
|
||||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
|
||||||
.idea/**/sonarlint/
|
|
||||||
|
|
||||||
# SonarQube Plugin
|
|
||||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
|
||||||
.idea/**/sonarIssues.xml
|
|
||||||
|
|
||||||
# Markdown Navigator plugin
|
|
||||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
|
||||||
.idea/**/markdown-navigator.xml
|
|
||||||
.idea/**/markdown-navigator-enh.xml
|
|
||||||
.idea/**/markdown-navigator/
|
|
||||||
|
|
||||||
# Cache file creation bug
|
|
||||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
|
||||||
.idea/$CACHE_FILE$
|
|
||||||
|
|
||||||
# CodeStream plugin
|
|
||||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
|
||||||
.idea/codestream.xml
|
|
||||||
|
|
||||||
### Java ###
|
|
||||||
# Compiled class file
|
|
||||||
*.class
|
|
||||||
|
|
||||||
# Log file
|
|
||||||
*.log
|
|
||||||
|
|
||||||
# BlueJ files
|
|
||||||
*.ctxt
|
|
||||||
|
|
||||||
# Mobile Tools for Java (J2ME)
|
|
||||||
.mtj.tmp/
|
|
||||||
|
|
||||||
# Package Files #
|
|
||||||
*.jar
|
|
||||||
!gradle/wrapper/gradle-wrapper.jar
|
|
||||||
*.war
|
|
||||||
*.nar
|
|
||||||
*.ear
|
|
||||||
*.zip
|
|
||||||
*.tar.gz
|
|
||||||
*.rar
|
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
|
||||||
hs_err_pid*
|
|
||||||
|
|
||||||
### Linux ###
|
|
||||||
*~
|
|
||||||
|
|
||||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
|
||||||
.fuse_hidden*
|
|
||||||
|
|
||||||
# KDE directory preferences
|
|
||||||
.directory
|
|
||||||
|
|
||||||
# Linux trash folder which might appear on any partition or disk
|
|
||||||
.Trash-*
|
|
||||||
|
|
||||||
# .nfs files are created when an open file is removed but is still being accessed
|
|
||||||
.nfs*
|
|
||||||
|
|
||||||
### macOS ###
|
|
||||||
# General
|
|
||||||
.DS_Store
|
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
|
|
||||||
# Icon must end with two \r
|
|
||||||
Icon
|
|
||||||
|
|
||||||
|
|
||||||
# Thumbnails
|
|
||||||
._*
|
|
||||||
|
|
||||||
# Files that might appear in the root of a volume
|
|
||||||
.DocumentRevisions-V100
|
|
||||||
.fseventsd
|
|
||||||
.Spotlight-V100
|
|
||||||
.TemporaryItems
|
|
||||||
.Trashes
|
|
||||||
.VolumeIcon.icns
|
|
||||||
.com.apple.timemachine.donotpresent
|
|
||||||
|
|
||||||
# Directories potentially created on remote AFP share
|
|
||||||
.AppleDB
|
|
||||||
.AppleDesktop
|
|
||||||
Network Trash Folder
|
|
||||||
Temporary Items
|
|
||||||
.apdisk
|
|
||||||
|
|
||||||
### Maven ###
|
|
||||||
target/
|
|
||||||
pom.xml.tag
|
|
||||||
pom.xml.releaseBackup
|
|
||||||
pom.xml.versionsBackup
|
|
||||||
pom.xml.next
|
|
||||||
release.properties
|
|
||||||
dependency-reduced-pom.xml
|
|
||||||
buildNumber.properties
|
|
||||||
.mvn/timing.properties
|
|
||||||
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
|
|
||||||
.mvn/wrapper/maven-wrapper.jar
|
|
||||||
.flattened-pom.xml
|
|
||||||
|
|
||||||
### NetBeans ###
|
|
||||||
**/nbproject/private/
|
|
||||||
**/nbproject/Makefile-*.mk
|
|
||||||
**/nbproject/Package-*.bash
|
|
||||||
build/
|
|
||||||
nbbuild/
|
|
||||||
dist/
|
|
||||||
nbdist/
|
|
||||||
.nb-gradle/
|
|
||||||
|
|
||||||
### Windows ###
|
|
||||||
# Windows thumbnail cache files
|
|
||||||
Thumbs.db
|
|
||||||
Thumbs.db:encryptable
|
|
||||||
ehthumbs.db
|
|
||||||
ehthumbs_vista.db
|
|
||||||
|
|
||||||
# Dump file
|
|
||||||
*.stackdump
|
|
||||||
|
|
||||||
# Folder config file
|
|
||||||
[Dd]esktop.ini
|
|
||||||
|
|
||||||
.dnf/*
|
|
||||||
|
|
||||||
# Recycle Bin used on file shares
|
|
||||||
$RECYCLE.BIN/
|
|
||||||
|
|
||||||
# Windows Installer files
|
|
||||||
*.cab
|
|
||||||
*.msi
|
|
||||||
*.msix
|
|
||||||
*.msm
|
|
||||||
*.msp
|
|
||||||
|
|
||||||
# Windows shortcuts
|
|
||||||
*.lnk
|
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,maven,java,intellij,eclipse,netbeans
|
|
||||||
/.gradle/
|
|
||||||
|
|||||||
@@ -1,22 +1,64 @@
|
|||||||
# Fedora 43 KDE Actions RPM Builder
|
# Fedora 43 KDE Copr x86_64-v3 Builder
|
||||||
|
|
||||||
This repository provides CI workflows for both GitHub Actions and Gitea Actions to rebuild Fedora 43
|
This repository now targets Copr rather than doing local `rpmbuild -ba` work inside CI.
|
||||||
KDE packages from Fedora dist-git with `x86-64-v3` and `x86-64-v4` code generation and `-O3`
|
It uses Copr's SCM `make_srpm` flow to generate SRPMs from Fedora dist-git content and installs
|
||||||
optimization.
|
a dedicated buildroot macro package so Copr's Fedora 43 `x86_64` builders compile with an
|
||||||
|
`x86-64-v3` ISA baseline.
|
||||||
|
|
||||||
## Workflows
|
## What changed
|
||||||
- **GitHub Actions**: `.github/workflows/build-v3-rpms.yml`
|
- `.copr/Makefile` is the Copr entrypoint used by the SCM `make_srpm` method.
|
||||||
- **Gitea Actions**: `.gitea/workflows/build-v3-rpms.yml`
|
- `ci/copr-distgit-make-srpm.py` clones Fedora dist-git, downloads lookaside sources, and builds SRPMs.
|
||||||
|
- `packaging/copr-rpm-macros-x86-64-v3.spec` produces the buildroot macro package that changes Fedora's
|
||||||
|
`%__cflags_arch_x86_64_level` to `-v3` while keeping the rest of `redhat-rpm-config` intact.
|
||||||
|
- `ci/sync-copr-packages.py` registers or updates Copr SCM package definitions and can queue builds.
|
||||||
|
|
||||||
Each build workflow now finishes with a repository assembly job that collects the shard artifacts,
|
## Why the macro package exists
|
||||||
generates `repodata/` with `createrepo_c`, and uploads a ready-to-serve DNF repository artifact.
|
|
||||||
|
|
||||||
## Repository artifacts
|
Copr does not expose a generic `rpmbuild --define` interface for package builds. The supported way to
|
||||||
- **GitHub Actions** uploads `dnf-repo-x86-64-v3`
|
change buildroot macros is to build a small RPM that drops a file into `%{rpmmacrodir}` and then add
|
||||||
- **Gitea Actions** uploads `dnf-repo-x86-64-v3` and `dnf-repo-x86-64-v4`
|
that package to the Copr chroot's additional packages list. This repo does that with
|
||||||
|
`copr-rpm-macros-x86-64-v3`.
|
||||||
|
|
||||||
Each repository artifact contains:
|
With that package installed in the buildroot, packages that honor Fedora's standard `%optflags` /
|
||||||
- `packages/` with the built binary RPMs
|
`%set_build_flags` path will compile with `-march=x86-64-v3` on Copr builders.
|
||||||
- `repodata/` generated for DNF
|
|
||||||
- a `.repo` template with a placeholder `baseurl`
|
## Local validation
|
||||||
- `README.txt` with Fedora 43 install instructions
|
|
||||||
|
Generate the macro SRPM:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -f .copr/Makefile srpm outdir=dist-srpms spec=packaging/copr-rpm-macros-x86-64-v3.spec
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a Fedora dist-git package SRPM the same way Copr will:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -f .copr/Makefile srpm outdir=dist-srpms spec=konsole
|
||||||
|
```
|
||||||
|
|
||||||
|
## Copr setup
|
||||||
|
|
||||||
|
1. Create or reuse a Copr project with the `fedora-43-x86_64` chroot enabled.
|
||||||
|
2. Sync this repository's SCM package definitions into that project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 ci/sync-copr-packages.py \
|
||||||
|
--project yourname/kde-x86-64-v3 \
|
||||||
|
--clone-url https://github.com/<owner>/<repo>.git \
|
||||||
|
--commit <git-ref> \
|
||||||
|
--submit-macro-build
|
||||||
|
```
|
||||||
|
|
||||||
|
3. After the macro package build succeeds, queue the package builds:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 ci/sync-copr-packages.py \
|
||||||
|
--project yourname/kde-x86-64-v3 \
|
||||||
|
--clone-url https://github.com/<owner>/<repo>.git \
|
||||||
|
--commit <git-ref> \
|
||||||
|
--submit-package-builds \
|
||||||
|
--nowait-package-builds
|
||||||
|
```
|
||||||
|
|
||||||
|
The sync step also sets the Copr chroot's additional packages list to `copr-rpm-macros-x86-64-v3`,
|
||||||
|
which is the piece that makes the `x86-64-v3` compile target apply on Copr builders.
|
||||||
|
|||||||
@@ -0,0 +1,244 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SOURCE_LINE_PATTERN = re.compile(
|
||||||
|
r"^(?P<algo>[A-Za-z0-9_+-]+)\s+\((?P<filename>.+)\)\s+=\s+(?P<checksum>[0-9A-Fa-f]+)$"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=(
|
||||||
|
"Create an SRPM either from a local spec in this repository or by "
|
||||||
|
"cloning a Fedora dist-git package and downloading its lookaside sources."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
parser.add_argument("--spec-ref", required=True, help="Local .spec path or Fedora dist-git package name")
|
||||||
|
parser.add_argument("--outdir", required=True, help="Directory where the generated SRPM should be written")
|
||||||
|
parser.add_argument("--branch", default="f43", help="Fedora dist-git branch to clone when spec-ref is a package")
|
||||||
|
parser.add_argument("--dist", default=".fc43", help="RPM dist suffix to define while generating the SRPM")
|
||||||
|
parser.add_argument("--namespace", default="rpms", help="Fedora dist-git namespace")
|
||||||
|
parser.add_argument(
|
||||||
|
"--lookaside-baseurl",
|
||||||
|
default="https://src.fedoraproject.org/repo/pkgs",
|
||||||
|
help="Base URL for the Fedora lookaside cache",
|
||||||
|
)
|
||||||
|
parser.add_argument("--retry-count", type=int, default=3, help="Number of retries for network operations")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def run(command: list[str], *, cwd: Path | None = None) -> None:
|
||||||
|
subprocess.run(command, cwd=cwd, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_within_repo(path: Path) -> Path | None:
|
||||||
|
candidate = (REPO_ROOT / path).resolve()
|
||||||
|
try:
|
||||||
|
candidate.relative_to(REPO_ROOT)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
if candidate.is_file() and candidate.suffix == ".spec":
|
||||||
|
return candidate
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def hash_file(path: Path, algorithm: str) -> str:
|
||||||
|
digest = hashlib.new(algorithm.lower())
|
||||||
|
with path.open("rb") as handle:
|
||||||
|
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||||
|
digest.update(chunk)
|
||||||
|
return digest.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def download_file(url: str, destination: Path, *, algorithm: str, checksum: str, attempts: int) -> None:
|
||||||
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
for attempt in range(1, attempts + 1):
|
||||||
|
if destination.exists() and hash_file(destination, algorithm) == checksum.lower():
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(url) as response, destination.open("wb") as handle:
|
||||||
|
shutil.copyfileobj(response, handle)
|
||||||
|
except Exception:
|
||||||
|
if destination.exists():
|
||||||
|
destination.unlink()
|
||||||
|
if attempt == attempts:
|
||||||
|
raise
|
||||||
|
time.sleep(attempt * 3)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if hash_file(destination, algorithm) == checksum.lower():
|
||||||
|
return
|
||||||
|
|
||||||
|
destination.unlink(missing_ok=True)
|
||||||
|
if attempt == attempts:
|
||||||
|
raise RuntimeError(f"checksum mismatch for {destination.name} from {url}")
|
||||||
|
time.sleep(attempt * 3)
|
||||||
|
|
||||||
|
|
||||||
|
def build_srpm(spec_path: Path, *, source_dir: Path, outdir: Path, dist: str) -> Path:
|
||||||
|
outdir.mkdir(parents=True, exist_ok=True)
|
||||||
|
with tempfile.TemporaryDirectory(prefix="copr-srpm-topdir-") as topdir_name:
|
||||||
|
topdir = Path(topdir_name)
|
||||||
|
command = [
|
||||||
|
"rpmbuild",
|
||||||
|
"-bs",
|
||||||
|
str(spec_path),
|
||||||
|
"--define",
|
||||||
|
f"_topdir {topdir}",
|
||||||
|
"--define",
|
||||||
|
f"_builddir {topdir / 'BUILD'}",
|
||||||
|
"--define",
|
||||||
|
f"_buildrootdir {topdir / 'BUILDROOT'}",
|
||||||
|
"--define",
|
||||||
|
f"_rpmdir {topdir / 'RPMS'}",
|
||||||
|
"--define",
|
||||||
|
f"_srcrpmdir {outdir}",
|
||||||
|
"--define",
|
||||||
|
f"_sourcedir {source_dir}",
|
||||||
|
"--define",
|
||||||
|
f"_specdir {spec_path.parent}",
|
||||||
|
]
|
||||||
|
if dist:
|
||||||
|
command.extend(["--define", f"dist {dist}"])
|
||||||
|
run(command, cwd=source_dir)
|
||||||
|
|
||||||
|
srpms = sorted(outdir.glob("*.src.rpm"), key=lambda path: path.stat().st_mtime, reverse=True)
|
||||||
|
if not srpms:
|
||||||
|
raise RuntimeError(f"rpmbuild did not produce an SRPM in {outdir}")
|
||||||
|
return srpms[0]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_sources_file(path: Path) -> list[tuple[str, str, str]]:
|
||||||
|
entries: list[tuple[str, str, str]] = []
|
||||||
|
if not path.exists():
|
||||||
|
return entries
|
||||||
|
|
||||||
|
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
match = SOURCE_LINE_PATTERN.match(line)
|
||||||
|
if not match:
|
||||||
|
raise RuntimeError(f"unsupported lookaside source format: {line}")
|
||||||
|
entries.append(
|
||||||
|
(
|
||||||
|
match.group("algo").lower(),
|
||||||
|
match.group("filename"),
|
||||||
|
match.group("checksum").lower(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def download_lookaside_sources(
|
||||||
|
package_name: str,
|
||||||
|
source_dir: Path,
|
||||||
|
*,
|
||||||
|
namespace: str,
|
||||||
|
lookaside_baseurl: str,
|
||||||
|
attempts: int,
|
||||||
|
) -> None:
|
||||||
|
for algorithm, filename, checksum in parse_sources_file(source_dir / "sources"):
|
||||||
|
encoded_filename = urllib.parse.quote(filename)
|
||||||
|
url = (
|
||||||
|
f"{lookaside_baseurl}/{namespace}/{package_name}/"
|
||||||
|
f"{encoded_filename}/{algorithm}/{checksum}/{encoded_filename}"
|
||||||
|
)
|
||||||
|
download_file(
|
||||||
|
url,
|
||||||
|
source_dir / filename,
|
||||||
|
algorithm=algorithm,
|
||||||
|
checksum=checksum,
|
||||||
|
attempts=attempts,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_from_distgit(
|
||||||
|
package_name: str,
|
||||||
|
*,
|
||||||
|
branch: str,
|
||||||
|
dist: str,
|
||||||
|
namespace: str,
|
||||||
|
lookaside_baseurl: str,
|
||||||
|
attempts: int,
|
||||||
|
outdir: Path,
|
||||||
|
) -> Path:
|
||||||
|
clone_url = f"https://src.fedoraproject.org/{namespace}/{package_name}.git"
|
||||||
|
with tempfile.TemporaryDirectory(prefix=f"{package_name}-distgit-") as tempdir_name:
|
||||||
|
tempdir = Path(tempdir_name)
|
||||||
|
package_dir = tempdir / package_name
|
||||||
|
for attempt in range(1, attempts + 1):
|
||||||
|
try:
|
||||||
|
if package_dir.exists():
|
||||||
|
shutil.rmtree(package_dir)
|
||||||
|
run(
|
||||||
|
[
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"--depth",
|
||||||
|
"1",
|
||||||
|
"--branch",
|
||||||
|
branch,
|
||||||
|
clone_url,
|
||||||
|
str(package_dir),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
break
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
if attempt == attempts:
|
||||||
|
raise
|
||||||
|
time.sleep(attempt * 3)
|
||||||
|
spec_files = sorted(package_dir.glob("*.spec"))
|
||||||
|
if not spec_files:
|
||||||
|
raise RuntimeError(f"no spec file found in {clone_url} branch {branch}")
|
||||||
|
spec_path = spec_files[0]
|
||||||
|
download_lookaside_sources(
|
||||||
|
package_name,
|
||||||
|
package_dir,
|
||||||
|
namespace=namespace,
|
||||||
|
lookaside_baseurl=lookaside_baseurl,
|
||||||
|
attempts=attempts,
|
||||||
|
)
|
||||||
|
return build_srpm(spec_path, source_dir=package_dir, outdir=outdir, dist=dist)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
outdir = Path(args.outdir).resolve()
|
||||||
|
|
||||||
|
local_spec = ensure_within_repo(Path(args.spec_ref))
|
||||||
|
if local_spec is not None:
|
||||||
|
srpm = build_srpm(local_spec, source_dir=local_spec.parent, outdir=outdir, dist=args.dist)
|
||||||
|
print(f"built local SRPM {srpm.name}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
srpm = build_from_distgit(
|
||||||
|
args.spec_ref,
|
||||||
|
branch=args.branch,
|
||||||
|
dist=args.dist,
|
||||||
|
namespace=args.namespace,
|
||||||
|
lookaside_baseurl=args.lookaside_baseurl.rstrip("/"),
|
||||||
|
attempts=args.retry_count,
|
||||||
|
outdir=outdir,
|
||||||
|
)
|
||||||
|
print(f"built dist-git SRPM {srpm.name}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -6,13 +6,11 @@ RUN mkdir -p /etc/dnf/dnf.conf.d \
|
|||||||
|
|
||||||
RUN dnf -y upgrade --refresh \
|
RUN dnf -y upgrade --refresh \
|
||||||
&& dnf -y install \
|
&& dnf -y install \
|
||||||
'dnf-command(builddep)' \
|
|
||||||
fedpkg \
|
|
||||||
git \
|
git \
|
||||||
nodejs \
|
make \
|
||||||
python3 \
|
python3 \
|
||||||
rpm-build \
|
rpm-build \
|
||||||
rpmdevtools \
|
copr-cli \
|
||||||
@kde-desktop \
|
ca-certificates \
|
||||||
@development-tools \
|
curl \
|
||||||
&& dnf clean all
|
&& dnf clean all
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=(
|
||||||
|
"Register or update Copr SCM package definitions that use this "
|
||||||
|
"repository's .copr/Makefile and optionally submit builds."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
parser.add_argument("--project", required=True, help="Copr project, e.g. user/project")
|
||||||
|
parser.add_argument("--clone-url", required=True, help="Git clone URL Copr should use for this repository")
|
||||||
|
parser.add_argument("--commit", default="HEAD", help="Git ref Copr should build from")
|
||||||
|
parser.add_argument("--chroot", default="fedora-43-x86_64", help="Copr chroot to configure and build for")
|
||||||
|
parser.add_argument("--packages-file", default="packages.txt", help="Package list to register")
|
||||||
|
parser.add_argument(
|
||||||
|
"--package-input",
|
||||||
|
default="",
|
||||||
|
help="Optional comma, space, or newline separated package list. Overrides --packages-file when set.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--macro-package-name",
|
||||||
|
default="copr-rpm-macros-x86-64-v3",
|
||||||
|
help="Name of the buildroot macro package in Copr",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--macro-package-spec",
|
||||||
|
default="packaging/copr-rpm-macros-x86-64-v3.spec",
|
||||||
|
help="Local spec reference for the buildroot macro package",
|
||||||
|
)
|
||||||
|
parser.add_argument("--webhook-rebuild", choices=("on", "off"), default="off")
|
||||||
|
parser.add_argument("--max-builds", type=int, default=0)
|
||||||
|
parser.add_argument("--timeout", type=int, default=18000)
|
||||||
|
parser.add_argument("--skip-chroot-update", action="store_true")
|
||||||
|
parser.add_argument("--submit-macro-build", action="store_true")
|
||||||
|
parser.add_argument("--submit-package-builds", action="store_true")
|
||||||
|
parser.add_argument("--nowait-package-builds", action="store_true")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def run(command: list[str], *, quiet: bool = False) -> subprocess.CompletedProcess[str]:
|
||||||
|
kwargs: dict[str, object] = {
|
||||||
|
"check": True,
|
||||||
|
"text": True,
|
||||||
|
}
|
||||||
|
if quiet:
|
||||||
|
kwargs["stdout"] = subprocess.DEVNULL
|
||||||
|
kwargs["stderr"] = subprocess.DEVNULL
|
||||||
|
return subprocess.run(command, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def package_exists(project: str, package_name: str) -> bool:
|
||||||
|
result = subprocess.run(
|
||||||
|
["copr", "get-package", project, "--name", package_name],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def load_packages(packages_file: Path, package_input: str) -> list[str]:
|
||||||
|
if package_input.strip():
|
||||||
|
packages = [entry for entry in re.split(r"[\s,]+", package_input.strip()) if entry]
|
||||||
|
else:
|
||||||
|
packages = [
|
||||||
|
line.strip()
|
||||||
|
for line in packages_file.read_text(encoding="utf-8").splitlines()
|
||||||
|
if line.strip() and not line.lstrip().startswith("#")
|
||||||
|
]
|
||||||
|
deduped: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for package in packages:
|
||||||
|
if package not in seen:
|
||||||
|
deduped.append(package)
|
||||||
|
seen.add(package)
|
||||||
|
return deduped
|
||||||
|
|
||||||
|
|
||||||
|
def upsert_scm_package(
|
||||||
|
*,
|
||||||
|
project: str,
|
||||||
|
package_name: str,
|
||||||
|
spec_ref: str,
|
||||||
|
clone_url: str,
|
||||||
|
commit: str,
|
||||||
|
webhook_rebuild: str,
|
||||||
|
max_builds: int,
|
||||||
|
timeout: int,
|
||||||
|
) -> None:
|
||||||
|
action = "edit-package-scm" if package_exists(project, package_name) else "add-package-scm"
|
||||||
|
command = [
|
||||||
|
"copr",
|
||||||
|
action,
|
||||||
|
project,
|
||||||
|
"--name",
|
||||||
|
package_name,
|
||||||
|
"--clone-url",
|
||||||
|
clone_url,
|
||||||
|
"--commit",
|
||||||
|
commit,
|
||||||
|
"--method",
|
||||||
|
"make_srpm",
|
||||||
|
"--spec",
|
||||||
|
spec_ref,
|
||||||
|
"--webhook-rebuild",
|
||||||
|
webhook_rebuild,
|
||||||
|
"--max-builds",
|
||||||
|
str(max_builds),
|
||||||
|
"--timeout",
|
||||||
|
str(timeout),
|
||||||
|
]
|
||||||
|
run(command)
|
||||||
|
|
||||||
|
|
||||||
|
def submit_build(project: str, package_name: str, chroot: str, *, nowait: bool) -> None:
|
||||||
|
command = [
|
||||||
|
"copr",
|
||||||
|
"build-package",
|
||||||
|
project,
|
||||||
|
"--name",
|
||||||
|
package_name,
|
||||||
|
"--chroot",
|
||||||
|
chroot,
|
||||||
|
]
|
||||||
|
if nowait:
|
||||||
|
command.append("--nowait")
|
||||||
|
run(command)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
packages = load_packages(Path(args.packages_file), args.package_input)
|
||||||
|
|
||||||
|
upsert_scm_package(
|
||||||
|
project=args.project,
|
||||||
|
package_name=args.macro_package_name,
|
||||||
|
spec_ref=args.macro_package_spec,
|
||||||
|
clone_url=args.clone_url,
|
||||||
|
commit=args.commit,
|
||||||
|
webhook_rebuild=args.webhook_rebuild,
|
||||||
|
max_builds=args.max_builds,
|
||||||
|
timeout=args.timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
for package_name in packages:
|
||||||
|
upsert_scm_package(
|
||||||
|
project=args.project,
|
||||||
|
package_name=package_name,
|
||||||
|
spec_ref=package_name,
|
||||||
|
clone_url=args.clone_url,
|
||||||
|
commit=args.commit,
|
||||||
|
webhook_rebuild=args.webhook_rebuild,
|
||||||
|
max_builds=args.max_builds,
|
||||||
|
timeout=args.timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not args.skip_chroot_update:
|
||||||
|
run(
|
||||||
|
[
|
||||||
|
"copr",
|
||||||
|
"edit-chroot",
|
||||||
|
f"{args.project}/{args.chroot}",
|
||||||
|
"--packages",
|
||||||
|
args.macro_package_name,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.submit_macro_build:
|
||||||
|
submit_build(args.project, args.macro_package_name, args.chroot, nowait=False)
|
||||||
|
|
||||||
|
if args.submit_package_builds:
|
||||||
|
for package_name in packages:
|
||||||
|
submit_build(
|
||||||
|
args.project,
|
||||||
|
package_name,
|
||||||
|
args.chroot,
|
||||||
|
nowait=args.nowait_package_builds,
|
||||||
|
)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
Name: copr-rpm-macros-x86-64-v3
|
||||||
|
Version: 1
|
||||||
|
Release: 1%{?dist}
|
||||||
|
Summary: Copr buildroot macros for x86_64-v3 rebuilds
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
BuildArch: noarch
|
||||||
|
|
||||||
|
%description
|
||||||
|
This package installs a small RPM macro override for Copr buildroots.
|
||||||
|
It keeps Fedora's standard compiler and linker flag stack intact while
|
||||||
|
raising the x86_64 ISA baseline from x86-64 to x86-64-v3.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
|
||||||
|
%build
|
||||||
|
|
||||||
|
%install
|
||||||
|
mkdir -p %{buildroot}%{rpmmacrodir}
|
||||||
|
cat > %{buildroot}%{rpmmacrodir}/macros.copr-x86-64-v3 <<'EOF'
|
||||||
|
%__cflags_arch_x86_64_level -v3
|
||||||
|
EOF
|
||||||
|
|
||||||
|
%files
|
||||||
|
%{rpmmacrodir}/macros.copr-x86-64-v3
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Mon Apr 27 2026 OpenAI Codex <codex@openai.com> - 1-1
|
||||||
|
- Install a Copr buildroot macro override for x86_64-v3
|
||||||
Reference in New Issue
Block a user