From 8220e366290f6983d4b77afdfef8030b102948ca Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Thu, 23 Jul 2026 00:58:32 +0200 Subject: [PATCH] feat(ci): automate arch package build and distribution The continuous integration pipeline is significantly enhanced to fully automate the process of building, packaging, and publishing Gitty as an Arch Linux repository. A new dedicated workflow job handles version setting, dependency resolution, and package creation using makepkg within a Docker container environment. This ensures that every release is properly packaged and uploaded to the configured MinIO/S3 location for system-wide distribution. - Added `publish-arch` job to CI pipeline - Implemented repository management logic in arch_repo.py - Updated PKGBUILD with necessary desktop dependencies --- .gitea/workflows/app_builder.yaml | 68 ++++++++++++++++++++++++ PKGBUILD | 2 +- README.md | 22 ++++++++ cicd_tool/arch_repo.py | 86 +++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 cicd_tool/arch_repo.py diff --git a/.gitea/workflows/app_builder.yaml b/.gitea/workflows/app_builder.yaml index 265f541..2108a51 100644 --- a/.gitea/workflows/app_builder.yaml +++ b/.gitea/workflows/app_builder.yaml @@ -155,3 +155,71 @@ jobs: run: | uv sync uv run main.py + + publish-arch: + name: Build and publish Arch package + runs-on: archlinux + environment: production + env: + MINIO_ENDPOINT: ${{ vars.MINIO_ENDPOINT }} + S3_BUCKET: ${{ vars.S3_BUCKET }} + S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }} + S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }} + + defaults: + run: + working-directory: GitLite + + steps: + - uses: actions/checkout@v5 + with: + path: GitLite + + - name: Set release version + env: + RELEASE_VERSION: ${{ github.ref_name }} + run: | + npm version "$RELEASE_VERSION" --no-git-tag-version --allow-same-version + node -e "const fs=require('fs'); const version=require('./package.json').version; const path='src-tauri/tauri.conf.json'; const config=JSON.parse(fs.readFileSync(path,'utf8')); config.version=version; fs.writeFileSync(path,JSON.stringify(config,null,2)+'\n');" + PACKAGE_VERSION="$(node -p "require('./package.json').version")" + sed -i "s/^pkgver=.*/pkgver=$PACKAGE_VERSION/; s/^pkgrel=.*/pkgrel=1/" PKGBUILD + + - name: Install uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b + + - name: Restore pacman repository database + working-directory: GitLite/cicd_tool + run: | + uv sync + uv run arch_repo.py download-db --output ../arch-repo/gitty.db.tar.gz + + - name: Build pkg.tar.zst with PKGBUILD + run: | + mkdir -p arch-output + docker run --rm \ + -v "$PWD:/source:ro" \ + -v "$PWD/arch-output:/output" \ + archlinux:base-devel bash -lc ' + pacman -Syu --noconfirm git nodejs npm rust webkit2gtk-4.1 gtk3 hicolor-icon-theme libappindicator-gtk3 librsvg xdotool && + useradd --create-home builder && + cp -a /source /build && + chown -R builder:builder /build && + runuser -u builder -- bash -lc "cd /build && makepkg --cleanbuild --noconfirm" && + cp /build/*.pkg.tar.zst /output/ && + chmod 0644 /output/*.pkg.tar.zst + ' + + - name: Update pacman repository database + run: | + mkdir -p arch-repo + docker run --rm \ + -v "$PWD/arch-output:/packages:ro" \ + -v "$PWD/arch-repo:/repo" \ + archlinux:base-devel bash -lc ' + cd /repo && + repo-add gitty.db.tar.gz /packages/*.pkg.tar.zst + ' + + - name: Upload package and repository database to CDN + working-directory: GitLite/cicd_tool + run: uv run arch_repo.py publish --packages-dir ../arch-output --repo-dir ../arch-repo diff --git a/PKGBUILD b/PKGBUILD index 33121dc..40f2798 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -11,7 +11,7 @@ pkgdesc="A lightweight, modern Git client built with Tauri" arch=('x86_64') url="https://git.cbsk-tech.de/Christoph/GitLite" license=('MIT') -depends=('webkit2gtk-4.1' 'gtk3' 'git' 'hicolor-icon-theme') +depends=('webkit2gtk-4.1' 'gtk3' 'git' 'hicolor-icon-theme' 'libappindicator-gtk3' 'librsvg' 'xdotool') makedepends=('rust' 'nodejs' 'npm') options=('!lto') diff --git a/README.md b/README.md index 273b981..3f3e356 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,28 @@ MIT License --- +## Arch Linux + +Release builds are published as an `x86_64` pacman repository. Add this block to +`/etc/pacman.conf` once: + +```ini +[gitty] +SigLevel = Optional TrustAll +Server = https://cdn.cbsk-tech.de/gitty/arch/$arch +``` + +Then install Gitty and receive future releases through the normal system update: + +```bash +sudo pacman -Syu gitty +``` + +The repository is currently distributed over HTTPS but is not GPG-signed. A +repository signing key should be added before recommending it to third parties. + +--- + ## SigNoz telemetry After the user opts in, Gitty sends privacy-filtered product events and technical errors as OTLP/HTTP JSON logs. Every Git/Tauri command is also captured as a trace span with its command name, duration and success state, but without command arguments or results. CPU utilization and resident memory usage of the Gitty process are sampled every 30 seconds and exported as OpenTelemetry gauges. The default endpoints are `https://telemetry.cbsk-tech.de/v1/logs`, `https://telemetry.cbsk-tech.de/v1/traces`, and `https://telemetry.cbsk-tech.de/v1/metrics`. diff --git a/cicd_tool/arch_repo.py b/cicd_tool/arch_repo.py new file mode 100644 index 0000000..efb710a --- /dev/null +++ b/cicd_tool/arch_repo.py @@ -0,0 +1,86 @@ +import argparse +import os +from pathlib import Path + +from minio import Minio +from minio.error import S3Error + +from main import ( + ConfigurationError, + _create_minio_client, + _ensure_bucket, + _parse_publish_targets, +) + + +REPO_PREFIX = "arch/x86_64" +DATABASE_NAME = "gitty.db.tar.gz" + + +def _targets(): + bucket_name = (os.environ.get("S3_BUCKET") or "gitty").lower() + app_dir = (os.environ.get("APP_DIR") or "gitty").strip("/") + return _parse_publish_targets(bucket_name, app_dir) + + +def download_database(client: Minio, output: Path) -> None: + target = _targets()[0] + object_name = f"{REPO_PREFIX}/{DATABASE_NAME}" + output.parent.mkdir(parents=True, exist_ok=True) + + try: + client.fget_object(target.bucket_name, object_name, str(output)) + print(f"Downloaded s3://{target.bucket_name}/{object_name} -> {output}") + except S3Error as exc: + if exc.code in {"NoSuchKey", "NoSuchObject"}: + print("No existing Arch repository database found; creating a new one.") + return + raise + + +def publish_repository(client: Minio, packages_dir: Path, repo_dir: Path) -> None: + packages = sorted(packages_dir.glob("*.pkg.tar.zst")) + if not packages: + raise FileNotFoundError(f"No .pkg.tar.zst package found in {packages_dir}") + + repository_files = [ + path + for path in repo_dir.iterdir() + if path.is_file() and path.name.startswith("gitty.") + ] + if not any(path.name == DATABASE_NAME for path in repository_files): + raise FileNotFoundError(f"Missing repository database: {repo_dir / DATABASE_NAME}") + + for target in _targets(): + _ensure_bucket(client, target.bucket_name) + for path in [*packages, *repository_files]: + object_name = f"{REPO_PREFIX}/{path.name}" + client.fput_object(target.bucket_name, object_name, str(path)) + print(f"Uploaded {path.name} -> s3://{target.bucket_name}/{object_name}") + + +def main() -> None: + parser = argparse.ArgumentParser(description="Manage the Gitty pacman repository on MinIO.") + subparsers = parser.add_subparsers(dest="command", required=True) + + download_parser = subparsers.add_parser("download-db") + download_parser.add_argument("--output", type=Path, required=True) + + publish_parser = subparsers.add_parser("publish") + publish_parser.add_argument("--packages-dir", type=Path, required=True) + publish_parser.add_argument("--repo-dir", type=Path, required=True) + + args = parser.parse_args() + client = _create_minio_client() + + if args.command == "download-db": + download_database(client, args.output) + else: + publish_repository(client, args.packages_dir, args.repo_dir) + + +if __name__ == "__main__": + try: + main() + except (ConfigurationError, FileNotFoundError, S3Error) as exc: + raise SystemExit(f"Arch repository operation failed: {exc}") from exc