From 83ae3ad3cc0e762324f286f383e63a9d203f64e5 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Mon, 24 Aug 2026 21:19:14 +0200 Subject: [PATCH] =?UTF-8?q?Feat:=20Einf=C3=BChrung=20eines=20Skripts=20zur?= =?UTF-8?q?=20dynamischen=20Bestimmung=20der=20Build-Nummer=20und=20Integr?= =?UTF-8?q?ation=20in=20die=20Build-Prozesse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/main.yaml | 31 +++++-- .gitea/workflows/testing.yaml | 31 +++++-- AGENTS.md | 3 +- scripts/get-build-number.py | 164 ++++++++++++++++++++++++++++++++++ scripts/package-arch.py | 32 ++++++- 5 files changed, 239 insertions(+), 22 deletions(-) create mode 100755 scripts/get-build-number.py diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml index 42b0760..4f3e1cb 100644 --- a/.gitea/workflows/main.yaml +++ b/.gitea/workflows/main.yaml @@ -42,24 +42,37 @@ jobs: cargo build --release --target aarch64-unknown-linux-gnu cargo build --release --target i686-unknown-linux-gnu + - name: Determine Build Number + id: build_num + env: + GITEA_URL: ${{ gitea.server_url || github.server_url }} + REPO: ${{ gitea.repository || github.repository }} + REPO_OWNER: ${{ gitea.repository_owner || github.repository_owner }} + TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }} + run: | + BUILD_NUM=$(python3 scripts/get-build-number.py) + echo "build_number=${BUILD_NUM}" >> $GITHUB_OUTPUT + echo "BUILD_NUMBER=${BUILD_NUM}" >> $GITHUB_ENV + echo "Ermittelte Build-Nummer: ${BUILD_NUM}" + - name: Build Debian Packages (.deb) run: | - cargo deb --target x86_64-unknown-linux-gnu --no-build - cargo deb --target aarch64-unknown-linux-gnu --no-build - cargo deb --target i686-unknown-linux-gnu --no-build + cargo deb --target x86_64-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build + cargo deb --target aarch64-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build + cargo deb --target i686-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build - name: Build Fedora / RPM Packages (.rpm) run: | mkdir -p target/generate-rpm - cargo generate-rpm --target x86_64-unknown-linux-gnu -o target/generate-rpm - cargo generate-rpm --target aarch64-unknown-linux-gnu -o target/generate-rpm - cargo generate-rpm --target i686-unknown-linux-gnu -o target/generate-rpm + cargo generate-rpm --target x86_64-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm + cargo generate-rpm --target aarch64-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm + cargo generate-rpm --target i686-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm - name: Build Arch Linux Packages (.pkg.tar.zst) run: | - python3 scripts/package-arch.py --target x86_64-unknown-linux-gnu - python3 scripts/package-arch.py --target aarch64-unknown-linux-gnu - python3 scripts/package-arch.py --target i686-unknown-linux-gnu + python3 scripts/package-arch.py --target x86_64-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" + python3 scripts/package-arch.py --target aarch64-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" + python3 scripts/package-arch.py --target i686-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" - name: Publish Packages to Gitea Package Registry env: diff --git a/.gitea/workflows/testing.yaml b/.gitea/workflows/testing.yaml index 8571718..e0c7561 100644 --- a/.gitea/workflows/testing.yaml +++ b/.gitea/workflows/testing.yaml @@ -42,24 +42,37 @@ jobs: cargo build --release --target aarch64-unknown-linux-gnu cargo build --release --target i686-unknown-linux-gnu + - name: Determine Build Number + id: build_num + env: + GITEA_URL: ${{ gitea.server_url || github.server_url }} + REPO: ${{ gitea.repository || github.repository }} + REPO_OWNER: ${{ gitea.repository_owner || github.repository_owner }} + TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }} + run: | + BUILD_NUM=$(python3 scripts/get-build-number.py) + echo "build_number=${BUILD_NUM}" >> $GITHUB_OUTPUT + echo "BUILD_NUMBER=${BUILD_NUM}" >> $GITHUB_ENV + echo "Ermittelte Build-Nummer: ${BUILD_NUM}" + - name: Build Debian Packages (.deb) run: | - cargo deb --target x86_64-unknown-linux-gnu --no-build - cargo deb --target aarch64-unknown-linux-gnu --no-build - cargo deb --target i686-unknown-linux-gnu --no-build + cargo deb --target x86_64-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build + cargo deb --target aarch64-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build + cargo deb --target i686-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build - name: Build Fedora / RPM Packages (.rpm) run: | mkdir -p target/generate-rpm - cargo generate-rpm --target x86_64-unknown-linux-gnu -o target/generate-rpm - cargo generate-rpm --target aarch64-unknown-linux-gnu -o target/generate-rpm - cargo generate-rpm --target i686-unknown-linux-gnu -o target/generate-rpm + cargo generate-rpm --target x86_64-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm + cargo generate-rpm --target aarch64-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm + cargo generate-rpm --target i686-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm - name: Build Arch Linux Packages (.pkg.tar.zst) run: | - python3 scripts/package-arch.py --target x86_64-unknown-linux-gnu - python3 scripts/package-arch.py --target aarch64-unknown-linux-gnu - python3 scripts/package-arch.py --target i686-unknown-linux-gnu + python3 scripts/package-arch.py --target x86_64-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" + python3 scripts/package-arch.py --target aarch64-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" + python3 scripts/package-arch.py --target i686-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" - name: Publish Packages to Gitea Package Registry env: diff --git a/AGENTS.md b/AGENTS.md index 3be3543..9552807 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,7 +32,8 @@ DockerUpdate/ │ ├── testing.yaml # CI/CD für testing-Branch (Tests, Testing-Packages & Preview-Release) │ └── main.yaml # CI/CD für main-Branch (Tests, Stable-Packages & Release) ├── scripts/ -│ └── package-arch.py # Hilfsskript zur Arch-Linux-Paketerstellung (.pkg.tar.zst) +│ ├── get-build-number.py # Ermittelt dynamisch die nächste Build-Nummer für Pakete +│ └── package-arch.py # Hilfsskript zur Arch-Linux-Paketerstellung (.pkg.tar.zst) ├── Cargo.toml # Rust-Manifest, Abhängigkeiten und Metadaten für deb / generate-rpm / arch ├── Cargo.lock ├── LICENSE # GPL-3.0 Lizenztext diff --git a/scripts/get-build-number.py b/scripts/get-build-number.py new file mode 100755 index 0000000..fc3d02d --- /dev/null +++ b/scripts/get-build-number.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +""" +Ermittelt automatisch die nächste Build-Nummer (Revision / Release / pkgrel) +für eine gegebene Paket-Version (z. B. 1.0.2 -> wenn 1.0.2-1 existiert, wird 2 zurückgegeben). +""" +import argparse +import json +import os +import re +import subprocess +import sys +import urllib.parse +import urllib.request + + +def get_cargo_pkg_info(): + try: + metadata = json.loads(subprocess.check_output(["cargo", "metadata", "--format-version", "1", "--no-deps"])) + pkg = metadata["packages"][0] + name = pkg.get("name", "docker-update") + version = pkg.get("version", "1.0.0") + repository = pkg.get("repository", "") + return name, version, repository + except Exception: + # Fallback auf einfaches Parsen der Cargo.toml + name = "docker-update" + version = "1.0.0" + repository = "" + if os.path.exists("Cargo.toml"): + with open("Cargo.toml", "r") as f: + for line in f: + line = line.strip() + if line.startswith("name ="): + name = line.split("=", 1)[1].strip().strip('"\'') + elif line.startswith("version ="): + version = line.split("=", 1)[1].strip().strip('"\'') + elif line.startswith("repository ="): + repository = line.split("=", 1)[1].strip().strip('"\'') + return name, version, repository + + +def parse_repo_info(repo_url): + """Extrahiert Server-URL, Owner und Repo-Name aus einer Repository-URL.""" + if not repo_url: + return None, None, None + parsed = urllib.parse.urlparse(repo_url) + server_url = f"{parsed.scheme}://{parsed.netloc}" + path_parts = [p for p in parsed.path.strip("/").split("/") if p] + if len(path_parts) >= 2: + owner = path_parts[0] + repo_name = path_parts[1] + if repo_name.endswith(".git"): + repo_name = repo_name[:-4] + return server_url, f"{owner}/{repo_name}", owner + return server_url, None, None + + +def query_existing_build_numbers(name, version, gitea_url, repo, owner, token=None): + build_nums = set() + pattern = re.compile(rf'(?:{re.escape(name)}[_-]|^v?){re.escape(version)}-(\d+)') + + headers = {"User-Agent": "docker-update-build-resolver"} + if token: + headers["Authorization"] = f"token {token}" + + # 1. Gitea Releases API + if gitea_url and repo: + try: + url = f"{gitea_url.rstrip('/')}/api/v1/repos/{repo}/releases?limit=50" + req = urllib.request.Request(url, headers=headers) + with urllib.request.urlopen(req, timeout=10) as resp: + releases = json.loads(resp.read().decode()) + for rel in releases: + tag = rel.get("tag_name", "") + m = pattern.search(tag) + if m: + build_nums.add(int(m.group(1))) + for asset in rel.get("assets", []): + asset_name = asset.get("name", "") + m = pattern.search(asset_name) + if m: + build_nums.add(int(m.group(1))) + except Exception as e: + sys.stderr.write(f"[Hinweis] Konnte Gitea Releases nicht abfragen: {e}\n") + + # 2. Gitea Packages API (falls Token vorhanden) + if gitea_url and owner and token: + try: + url = f"{gitea_url.rstrip('/')}/api/v1/packages/{owner}?limit=50" + req = urllib.request.Request(url, headers=headers) + with urllib.request.urlopen(req, timeout=10) as resp: + packages = json.loads(resp.read().decode()) + for pkg in packages: + pkg_ver = pkg.get("version", "") + m = pattern.search(pkg_ver) + if m: + build_nums.add(int(m.group(1))) + except Exception as e: + sys.stderr.write(f"[Hinweis] Konnte Gitea Packages nicht abfragen: {e}\n") + + # 3. Lokales Target-Verzeichnis prüfen + target_dirs = ["target/debian", "target/generate-rpm", "target/arch"] + for t_dir in target_dirs: + if os.path.isdir(t_dir): + for file in os.listdir(t_dir): + m = pattern.search(file) + if m: + build_nums.add(int(m.group(1))) + + return build_nums + + +def get_next_build_number(version=None, gitea_url=None, repo=None, owner=None, token=None, explicit_build_num=None): + if explicit_build_num is not None: + return int(explicit_build_num) + + env_build_num = os.environ.get("BUILD_NUMBER") or os.environ.get("BUILD_NUM") or os.environ.get("PKGREL") + if env_build_num: + try: + return int(env_build_num) + except ValueError: + pass + + pkg_name, pkg_version, pkg_repo = get_cargo_pkg_info() + if not version: + version = pkg_version + + default_server, default_repo, default_owner = parse_repo_info(pkg_repo) + + gitea_url = gitea_url or os.environ.get("GITEA_URL") or os.environ.get("GITHUB_SERVER_URL") or default_server + repo = repo or os.environ.get("REPO") or os.environ.get("GITHUB_REPOSITORY") or default_repo + owner = owner or os.environ.get("REPO_OWNER") or os.environ.get("GITHUB_REPOSITORY_OWNER") or default_owner + token = token or os.environ.get("TOKEN") or os.environ.get("GITEA_TOKEN") or os.environ.get("PACKAGE_TOKEN") or os.environ.get("RELEASE_TOKEN") + + existing_nums = query_existing_build_numbers(pkg_name, version, gitea_url, repo, owner, token) + if existing_nums: + return max(existing_nums) + 1 + return 1 + + +def main(): + parser = argparse.ArgumentParser(description="Ermittelt die nächste Build-Nummer für die Paketierung.") + parser.add_argument("--version", help="Paket-Version (Standard: aus Cargo.toml)") + parser.add_argument("--gitea-url", help="Gitea Basis-URL") + parser.add_argument("--repo", help="Repository (z. B. Owner/Repo)") + parser.add_argument("--owner", help="Repository Owner / Organisation") + parser.add_argument("--token", help="API-Token") + parser.add_argument("--build-number", type=int, help="Explizite Build-Nummer erzwingen") + + args = parser.parse_args() + + build_num = get_next_build_number( + version=args.version, + gitea_url=args.gitea_url, + repo=args.repo, + owner=args.owner, + token=args.token, + explicit_build_num=args.build_number, + ) + print(build_num) + + +if __name__ == "__main__": + main() diff --git a/scripts/package-arch.py b/scripts/package-arch.py index d6996ad..3bc56ed 100755 --- a/scripts/package-arch.py +++ b/scripts/package-arch.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 import argparse +import importlib.util import json import os import subprocess +import sys import tempfile import time @@ -22,7 +24,28 @@ TARGET_ARCH_MAP = { } -def build_package(target_triple=None, target_arch=None): +def resolve_pkgrel(version=None, default="1"): + # 1. Environment Variable + env_pkgrel = os.environ.get("BUILD_NUMBER") or os.environ.get("BUILD_NUM") or os.environ.get("PKGREL") + if env_pkgrel: + return str(env_pkgrel) + + # 2. get-build-number.py falls vorhanden + script_dir = os.path.dirname(os.path.abspath(__file__)) + getter_path = os.path.join(script_dir, "get-build-number.py") + if os.path.exists(getter_path): + try: + spec = importlib.util.spec_from_file_location("get_build_number", getter_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return str(module.get_next_build_number(version=version)) + except Exception as e: + sys.stderr.write(f"[Hinweis] Konnte get-build-number nicht ausführen: {e}\n") + + return str(default) + + +def build_package(target_triple=None, target_arch=None, pkgrel=None): metadata = json.loads(subprocess.check_output(["cargo", "metadata", "--format-version", "1", "--no-deps"])) pkg = metadata["packages"][0] name = pkg["name"] @@ -34,7 +57,9 @@ def build_package(target_triple=None, target_arch=None): author = authors[0] if authors else "Unknown" arch_meta = pkg.get("metadata", {}).get("arch", {}) - pkgrel = arch_meta.get("pkgrel", "1") + default_pkgrel = arch_meta.get("pkgrel", "1") + if not pkgrel: + pkgrel = resolve_pkgrel(version=version, default=default_pkgrel) default_arch = arch_meta.get("arch", "x86_64") if target_arch: @@ -111,9 +136,10 @@ def main(): parser = argparse.ArgumentParser(description="Erstellt Arch Linux-Pakete (.pkg.tar.zst)") parser.add_argument("--target", help="Rust Target-Triple (z.B. x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, i686-unknown-linux-gnu)") parser.add_argument("--arch", help="Architektur (z.B. x86_64, aarch64, i686)") + parser.add_argument("--pkgrel", help="Release-/Build-Nummer (z.B. 1, 2, ...)") args = parser.parse_args() - build_package(target_triple=args.target, target_arch=args.arch) + build_package(target_triple=args.target, target_arch=args.arch, pkgrel=args.pkgrel) if __name__ == "__main__":