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
This commit is contained in:
@@ -155,3 +155,71 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
uv sync
|
uv sync
|
||||||
uv run main.py
|
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
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pkgdesc="A lightweight, modern Git client built with Tauri"
|
|||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url="https://git.cbsk-tech.de/Christoph/GitLite"
|
url="https://git.cbsk-tech.de/Christoph/GitLite"
|
||||||
license=('MIT')
|
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')
|
makedepends=('rust' 'nodejs' 'npm')
|
||||||
options=('!lto')
|
options=('!lto')
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
## 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`.
|
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`.
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user