fix(release): attach the snap to GitHub Releases (#839)
* fix(release): attach the snap to GitHub Releases v0.17.0 packed the snap, then Store review of uinput failed the job, so the file never reached the tag. Build and attach first. A Store hold is a warning, not a failed release. SHA256SUMS covers the .snap. snap-backfill.yml rebuilds an existing tag, appends the snap to the manifest, and attests only that file. * fix(release): keep snap tags out of the shell and fail real Store errors Pass the tag through the environment like verify-release.yml. Treat only an allow-installation review as a successful Store publish. snap-backfill runs verify_release.py after it rewrites SHA256SUMS. * style(test): format snap-backfill shell-injection guard with black
This commit is contained in:
+102
-20
@@ -176,10 +176,22 @@ jobs:
|
||||
Whisper.cpp engine only. Hotkeys need device access as documented in
|
||||
packaging/flatpak/README.md. Not on Flathub (https://github.com/flathub/flathub/pull/9368).
|
||||
|
||||
### Snap
|
||||
Tagged releases also attach `vocalinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap`
|
||||
and try the Snap Store (`edge` and `candidate`). `stable` is a manual promote.
|
||||
Super-privileged plugs such as `uinput` can hold the Store revision in human
|
||||
review; the GitHub `.snap` is the sideload in that case:
|
||||
```bash
|
||||
sudo snap install --dangerous ./vocalinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap
|
||||
sudo snap connect vocalinux:uinput
|
||||
```
|
||||
`--dangerous` is required because this file is not a Store revision. It will not
|
||||
refresh from the Store. amd64 only.
|
||||
|
||||
### Verifying what you downloaded
|
||||
`SHA256SUMS` covers every file here and is attached once the aarch64 AppImage and
|
||||
both Flatpak builds finish, a few minutes after this release appears. The wheel and
|
||||
sdist below are the same bytes published to PyPI.
|
||||
`SHA256SUMS` covers every file here and is attached once the aarch64 AppImage,
|
||||
both Flatpak builds, and the snap finish, a few minutes after this release appears.
|
||||
The wheel and the sdist below are the same bytes published to PyPI.
|
||||
```bash
|
||||
sha256sum -c --ignore-missing SHA256SUMS
|
||||
```
|
||||
@@ -355,12 +367,12 @@ jobs:
|
||||
--repo ${{ github.repository }} \
|
||||
--clobber
|
||||
|
||||
# Runs last because the aarch64 AppImage and both Flatpak bundles land after
|
||||
# the release is created: a checksum manifest that covers some of the files
|
||||
# is worse than none, since the missing one looks like the tampered one.
|
||||
# Runs last because the aarch64 AppImage, both Flatpak bundles, and the snap
|
||||
# land after the release is created: a checksum manifest that covers some of
|
||||
# the files is worse than none, since the missing one looks like the tampered one.
|
||||
publish-checksums:
|
||||
name: Checksums and Provenance
|
||||
needs: [build-and-release, build-appimage-arm64, build-flatpak-amd64, build-flatpak-arm64, attach-flatpak]
|
||||
needs: [build-and-release, build-appimage-arm64, build-flatpak-amd64, build-flatpak-arm64, attach-flatpak, attach-snap]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -380,7 +392,7 @@ jobs:
|
||||
ls -la
|
||||
# Bare names, so `sha256sum -c` works in whatever directory the user
|
||||
# downloaded the release into.
|
||||
sha256sum -- *.whl *.tar.gz *.AppImage *.flatpak | tee SHA256SUMS
|
||||
sha256sum -- *.whl *.tar.gz *.AppImage *.flatpak *.snap | tee SHA256SUMS
|
||||
|
||||
# Attesting from the manifest rather than a second glob: the file we
|
||||
# publish and the set we attest cannot drift apart.
|
||||
@@ -477,12 +489,13 @@ jobs:
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: 'Release ${{ github.ref_name }}'
|
||||
|
||||
# Store-only: do not attach the .snap to the GitHub Release. grade: stable
|
||||
# allows candidate; do not auto-release to stable — promote after QA.
|
||||
# GitHub Release first, Store second. uinput is super-privileged, so the
|
||||
# Store can queue a human review and refuse edge/candidate; the .snap still
|
||||
# has to land on the tag for sideload. grade: stable allows candidate; do
|
||||
# not auto-release to stable — promote after QA.
|
||||
# Secret: SNAPCRAFT_STORE_CREDENTIALS (snapcraft export-login).
|
||||
publish-snap:
|
||||
name: Build and Publish Snap
|
||||
needs: build-and-release
|
||||
build-snap:
|
||||
name: Build Snap
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -490,6 +503,47 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Build snap
|
||||
id: build
|
||||
uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1
|
||||
|
||||
- name: Upload the snap
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: snap-amd64
|
||||
path: ${{ steps.build.outputs.snap }}
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
attach-snap:
|
||||
name: Attach Snap
|
||||
needs: [build-and-release, build-snap]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download the snap
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: snap-amd64
|
||||
path: dist
|
||||
|
||||
- name: Attach to release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ls -la dist/*.snap
|
||||
gh release upload "$TAG" dist/*.snap --clobber
|
||||
|
||||
publish-snap:
|
||||
name: Publish Snap to Store
|
||||
needs: build-snap
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Require Snap Store credentials
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
@@ -499,17 +553,45 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build snap
|
||||
id: build
|
||||
uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1
|
||||
- name: Download the snap
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: snap-amd64
|
||||
path: dist
|
||||
|
||||
- name: Locate the snap
|
||||
id: snapfile
|
||||
run: |
|
||||
set -euo pipefail
|
||||
file=$(ls dist/*.snap)
|
||||
echo "file=$file" >> "$GITHUB_OUTPUT"
|
||||
ls -la "$file"
|
||||
|
||||
- name: Install snapcraft
|
||||
run: sudo snap install snapcraft --classic
|
||||
|
||||
- name: Publish snap to edge and candidate
|
||||
uses: snapcore/action-publish@214b86e5ca036ead1668c79afb81e550e6c54d40 # v1
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
with:
|
||||
snap: ${{ steps.build.outputs.snap }}
|
||||
release: edge,candidate
|
||||
SNAP_FILE: ${{ steps.snapfile.outputs.file }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
log=/tmp/snap-upload.log
|
||||
set +e
|
||||
/snap/bin/snapcraft upload "$SNAP_FILE" --release edge,candidate >"$log" 2>&1
|
||||
status=$?
|
||||
set -e
|
||||
cat "$log"
|
||||
if [ "$status" -eq 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
if grep -F -q "will need manual review" "$log" \
|
||||
&& grep -F -q "allow-installation" "$log"; then
|
||||
echo "::warning::Snap Store queued a human review (allow-installation). The .snap is on the GitHub Release."
|
||||
exit 0
|
||||
fi
|
||||
echo "::error::Snap Store publish failed (exit $status) for a reason other than allow-installation review."
|
||||
exit "$status"
|
||||
|
||||
deploy-website:
|
||||
name: Deploy Website
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
name: Attach snap to an existing release
|
||||
|
||||
# v0.17.0 built the snap and then lost it: Store review failed the job, and
|
||||
# the workflow did not attach the .snap to the GitHub Release. Run this after
|
||||
# that policy change for a tag that already exists:
|
||||
# gh workflow run snap-backfill.yml -f tag=v0.17.0
|
||||
#
|
||||
# Attests only the snap. Other release assets keep the original provenance.
|
||||
# Caller-supplied tag values stay in the environment, never in run: source.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Release tag to attach the snap to (e.g. v0.17.0)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
backfill:
|
||||
name: Build, attach, checksum, attest
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
steps:
|
||||
- name: Require a version tag
|
||||
env:
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
case "$TAG" in
|
||||
v[0-9]*) ;;
|
||||
*)
|
||||
echo "refusing tag that is not v<digit>…: $TAG" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
if printf '%s' "$TAG" | grep -q '[^A-Za-z0-9._-]'; then
|
||||
echo "refusing tag with unexpected characters: $TAG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout the tagged commit
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
|
||||
- name: Build snap
|
||||
id: build
|
||||
uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1
|
||||
|
||||
- name: Stage the snap and extend SHA256SUMS
|
||||
id: stage
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
SNAP_PATH: ${{ steps.build.outputs.snap }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p dist
|
||||
test -f "$SNAP_PATH"
|
||||
name=$(basename "$SNAP_PATH")
|
||||
cp -v "$SNAP_PATH" "dist/$name"
|
||||
echo "name=$name" >> "$GITHUB_OUTPUT"
|
||||
gh release download "$TAG" --pattern SHA256SUMS --output dist/SHA256SUMS
|
||||
grep -v " ${name}$" dist/SHA256SUMS > dist/SHA256SUMS.next || true
|
||||
(cd dist && sha256sum -- "$name") >> dist/SHA256SUMS.next
|
||||
mv dist/SHA256SUMS.next dist/SHA256SUMS
|
||||
cat dist/SHA256SUMS
|
||||
|
||||
- name: Attest the snap
|
||||
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
|
||||
with:
|
||||
subject-path: dist/${{ steps.stage.outputs.name }}
|
||||
|
||||
- name: Attach snap and SHA256SUMS
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh release upload "$TAG" dist/*.snap dist/SHA256SUMS --clobber
|
||||
|
||||
verify:
|
||||
name: Verify the published release
|
||||
needs: backfill
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
attestations: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: python3 scripts/verify_release.py "$TAG"
|
||||
Reference in New Issue
Block a user