Repository navigation
Recipe tests #130
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Recipe tests | |
| # Runs each lux recipe end-to-end inside a fresh container per supported | |
| # distro. Catches both "PR broke a recipe" and "upstream package / | |
| # repo / install path drifted" — the latter via the nightly cron. | |
| on: | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - 'crates/lux-knowledge/recipes/**' | |
| - 'crates/lux-knowledge/src/**' | |
| - 'crates/lux-tools/src/recipes.rs' | |
| - 'crates/lux-cli/src/main.rs' | |
| - '.github/workflows/recipe-tests.yml' | |
| schedule: | |
| - cron: '0 6 * * *' # 06:00 UTC daily — catches upstream drift | |
| workflow_dispatch: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| build: | |
| name: Build lux binary | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Build release binary | |
| run: cargo build --release --bin lux | |
| - name: Upload binary | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: lux-binary | |
| path: target/release/lux | |
| retention-days: 1 | |
| if-no-files-found: error | |
| recipe-test: | |
| name: ${{ matrix.recipe }} on ${{ matrix.distro }} | |
| needs: build | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false # collect every cell — the matrix table is the diagnostic | |
| matrix: | |
| include: | |
| - recipe: browser-chromium | |
| distro: fedora | |
| image: fedora:latest | |
| expect: success | |
| verify: command -v chromium-browser >/dev/null 2>&1 || command -v chromium >/dev/null 2>&1 | |
| - recipe: browser-chromium | |
| distro: archlinux | |
| image: archlinux:latest | |
| expect: success | |
| verify: command -v chromium >/dev/null 2>&1 | |
| - recipe: browser-chromium | |
| distro: debian | |
| image: debian:stable | |
| expect: success | |
| verify: command -v chromium >/dev/null 2>&1 | |
| - recipe: browser-chromium | |
| distro: ubuntu | |
| image: ubuntu:latest | |
| expect: refuse | |
| refuse_match: "snap shim" | |
| - recipe: editor-sublime | |
| distro: fedora | |
| image: fedora:latest | |
| expect: success | |
| verify: command -v subl >/dev/null 2>&1 | |
| - recipe: editor-sublime | |
| distro: archlinux | |
| image: archlinux:latest | |
| expect: success | |
| verify: command -v subl >/dev/null 2>&1 | |
| - recipe: editor-sublime | |
| distro: debian | |
| image: debian:stable | |
| expect: success | |
| verify: command -v subl >/dev/null 2>&1 | |
| - recipe: editor-sublime | |
| distro: ubuntu | |
| image: ubuntu:latest | |
| expect: success | |
| verify: command -v subl >/dev/null 2>&1 | |
| - recipe: zoom | |
| distro: fedora | |
| image: fedora:latest | |
| expect: success | |
| verify: command -v zoom >/dev/null 2>&1 | |
| - recipe: zoom | |
| distro: debian | |
| image: debian:stable | |
| expect: success | |
| verify: command -v zoom >/dev/null 2>&1 | |
| - recipe: zoom | |
| distro: ubuntu | |
| image: ubuntu:latest | |
| expect: success | |
| verify: command -v zoom >/dev/null 2>&1 | |
| - recipe: zoom | |
| distro: archlinux | |
| image: archlinux:latest | |
| expect: refuse | |
| refuse_match: "AUR" | |
| container: | |
| image: ${{ matrix.image }} | |
| steps: | |
| - name: Install minimal container deps | |
| shell: bash | |
| run: | | |
| set -eux | |
| if command -v dnf >/dev/null 2>&1; then | |
| dnf install -y --setopt=install_weak_deps=False ca-certificates | |
| elif command -v pacman >/dev/null 2>&1; then | |
| # Refresh package DB; archlinux:latest is rolling. | |
| pacman -Sy --noconfirm ca-certificates | |
| elif command -v apt-get >/dev/null 2>&1; then | |
| apt-get update | |
| apt-get install -y --no-install-recommends ca-certificates | |
| fi | |
| - name: Shim sudo (containers run as root by default) | |
| shell: bash | |
| run: | | |
| # The recipe shells use `sudo`; in the container we're already | |
| # root, so make `sudo` a transparent passthrough rather than | |
| # installing the real package per distro. | |
| printf '#!/bin/sh\nexec "$@"\n' > /usr/local/bin/sudo | |
| chmod +x /usr/local/bin/sudo | |
| - name: Download lux binary | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: lux-binary | |
| path: . | |
| - name: Make lux executable | |
| shell: bash | |
| run: chmod +x ./lux | |
| - name: Apply recipe ${{ matrix.recipe }} (expect ${{ matrix.expect }}) | |
| shell: bash | |
| run: | | |
| set +e | |
| OUTPUT=$(./lux recipe apply ${{ matrix.recipe }} --yes 2>&1) | |
| STATUS=$? | |
| set -e | |
| echo "::group::lux output (exit=$STATUS)" | |
| echo "$OUTPUT" | |
| echo "::endgroup::" | |
| case "${{ matrix.expect }}" in | |
| success) | |
| if [ "$STATUS" -ne 0 ]; then | |
| echo "::error::Expected success on ${{ matrix.distro }}, got exit $STATUS" | |
| exit 1 | |
| fi | |
| # Post-install verify: catches silent-green recipes that | |
| # exited 0 without actually installing the thing. Without | |
| # this, a recipe whose distro branch fell through to a | |
| # no-op would still report success. | |
| VERIFY='${{ matrix.verify }}' | |
| if [ -n "$VERIFY" ]; then | |
| if ! bash -c "$VERIFY"; then | |
| echo "::error::Recipe exited 0 but verify failed on ${{ matrix.distro }}: $VERIFY" | |
| exit 1 | |
| fi | |
| fi | |
| ;; | |
| refuse) | |
| # Recipe should refuse cleanly (exit != 0) and mention the | |
| # reason in its output, so users know why and what to do | |
| # instead. The needle to grep for is per-cell (matrix.refuse_match). | |
| if [ "$STATUS" -eq 0 ]; then | |
| echo "::error::Expected ${{ matrix.distro }} to refuse, but recipe succeeded" | |
| exit 1 | |
| fi | |
| NEEDLE='${{ matrix.refuse_match }}' | |
| if [ -z "$NEEDLE" ]; then | |
| echo "::error::expect=refuse but matrix.refuse_match is empty" | |
| exit 1 | |
| fi | |
| if ! printf '%s' "$OUTPUT" | grep -qi "$NEEDLE"; then | |
| echo "::error::Expected refusal message to contain '$NEEDLE' on ${{ matrix.distro }}" | |
| exit 1 | |
| fi | |
| ;; | |
| *) | |
| echo "::error::Unknown expect value: ${{ matrix.expect }}" | |
| exit 1 | |
| ;; | |
| esac |