* ci(install): gate the remote install path end to end A curl|bash user takes a different path than the local install gate covers: bootstrap outside a checkout, tag selection, clone/fetch of the selected tag, then handoff to the tagged installer with the original arguments. Add scripts/install-remote-test.sh, which publishes HEAD as a tag in a local mirror, runs install.sh against it twice (fresh clone, then fetch+reset of the existing clone), and fails closed when the clone's HEAD is not the selected tag commit. install.sh gains a VOCALINUX_REPO_URL override so the mirror can stand in for the public remote. Wire a single-distro job into the distro matrix and a just remote-install-gate recipe. Refs #701 * ci(install): publish the gate tag as an orphan from HEAD's tree CI checks out with fetch-depth 1, and git refuses to push or fetch a shallow boundary commit into a ref. Build the tag on an orphan commit created from HEAD's tree instead, and make tree equality the fail-closed check that installer, modules and exports are this commit's files. * test(install): force the re-run arm to prove fetch and reset Review feedback on the remote-install gate: the second run reused a clone already on the tag, so a broken fetch or reset would still pass. Move the clone onto a descendant commit and delete its local tag ref before the re-run, so only a real fetch + reset restores them. Also list the new recipe in AGENTS.md's command guide. --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
122 lines
4.1 KiB
YAML
122 lines
4.1 KiB
YAML
name: Distro Test Matrix
|
|
|
|
on:
|
|
push:
|
|
branches: [ main, master ]
|
|
paths:
|
|
- 'install.sh'
|
|
- '**/*.sh'
|
|
- 'scripts/**'
|
|
- 'pyproject.toml'
|
|
- 'uv.lock'
|
|
- 'requirements/**'
|
|
- 'src/vocalinux/version.py'
|
|
- 'vocalinux.desktop'
|
|
- '.github/workflows/distro-test-matrix.yml'
|
|
pull_request:
|
|
branches: [ main, master ]
|
|
paths:
|
|
- 'install.sh'
|
|
- '**/*.sh'
|
|
- 'scripts/**'
|
|
- 'pyproject.toml'
|
|
- 'uv.lock'
|
|
- 'requirements/**'
|
|
- 'src/vocalinux/version.py'
|
|
- 'vocalinux.desktop'
|
|
- '.github/workflows/distro-test-matrix.yml'
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
# Six containers a run, on a filter that any shell script matches: a second
|
|
# push should not leave the first one's matrix running to completion.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# First, because there is no point spending six containers on a script that
|
|
# does not parse.
|
|
syntax:
|
|
name: Shell syntax
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
- name: bash -n every shell script we ship
|
|
run: |
|
|
set -euo pipefail
|
|
git ls-files -z '*.sh' | xargs -0 -n1 -t bash -n
|
|
|
|
install:
|
|
name: install.sh on ${{ matrix.container }}
|
|
runs-on: ubuntu-latest
|
|
needs: syntax
|
|
# A real install pulls the distro's whole GTK and build-toolchain closure
|
|
# before pip sees a single wheel.
|
|
timeout-minutes: 45
|
|
strategy:
|
|
# Every distro reports independently: one failing arm is a finding about
|
|
# that distro, not a reason to lose the other five results.
|
|
fail-fast: false
|
|
matrix:
|
|
# Adding a container here means adding a bootstrap arm to
|
|
# scripts/install-test.sh; tests/test_installer_gate.py holds the two
|
|
# lists together.
|
|
container:
|
|
- ubuntu:24.04
|
|
- ubuntu:26.04
|
|
- debian:12
|
|
- fedora:42
|
|
- archlinux:latest
|
|
- opensuse/tumbleweed
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
# The gate takes the tree with `git archive HEAD`, so it needs the
|
|
# commit's objects. Shallow is enough; the history is not.
|
|
fetch-depth: 1
|
|
|
|
# Not `container:` on the job: that would run every step inside the image
|
|
# as root, and install.sh refuses to run as root. Docker from the runner
|
|
# lets the gate create an unprivileged user with sudo instead.
|
|
- name: Run install.sh unattended in ${{ matrix.container }}
|
|
run: |
|
|
set -euo pipefail
|
|
docker run --rm \
|
|
-v "$PWD:$PWD:ro" -e REPO="$PWD" \
|
|
"${{ matrix.container }}" \
|
|
bash "$PWD/scripts/install-test.sh"
|
|
|
|
# The install matrix gates the local path: it hands install.sh a checkout.
|
|
# A curl|bash user takes a different one — bootstrap outside a checkout, tag
|
|
# selection, clone, handoff to the tagged installer — which scripts/
|
|
# install-test.sh's own header admits it does not cover (#701). One container:
|
|
# the remote-only half (bootstrap, tag, handoff) is distro-agnostic, and the
|
|
# tagged installer's per-distro work is already covered by the matrix above.
|
|
install-remote:
|
|
name: remote install.sh on debian:12
|
|
runs-on: ubuntu-latest
|
|
needs: syntax
|
|
# Two sequential remote installs (fresh clone, then fetch+reset) plus a
|
|
# real package install on each.
|
|
timeout-minutes: 45
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
# The gate publishes HEAD as a tag in its mirror; it needs the
|
|
# commit's objects, not history.
|
|
fetch-depth: 1
|
|
|
|
- name: Run the remote install path in debian:12
|
|
run: |
|
|
set -euo pipefail
|
|
docker run --rm \
|
|
-v "$PWD:$PWD:ro" -e REPO="$PWD" \
|
|
debian:12 \
|
|
bash "$PWD/scripts/install-remote-test.sh"
|