mirror of
https://github.com/funkemunky/KDE-x86_64-v4-Fedora.git
synced 2026-05-31 09:01:56 +00:00
Adding gitea workflow
This commit is contained in:
@@ -1,343 +0,0 @@
|
||||
name: Build Fedora RPMs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package_kind:
|
||||
description: "Interpret package_input as source or binary package names"
|
||||
required: true
|
||||
default: "source"
|
||||
type: choice
|
||||
options:
|
||||
- source
|
||||
- binary
|
||||
package_input:
|
||||
description: "Comma, space, or newline separated package list. Leave blank to use the full manifest."
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
source_manifest:
|
||||
description: "Checked-in source manifest path"
|
||||
required: true
|
||||
default: "manifests/fedora-43/source-packages.txt"
|
||||
type: string
|
||||
source_map:
|
||||
description: "Checked-in binary to source map path"
|
||||
required: true
|
||||
default: "manifests/fedora-43/source-map.tsv"
|
||||
type: string
|
||||
fedora_branch:
|
||||
description: "Fedora dist-git branch"
|
||||
required: true
|
||||
default: "f43"
|
||||
type: string
|
||||
fedora_release:
|
||||
description: "Fedora container release"
|
||||
required: true
|
||||
default: "43"
|
||||
type: string
|
||||
builder_image:
|
||||
description: "Prebuilt Fedora RPM builder image reference"
|
||||
required: true
|
||||
default: "ghcr.io/dawsonc/kde-x86_64-v4-fedora-builder:f43"
|
||||
type: string
|
||||
optimization_level:
|
||||
description: "Compiler optimization level"
|
||||
required: true
|
||||
default: "-O3"
|
||||
type: choice
|
||||
options:
|
||||
- -O2
|
||||
- -O3
|
||||
target_march:
|
||||
description: "Target march override"
|
||||
required: true
|
||||
default: "x86-64-v4"
|
||||
type: string
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.select.outputs.matrix }}
|
||||
package_count: ${{ steps.select.outputs.package_count }}
|
||||
shard_count: ${{ steps.select.outputs.shard_count }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- id: select
|
||||
env:
|
||||
PACKAGE_KIND: ${{ inputs.package_kind }}
|
||||
PACKAGE_INPUT: ${{ inputs.package_input }}
|
||||
SOURCE_MANIFEST: ${{ inputs.source_manifest }}
|
||||
SOURCE_MAP: ${{ inputs.source_map }}
|
||||
run: |
|
||||
python3 <<'PY'
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
source_manifest = Path(os.environ["SOURCE_MANIFEST"])
|
||||
source_map_path = Path(os.environ["SOURCE_MAP"])
|
||||
requested_source_map_path = source_map_path
|
||||
package_kind = os.environ["PACKAGE_KIND"].strip()
|
||||
package_input = os.environ["PACKAGE_INPUT"]
|
||||
if not source_manifest.exists():
|
||||
raise SystemExit(f"Source manifest not found: {source_manifest}")
|
||||
if not source_map_path.exists() and source_map_path.name == "binary-map.tsv":
|
||||
compatibility_path = source_map_path.with_name("source-map.tsv")
|
||||
if compatibility_path.exists():
|
||||
source_map_path = compatibility_path
|
||||
if not source_map_path.exists():
|
||||
raise SystemExit(
|
||||
f"Source map not found: {requested_source_map_path}. "
|
||||
f"Expected an existing TSV such as {requested_source_map_path.with_name('source-map.tsv')}"
|
||||
)
|
||||
source_packages = [
|
||||
line.strip()
|
||||
for line in source_manifest.read_text(encoding="ascii").splitlines()
|
||||
if line.strip()
|
||||
]
|
||||
|
||||
binary_to_source = {}
|
||||
for line in source_map_path.read_text(encoding="ascii").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
binary_name, source_name = line.split("\t", 1)
|
||||
binary_to_source[binary_name] = source_name
|
||||
|
||||
def tokenize(raw: str):
|
||||
parts = re.split(r"[\s,]+", raw.strip())
|
||||
return [part for part in parts if part]
|
||||
|
||||
if package_input.strip():
|
||||
selected = tokenize(package_input)
|
||||
else:
|
||||
selected = source_packages
|
||||
|
||||
if package_kind == "binary":
|
||||
mapped = []
|
||||
missing = []
|
||||
for binary_name in selected:
|
||||
source_name = binary_to_source.get(binary_name)
|
||||
if source_name is None:
|
||||
missing.append(binary_name)
|
||||
else:
|
||||
mapped.append(source_name)
|
||||
if missing:
|
||||
raise SystemExit(
|
||||
"Missing binary-to-source mappings for: " + ", ".join(sorted(missing))
|
||||
)
|
||||
selected = mapped
|
||||
|
||||
deduped = []
|
||||
seen = set()
|
||||
for package_name in selected:
|
||||
if package_name not in seen:
|
||||
seen.add(package_name)
|
||||
deduped.append(package_name)
|
||||
|
||||
max_matrix = 256
|
||||
if not deduped:
|
||||
matrix = []
|
||||
else:
|
||||
shard_size = max(1, -(-len(deduped) // max_matrix))
|
||||
shards = [
|
||||
deduped[index:index + shard_size]
|
||||
for index in range(0, len(deduped), shard_size)
|
||||
]
|
||||
matrix = [
|
||||
{
|
||||
"shard": shard_index + 1,
|
||||
"packages": shard,
|
||||
"package_count": len(shard),
|
||||
}
|
||||
for shard_index, shard in enumerate(shards)
|
||||
]
|
||||
|
||||
github_output = Path(os.environ["GITHUB_OUTPUT"])
|
||||
with github_output.open("a", encoding="utf-8") as fh:
|
||||
fh.write(f"matrix={json.dumps(matrix)}\n")
|
||||
fh.write(f"package_count={len(deduped)}\n")
|
||||
fh.write(f"shard_count={len(matrix)}\n")
|
||||
PY
|
||||
|
||||
- name: Summarize selection
|
||||
run: |
|
||||
echo "Selected packages: ${{ steps.select.outputs.package_count }}" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Shards: ${{ steps.select.outputs.shard_count }}" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo '${{ steps.select.outputs.matrix }}' >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
build:
|
||||
needs: prepare
|
||||
if: ${{ needs.prepare.outputs.package_count != '0' }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ inputs.builder_image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 128
|
||||
matrix:
|
||||
include: ${{ fromJSON(needs.prepare.outputs.matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Cache Fedora DNF data
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
/var/cache/dnf
|
||||
/var/lib/dnf
|
||||
key: dnf-fedora-${{ inputs.fedora_release }}
|
||||
restore-keys: |
|
||||
dnf-fedora-${{ inputs.fedora_release }}-
|
||||
|
||||
- name: Verify builder toolchain
|
||||
run: |
|
||||
command -v dnf
|
||||
command -v fedpkg
|
||||
command -v rpmbuild
|
||||
command -v git
|
||||
command -v node
|
||||
command -v gcc
|
||||
command -v g-ir-scanner
|
||||
command -v gi-docgen
|
||||
rpm -q rpm-build rpmdevtools dnf-plugins-core
|
||||
|
||||
- name: Build shard packages
|
||||
env:
|
||||
SHARD_INDEX: ${{ matrix.shard }}
|
||||
PACKAGES_JSON: ${{ toJSON(matrix.packages) }}
|
||||
FEDORA_BRANCH: ${{ inputs.fedora_branch }}
|
||||
OPT_LEVEL: ${{ inputs.optimization_level }}
|
||||
TARGET_MARCH: ${{ inputs.target_march }}
|
||||
run: |
|
||||
python3 <<'PY'
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
workspace = Path(os.environ["GITHUB_WORKSPACE"])
|
||||
shard_index = int(os.environ["SHARD_INDEX"])
|
||||
packages = json.loads(os.environ["PACKAGES_JSON"])
|
||||
fedora_branch = os.environ["FEDORA_BRANCH"]
|
||||
opt_level = os.environ["OPT_LEVEL"]
|
||||
target_march = os.environ["TARGET_MARCH"]
|
||||
optflags_override = (
|
||||
f"{opt_level} -flto=auto -ffat-lto-objects -fexceptions -g "
|
||||
"-grecord-gcc-switches -pipe -Wall -Wno-complain-wrong-lang "
|
||||
"-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)
|
||||
|
||||
for package_name in packages:
|
||||
package_dir = workspace / package_name
|
||||
if package_dir.exists():
|
||||
shutil.rmtree(package_dir)
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"clone",
|
||||
"--depth",
|
||||
"1",
|
||||
"--branch",
|
||||
fedora_branch,
|
||||
f"https://src.fedoraproject.org/rpms/{package_name}.git",
|
||||
str(package_dir),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(["fedpkg", "sources"], cwd=package_dir, check=True)
|
||||
spec_file = next(package_dir.glob("*.spec"), None)
|
||||
if spec_file is None:
|
||||
raise SystemExit(f"No spec file found for {package_name}")
|
||||
subprocess.run(
|
||||
[
|
||||
"dnf",
|
||||
"-y",
|
||||
"builddep",
|
||||
"--setopt=keepcache=1",
|
||||
"--setopt=skip_if_unavailable=True",
|
||||
str(spec_file),
|
||||
],
|
||||
cwd=package_dir,
|
||||
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}",
|
||||
],
|
||||
cwd=package_dir,
|
||||
check=True,
|
||||
)
|
||||
|
||||
outdir = shard_root / package_name
|
||||
outdir.mkdir(parents=True, exist_ok=True)
|
||||
for rpm_path in (topdir / "RPMS").rglob("*.rpm"):
|
||||
shutil.copy2(rpm_path, outdir / rpm_path.name)
|
||||
for src_path in (topdir / "SRPMS").rglob("*.src.rpm"):
|
||||
shutil.copy2(src_path, outdir / src_path.name)
|
||||
print(f"Built {package_name}")
|
||||
PY
|
||||
|
||||
- name: Collect artifacts
|
||||
env:
|
||||
SHARD_INDEX: ${{ matrix.shard }}
|
||||
run: |
|
||||
SHARD_LABEL=$(printf "%03d" "${SHARD_INDEX}")
|
||||
OUTDIR="${GITHUB_WORKSPACE}/artifacts/shard-${SHARD_LABEL}"
|
||||
ls -la "${OUTDIR}"
|
||||
|
||||
- name: Upload RPM artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rpm-shard-${{ format('{0:03}', matrix.shard) }}
|
||||
path: artifacts/shard-${{ format('{0:03}', matrix.shard) }}/
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Append build summary
|
||||
if: always()
|
||||
env:
|
||||
SHARD_INDEX: ${{ matrix.shard }}
|
||||
run: |
|
||||
SHARD_LABEL=$(printf "%03d" "${SHARD_INDEX}")
|
||||
echo "### shard-${SHARD_LABEL}" >> "$GITHUB_STEP_SUMMARY"
|
||||
if [ -d "artifacts/shard-${SHARD_LABEL}" ]; then
|
||||
find "artifacts/shard-${SHARD_LABEL}" -maxdepth 2 -type f | sort >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "No artifacts collected." >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
@@ -0,0 +1,183 @@
|
||||
name: Build Fedora x86_64-v3 RPMs (Gitea)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'packages.txt'
|
||||
- '.gitea/workflows/build-v3-rpms.yml'
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.select.outputs.matrix }}
|
||||
package_count: ${{ steps.select.outputs.package_count }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- id: select
|
||||
run: |
|
||||
python3 <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
packages_file = Path("packages.txt")
|
||||
if not packages_file.exists():
|
||||
print("::error::packages.txt not found")
|
||||
exit(1)
|
||||
|
||||
packages = [
|
||||
line.strip()
|
||||
for line in packages_file.read_text(encoding="utf-8").splitlines()
|
||||
if line.strip() and not line.startswith("#")
|
||||
]
|
||||
|
||||
# Gitea Actions might have different limits, but 256 is generally safe for matrix
|
||||
max_matrix = 256
|
||||
matrix = []
|
||||
if packages:
|
||||
shard_size = max(1, -(-len(packages) // max_matrix))
|
||||
shards = [
|
||||
packages[index:index + shard_size]
|
||||
for index in range(0, len(packages), shard_size)
|
||||
]
|
||||
matrix = [
|
||||
{
|
||||
"shard": shard_index + 1,
|
||||
"shard_label": f"{shard_index + 1:03d}",
|
||||
"packages": shard,
|
||||
}
|
||||
for shard_index, shard in enumerate(shards)
|
||||
]
|
||||
|
||||
# Gitea Actions supports GITHUB_OUTPUT
|
||||
output_file = os.environ.get("GITHUB_OUTPUT")
|
||||
if output_file:
|
||||
with open(output_file, "a") as fh:
|
||||
fh.write(f"matrix={json.dumps(matrix)}\n")
|
||||
fh.write(f"package_count={len(packages)}\n")
|
||||
else:
|
||||
print(f"::set-output name=matrix::{json.dumps(matrix)}")
|
||||
print(f"::set-output name=package_count::{len(packages)}")
|
||||
PY
|
||||
|
||||
build:
|
||||
needs: prepare
|
||||
if: ${{ needs.prepare.outputs.package_count != '0' }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: fedora:43
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJSON(needs.prepare.outputs.matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build tools
|
||||
run: |
|
||||
dnf -y install 'dnf-command(builddep)' fedpkg rpm-build rpmdevtools git python3
|
||||
|
||||
- name: Build shard packages
|
||||
env:
|
||||
SHARD_INDEX: ${{ matrix.shard }}
|
||||
PACKAGES_JSON: ${{ toJSON(matrix.packages) }}
|
||||
FEDORA_BRANCH: f43
|
||||
TARGET_MARCH: x86-64-v3
|
||||
run: |
|
||||
python3 <<'PY'
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
workspace = Path(os.environ.get("GITHUB_WORKSPACE", "."))
|
||||
shard_index = int(os.environ["SHARD_INDEX"])
|
||||
packages = json.loads(os.environ["PACKAGES_JSON"])
|
||||
fedora_branch = os.environ["FEDORA_BRANCH"]
|
||||
target_march = os.environ["TARGET_MARCH"]
|
||||
|
||||
# Standard Fedora optflags with march override
|
||||
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(
|
||||
[
|
||||
"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
|
||||
|
||||
- name: Upload RPM artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rpm-shard-${{ matrix.shard_label }}
|
||||
path: artifacts/shard-${{ matrix.shard_label }}/
|
||||
if-no-files-found: error
|
||||
@@ -1,5 +1,9 @@
|
||||
# Fedora 43 KDE GitHub Actions RPM Builder
|
||||
# Fedora 43 KDE Actions RPM Builder
|
||||
|
||||
This repository is a static GitHub Actions input repo for rebuilding Fedora 43
|
||||
This repository provides CI workflows for both GitHub Actions and Gitea Actions to rebuild Fedora 43
|
||||
KDE packages from Fedora dist-git with `x86-64-v3` code generation and `-O3`
|
||||
optimization.
|
||||
|
||||
## Workflows
|
||||
- **GitHub Actions**: `.github/workflows/build-v3-rpms.yml`
|
||||
- **Gitea Actions**: `.gitea/workflows/build-v3-rpms.yml`
|
||||
Reference in New Issue
Block a user