diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b71fb1b..fd89abe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,8 @@ name: CI # job that exercises the real source sites and the token-gated pixiv tests. # # Layering: +# changes — decides whether anything but documentation changed; a docs-only +# push/PR skips `test` (which then reports as skipped, not missing). # test — fmt + clippy + the full offline unit suite + a release-profile # build + cargo-audit dependency gate. Runs on every push and PR, # including forks (it needs no secrets). @@ -46,7 +48,53 @@ env: RUST_BACKTRACE: 1 jobs: + # Docs-only changes skip the heavy job: a README edit does not need a four + # minute Rust build (and it cannot break one). A gate job rather than a + # workflow-level `paths` filter — that leaves the run without a `test` check + # at all, and a required status check then waits for something that will + # never be reported, while a *skipped* job reports as neutral. + changes: + runs-on: ubuntu-latest + timeout-minutes: 10 + outputs: + code: ${{ steps.diff.outputs.code }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 # the diff below needs the pushed range + - id: diff + shell: bash + run: | + set -euo pipefail + zero=0000000000000000000000000000000000000000 + if [ "${{ github.event_name }}" = "pull_request" ]; then + base="origin/${{ github.base_ref }}" + git fetch --quiet --no-tags origin "${{ github.base_ref }}" + changed="$(git diff --name-only "$base...HEAD")" + else + before="${{ github.event.before }}" + if [ -z "$before" ] || [ "$before" = "$zero" ]; then + # New branch or force push: no usable base to compare against, + # so the full suite runs. Same for schedule/dispatch, which have + # no `before` at all. + changed="" + else + changed="$(git diff --name-only "$before..${{ github.sha }}")" + fi + fi + # Only a change that is *entirely* markdown may skip the job; + # anything else — and an empty diff, i.e. a re-run of the same + # commit — counts as code. + code=true + if [ -n "$changed" ] && ! grep -qvE '\.md$' <<<"$changed"; then + code=false + fi + echo "changed: ${changed:-}" + echo "code=$code" >> "$GITHUB_OUTPUT" + test: + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: ubuntu-latest # Generous on purpose: the release-profile build below is cold on the very # first run (thin LTO + codegen-units = 1 across every dependency), and a diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index af76284..4d41e1a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -75,6 +75,7 @@ jobs: - id: check shell: bash run: | + zero=0000000000000000000000000000000000000000 if [ "$GITHUB_REF_TYPE" = "branch" ]; then # A branch run can start before the release tag for its commit # reaches the remote — pushing master first is the usual way to hit @@ -87,13 +88,25 @@ jobs: # pushing the branch and the tag together # (`git push origin master vX.Y.Z`) removes it. git fetch --tags --force --quiet origin + if git tag --points-at "$GITHUB_SHA" | grep -q .; then + echo "commit already tagged; the tag run builds the image" + echo "build=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + # Nothing the image is made of changed — a documentation or + # workflow-only commit — so there is no new image to publish. The + # PR trigger's path list plus the crate sources, which the image + # compiles into the binary. + before="${{ github.event.before }}" + if [ -n "$before" ] && [ "$before" != "$zero" ] \ + && ! git diff --name-only "$before..$GITHUB_SHA" \ + | grep -qE '^(Dockerfile|docker-entrypoint\.sh|\.dockerignore|Cargo\.toml|Cargo\.lock|\.github/workflows/docker\.yml|crates/)'; then + echo "no build input changed; skipping the image build" + echo "build=false" >> "$GITHUB_OUTPUT" + exit 0 + fi fi - if [ "$GITHUB_REF_TYPE" = "branch" ] && git tag --points-at "$GITHUB_SHA" | grep -q .; then - echo "commit already tagged; the tag run builds the image" - echo "build=false" >> "$GITHUB_OUTPUT" - else - echo "build=true" >> "$GITHUB_OUTPUT" - fi + echo "build=true" >> "$GITHUB_OUTPUT" # No `actions/checkout` here on purpose: `docker/build-push-action` defaults # to the Git context (`https://github.com//.git#`), so