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"
|
||||
+1
-1
@@ -14,7 +14,7 @@ Release history for Vocalinux.
|
||||
|
||||
**[v0.17.0](https://github.com/VocaHQ/vocalinux/releases/tag/v0.17.0)** (2026-09-16)
|
||||
|
||||
Minor on the stable line: Faster Whisper and Parakeet local engines, Speech Model simple setup, first-run system language, Snap packaging with ydotool/`uinput`, Flatpak bundles attached by the release workflow, and injection/settings/audio fixes (HDA analog mics at native layout, native GTK OS dark/light, English-only models no longer trap other languages).
|
||||
Minor on the stable line: Faster Whisper and Parakeet local engines, Speech Model simple setup, first-run system language, Snap packaging with ydotool/`uinput` (GitHub `.snap` for sideload while Store review is pending), Flatpak bundles attached by the release workflow, and injection/settings/audio fixes (HDA analog mics at native layout, native GTK OS dark/light, English-only models no longer trap other languages).
|
||||
|
||||
See [docs/UPDATE.md](docs/UPDATE.md#whats-new-in-v0170) for the highlight table, or the [GitHub Release](https://github.com/VocaHQ/vocalinux/releases/tag/v0.17.0).
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ Ships whisper.cpp with Vulkan. It is **not on Flathub** (submission [flathub#936
|
||||
|
||||
### Snap (Ubuntu Snap Store)
|
||||
|
||||
Listing: [snapcraft.io/vocalinux](https://snapcraft.io/vocalinux). `stable` is still a manual promote after QA.
|
||||
Listing: [snapcraft.io/vocalinux](https://snapcraft.io/vocalinux). `stable` is still a manual promote after QA. Tagged releases also attach `vocalinux_<version>_amd64.snap` on GitHub.
|
||||
|
||||
```bash
|
||||
sudo snap install vocalinux --edge
|
||||
@@ -186,6 +186,13 @@ sudo snap connect vocalinux:uinput # native Wayland typing (ydotool)
|
||||
|
||||
v0.17.0 includes ydotool and the `uinput` plug. The Store still has to finish review of that plug, so `snap info vocalinux` may still show **v0.16.2** (rev 7) on edge. That revision has no `uinput` plug; `snap connect vocalinux:uinput` fails until a 0.17.0+ revision is listed.
|
||||
|
||||
Until then, sideload the GitHub file:
|
||||
|
||||
```bash
|
||||
sudo snap install --dangerous ./vocalinux_0.17.0_amd64.snap
|
||||
sudo snap connect vocalinux:uinput
|
||||
```
|
||||
|
||||
### From source
|
||||
|
||||
```bash
|
||||
|
||||
+16
-3
@@ -8,7 +8,7 @@ How to install Vocalinux on Linux. Short overview: [project README](../README.md
|
||||
| [AppImage](#appimage) | Portable binary; no system package install |
|
||||
| [AUR](#arch-linux-aur) | Arch / Manjaro |
|
||||
| [Flatpak](#flatpak) | Release `.flatpak` or local build |
|
||||
| [Snap](#snap-ubuntu-snap-store) | Ubuntu Snap Store (`--edge`) |
|
||||
| [Snap](#snap-ubuntu-snap-store) | Ubuntu Snap Store (`--edge`) or GitHub `.snap` |
|
||||
| [From source](#from-source) | Contributors or custom trees |
|
||||
| [Manual / PyPI](INSTALL_MANUAL.md) | Full control or pip-only workflows |
|
||||
| [Troubleshooting](TROUBLESHOOTING.md) | Tray, audio, injection, models |
|
||||
@@ -112,7 +112,9 @@ Whisper.cpp + Vulkan. It is **not on Flathub** (submission [flathub#9368](https:
|
||||
|
||||
## Snap (Ubuntu Snap Store)
|
||||
|
||||
Listing: [snapcraft.io/vocalinux](https://snapcraft.io/vocalinux) (issue [#48](https://github.com/VocaHQ/vocalinux/issues/48)). Recipe: `snap/snapcraft.yaml`. Tagged `v*` releases publish to Snap Store `edge` and `candidate` when credentials are set. `stable` is still a manual promote after QA.
|
||||
Listing: [snapcraft.io/vocalinux](https://snapcraft.io/vocalinux) (issue [#48](https://github.com/VocaHQ/vocalinux/issues/48)). Recipe: `snap/snapcraft.yaml`. Tagged `v*` releases attach `vocalinux_<version>_amd64.snap` to the GitHub Release and try Snap Store `edge` and `candidate` when credentials are set. `stable` is still a manual promote after QA.
|
||||
|
||||
Store install, once Canonical lists the revision:
|
||||
|
||||
```bash
|
||||
sudo snap install vocalinux --edge
|
||||
@@ -121,7 +123,18 @@ sudo snap connect vocalinux:raw-input # global keyboard shortcuts (evdev)
|
||||
sudo snap connect vocalinux:uinput # native Wayland typing (ydotool)
|
||||
```
|
||||
|
||||
**v0.17.0** ships ydotool and the `uinput` plug. After `sudo snap install vocalinux --edge`, connect `uinput` and restart Vocalinux for native GNOME/GTK/Qt windows. **v0.16.2 edge (rev 7)** has no such plug and only types into XWayland apps; `sudo snap refresh vocalinux` first.
|
||||
**v0.17.0** ships ydotool and the `uinput` plug. That plug is super-privileged, so the Store held 0.17.0 for human review (`allow-installation`). Until a 0.17.0+ revision is listed, `snap info vocalinux` still shows **v0.16.2 (rev 7)** on edge. That revision has no `uinput` plug; `sudo snap connect vocalinux:uinput` fails.
|
||||
|
||||
Sideload the GitHub `.snap` (amd64) while the Store is waiting:
|
||||
|
||||
```bash
|
||||
sudo snap install --dangerous ./vocalinux_0.17.0_amd64.snap
|
||||
sudo snap connect vocalinux:audio-record
|
||||
sudo snap connect vocalinux:raw-input
|
||||
sudo snap connect vocalinux:uinput
|
||||
```
|
||||
|
||||
`--dangerous` is required because this file is not a Store revision. It will not refresh from the Store. After Canonical grants `uinput`, switch to `sudo snap install vocalinux --edge` (or `snap refresh`).
|
||||
|
||||
## From source
|
||||
|
||||
|
||||
+18
-8
@@ -88,7 +88,9 @@ Install `just` from https://just.systems or the distro package `just`.
|
||||
|
||||
`verify-release.yml` runs the same check when the release workflow finishes.
|
||||
It cannot see a hand edit made afterwards, which is how every v0.16.2 defect
|
||||
arrived, so run it yourself after touching a published release.
|
||||
arrived, so run it yourself after touching a published release. The one
|
||||
exception is `snap-backfill.yml`: that workflow attaches the snap, rewrites
|
||||
`SHA256SUMS`, and then runs `scripts/verify_release.py` itself.
|
||||
|
||||
## Detailed Release Steps
|
||||
|
||||
@@ -166,7 +168,8 @@ Use these rules for every GitHub Release body (and for the draft pasted into the
|
||||
5. `## Bug Fixes` - group by area (IBus, Installer, AUR, Text injection, …)
|
||||
6. Optional: `## Improvements`, `## Docs`, `## Packaging`
|
||||
7. `## Thanks` - external PR authors and issue reporters by `@handle`
|
||||
8. `## Install / Upgrade` - `install.sh`, AUR, PyPI, **AppImage**, Flatpak status (honest)
|
||||
8. `## Install / Upgrade` - `install.sh`, AUR, PyPI, **AppImage**, Flatpak, Snap
|
||||
(Store channels plus GitHub `.snap` sideload when Store review is pending)
|
||||
9. `### Verifying what you downloaded` (required, and easy to lose). `release.yml`
|
||||
generates it, with `sha256sum -c --ignore-missing SHA256SUMS` and
|
||||
`gh attestation verify`. A hand-written body replaces the generated one, so carry
|
||||
@@ -380,21 +383,28 @@ After pushing the tag, the GitHub Actions workflow will automatically:
|
||||
rebuilding, so the wheel on PyPI is byte-for-byte the wheel on the release
|
||||
2. Build and attach AppImages for x86_64 and aarch64, both from that same wheel
|
||||
3. Create a GitHub Release with auto-generated notes
|
||||
4. Attach `SHA256SUMS` covering all four artifacts, and generate build provenance
|
||||
attestations from that manifest (runs after the aarch64 AppImage lands, so a
|
||||
partial manifest never gets published)
|
||||
4. Attach `SHA256SUMS` covering every GitHub asset (wheel, sdist, both AppImages,
|
||||
both Flatpaks, the amd64 snap) and generate build provenance from that
|
||||
manifest (runs after the aarch64 AppImage, both Flatpaks, and the snap land,
|
||||
so a partial manifest never gets published)
|
||||
5. Publish to PyPI via trusted publishing
|
||||
6. Publish the AUR package (when the `AUR_SSH_PRIVATE_KEY` secret is configured)
|
||||
7. Deploy the website to vocalinux.com
|
||||
8. Mark as pre-release if version contains alpha/beta/rc
|
||||
8. Build the amd64 snap, attach it to the GitHub Release, and try Snap Store
|
||||
`edge`/`candidate`. Store human review (for example `uinput` allow-installation)
|
||||
must not block the GitHub `.snap`. `stable` is still a manual promote
|
||||
9. Mark as pre-release if version contains alpha/beta/rc
|
||||
|
||||
Monitor at: https://github.com/VocaHQ/vocalinux/actions
|
||||
|
||||
### Step 9: Post-Release Tasks
|
||||
|
||||
- [ ] Verify GitHub Release was created correctly
|
||||
- [ ] Verify `SHA256SUMS` is attached and lists all four artifacts (wheel, sdist,
|
||||
both AppImages) - the release notes tell users to run `sha256sum -c` against it
|
||||
- [ ] Verify `SHA256SUMS` is attached and lists every GitHub asset (wheel, sdist,
|
||||
both AppImages, both Flatpaks, the amd64 snap) - the release notes tell
|
||||
users to run `sha256sum -c` against it
|
||||
- [ ] If the Store held the snap for `uinput` review, confirm the GitHub `.snap`
|
||||
is still attached and the notes document `snap install --dangerous`
|
||||
- [ ] Verify provenance: `gh attestation verify <artifact> --repo VocaHQ/vocalinux`
|
||||
- [ ] Verify PyPI package was published (if applicable), and that its wheel sha256
|
||||
matches the line for that wheel in `SHA256SUMS`
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ git pull origin main
|
||||
|--------|---------|
|
||||
| AUR | `yay -S vocalinux` (or your AUR helper) |
|
||||
| AppImage | Download the new file from [Releases](https://github.com/VocaHQ/vocalinux/releases) |
|
||||
| Snap | `sudo snap refresh vocalinux` (`--edge` until stable is promoted). **v0.17.0** ships `uinput`; `sudo snap connect vocalinux:uinput` then restart for native Wayland apps. v0.16.2 rev 7 has no such plug. |
|
||||
| Snap | Store: `sudo snap refresh vocalinux` (`--edge` until stable is promoted). Until Canonical lists 0.17.0, sideload `vocalinux_0.17.0_amd64.snap` from the GitHub Release (`sudo snap install --dangerous ./vocalinux_0.17.0_amd64.snap`), then `sudo snap connect vocalinux:uinput`. v0.16.2 rev 7 has no such plug. |
|
||||
| Flatpak (release bundle) | Install the new `.flatpak` from Releases; bundles do not auto-update |
|
||||
| PyPI | Reinstall in the same venv after system packages are current |
|
||||
|
||||
@@ -88,7 +88,7 @@ Missing system packages: see [INSTALL.md](INSTALL.md) or [DISTRO_COMPATIBILITY.m
|
||||
| **Searchable open picker** | Language list filters while open (#798) |
|
||||
| **Localized punctuation commands** | it/fr/de/es/pt/nl/pl/ru; English phrases still work (#642) |
|
||||
| **Bare F-keys** | F1–F24 are valid push-to-talk shortcuts (#815) |
|
||||
| **Snap** | Store listing, ydotool + `uinput` for native Wayland (#519, #823, #822) |
|
||||
| **Snap** | Store listing, ydotool + `uinput` for native Wayland; GitHub `.snap` for sideload while Store review is pending (#519, #823, #822) |
|
||||
| **Flatpak on the tag** | Workflow attaches `.flatpak` assets and checksums them (#786) |
|
||||
|
||||
### Also in v0.17.0
|
||||
|
||||
@@ -136,7 +136,7 @@ def test_the_manifest_covers_every_kind_of_artifact_we_publish():
|
||||
assert "merge-multiple: true" in block, "it collects one artifact, not all of them"
|
||||
checksum_line = re.search(r"sha256sum -- (.+)$", block, re.M)
|
||||
assert checksum_line, "publish-checksums does not generate SHA256SUMS"
|
||||
for pattern in ("*.whl", "*.tar.gz", "*.AppImage", "*.flatpak"):
|
||||
for pattern in ("*.whl", "*.tar.gz", "*.AppImage", "*.flatpak", "*.snap"):
|
||||
assert pattern in checksum_line.group(1), f"{pattern} is published but unchecksummed"
|
||||
|
||||
|
||||
|
||||
@@ -80,17 +80,76 @@ def test_snap_strips_pygobject_and_uses_gnome_gi() -> None:
|
||||
assert "python3-gi-cairo" not in stage
|
||||
|
||||
|
||||
def test_release_publishes_snap_to_edge_and_candidate() -> None:
|
||||
"""v* tags must ship the snap to the store, not attach it to GitHub."""
|
||||
def test_release_attaches_snap_then_tries_the_store() -> None:
|
||||
"""Store review of uinput must not swallow the GitHub .snap (v0.17.0)."""
|
||||
text = (REPO_ROOT / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8")
|
||||
assert " build-snap:\n" in text
|
||||
assert " attach-snap:\n" in text
|
||||
assert " publish-snap:\n" in text
|
||||
job = text.split(" publish-snap:\n", 1)[1].split("\n deploy-website:", 1)[0]
|
||||
assert "needs: build-and-release" in job
|
||||
assert "snapcore/action-build@" in job
|
||||
assert "snapcore/action-publish@" in job
|
||||
assert "release: edge,candidate" in job
|
||||
assert "release: stable" not in job
|
||||
assert "SNAPCRAFT_STORE_CREDENTIALS is unset; cannot publish the snap" in job
|
||||
assert "gh release upload" not in job
|
||||
assert "action-gh-release" not in job
|
||||
assert "upload-artifact" not in job
|
||||
|
||||
build = text.split(" build-snap:\n", 1)[1].split("\n attach-snap:", 1)[0]
|
||||
assert "snapcore/action-build@" in build
|
||||
assert "upload-artifact" in build
|
||||
assert "name: snap-amd64" in build
|
||||
assert "gh release upload" not in build
|
||||
assert "action-publish" not in build
|
||||
|
||||
attach = text.split(" attach-snap:\n", 1)[1].split("\n publish-snap:", 1)[0]
|
||||
assert "needs: [build-and-release, build-snap]" in attach
|
||||
assert "gh release upload" in attach
|
||||
assert "dist/*.snap" in attach
|
||||
assert "--clobber" in attach
|
||||
assert "contents: write" in attach
|
||||
assert "TAG: ${{ github.ref_name }}" in attach
|
||||
assert 'gh release upload "$TAG"' in attach
|
||||
assert '"${{ github.ref_name }}"' not in attach
|
||||
|
||||
store = text.split(" publish-snap:\n", 1)[1].split("\n deploy-website:", 1)[0]
|
||||
assert "needs: build-snap" in store
|
||||
assert "--release edge,candidate" in store
|
||||
assert "release: stable" not in store
|
||||
assert "SNAPCRAFT_STORE_CREDENTIALS is unset; cannot publish the snap" in store
|
||||
assert "will need manual review" in store
|
||||
assert "allow-installation" in store
|
||||
assert "continue-on-error" not in store
|
||||
assert "snapcore/action-publish" not in store
|
||||
assert "SNAP_FILE:" in store
|
||||
assert 'snapcraft upload "$SNAP_FILE"' in store
|
||||
assert "gh release upload" not in store
|
||||
assert "action-gh-release" not in store
|
||||
|
||||
|
||||
def test_release_notes_document_snap_sideload() -> None:
|
||||
body = (REPO_ROOT / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8")
|
||||
assert "snap install --dangerous" in body
|
||||
assert "snap connect vocalinux:uinput" in body
|
||||
assert "vocalinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap" in body
|
||||
|
||||
|
||||
def test_snap_backfill_workflow_attests_only_the_snap() -> None:
|
||||
"""Existing tags (v0.17.0) need a dispatch that does not re-attest AppImages."""
|
||||
path = REPO_ROOT / ".github" / "workflows" / "snap-backfill.yml"
|
||||
text = path.read_text(encoding="utf-8")
|
||||
assert "workflow_dispatch:" in text
|
||||
assert "snapcore/action-build@" in text
|
||||
assert "gh release upload" in text
|
||||
assert "SHA256SUMS" in text
|
||||
assert "actions/attest-build-provenance@" in text
|
||||
assert "subject-path: dist/${{ steps.stage.outputs.name }}" in text
|
||||
assert "subject-checksums:" not in text
|
||||
assert " verify:\n" in text
|
||||
assert "scripts/verify_release.py" in text
|
||||
assert 'python3 scripts/verify_release.py "$TAG"' in text
|
||||
|
||||
|
||||
def test_snap_backfill_dispatch_input_never_reaches_the_shell() -> None:
|
||||
"""Same rule as verify-release.yml: interpolating the tag into run: is injection."""
|
||||
import re
|
||||
|
||||
text = (REPO_ROOT / ".github" / "workflows" / "snap-backfill.yml").read_text(encoding="utf-8")
|
||||
run_lines = [line for line in text.splitlines() if re.match(r"^\s*-?\s*run:", line)]
|
||||
assert run_lines, "found no run: step, so this guard is scanning nothing"
|
||||
for line in run_lines:
|
||||
assert "${{" not in line, f"a run: step interpolates a template expression: {line.strip()}"
|
||||
assert "TAG: ${{ inputs.tag }}" in text
|
||||
assert 'gh release upload "$TAG"' in text
|
||||
|
||||
@@ -27,7 +27,7 @@ const releases = [
|
||||
"Recommended model button sets size and specialization together and will not ignore a file already on disk (PR #797, #778)",
|
||||
"Localized punctuation voice commands for it/fr/de/es/pt/nl/pl/ru (PR #642, #640)",
|
||||
"Bare F1–F24 push-to-talk shortcuts (PR #815)",
|
||||
"Snap packaging with ydotool and uinput for native Wayland; Store --edge (PR #519, #823, #822)",
|
||||
"Snap packaging with ydotool and uinput for native Wayland; GitHub .snap sideload while Store review of uinput is pending (PR #519, #823, #822)",
|
||||
"Release workflow attaches Vocalinux-<version>-{x86_64,aarch64}.flatpak (PR #786, #784)",
|
||||
"XWayland clipboard paste instead of layout-garbled xdotool type (PR #680, #657)",
|
||||
"Layout-aware Ctrl+V paste; ydotool releases Ctrl after a timed-out paste (PR #788, #675, #658)",
|
||||
|
||||
Reference in New Issue
Block a user