From 8d9f9cb99890c103819ea0ae44aab374a77e1843 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Sun, 24 May 2026 21:13:51 -0400 Subject: [PATCH] ci: skip expensive jobs for docs-only changes --- .github/scripts/detect-code-changes.sh | 50 ++++++++++++++++++++++++++ .github/workflows/ci.yml | 31 +++++++++++++--- .github/workflows/nix.yml | 28 +++++++++------ .github/workflows/pr-ci.yml | 25 ++++++++++--- 4 files changed, 114 insertions(+), 20 deletions(-) create mode 100755 .github/scripts/detect-code-changes.sh diff --git a/.github/scripts/detect-code-changes.sh b/.github/scripts/detect-code-changes.sh new file mode 100755 index 00000000..f832b72e --- /dev/null +++ b/.github/scripts/detect-code-changes.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +base_sha="${1:?base commit required}" +head_sha="${2:?head commit required}" + +if [[ "$base_sha" =~ ^0+$ ]]; then + base_sha="$(git hash-object -t tree /dev/null)" +fi + +# Ensure the comparison commits are available even when checkout used a shallow clone. +for sha in "$base_sha" "$head_sha"; do + if ! git cat-file -e "$sha^{commit}" 2>/dev/null && ! git cat-file -e "$sha^{tree}" 2>/dev/null; then + git fetch --no-tags --depth=1 origin "$sha" + fi +done + +is_docs_only_path() { + local path="$1" + + case "$path" in + *.md | docs/* | assets/* | LICENSE) + return 0 + ;; + *) + return 1 + ;; + esac +} + +code_changed=false +while IFS= read -r path; do + [[ -z "$path" ]] && continue + + if ! is_docs_only_path "$path"; then + code_changed=true + break + fi +# Disable rename detection so code-to-docs renames still expose the removed code path. +done < <(git diff --name-only --no-renames "$base_sha" "$head_sha") + +if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + echo "code_changed=$code_changed" >> "$GITHUB_OUTPUT" +fi + +if [[ "$code_changed" == "true" ]]; then + echo "Code changes detected; expensive CI jobs should run." +else + echo "Only docs/assets metadata changes detected; expensive CI jobs can be skipped." +fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d44e63c..b0979c3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,6 @@ on: push: branches: - main - paths-ignore: - - "**/*.md" - - "docs/**" - - "assets/**" - - "LICENSE" env: SKIP_INSTALL_SIMPLE_GIT_HOOKS: "1" @@ -18,8 +13,26 @@ concurrency: cancel-in-progress: true jobs: + changes: + name: Detect code changes + runs-on: ubuntu-latest + outputs: + code: ${{ steps.detect.outputs.code_changed }} + steps: + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Detect code changes + id: detect + env: + BASE_SHA: ${{ github.event.before }} + HEAD_SHA: ${{ github.sha }} + run: .github/scripts/detect-code-changes.sh "$BASE_SHA" "$HEAD_SHA" + validate: name: Typecheck + tests + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: ubuntu-latest steps: - name: Check out repository @@ -55,6 +68,8 @@ jobs: tty-smoke: name: Terminal smoke tests + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: ubuntu-latest steps: - name: Check out repository @@ -78,6 +93,8 @@ jobs: pack-npm: name: Verify npm package + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: ubuntu-latest steps: - name: Check out repository @@ -119,6 +136,8 @@ jobs: prebuilt-npm: name: Verify prebuilt npm package (${{ matrix.os }}) + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -155,6 +174,8 @@ jobs: build-bin: name: Build compiled binary + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: ubuntu-latest steps: - name: Check out repository diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index c7975a69..1ebed5d6 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -2,27 +2,35 @@ name: Nix on: pull_request: - paths-ignore: - - "**/*.md" - - "docs/**" - - "assets/**" - - "LICENSE" push: branches: - main - paths-ignore: - - "**/*.md" - - "docs/**" - - "assets/**" - - "LICENSE" concurrency: group: nix-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: + changes: + name: Detect code changes + runs-on: ubuntu-latest + outputs: + code: ${{ steps.detect.outputs.code_changed }} + steps: + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Detect code changes + id: detect + env: + BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} + HEAD_SHA: ${{ github.sha }} + run: .github/scripts/detect-code-changes.sh "$BASE_SHA" "$HEAD_SHA" + package: name: Package + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: ubuntu-latest steps: - name: Check out repository diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 6fd8968e..8495d7cf 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -2,11 +2,6 @@ name: CI on: pull_request: - paths-ignore: - - "**/*.md" - - "docs/**" - - "assets/**" - - "LICENSE" env: SKIP_INSTALL_SIMPLE_GIT_HOOKS: "1" @@ -16,8 +11,26 @@ concurrency: cancel-in-progress: true jobs: + changes: + name: Detect code changes + runs-on: ubuntu-latest + outputs: + code: ${{ steps.detect.outputs.code_changed }} + steps: + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Detect code changes + id: detect + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.sha }} + run: .github/scripts/detect-code-changes.sh "$BASE_SHA" "$HEAD_SHA" + windows-compat: name: Windows compatibility + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: windows-latest steps: - name: Disable automatic CRLF conversion @@ -58,6 +71,8 @@ jobs: pr-validate: name: Typecheck + Test + Smoke + needs: changes + if: needs.changes.outputs.code == 'true' runs-on: ubuntu-latest steps: - name: Check out repository