diff --git a/.gitea/workflows/build-v3-rpms.yml b/.gitea/workflows/build-v3-rpms.yml index 7789386..54c2d38 100644 --- a/.gitea/workflows/build-v3-rpms.yml +++ b/.gitea/workflows/build-v3-rpms.yml @@ -5,6 +5,7 @@ on: push: paths: - 'packages.txt' + - 'ci/assemble-dnf-repo.py' - '.gitea/workflows/build-v3-rpms.yml' jobs: @@ -300,3 +301,59 @@ jobs: 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 + container: + image: ghcr.io/funkemunky/kde-x86_64-v4-fedora-rpm-builder:latest + strategy: + fail-fast: false + matrix: + include: + - arch: x86-64-v3 + repo_id: fedora43-kde-x86-64-v3 + - arch: x86-64-v4 + repo_id: fedora43-kde-x86-64-v4 + steps: + - uses: actions/checkout@v4 + + - name: Download RPM shard artifacts + uses: actions/download-artifact@v3 + with: + path: downloaded-rpms + + - name: Stage matching shard artifacts + run: | + mkdir -p filtered-rpms + 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 + run: | + if command -v createrepo_c >/dev/null 2>&1 + then + echo "createrepo_c 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 createrepo_c + + - name: Assemble DNF repository + run: | + python3 ci/assemble-dnf-repo.py \ + --input-dir filtered-rpms \ + --output-dir repo/${{ matrix.arch }} \ + --repo-id ${{ matrix.repo_id }} \ + --repo-name 'Fedora 43 KDE ${{ matrix.arch }}' + + - name: Upload DNF repository artifact + uses: actions/upload-artifact@v3 + with: + name: dnf-repo-${{ matrix.arch }} + path: repo/${{ matrix.arch }}/ + if-no-files-found: error diff --git a/.github/workflows/build-v3-rpms.yml b/.github/workflows/build-v3-rpms.yml index 64a4f66..3f8c7e9 100644 --- a/.github/workflows/build-v3-rpms.yml +++ b/.github/workflows/build-v3-rpms.yml @@ -5,6 +5,7 @@ on: push: paths: - 'packages.txt' + - 'ci/assemble-dnf-repo.py' - '.github/workflows/build-v3-rpms.yml' permissions: @@ -199,3 +200,44 @@ jobs: name: rpm-shard-${{ matrix.shard_label }} path: artifacts/shard-${{ matrix.shard_label }}/ if-no-files-found: error + + assemble-repo: + needs: build + runs-on: ubuntu-latest + container: + image: ghcr.io/funkemunky/kde-x86_64-v4-fedora-rpm-builder:latest + steps: + - uses: actions/checkout@v4 + + - name: Download RPM shard artifacts + uses: actions/download-artifact@v4 + with: + pattern: rpm-shard-* + path: downloaded-rpms + + - name: Install repo assembly tools + run: | + if command -v createrepo_c >/dev/null 2>&1 + then + echo "createrepo_c 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 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 DNF repository artifact + uses: actions/upload-artifact@v4 + with: + name: dnf-repo-x86-64-v3 + path: repo/fedora43-x86-64-v3/ + if-no-files-found: error diff --git a/README.md b/README.md index 12c355f..a88d0ef 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,17 @@ optimization. ## Workflows - **GitHub Actions**: `.github/workflows/build-v3-rpms.yml` -- **Gitea Actions**: `.gitea/workflows/build-v3-rpms.yml` \ No newline at end of file +- **Gitea Actions**: `.gitea/workflows/build-v3-rpms.yml` + +Each build workflow now finishes with a repository assembly job that collects the shard artifacts, +generates `repodata/` with `createrepo_c`, and uploads a ready-to-serve DNF repository artifact. + +## Repository artifacts +- **GitHub Actions** uploads `dnf-repo-x86-64-v3` +- **Gitea Actions** uploads `dnf-repo-x86-64-v3` and `dnf-repo-x86-64-v4` + +Each repository artifact contains: +- `packages/` with the built binary RPMs +- `repodata/` generated for DNF +- a `.repo` template with a placeholder `baseurl` +- `README.txt` with Fedora 43 install instructions diff --git a/ci/assemble-dnf-repo.py b/ci/assemble-dnf-repo.py new file mode 100644 index 0000000..5388243 --- /dev/null +++ b/ci/assemble-dnf-repo.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +import argparse +import shutil +import subprocess +from pathlib import Path + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Assemble built RPM artifacts into a DNF repository." + ) + parser.add_argument("--input-dir", required=True, help="Directory containing built RPM artifacts") + parser.add_argument("--output-dir", required=True, help="Directory where the repo should be created") + parser.add_argument("--repo-id", required=True, help="DNF repository ID to embed in the template") + parser.add_argument("--repo-name", required=True, help="Human-readable repository name") + return parser.parse_args() + + +def main() -> int: + args = parse_args() + input_dir = Path(args.input_dir).resolve() + output_dir = Path(args.output_dir).resolve() + packages_dir = output_dir / "packages" + + if not input_dir.exists(): + raise SystemExit(f"input directory does not exist: {input_dir}") + + output_dir.mkdir(parents=True, exist_ok=True) + packages_dir.mkdir(parents=True, exist_ok=True) + + rpm_paths = sorted( + path + for path in input_dir.rglob("*.rpm") + if not path.name.endswith(".src.rpm") + ) + + if not rpm_paths: + raise SystemExit(f"no binary RPMs found under {input_dir}") + + copied = 0 + for rpm_path in rpm_paths: + destination = packages_dir / rpm_path.name + if destination.exists(): + if rpm_path.stat().st_size != destination.stat().st_size: + raise SystemExit(f"conflicting RPM filename encountered: {rpm_path.name}") + continue + shutil.copy2(rpm_path, destination) + copied += 1 + + subprocess.run( + ["createrepo_c", "--database", str(output_dir)], + check=True, + ) + + repo_file = output_dir / f"{args.repo_id}.repo" + repo_file.write_text( + "\n".join( + [ + f"[{args.repo_id}]", + f"name={args.repo_name}", + "baseurl=REPLACE_WITH_REPO_URL", + "enabled=1", + "gpgcheck=0", + "repo_gpgcheck=0", + "", + ] + ), + encoding="utf-8", + ) + + readme_file = output_dir / "README.txt" + readme_file.write_text( + "\n".join( + [ + args.repo_name, + "", + f"Binary RPMs copied: {copied}", + "", + "Usage:", + "1. Publish this directory over HTTP(S) or copy it to a local path on Fedora 43.", + f"2. Edit {repo_file.name} and replace REPLACE_WITH_REPO_URL with the repo root URL.", + " Example HTTP URL: http://your-server/path/to/repo", + " Example local URL: file:///srv/repos/your-repo", + "3. Install the repo file on the target machine:", + f" sudo install -Dm0644 {repo_file.name} /etc/yum.repos.d/{repo_file.name}", + "4. Refresh metadata and install packages:", + " sudo dnf clean all", + " sudo dnf makecache", + " sudo dnf install ", + "", + ] + ), + encoding="utf-8", + ) + + print(f"assembled {copied} RPMs into {output_dir}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())