From 46a9f48043ed923054803f394f644e82cc3a12a6 Mon Sep 17 00:00:00 2001 From: OneNoted Date: Mon, 13 Apr 2026 12:27:11 +0200 Subject: [PATCH 1/4] Make CUDA-enabled installs explicit in the AUR package set Keep the existing `whispers-bin` and `whispers-git` packages as the portable non-CUDA path, add explicit `whispers-cuda-bin` and `whispers-cuda-git` variants, and update the README/package docs so Arch users can pick the right install path without guessing. Constraint: Existing live AUR package names should not silently change semantics Rejected: Turn `whispers-bin` and `whispers-git` into CUDA-first packages | breaking change for current portable-package users Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep portable and CUDA package names/documentation explicit so users know which runtime path they are choosing Tested: makepkg --printsrcinfo; makepkg --verifysource (whispers-cuda-git); local bundle package build for whispers-cuda-bin Not-tested: Live CUDA release asset checksum until workflow publication --- README.md | 29 ++++++--- packaging/aur/README.md | 19 ++++-- packaging/aur/whispers-bin/.SRCINFO | 2 + packaging/aur/whispers-bin/PKGBUILD | 2 +- packaging/aur/whispers-cuda-bin/.SRCINFO | 23 +++++++ packaging/aur/whispers-cuda-bin/PKGBUILD | 45 ++++++++++++++ packaging/aur/whispers-cuda-git/.SRCINFO | 29 +++++++++ packaging/aur/whispers-cuda-git/PKGBUILD | 76 ++++++++++++++++++++++++ packaging/aur/whispers-git/.SRCINFO | 2 + packaging/aur/whispers-git/PKGBUILD | 2 +- 10 files changed, 214 insertions(+), 15 deletions(-) create mode 100644 packaging/aur/whispers-cuda-bin/.SRCINFO create mode 100644 packaging/aur/whispers-cuda-bin/PKGBUILD create mode 100644 packaging/aur/whispers-cuda-git/.SRCINFO create mode 100644 packaging/aur/whispers-cuda-git/PKGBUILD diff --git a/README.md b/README.md index 22569a4..e6e99c7 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,20 @@ ### Arch Linux (`paru`) +#### CUDA-enabled + +```sh +# prebuilt GitHub release bundle with CUDA support +paru -S whispers-cuda-bin + +# latest main branch build with CUDA support +paru -S whispers-cuda-git +``` + +Use these when you want the CUDA-enabled local path from the AUR. + +#### Portable / non-CUDA + ```sh # prebuilt GitHub release bundle paru -S whispers-bin @@ -44,9 +58,10 @@ paru -S whispers-bin paru -S whispers-git ``` -- `whispers-bin` installs the published Linux x86_64 release bundle. -- `whispers-git` builds the latest `main` branch from source. -- Both AUR packages currently ship the portable `local-rewrite,osd` feature set. +- `whispers-cuda-bin` installs the published Linux x86_64 CUDA bundle. +- `whispers-cuda-git` builds the latest `main` branch with `cuda,local-rewrite,osd`. +- `whispers-bin` installs the published portable non-CUDA Linux x86_64 bundle. +- `whispers-git` builds the latest `main` branch with the portable `local-rewrite,osd` feature set. ### Cargo @@ -175,13 +190,13 @@ Those bundles include the current status snapshot plus best-effort stack and ope Tagged releases publish a Linux x86_64 bundle with: -- `whispers` -- `whispers-osd` -- `whispers-rewrite-worker` +- a portable `whispers--x86_64-unknown-linux-gnu.tar.gz` +- a CUDA-enabled `whispers-cuda--x86_64-unknown-linux-gnu.tar.gz` +- `whispers`, `whispers-osd`, and `whispers-rewrite-worker` - Bash, Zsh, and Fish completions - `README.md`, `config.example.toml`, `LICENSE`, and `NOTICE` -That bundle is what the `whispers-bin` AUR package installs. +Those bundles are what the `whispers-bin` and `whispers-cuda-bin` AUR packages install. ## License diff --git a/packaging/aur/README.md b/packaging/aur/README.md index 18f8393..6f38e3c 100644 --- a/packaging/aur/README.md +++ b/packaging/aur/README.md @@ -2,10 +2,15 @@ This repository keeps the maintained AUR sources for: -- `whispers-bin`: installs the GitHub release bundle -- `whispers-git`: builds the latest `main` branch from source +- `whispers-bin`: installs the portable GitHub release bundle +- `whispers-git`: builds the latest `main` branch without CUDA +- `whispers-cuda-bin`: installs the CUDA-enabled GitHub release bundle +- `whispers-cuda-git`: builds the latest `main` branch with CUDA -Both packages currently ship the portable `local-rewrite,osd` feature set. +Current feature sets: + +- `whispers-bin` / `whispers-git`: `local-rewrite,osd` +- `whispers-cuda-bin` / `whispers-cuda-git`: `cuda,local-rewrite,osd` ## Refreshing metadata @@ -16,11 +21,11 @@ directory: makepkg --printsrcinfo > .SRCINFO ``` -## Updating `whispers-bin` +## Updating `whispers-bin` / `whispers-cuda-bin` 1. Cut a GitHub release for `vX.Y.Z`. -2. Update `pkgver` and `sha256sums` in `packaging/aur/whispers-bin/PKGBUILD`. -3. Regenerate `packaging/aur/whispers-bin/.SRCINFO`. +2. Update `pkgver` and `sha256sums` in the matching `*-bin/PKGBUILD`. +3. Regenerate the matching `*-bin/.SRCINFO`. ## Publishing to the AUR @@ -29,3 +34,5 @@ matching directory to: - `ssh://aur@aur.archlinux.org/whispers-bin.git` - `ssh://aur@aur.archlinux.org/whispers-git.git` +- `ssh://aur@aur.archlinux.org/whispers-cuda-bin.git` +- `ssh://aur@aur.archlinux.org/whispers-cuda-git.git` diff --git a/packaging/aur/whispers-bin/.SRCINFO b/packaging/aur/whispers-bin/.SRCINFO index e58b69e..48b85be 100644 --- a/packaging/aur/whispers-bin/.SRCINFO +++ b/packaging/aur/whispers-bin/.SRCINFO @@ -14,6 +14,8 @@ pkgbase = whispers-bin provides = whispers conflicts = whispers conflicts = whispers-git + conflicts = whispers-cuda-bin + conflicts = whispers-cuda-git source = whispers-bin-0.2.1.tar.gz::https://github.com/OneNoted/whispers/releases/download/v0.2.1/whispers-0.2.1-x86_64-unknown-linux-gnu.tar.gz sha256sums = b5f1ce5d1e66cece42bb5129c0eb7ab93cc118a7e705847a4a1c8e75cd60ad1e diff --git a/packaging/aur/whispers-bin/PKGBUILD b/packaging/aur/whispers-bin/PKGBUILD index 864d1c7..3cc8ff2 100644 --- a/packaging/aur/whispers-bin/PKGBUILD +++ b/packaging/aur/whispers-bin/PKGBUILD @@ -8,7 +8,7 @@ license=('MIT') depends=('alsa-lib' 'gcc-libs' 'glibc' 'noto-fonts' 'wl-clipboard') optdepends=('python: experimental faster-whisper and NeMo runtimes') provides=('whispers') -conflicts=('whispers' 'whispers-git') +conflicts=('whispers' 'whispers-git' 'whispers-cuda-bin' 'whispers-cuda-git') source=( "$pkgname-$pkgver.tar.gz::$url/releases/download/v$pkgver/whispers-$pkgver-x86_64-unknown-linux-gnu.tar.gz" ) diff --git a/packaging/aur/whispers-cuda-bin/.SRCINFO b/packaging/aur/whispers-cuda-bin/.SRCINFO new file mode 100644 index 0000000..e91ffaa --- /dev/null +++ b/packaging/aur/whispers-cuda-bin/.SRCINFO @@ -0,0 +1,23 @@ +pkgbase = whispers-cuda-bin + pkgdesc = Local-first speech-to-text dictation for Wayland (prebuilt CUDA release bundle) + pkgver = 0.2.1 + pkgrel = 1 + url = https://github.com/OneNoted/whispers + arch = x86_64 + license = MIT + depends = alsa-lib + depends = cuda + depends = gcc-libs + depends = glibc + depends = noto-fonts + depends = wl-clipboard + optdepends = python: experimental faster-whisper and NeMo runtimes + provides = whispers + conflicts = whispers + conflicts = whispers-bin + conflicts = whispers-git + conflicts = whispers-cuda-git + source = whispers-cuda-bin-0.2.1.tar.gz::https://github.com/OneNoted/whispers/releases/download/v0.2.1/whispers-cuda-0.2.1-x86_64-unknown-linux-gnu.tar.gz + sha256sums = REPLACE_WITH_CUDA_RELEASE_SHA256 + +pkgname = whispers-cuda-bin diff --git a/packaging/aur/whispers-cuda-bin/PKGBUILD b/packaging/aur/whispers-cuda-bin/PKGBUILD new file mode 100644 index 0000000..c0802b6 --- /dev/null +++ b/packaging/aur/whispers-cuda-bin/PKGBUILD @@ -0,0 +1,45 @@ +pkgname=whispers-cuda-bin +pkgver=0.2.1 +pkgrel=1 +pkgdesc='Local-first speech-to-text dictation for Wayland (prebuilt CUDA release bundle)' +arch=('x86_64') +url='https://github.com/OneNoted/whispers' +license=('MIT') +depends=('alsa-lib' 'cuda' 'gcc-libs' 'glibc' 'noto-fonts' 'wl-clipboard') +optdepends=('python: experimental faster-whisper and NeMo runtimes') +provides=('whispers') +conflicts=('whispers' 'whispers-bin' 'whispers-git' 'whispers-cuda-git') +source=( + "$pkgname-$pkgver.tar.gz::$url/releases/download/v$pkgver/whispers-cuda-$pkgver-x86_64-unknown-linux-gnu.tar.gz" +) +sha256sums=('REPLACE_WITH_CUDA_RELEASE_SHA256') + +package() { + local bundle_dir="$srcdir/whispers-cuda-$pkgver-x86_64-unknown-linux-gnu" + + install -Dm755 "$bundle_dir/bin/whispers" \ + "$pkgdir/usr/bin/whispers" + install -Dm755 "$bundle_dir/bin/whispers-osd" \ + "$pkgdir/usr/bin/whispers-osd" + install -Dm755 "$bundle_dir/bin/whispers-rewrite-worker" \ + "$pkgdir/usr/bin/whispers-rewrite-worker" + + install -Dm644 "$bundle_dir/share/bash-completion/completions/whispers" \ + "$pkgdir/usr/share/bash-completion/completions/whispers" + install -Dm644 "$bundle_dir/share/zsh/site-functions/_whispers" \ + "$pkgdir/usr/share/zsh/site-functions/_whispers" + install -Dm644 "$bundle_dir/share/fish/vendor_completions.d/whispers.fish" \ + "$pkgdir/usr/share/fish/vendor_completions.d/whispers.fish" + + install -Dm644 "$bundle_dir/share/doc/whispers/README.md" \ + "$pkgdir/usr/share/doc/whispers/README.md" + install -Dm644 "$bundle_dir/share/doc/whispers/config.example.toml" \ + "$pkgdir/usr/share/doc/whispers/config.example.toml" + install -Dm644 "$bundle_dir/share/doc/whispers/RELEASE-BUNDLE.txt" \ + "$pkgdir/usr/share/doc/whispers/RELEASE-BUNDLE.txt" + + install -Dm644 "$bundle_dir/share/licenses/whispers/LICENSE" \ + "$pkgdir/usr/share/licenses/whispers/LICENSE" + install -Dm644 "$bundle_dir/share/licenses/whispers/NOTICE" \ + "$pkgdir/usr/share/licenses/whispers/NOTICE" +} diff --git a/packaging/aur/whispers-cuda-git/.SRCINFO b/packaging/aur/whispers-cuda-git/.SRCINFO new file mode 100644 index 0000000..ba8ba06 --- /dev/null +++ b/packaging/aur/whispers-cuda-git/.SRCINFO @@ -0,0 +1,29 @@ +pkgbase = whispers-cuda-git + pkgdesc = Local-first speech-to-text dictation for Wayland (latest git build with CUDA) + pkgver = r0.0000000 + pkgrel = 1 + url = https://github.com/OneNoted/whispers + arch = x86_64 + license = MIT + makedepends = cargo + makedepends = clang + makedepends = cmake + makedepends = cuda + makedepends = git + makedepends = pkgconf + depends = alsa-lib + depends = cuda + depends = gcc-libs + depends = glibc + depends = noto-fonts + depends = wl-clipboard + optdepends = python: experimental faster-whisper and NeMo runtimes + provides = whispers + conflicts = whispers + conflicts = whispers-bin + conflicts = whispers-git + conflicts = whispers-cuda-bin + source = whispers::git+https://github.com/OneNoted/whispers.git + sha256sums = SKIP + +pkgname = whispers-cuda-git diff --git a/packaging/aur/whispers-cuda-git/PKGBUILD b/packaging/aur/whispers-cuda-git/PKGBUILD new file mode 100644 index 0000000..3d9effe --- /dev/null +++ b/packaging/aur/whispers-cuda-git/PKGBUILD @@ -0,0 +1,76 @@ +pkgname=whispers-cuda-git +_pkgname=whispers +pkgver=r0.0000000 +pkgrel=1 +pkgdesc='Local-first speech-to-text dictation for Wayland (latest git build with CUDA)' +arch=('x86_64') +url='https://github.com/OneNoted/whispers' +license=('MIT') +makedepends=('cargo' 'clang' 'cmake' 'cuda' 'git' 'pkgconf') +depends=('alsa-lib' 'cuda' 'gcc-libs' 'glibc' 'noto-fonts' 'wl-clipboard') +optdepends=('python: experimental faster-whisper and NeMo runtimes') +provides=('whispers') +conflicts=('whispers' 'whispers-bin' 'whispers-git' 'whispers-cuda-bin') +source=("$_pkgname::git+$url.git") +sha256sums=('SKIP') + +pkgver() { + cd "$srcdir/$_pkgname" + + if git describe --long --abbrev=7 --tags >/dev/null 2>&1; then + git describe --long --abbrev=7 --tags | sed 's/^v//; s/-/.r/; s/-/./g' + else + printf 'r%s.%s' \ + "$(git rev-list --count HEAD)" \ + "$(git rev-parse --short=7 HEAD)" + fi +} + +prepare() { + cd "$srcdir/$_pkgname" + cargo fetch --locked +} + +build() { + cd "$srcdir/$_pkgname" + + export CARGO_TARGET_DIR=target + cargo build \ + --frozen \ + --release \ + --no-default-features \ + --features cuda,local-rewrite,osd + + mkdir -p completions + target/release/whispers completions bash > completions/whispers.bash + target/release/whispers completions zsh > completions/_whispers + target/release/whispers completions fish > completions/whispers.fish +} + +package() { + cd "$srcdir/$_pkgname" + + install -Dm755 target/release/whispers \ + "$pkgdir/usr/bin/whispers" + install -Dm755 target/release/whispers-osd \ + "$pkgdir/usr/bin/whispers-osd" + install -Dm755 target/release/whispers-rewrite-worker \ + "$pkgdir/usr/bin/whispers-rewrite-worker" + + install -Dm644 completions/whispers.bash \ + "$pkgdir/usr/share/bash-completion/completions/whispers" + install -Dm644 completions/_whispers \ + "$pkgdir/usr/share/zsh/site-functions/_whispers" + install -Dm644 completions/whispers.fish \ + "$pkgdir/usr/share/fish/vendor_completions.d/whispers.fish" + + install -Dm644 README.md \ + "$pkgdir/usr/share/doc/whispers/README.md" + install -Dm644 config.example.toml \ + "$pkgdir/usr/share/doc/whispers/config.example.toml" + + install -Dm644 LICENSE \ + "$pkgdir/usr/share/licenses/whispers/LICENSE" + install -Dm644 NOTICE \ + "$pkgdir/usr/share/licenses/whispers/NOTICE" +} diff --git a/packaging/aur/whispers-git/.SRCINFO b/packaging/aur/whispers-git/.SRCINFO index 51c2e89..77a7576 100644 --- a/packaging/aur/whispers-git/.SRCINFO +++ b/packaging/aur/whispers-git/.SRCINFO @@ -19,6 +19,8 @@ pkgbase = whispers-git provides = whispers conflicts = whispers conflicts = whispers-bin + conflicts = whispers-cuda-bin + conflicts = whispers-cuda-git source = whispers::git+https://github.com/OneNoted/whispers.git sha256sums = SKIP diff --git a/packaging/aur/whispers-git/PKGBUILD b/packaging/aur/whispers-git/PKGBUILD index 52d4dd2..2e7e1b0 100644 --- a/packaging/aur/whispers-git/PKGBUILD +++ b/packaging/aur/whispers-git/PKGBUILD @@ -10,7 +10,7 @@ makedepends=('cargo' 'clang' 'cmake' 'git' 'pkgconf') depends=('alsa-lib' 'gcc-libs' 'glibc' 'noto-fonts' 'wl-clipboard') optdepends=('python: experimental faster-whisper and NeMo runtimes') provides=('whispers') -conflicts=('whispers' 'whispers-bin') +conflicts=('whispers' 'whispers-bin' 'whispers-cuda-bin' 'whispers-cuda-git') source=("$_pkgname::git+$url.git") sha256sums=('SKIP') From 3a8397585477ac7001d338f2e60c7d77e522e32c Mon Sep 17 00:00:00 2001 From: OneNoted Date: Mon, 13 Apr 2026 12:27:11 +0200 Subject: [PATCH 2/4] fix: allow release notes generation with a checkout The workflow bootstrap job can create a missing GitHub release, but `gh release create --generate-notes` needs a Git checkout to inspect history. Add a checkout so the bootstrap path works for manual CUDA publish runs too. Constraint: The release bootstrap path must work before the asset-building jobs start Rejected: Drop generated notes entirely | keeps the release bootstrap simpler but regresses release metadata quality Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep the release-bootstrap job checkout lightweight but present if release creation still uses generated notes Tested: failure log analysis for workflow run 24339607306; ruby YAML parse for .github/workflows/release.yml Not-tested: Successful live rerun before push --- .github/workflows/release.yml | 173 +++++++++++++++++++++++++++++--- scripts/build-release-bundle.sh | 3 +- 2 files changed, 163 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a2ed50..0ac449a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,12 +15,44 @@ on: description: Release tag to publish (required when publish_release is true) required: false type: string + bundle_variant: + description: Which bundle variant to build when manually dispatching + required: false + default: portable + type: choice + options: + - portable + - cuda permissions: contents: write jobs: - build-and-release: + ensure-release: + runs-on: ubuntu-latest + steps: + - name: Ensure GitHub release exists + env: + GH_TOKEN: ${{ github.token }} + TAG_NAME: ${{ github.event_name == 'push' && github.ref_name || inputs.tag_name }} + run: | + set -euo pipefail + + if [[ "${GITHUB_EVENT_NAME}" != "push" && "${{ inputs.publish_release }}" != "true" ]]; then + exit 0 + fi + + if gh release view "$TAG_NAME" >/dev/null 2>&1; then + exit 0 + fi + + gh release create "$TAG_NAME" \ + --title "$TAG_NAME" \ + --generate-notes + + build-portable: + if: github.event_name == 'push' || inputs.bundle_variant != 'cuda' + needs: ensure-release runs-on: ubuntu-latest steps: - name: Checkout current ref @@ -90,6 +122,9 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Build release bundle + env: + WHISPERS_RELEASE_BUNDLE_PREFIX: whispers + WHISPERS_RELEASE_FEATURES: local-rewrite,osd run: scripts/build-release-bundle.sh "${{ steps.meta.outputs.version }}" - name: Upload release bundle artifact @@ -109,16 +144,130 @@ jobs: run: | set -euo pipefail - assets=( - "dist/${BUNDLE_NAME}.tar.gz" - "dist/${BUNDLE_NAME}.tar.gz.sha256" - ) + assets=("dist/${BUNDLE_NAME}.tar.gz" "dist/${BUNDLE_NAME}.tar.gz.sha256") - if gh release view "$TAG_NAME" >/dev/null 2>&1; then - gh release upload "$TAG_NAME" "${assets[@]}" --clobber - else - gh release create "$TAG_NAME" "${assets[@]}" \ - --title "$TAG_NAME" \ - --generate-notes \ - --target "$GITHUB_SHA" + gh release upload "$TAG_NAME" "${assets[@]}" --clobber + + build-cuda: + if: github.event_name == 'push' || inputs.bundle_variant == 'cuda' + needs: ensure-release + runs-on: ubuntu-latest + steps: + - name: Checkout current ref + if: github.event_name != 'workflow_dispatch' || inputs.publish_release != true + uses: actions/checkout@v5 + + - name: Checkout requested release tag + if: github.event_name == 'workflow_dispatch' && inputs.publish_release == true + uses: actions/checkout@v5 + with: + ref: ${{ inputs.tag_name }} + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Install Linux build deps + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake clang pkg-config libasound2-dev + + - name: Install CUDA toolkit + uses: Jimver/cuda-toolkit@v0.2.30 + with: + cuda: "12.5.0" + + - name: Expose CUDA toolkit paths + run: | + echo "CUDAToolkit_ROOT=$CUDA_PATH" >> "$GITHUB_ENV" + sudo ln -sfn "$CUDA_PATH" /usr/local/cuda + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + + - name: Compute release metadata + id: meta + env: + INPUT_PUBLISH_RELEASE: ${{ inputs.publish_release }} + INPUT_TAG_NAME: ${{ inputs.tag_name }} + run: | + set -euo pipefail + + version=$(awk -F '"' '$1 ~ /^version *=/ { print $2; exit }' Cargo.toml) + source_bundle="whispers-${version}-x86_64-unknown-linux-gnu" + final_bundle="whispers-cuda-${version}-x86_64-unknown-linux-gnu" + publish=false + tag_name="" + + if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then + publish=true + tag_name="${GITHUB_REF_NAME}" + elif [[ "${INPUT_PUBLISH_RELEASE:-false}" == "true" ]]; then + publish=true + tag_name="${INPUT_TAG_NAME}" + fi + + if [[ "$publish" == "true" ]]; then + if [[ -z "$tag_name" ]]; then + echo "::error::tag_name is required when publish_release=true" + exit 1 + fi + if [[ "$tag_name" != "v$version" ]]; then + echo "::error::tag ${tag_name} does not match Cargo.toml version v${version}" + exit 1 + fi + checked_out_commit=$(git rev-parse HEAD) + tag_commit=$(git rev-list -n1 "$tag_name") + if [[ "$checked_out_commit" != "$tag_commit" ]]; then + echo "::error::checked out ${checked_out_commit} but tag ${tag_name} points to ${tag_commit}" + exit 1 + fi fi + + { + echo "source_bundle=$source_bundle" + echo "bundle=$final_bundle" + echo "publish=$publish" + echo "tag_name=$tag_name" + echo "version=$version" + } >> "$GITHUB_OUTPUT" + + - name: Build CUDA release bundle + env: + WHISPERS_RELEASE_BUNDLE_PREFIX: whispers-cuda + WHISPERS_RELEASE_FEATURES: cuda,local-rewrite,osd + run: | + set -euo pipefail + + scripts/build-release-bundle.sh "${{ steps.meta.outputs.version }}" + + source_bundle="${{ steps.meta.outputs.source_bundle }}" + final_bundle="${{ steps.meta.outputs.bundle }}" + + if [[ "$source_bundle" != "$final_bundle" && -f "dist/${source_bundle}.tar.gz" ]]; then + mv "dist/${source_bundle}.tar.gz" "dist/${final_bundle}.tar.gz" + ( + cd dist + sha256sum "${final_bundle}.tar.gz" > "${final_bundle}.tar.gz.sha256" + rm -f "${source_bundle}.tar.gz.sha256" + ) + fi + + - name: Upload CUDA release bundle artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.meta.outputs.bundle }} + path: | + dist/${{ steps.meta.outputs.bundle }}.tar.gz + dist/${{ steps.meta.outputs.bundle }}.tar.gz.sha256 + + - name: Publish GitHub release + if: steps.meta.outputs.publish == 'true' + env: + GH_TOKEN: ${{ github.token }} + TAG_NAME: ${{ steps.meta.outputs.tag_name }} + BUNDLE_NAME: ${{ steps.meta.outputs.bundle }} + run: | + set -euo pipefail + + assets=("dist/${BUNDLE_NAME}.tar.gz" "dist/${BUNDLE_NAME}.tar.gz.sha256") + gh release upload "$TAG_NAME" "${assets[@]}" --clobber diff --git a/scripts/build-release-bundle.sh b/scripts/build-release-bundle.sh index 3c66f81..ae72dd3 100755 --- a/scripts/build-release-bundle.sh +++ b/scripts/build-release-bundle.sh @@ -8,8 +8,9 @@ version="${1:-$(awk -F '"' '$1 ~ /^version *=/ { print $2; exit }' Cargo.toml)}" profile="${WHISPERS_RELEASE_PROFILE:-release}" features="${WHISPERS_RELEASE_FEATURES:-local-rewrite,osd}" target_triple="${WHISPERS_RELEASE_TARGET:-x86_64-unknown-linux-gnu}" +bundle_prefix="${WHISPERS_RELEASE_BUNDLE_PREFIX:-whispers}" dist_dir="${WHISPERS_RELEASE_DIST_DIR:-$repo_root/dist}" -bundle_name="whispers-${version}-${target_triple}" +bundle_name="${bundle_prefix}-${version}-${target_triple}" bundle_root="$dist_dir/$bundle_name" tarball_path="$dist_dir/${bundle_name}.tar.gz" sha_path="$dist_dir/${bundle_name}.tar.gz.sha256" From 4df9b4cfeed6b7b8bde38e557a4b9440e352ce40 Mon Sep 17 00:00:00 2001 From: OneNoted Date: Mon, 13 Apr 2026 12:50:51 +0200 Subject: [PATCH 3/4] fix: publish release assets only after the right bundle succeeds Gate release uploads on the completed build jobs so tagged releases do not end up with only the portable bundle if the CUDA build fails, verify the requested tag exists before creating a release, and repack legacy CUDA bundles instead of only renaming the tarball path. Constraint: Release assets must not be published from partial or tag-less runs Rejected: Upload assets directly from each build job | races and partial releases leave the GitHub release in an inconsistent state Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep release publication downstream of successful build artifacts, especially when multiple bundle variants are involved Tested: ruby YAML parse for .github/workflows/release.yml; workflow failure analysis for runs 24339651972 and 24339701954 Not-tested: Fresh successful GitHub Actions run after this follow-up --- .github/workflows/release.yml | 117 +++++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 23 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ac449a..4de914c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,6 +31,9 @@ jobs: ensure-release: runs-on: ubuntu-latest steps: + - name: Checkout workflow ref + uses: actions/checkout@v5 + - name: Ensure GitHub release exists env: GH_TOKEN: ${{ github.token }} @@ -46,6 +49,11 @@ jobs: exit 0 fi + if ! git ls-remote --exit-code origin "refs/tags/$TAG_NAME" >/dev/null 2>&1; then + echo "::error::tag $TAG_NAME does not exist on origin" + exit 1 + fi + gh release create "$TAG_NAME" \ --title "$TAG_NAME" \ --generate-notes @@ -130,24 +138,11 @@ jobs: - name: Upload release bundle artifact uses: actions/upload-artifact@v4 with: - name: ${{ steps.meta.outputs.bundle }} + name: portable-release-bundle path: | dist/${{ steps.meta.outputs.bundle }}.tar.gz dist/${{ steps.meta.outputs.bundle }}.tar.gz.sha256 - - name: Publish GitHub release - if: steps.meta.outputs.publish == 'true' - env: - GH_TOKEN: ${{ github.token }} - TAG_NAME: ${{ steps.meta.outputs.tag_name }} - BUNDLE_NAME: ${{ steps.meta.outputs.bundle }} - run: | - set -euo pipefail - - assets=("dist/${BUNDLE_NAME}.tar.gz" "dist/${BUNDLE_NAME}.tar.gz.sha256") - - gh release upload "$TAG_NAME" "${assets[@]}" --clobber - build-cuda: if: github.event_name == 'push' || inputs.bundle_variant == 'cuda' needs: ensure-release @@ -244,30 +239,106 @@ jobs: final_bundle="${{ steps.meta.outputs.bundle }}" if [[ "$source_bundle" != "$final_bundle" && -f "dist/${source_bundle}.tar.gz" ]]; then - mv "dist/${source_bundle}.tar.gz" "dist/${final_bundle}.tar.gz" + temp_dir="$(mktemp -d)" + cleanup() { + rm -rf "$temp_dir" + } + trap cleanup EXIT + + tar -xzf "dist/${source_bundle}.tar.gz" -C "$temp_dir" + mv "$temp_dir/${source_bundle}" "$temp_dir/${final_bundle}" + + tar \ + --sort=name \ + --mtime="@$(git log -1 --format=%ct HEAD)" \ + --owner=0 \ + --group=0 \ + --numeric-owner \ + -C "$temp_dir" \ + -czf "dist/${final_bundle}.tar.gz" \ + "$final_bundle" + ( cd dist sha256sum "${final_bundle}.tar.gz" > "${final_bundle}.tar.gz.sha256" - rm -f "${source_bundle}.tar.gz.sha256" + rm -f "${source_bundle}.tar.gz" "${source_bundle}.tar.gz.sha256" ) fi - name: Upload CUDA release bundle artifact uses: actions/upload-artifact@v4 with: - name: ${{ steps.meta.outputs.bundle }} + name: cuda-release-bundle path: | dist/${{ steps.meta.outputs.bundle }}.tar.gz dist/${{ steps.meta.outputs.bundle }}.tar.gz.sha256 - - name: Publish GitHub release - if: steps.meta.outputs.publish == 'true' + publish-release-push: + if: github.event_name == 'push' + needs: + - ensure-release + - build-portable + - build-cuda + runs-on: ubuntu-latest + steps: + - name: Download portable artifact + uses: actions/download-artifact@v4 + with: + name: portable-release-bundle + path: dist + + - name: Download CUDA artifact + uses: actions/download-artifact@v4 + with: + name: cuda-release-bundle + path: dist + + - name: Publish release assets env: GH_TOKEN: ${{ github.token }} - TAG_NAME: ${{ steps.meta.outputs.tag_name }} - BUNDLE_NAME: ${{ steps.meta.outputs.bundle }} + TAG_NAME: ${{ github.ref_name }} run: | set -euo pipefail + gh release upload "$TAG_NAME" dist/* --clobber - assets=("dist/${BUNDLE_NAME}.tar.gz" "dist/${BUNDLE_NAME}.tar.gz.sha256") - gh release upload "$TAG_NAME" "${assets[@]}" --clobber + publish-release-portable-dispatch: + if: github.event_name == 'workflow_dispatch' && inputs.publish_release == true && inputs.bundle_variant != 'cuda' + needs: + - ensure-release + - build-portable + runs-on: ubuntu-latest + steps: + - name: Download portable artifact + uses: actions/download-artifact@v4 + with: + name: portable-release-bundle + path: dist + + - name: Publish portable release assets + env: + GH_TOKEN: ${{ github.token }} + TAG_NAME: ${{ inputs.tag_name }} + run: | + set -euo pipefail + gh release upload "$TAG_NAME" dist/* --clobber + + publish-release-cuda-dispatch: + if: github.event_name == 'workflow_dispatch' && inputs.publish_release == true && inputs.bundle_variant == 'cuda' + needs: + - ensure-release + - build-cuda + runs-on: ubuntu-latest + steps: + - name: Download CUDA artifact + uses: actions/download-artifact@v4 + with: + name: cuda-release-bundle + path: dist + + - name: Publish CUDA release assets + env: + GH_TOKEN: ${{ github.token }} + TAG_NAME: ${{ inputs.tag_name }} + run: | + set -euo pipefail + gh release upload "$TAG_NAME" dist/* --clobber From 4ca48db6a1d0c4550e2baa1771f3692144cb6efa Mon Sep 17 00:00:00 2001 From: OneNoted Date: Mon, 13 Apr 2026 12:50:51 +0200 Subject: [PATCH 4/4] fix: record the live CUDA release checksum Now that the `whispers-cuda-0.2.1` release asset is live, replace the placeholder checksum in `whispers-cuda-bin` and regenerate `.SRCINFO` so AUR source validation succeeds for the published package. Constraint: The committed AUR metadata must match the actual uploaded GitHub release asset Rejected: Leave the placeholder until after merge | keeps the PR review unresolved and makes the package definition invalid Confidence: high Scope-risk: narrow Reversibility: clean Directive: Do not merge or publish AUR `-bin` metadata with placeholder checksums; always update from the live release asset first Tested: makepkg --printsrcinfo; makepkg --verifysource (whispers-cuda-bin against live GitHub asset) Not-tested: AUR web index propagation after push --- packaging/aur/whispers-cuda-bin/.SRCINFO | 2 +- packaging/aur/whispers-cuda-bin/PKGBUILD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/aur/whispers-cuda-bin/.SRCINFO b/packaging/aur/whispers-cuda-bin/.SRCINFO index e91ffaa..e9b3a94 100644 --- a/packaging/aur/whispers-cuda-bin/.SRCINFO +++ b/packaging/aur/whispers-cuda-bin/.SRCINFO @@ -18,6 +18,6 @@ pkgbase = whispers-cuda-bin conflicts = whispers-git conflicts = whispers-cuda-git source = whispers-cuda-bin-0.2.1.tar.gz::https://github.com/OneNoted/whispers/releases/download/v0.2.1/whispers-cuda-0.2.1-x86_64-unknown-linux-gnu.tar.gz - sha256sums = REPLACE_WITH_CUDA_RELEASE_SHA256 + sha256sums = 8cc719325a543794838028bf332203012628f35224c093154a65d4374530bcf0 pkgname = whispers-cuda-bin diff --git a/packaging/aur/whispers-cuda-bin/PKGBUILD b/packaging/aur/whispers-cuda-bin/PKGBUILD index c0802b6..d3d1e33 100644 --- a/packaging/aur/whispers-cuda-bin/PKGBUILD +++ b/packaging/aur/whispers-cuda-bin/PKGBUILD @@ -12,7 +12,7 @@ conflicts=('whispers' 'whispers-bin' 'whispers-git' 'whispers-cuda-git') source=( "$pkgname-$pkgver.tar.gz::$url/releases/download/v$pkgver/whispers-cuda-$pkgver-x86_64-unknown-linux-gnu.tar.gz" ) -sha256sums=('REPLACE_WITH_CUDA_RELEASE_SHA256') +sha256sums=('8cc719325a543794838028bf332203012628f35224c093154a65d4374530bcf0') package() { local bundle_dir="$srcdir/whispers-cuda-$pkgver-x86_64-unknown-linux-gnu"