From f2b4b5f85f687a4846df5ece27975ae25417de4b Mon Sep 17 00:00:00 2001 From: Commanderx-code Date: Thu, 24 Sep 2026 02:42:06 -0400 Subject: [PATCH] Attach the Arch package to GitHub releases - The arch CI job uploads its package as the arch-package artifact. Tag pushes (and arch_release dispatches) build PKGBUILD unmodified from its release tag and fail if pkgver does not match the tag. - release:draft attaches the Arch package and writes one SHA256SUMS for the .deb, .rpm and Arch package. - npm run check fails if PKGBUILD pkgver differs from package.json. - Document pacman installation of the release asset. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/linux-packages.yml | 33 ++++++++++++++++++++++++---- README.md | 5 +++-- docs/installation.md | 8 +++++++ docs/release-notes.md | 5 +++-- docs/releases.md | 4 ++-- scripts/check.mjs | 3 +++ scripts/release-draft.mjs | 18 +++++++++++---- 7 files changed, 62 insertions(+), 14 deletions(-) diff --git a/.github/workflows/linux-packages.yml b/.github/workflows/linux-packages.yml index 847dbea..7e7e5bb 100644 --- a/.github/workflows/linux-packages.yml +++ b/.github/workflows/linux-packages.yml @@ -5,6 +5,11 @@ on: tags: ["v*"] pull_request: workflow_dispatch: + inputs: + arch_release: + description: Build the Arch package from the release tag named in PKGBUILD, for attaching to that release + type: boolean + default: false permissions: contents: read concurrency: @@ -105,7 +110,9 @@ jobs: - name: Launch installed application as a normal user run: useradd -m smoke && runuser -u smoke -- bash scripts/smoke-desktop.sh /usr/bin/command-center - # Build the Arch recipe from this commit in a clean container, lint it, install it and launch it. + # Build the Arch recipe in a clean container, lint it, install it and launch it. Tag pushes + # (and arch_release dispatches) build the recipe unmodified from its release tag, exactly as + # AUR users will; other pushes build this commit. arch: runs-on: ubuntu-24.04 container: archlinux:base-devel @@ -114,16 +121,34 @@ jobs: - name: Install build, lint and launch-test tools run: pacman -Syu --noconfirm --needed git nodejs npm rust webkit2gtk-4.1 gtk3 libappindicator-gtk3 namcap xorg-server-xvfb xorg-xauth dbus - uses: actions/checkout@v4 - - name: Build package from the checked-out commit + - name: Build package + env: + RELEASE_BUILD: ${{ github.ref_type == 'tag' || inputs.arch_release }} run: | useradd -m builder git config --system --add safe.directory '*' mkdir /build && cp packaging/aur/PKGBUILD /build/ - # The published recipe builds a release tag; CI builds this commit instead. - sed -i "s|^source=.*|source=(\"command-center::git+file://$GITHUB_WORKSPACE#commit=$GITHUB_SHA\")|" /build/PKGBUILD + if [ "$RELEASE_BUILD" = true ]; then + . /build/PKGBUILD + if [ "$GITHUB_REF_TYPE" = tag ] && [ "v$pkgver" != "$GITHUB_REF_NAME" ]; then + echo "PKGBUILD pkgver $pkgver does not match tag $GITHUB_REF_NAME" >&2; exit 1 + fi + else + sed -i "s|^source=.*|source=(\"command-center::git+file://$GITHUB_WORKSPACE#commit=$GITHUB_SHA\")|" /build/PKGBUILD + fi chown -R builder /build cd /build && runuser -u builder -- makepkg --noconfirm - name: Lint recipe and package run: cd /build && namcap PKGBUILD && namcap ./*.pkg.tar.zst - name: Install package and launch as a normal user run: pacman -U --noconfirm /build/*.pkg.tar.zst && runuser -u builder -- bash scripts/smoke-desktop.sh /usr/bin/command-center + - name: Prepare release asset + run: | + mkdir /arch-release + cp /build/command-center-[0-9]*-x86_64.pkg.tar.zst /arch-release/ + cd /arch-release && test "$(ls | wc -l)" -eq 1 && sha256sum -- *.pkg.tar.zst > SHA256SUMS && cat SHA256SUMS + - uses: actions/upload-artifact@v4 + with: + name: arch-package + path: /arch-release/* + if-no-files-found: error diff --git a/README.md b/README.md index 0171c51..83c1352 100644 --- a/README.md +++ b/README.md @@ -83,10 +83,11 @@ Read [Workflows, profiles, and recovery verification](docs/operations.md) for se | --------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | | Debian / Ubuntu | [`.deb` · amd64](https://github.com/Commanderx-code/command-center/releases/download/v0.6.0/command-center_0.6.0_amd64.deb) | `sudo apt install ./command-center_0.6.0_amd64.deb` | | Fedora / RPM | [`.rpm` · x86_64](https://github.com/Commanderx-code/command-center/releases/download/v0.6.0/command-center-0.6.0-1.x86_64.rpm) | `sudo dnf install ./command-center-0.6.0-1.x86_64.rpm` | +| Arch / Garuda | [`.pkg.tar.zst` · x86_64](https://github.com/Commanderx-code/command-center/releases/download/v0.6.0/command-center-0.6.0-1-x86_64.pkg.tar.zst) | `sudo pacman -U ./command-center-0.6.0-1-x86_64.pkg.tar.zst` | -Version 0.6.0 packages require **Linux x86_64, glibc 2.35+, GTK 3, and WebKitGTK 4.1**. They are install-tested on Ubuntu 22.04 and 24.04, Debian 12, and Fedora 43; the release notes link the workflow run. Release assets include `SHA256SUMS` for verification. +Version 0.6.0 packages require **Linux x86_64, glibc 2.35+, GTK 3, and WebKitGTK 4.1**. They are install-tested on Ubuntu 22.04 and 24.04, Debian 12, and Fedora 43, and the Arch package on current Arch; the release notes link the workflow run. Release assets include `SHA256SUMS` for verification. -For Arch/Garuda, build and install from source using the [installation guide](docs/installation.md#from-source-on-archgaruda). +Arch/Garuda users can also build the same package with `makepkg -si` from `packaging/aur/`, or develop from source using the [installation guide](docs/installation.md#from-source-on-archgaruda). ## Get started diff --git a/docs/installation.md b/docs/installation.md index f40e899..74c8195 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -28,6 +28,14 @@ On a compatible Fedora/RPM system: sudo dnf install ./command-center-0.6.0-1.x86_64.rpm ``` +On Arch or an Arch-based system such as Garuda (0.6.0 and later): + +```bash +sudo pacman -U ./command-center-0.6.0-1-x86_64.pkg.tar.zst +``` + +The Arch package is built from the release tag with `packaging/aur/PKGBUILD` in a clean Arch container and tracks current Arch libraries; update your system before installing it. To build it yourself instead, run `makepkg -si` from a copy of `packaging/aur/`. + Launch **Command Center** from your application menu. Run the app as your normal user, without `sudo`. ## From source on Arch/Garuda diff --git a/docs/release-notes.md b/docs/release-notes.md index e723617..a778365 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -26,6 +26,9 @@ sudo apt install ./command-center_0.6.0_amd64.deb # Fedora / RPM sudo dnf install ./command-center-0.6.0-1.x86_64.rpm + +# Arch / Garuda (built against current Arch libraries) +sudo pacman -U ./command-center-0.6.0-1-x86_64.pkg.tar.zst ``` Download `SHA256SUMS` beside the package and run: @@ -33,5 +36,3 @@ Download `SHA256SUMS` beside the package and run: ```sh sha256sum --check --ignore-missing SHA256SUMS ``` - -Arch users can build `packaging/aur/PKGBUILD` with `makepkg -si`. diff --git a/docs/releases.md b/docs/releases.md index 78c4f06..b88b21e 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -30,7 +30,7 @@ git push origin v0.6.0 npm run release:draft -- v0.6.0 ``` -The draft command requires an authenticated GitHub CLI (`gh`). It checks for a clean working tree and a matching local/remote tag and runs the checks and tests. It then finds the successful **Linux packages** run for the tagged commit, downloads that run's `packages` artifact, verifies its `SHA256SUMS`, and creates an **unpublished** GitHub release. The release notes are docs/release-notes.md plus a build-and-validation section linking the workflow run. If CI has not passed for the tag, no draft is created. The command never builds release packages locally: a build on a newer distribution such as Garuda would require a newer glibc than the packages declare. It does not push tags or publish the draft. Download and verify the hosted assets and test the app on your machine before publishing. +The draft command requires an authenticated GitHub CLI (`gh`). It checks for a clean working tree and a matching local/remote tag and runs the checks and tests. It then finds the successful **Linux packages** run for the tagged commit, downloads that run's `packages` and `arch-package` artifacts, verifies their checksums, writes one `SHA256SUMS` covering the `.deb`, `.rpm`, and Arch package, and creates an **unpublished** GitHub release. The release notes are docs/release-notes.md plus a build-and-validation section linking the workflow run. If CI has not passed for the tag, no draft is created. The command never builds release packages locally: a build on a newer distribution such as Garuda would require a newer glibc than the packages declare. It does not push tags or publish the draft. Download and verify the hosted assets and test the app on your machine before publishing. The app discovers only published releases. Building a package, creating a tag, or preparing a draft does not publish a release. @@ -44,7 +44,7 @@ The **Linux packages** workflow runs these jobs: | `build` | Builds the `.deb` and `.rpm` once in an `ubuntu:22.04` container (glibc 2.35), validates them, and uploads the `packages` artifact with `SHA256SUMS`. | | `install-deb` | Installs that `.deb` on Ubuntu 22.04, Debian 12, and Ubuntu 24.04 and runs a 20-second launch check under a virtual display as an ordinary user. | | `install-rpm` | Installs that `.rpm` on Fedora 43 and runs the same launch check. | -| `arch` | Builds `packaging/aur/PKGBUILD` from the pushed commit in a clean `archlinux` container, lints the recipe and package with namcap, installs, and launches it. | +| `arch` | Builds `packaging/aur/PKGBUILD` in a clean `archlinux` container, lints the recipe and package with namcap, installs, and launches it. Tag pushes build the recipe unmodified from its release tag (the pkgver must match the tag) and upload the `arch-package` artifact; other pushes build the pushed commit. | These are installation and launch checks, not end-to-end validation of system-changing workflows. diff --git a/scripts/check.mjs b/scripts/check.mjs index fc3b9df..fca53b5 100644 --- a/scripts/check.mjs +++ b/scripts/check.mjs @@ -28,6 +28,9 @@ const toolbox = await readFile( new URL("src-tauri/src/toolbox.rs", root), "utf8", ); +const pkgbuild = await readFile(new URL("packaging/aur/PKGBUILD", root), "utf8"); +if (pkgbuild.match(/^pkgver=(.+)$/m)?.[1] !== pkg.version) + throw new Error("packaging/aur/PKGBUILD pkgver must match package.json"); const pinned = cargo.match(/linutil_core[^\n]+rev = "([a-f0-9]+)"/)?.[1]; const reported = toolbox.match(/REVISION: &str = "([a-f0-9]+)"/)?.[1]; if (!pinned || pinned !== reported) diff --git a/scripts/release-draft.mjs b/scripts/release-draft.mjs index ccbf8f4..ab2cd89 100644 --- a/scripts/release-draft.mjs +++ b/scripts/release-draft.mjs @@ -1,4 +1,4 @@ -import {readFileSync,writeFileSync,rmSync,mkdtempSync} from 'node:fs'; +import {readFileSync,writeFileSync,rmSync,mkdtempSync,copyFileSync} from 'node:fs'; import {tmpdir} from 'node:os'; import {join} from 'node:path'; import {spawnSync} from 'node:child_process'; @@ -25,11 +25,21 @@ if(!passed)throw new Error(`No successful Linux packages run for ${head.slice(0, const out=new URL('artifacts/release/',root).pathname; rmSync(out,{recursive:true,force:true}); run('gh',['run','download',String(passed.databaseId),'--repo',repo,'--name','packages','--dir',out]); -const assets=[`command-center_${version}_amd64.deb`,`command-center-${version}-1.x86_64.rpm`]; +const packages=[`command-center_${version}_amd64.deb`,`command-center-${version}-1.x86_64.rpm`]; const sums=readFileSync(join(out,'SHA256SUMS'),'utf8'); -for(const asset of assets)if(!sums.includes(` ${asset}\n`))throw new Error(`${asset} is missing from the CI artifact`); +for(const asset of packages)if(!sums.includes(` ${asset}\n`))throw new Error(`${asset} is missing from the CI artifact`); run('sha256sum',['--check','--strict','SHA256SUMS'],out); +// The Arch package is built from the release tag by the same run's arch job. +const pkgrel=readFileSync(new URL('packaging/aur/PKGBUILD',root),'utf8').match(/^pkgrel=(\d+)$/m)?.[1]; +const arch=`command-center-${version}-${pkgrel}-x86_64.pkg.tar.zst`; +const archDir=mkdtempSync(join(tmpdir(),'command-center-arch-')); +run('gh',['run','download',String(passed.databaseId),'--repo',repo,'--name','arch-package','--dir',archDir]); +if(!readFileSync(join(archDir,'SHA256SUMS'),'utf8').includes(` ${arch}\n`))throw new Error(`${arch} is missing from the CI artifact`); +run('sha256sum',['--check','--strict','SHA256SUMS'],archDir); +copyFileSync(join(archDir,arch),join(out,arch)); +const assets=[...packages,arch]; +writeFileSync(join(out,'SHA256SUMS'),run('sha256sum',assets,out)+'\n'); const notes=join(mkdtempSync(join(tmpdir(),'command-center-release-')),'notes.md'); -writeFileSync(notes,`${readFileSync(new URL('docs/release-notes.md',root),'utf8').trimEnd()}\n\n### Build and validation\n\nBoth packages were built once on Ubuntu 22.04 (glibc 2.35), then installed and launched as a normal user on Ubuntu 22.04, Debian 12, Ubuntu 24.04 and Fedora 43. The Arch recipe was built from the same commit in a clean container, linted with namcap, installed and launched. Workflow run: ${passed.url}\n`); +writeFileSync(notes,`${readFileSync(new URL('docs/release-notes.md',root),'utf8').trimEnd()}\n\n### Build and validation\n\nBoth packages were built once on Ubuntu 22.04 (glibc 2.35), then installed and launched as a normal user on Ubuntu 22.04, Debian 12, Ubuntu 24.04 and Fedora 43. The Arch package was built from the release tag with packaging/aur/PKGBUILD in a clean container, linted with namcap, installed and launched. Workflow run: ${passed.url}\n`); console.log(run('gh',['release','create',tag,...assets.map(a=>join(out,a)),join(out,'SHA256SUMS'),'--repo',repo,'--verify-tag','--draft','--title',`Command Center ${tag}`,'--notes-file',notes])); console.log(`Draft created from ${passed.url}. Download and verify the hosted assets, test the app on your machine, then publish on GitHub.`);