name: CI on: push: branches: [main, "feat/**", "fix/**"] pull_request: workflow_dispatch: permissions: contents: read jobs: shell: name: shell (shellcheck + syntax) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: bash syntax check run: | fail=0 while IFS= read -r f; do bash -n "$f" || { echo "::error file=$f::bash syntax error"; fail=1; } done < <(find . -name '*.sh' -not -path './.git/*') exit $fail # Pinned to a release tag, not @master: a third-party action on a moving # branch runs whatever that branch contains at the time CI fires. - name: shellcheck uses: ludeeus/action-shellcheck@2.0.0 env: SHELLCHECK_OPTS: -S warning -e SC1091 python: name: python ${{ matrix.python }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: # TrueNAS SCALE middleware runs 3.11+; keep the patch importable across # the versions it may be injected into. python: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 # uv-managed interpreters instead of actions/setup-python: the prebuilt-CPython # download path setup-python relies on does not work on the self-hosted Gitea # runner (all three matrix jobs failed at setup there while passing on GitHub); # uv works identically on both. # # Installed by a plain `run:` step rather than astral-sh/setup-uv, because # act -- the engine behind the Gitea runner -- caches each ACTION as a single # shared git clone under /root/.cache/act/ and re-pulls it per job. The # three matrix jobs start within the same second on one runner, so they race # on that directory and whichever loses dies with # # lstat /root/.cache/act//.npmrc: no such file or directory # # before a single test runs -- a red `main` with zero suite output, and a # different victim each time (3.12 on one push, 3.11 on the next). A `run:` # step has no action-cache entry, so it cannot race. Serialising the matrix # would have been the other option; it costs 3x the wall clock and still # leaves actions/checkout sharing a cache across the four jobs. # # Pinned for the same reason ruff is pinned below: an unpinned uv means any # upstream release can turn main red with no code change here. - name: install uv env: UV_VERSION: "0.11.21" run: | curl -LsSf "https://astral.sh/uv/${UV_VERSION}/install.sh" | sh echo "$HOME/.local/bin" >> "$GITHUB_PATH" - name: ruff # Pinned: an unpinned ruff means any upstream release can turn main red # with no code change. run: uvx ruff@0.16.1 check patch tests tools - name: pytest run: uvx --python ${{ matrix.python }} pytest tests -v - name: verify injected middleware blocks compile # Belt-and-braces: the *_BLOCK strings are appended into live middlewared # modules. A syntax error there would break the box at boot. run: uvx --python ${{ matrix.python }} pytest tests/test_apply_blocks.py -v