* 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
656 lines
25 KiB
YAML
656 lines
25 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
|
|
# Least privilege, granted per job. `id-token: write` at the top level handed an
|
|
# OIDC token to every job in the workflow, including publish-aur, which runs a
|
|
# third-party action with our AUR signing key.
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
# Build against the supported floor (requires-python in pyproject.toml).
|
|
PYTHON_VERSION: '3.11'
|
|
|
|
jobs:
|
|
build-and-release:
|
|
name: Build and Create Release
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
cache: 'pip'
|
|
|
|
- name: Install build dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install build wheel setuptools
|
|
|
|
- name: Extract version from tag
|
|
id: get_version
|
|
run: |
|
|
VERSION=${GITHUB_REF#refs/tags/v}
|
|
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
|
|
echo "Building version: $VERSION"
|
|
|
|
- name: Verify version consistency
|
|
run: |
|
|
# Get version from version.py file directly (avoid importing full package which needs gi)
|
|
PACKAGE_VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' src/vocalinux/version.py)
|
|
TAG_VERSION=${{ steps.get_version.outputs.VERSION }}
|
|
echo "Package version: $PACKAGE_VERSION"
|
|
echo "Tag version: $TAG_VERSION"
|
|
# Allow alpha/beta suffixes in package version
|
|
if [[ ! "$PACKAGE_VERSION" == *"$TAG_VERSION"* ]] && [[ ! "$TAG_VERSION" == *"$PACKAGE_VERSION"* ]]; then
|
|
echo "Warning: Version mismatch between tag ($TAG_VERSION) and package ($PACKAGE_VERSION)"
|
|
fi
|
|
|
|
# Archive timestamps are what made two builds of one tag differ. Pinned to
|
|
# the tagged commit, re-running this workflow reproduces the bytes we
|
|
# published, so a checksum stays true for the life of the release.
|
|
- name: Pin the build timestamp to the tagged commit
|
|
run: |
|
|
echo "SOURCE_DATE_EPOCH=$(git log -1 --format=%ct "$GITHUB_SHA")" >> "$GITHUB_ENV"
|
|
|
|
# The only `python -m build` in this workflow. PyPI, the aarch64 AppImage
|
|
# and the checksums all consume the artifact below, so the wheel on PyPI
|
|
# is the wheel on the release. It used to be built three times, on three
|
|
# runners, and nothing guaranteed the three agreed.
|
|
- name: Build package
|
|
run: |
|
|
python -m build
|
|
echo "Built packages:"
|
|
ls -la dist/
|
|
|
|
- name: Upload the built distributions
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: python-dist
|
|
path: |
|
|
dist/*.whl
|
|
dist/*.tar.gz
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
# glslc is built from the pinned shaderc commit inside the container -
|
|
# Ubuntu 22.04 packages none - and depends on nothing but that commit.
|
|
- name: Cache AppImage build tools
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ~/.cache/vocalinux-appimage
|
|
key: appimage-tools-${{ runner.arch }}-${{ hashFiles('packaging/appimage/tool_checksums.txt') }}
|
|
|
|
- name: Build AppImage (x86_64)
|
|
env:
|
|
VOCALINUX_APPIMAGE_REQUIRE_VULKAN: "1"
|
|
run: |
|
|
bash packaging/appimage/docker-build.sh dist/*.whl "${{ steps.get_version.outputs.VERSION }}" dist
|
|
ls -la dist/*.AppImage
|
|
|
|
- name: Upload the x86_64 AppImage
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: appimage-x86_64
|
|
path: dist/*.AppImage
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
- name: Create Release
|
|
id: create_release
|
|
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
|
|
with:
|
|
files: |
|
|
dist/*.whl
|
|
dist/*.tar.gz
|
|
dist/*.AppImage
|
|
generate_release_notes: true
|
|
draft: false
|
|
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
|
|
body: |
|
|
## Installation
|
|
|
|
### Quick Install (Recommended)
|
|
```bash
|
|
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/${{ github.ref_name }}/install.sh | bash
|
|
```
|
|
|
|
### From Source
|
|
```bash
|
|
git clone https://github.com/VocaHQ/vocalinux.git
|
|
cd vocalinux
|
|
./install.sh
|
|
```
|
|
|
|
### From PyPI
|
|
```bash
|
|
# Install the Linux system packages first:
|
|
# GTK/AppIndicator, PortAudio, and xdotool/wtype are required.
|
|
python3 -m venv ~/.local/share/vocalinux-pypi/venv --system-site-packages
|
|
source ~/.local/share/vocalinux-pypi/venv/bin/activate
|
|
pip install vocalinux
|
|
vocalinux
|
|
```
|
|
|
|
PyPI installs the Python package only. For automatic system dependency,
|
|
desktop integration, and model setup, use the Quick Install command above.
|
|
See docs/INSTALL.md for distro-specific PyPI prerequisites.
|
|
|
|
### Arch Linux (AUR)
|
|
```bash
|
|
yay -S vocalinux
|
|
```
|
|
|
|
### AppImage (no install, no root)
|
|
Download the `.AppImage` matching your CPU (`x86_64` or `aarch64`, the latter attached
|
|
shortly after this release is published) below:
|
|
```bash
|
|
chmod +x Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage
|
|
./Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage
|
|
```
|
|
Still requires `xdotool`/`wtype`/`ydotool` on the host for text injection, same as the PyPI path.
|
|
|
|
### Flatpak
|
|
Download the `.flatpak` matching your CPU (`x86_64` or `aarch64`, attached shortly
|
|
after this release is published). The app is **not on Flathub**; these are GitHub
|
|
Release bundles with **no auto-update**.
|
|
```bash
|
|
# once: Flathub remote + GNOME runtime (the app itself is not on Flathub)
|
|
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
|
flatpak install flathub org.gnome.Platform//50
|
|
|
|
flatpak install --user ./Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.flatpak
|
|
flatpak run com.vocalinux.Vocalinux
|
|
```
|
|
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,
|
|
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
|
|
```
|
|
Each artifact also carries GitHub build provenance, which ties it to this workflow run:
|
|
```bash
|
|
gh attestation verify Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage \
|
|
--repo ${{ github.repository }}
|
|
```
|
|
|
|
---
|
|
See [CHANGELOG](https://github.com/VocaHQ/vocalinux/releases) for detailed changes.
|
|
|
|
build-appimage-arm64:
|
|
name: Build and Attach AppImage (aarch64)
|
|
needs: build-and-release
|
|
runs-on: ubuntu-24.04-arm
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Extract version from tag
|
|
id: get_version
|
|
run: |
|
|
VERSION=${GITHUB_REF#refs/tags/v}
|
|
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
|
|
|
|
# The wheel is py3-none-any, so this is the wheel the release and PyPI
|
|
# carry. Building it again here made a third copy of the same version that
|
|
# no published checksum could describe.
|
|
- name: Download the built distributions
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: python-dist
|
|
path: dist
|
|
|
|
# glslc is built from the pinned shaderc commit inside the container -
|
|
# Ubuntu 22.04 packages none - and depends on nothing but that commit.
|
|
- name: Cache AppImage build tools
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ~/.cache/vocalinux-appimage
|
|
key: appimage-tools-${{ runner.arch }}-${{ hashFiles('packaging/appimage/tool_checksums.txt') }}
|
|
|
|
- name: Build AppImage (aarch64)
|
|
env:
|
|
VOCALINUX_APPIMAGE_REQUIRE_VULKAN: "1"
|
|
run: |
|
|
bash packaging/appimage/docker-build.sh dist/*.whl "${{ steps.get_version.outputs.VERSION }}" dist
|
|
ls -la dist/*.AppImage
|
|
|
|
- name: Upload the aarch64 AppImage
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: appimage-aarch64
|
|
path: dist/*.AppImage
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
- name: Attach to release
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release upload "${{ github.ref_name }}" \
|
|
dist/*.AppImage \
|
|
--repo ${{ github.repository }} \
|
|
--clobber
|
|
|
|
# Pins match .github/workflows/flatpak.yml. These jobs start with the tag
|
|
# (no needs) so they overlap the wheel / x86_64 AppImage job. The builder
|
|
# image is Freedesktop SDK, not a GitHub-hosted runner: no `gh`, so attach
|
|
# is a follow-up job on ubuntu-latest, same `gh release upload --clobber`
|
|
# as the aarch64 AppImage. The image is digest-pinned: it runs privileged.
|
|
build-flatpak-amd64:
|
|
name: Build Flatpak (x86_64)
|
|
runs-on: ubuntu-latest
|
|
container:
|
|
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-50@sha256:1fb2df10a57276f90806e1f35454048e30bf1855b7b4ff4808c9ee55887bd852
|
|
options: --privileged
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Extract version from tag
|
|
id: get_version
|
|
run: |
|
|
VERSION=${GITHUB_REF#refs/tags/v}
|
|
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
|
|
|
|
- name: Build and bundle
|
|
uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6
|
|
with:
|
|
bundle: Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.flatpak
|
|
manifest-path: packaging/flatpak/com.vocalinux.Vocalinux.yml
|
|
# Same prefix rule as flatpak.yml: restore-keys is
|
|
# `flatpak-builder-${arch}`. The action appends -${arch} again.
|
|
cache-key: flatpak-builder-x86_64-${{ hashFiles('packaging/flatpak/**') }}
|
|
arch: x86_64
|
|
# Pinned actions/upload-artifact below, same digest as AppImage.
|
|
upload-artifact: false
|
|
mirror-screenshots-url: https://dl.flathub.org/media
|
|
|
|
- name: Upload the x86_64 Flatpak
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: flatpak-x86_64
|
|
path: Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.flatpak
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
build-flatpak-arm64:
|
|
name: Build Flatpak (aarch64)
|
|
runs-on: ubuntu-24.04-arm
|
|
container:
|
|
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-50@sha256:1fb2df10a57276f90806e1f35454048e30bf1855b7b4ff4808c9ee55887bd852
|
|
options: --privileged
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Extract version from tag
|
|
id: get_version
|
|
run: |
|
|
VERSION=${GITHUB_REF#refs/tags/v}
|
|
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
|
|
|
|
- name: Build and bundle
|
|
uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6
|
|
with:
|
|
bundle: Vocalinux-${{ steps.get_version.outputs.VERSION }}-aarch64.flatpak
|
|
manifest-path: packaging/flatpak/com.vocalinux.Vocalinux.yml
|
|
cache-key: flatpak-builder-aarch64-${{ hashFiles('packaging/flatpak/**') }}
|
|
arch: aarch64
|
|
upload-artifact: false
|
|
mirror-screenshots-url: https://dl.flathub.org/media
|
|
|
|
- name: Upload the aarch64 Flatpak
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: flatpak-aarch64
|
|
path: Vocalinux-${{ steps.get_version.outputs.VERSION }}-aarch64.flatpak
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
attach-flatpak:
|
|
name: Attach Flatpak bundles
|
|
needs: [build-and-release, build-flatpak-amd64, build-flatpak-arm64]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Download the x86_64 Flatpak
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: flatpak-x86_64
|
|
path: dist
|
|
|
|
- name: Download the aarch64 Flatpak
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: flatpak-aarch64
|
|
path: dist
|
|
|
|
- name: Attach to release
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
ls -la dist/*.flatpak
|
|
gh release upload "${{ github.ref_name }}" \
|
|
dist/*.flatpak \
|
|
--repo ${{ github.repository }} \
|
|
--clobber
|
|
|
|
# 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, attach-snap]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
id-token: write
|
|
attestations: write
|
|
steps:
|
|
- name: Collect every artifact this run published
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
merge-multiple: true
|
|
path: dist
|
|
|
|
- name: Generate SHA256SUMS
|
|
working-directory: dist
|
|
run: |
|
|
set -euo pipefail
|
|
ls -la
|
|
# Bare names, so `sha256sum -c` works in whatever directory the user
|
|
# downloaded the release into.
|
|
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.
|
|
- name: Attest build provenance
|
|
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
|
|
with:
|
|
subject-checksums: dist/SHA256SUMS
|
|
|
|
- name: Attach SHA256SUMS to the release
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release upload "${{ github.ref_name }}" \
|
|
dist/SHA256SUMS \
|
|
--repo ${{ github.repository }} \
|
|
--clobber
|
|
|
|
publish-pypi:
|
|
name: Publish to PyPI
|
|
needs: build-and-release
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
id-token: write # PyPI trusted publishing; there is no API token to leak
|
|
environment:
|
|
name: pypi
|
|
url: https://pypi.org/project/vocalinux/
|
|
steps:
|
|
# Not a rebuild: the artifact attached to the GitHub release, so one
|
|
# checksum describes both.
|
|
- name: Download the built distributions
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: python-dist
|
|
path: dist
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
|
|
- name: Verify package
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install twine
|
|
twine check dist/*
|
|
|
|
- name: Publish to PyPI
|
|
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
with:
|
|
skip-existing: true
|
|
|
|
publish-aur:
|
|
name: Publish to AUR
|
|
needs: build-and-release
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
# Secrets: AUR_SSH_PRIVATE_KEY, AUR_USERNAME, AUR_EMAIL — see docs/AUR.md
|
|
steps:
|
|
- name: Check for AUR credentials
|
|
id: aur
|
|
env:
|
|
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
|
run: |
|
|
if [ -z "$AUR_SSH_PRIVATE_KEY" ]; then
|
|
echo "configured=false" >> "$GITHUB_OUTPUT"
|
|
echo "AUR_SSH_PRIVATE_KEY not set; skipping (docs/AUR.md)"
|
|
else
|
|
echo "configured=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Checkout code
|
|
if: steps.aur.outputs.configured == 'true'
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Bump PKGBUILD version
|
|
if: steps.aur.outputs.configured == 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
TAG="${GITHUB_REF#refs/tags/v}"
|
|
PKGBUILD=packaging/aur/vocalinux/PKGBUILD
|
|
sed -i "s/^pkgver=.*/pkgver=${TAG//-/}/; s/^_tag=.*/_tag=${TAG}/; s/^pkgrel=.*/pkgrel=1/" "$PKGBUILD"
|
|
grep -E '^(pkgver|_tag|pkgrel)=' "$PKGBUILD"
|
|
|
|
- name: Publish AUR package
|
|
if: steps.aur.outputs.configured == 'true'
|
|
uses: KSXGitHub/github-actions-deploy-aur@084b0d9b15415bf9cdb65d44dad1efe37a354050 # v4.2.0
|
|
with:
|
|
pkgname: vocalinux
|
|
pkgbuild: packaging/aur/vocalinux/PKGBUILD
|
|
updpkgsums: 'true'
|
|
commit_username: ${{ secrets.AUR_USERNAME }}
|
|
commit_email: ${{ secrets.AUR_EMAIL }}
|
|
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
|
commit_message: 'Release ${{ github.ref_name }}'
|
|
|
|
# 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).
|
|
build-snap:
|
|
name: Build Snap
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- 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 }}
|
|
run: |
|
|
if [ -z "$SNAPCRAFT_STORE_CREDENTIALS" ]; then
|
|
echo "SNAPCRAFT_STORE_CREDENTIALS is unset; cannot publish the snap"
|
|
exit 1
|
|
fi
|
|
|
|
- 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
|
|
env:
|
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
|
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
|
|
runs-on: ubuntu-latest
|
|
needs: [build-and-release, publish-pypi]
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '24'
|
|
cache: 'npm'
|
|
cache-dependency-path: './web/package-lock.json'
|
|
|
|
- name: Install dependencies
|
|
working-directory: ./web
|
|
run: npm ci || npm install
|
|
|
|
- name: Sync version from source code
|
|
run: |
|
|
VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' src/vocalinux/version.py)
|
|
echo "Syncing version: $VERSION"
|
|
# JSON-LD uses unquoted JS keys: softwareVersion: "x.y.z"
|
|
if grep -qE 'softwareVersion:\s*"' web/src/app/page.tsx; then
|
|
sed -i 's/softwareVersion: "[^"]*"/softwareVersion: "'"$VERSION"'"/' web/src/app/page.tsx
|
|
echo "Updated page.tsx version:"
|
|
grep -oP 'softwareVersion:\s*"\K[^"]+' web/src/app/page.tsx
|
|
elif grep -qE 'softwareVersion:\s*"' web/src/app/layout.tsx; then
|
|
sed -i 's/softwareVersion: "[^"]*"/softwareVersion: "'"$VERSION"'"/' web/src/app/layout.tsx
|
|
echo "Updated layout.tsx version:"
|
|
grep -oP 'softwareVersion:\s*"\K[^"]+' web/src/app/layout.tsx
|
|
else
|
|
echo "::warning::No softwareVersion field found in page.tsx or layout.tsx; skipping sync"
|
|
fi
|
|
# Update package.json version (remove -alpha/beta/rc suffix for npm)
|
|
NPM_VERSION=${VERSION%-*}
|
|
NPM_VERSION=${NPM_VERSION%-beta*}
|
|
NPM_VERSION=${NPM_VERSION%-rc*}
|
|
sed -i 's/"version": "[^"]*"/"version": "'"$NPM_VERSION"'"/' web/package.json
|
|
# Verify the changes
|
|
echo "Updated package.json version:"
|
|
grep -oP '"version":\s*"\K[^"]+' web/package.json
|
|
|
|
- name: Build and deploy website
|
|
working-directory: ./web
|
|
run: |
|
|
npm ci || npm install
|
|
npm run deploy
|
|
|
|
- name: Deploy to gh-pages
|
|
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4
|
|
with:
|
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
publish_dir: ./web/out
|
|
publish_branch: gh-pages
|
|
cname: vocalinux.com
|
|
force_orphan: true
|