Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions .github/workflows/Semgrep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ permissions:
contents: read

jobs:
# Docs-only PRs skip the semgrep scan; a skipped required check is treated as
# passing by branch protection. Non-PR events (push, schedule) always scan.
# See .github/workflows/_changes.yml.
changes:
uses: ./.github/workflows/_changes.yml

semgrep:
# User definable name of this GitHub Actions job.
needs: [changes]
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
Expand All @@ -38,11 +45,14 @@ jobs:
# branch — the Create Release PR workflow posts the required checks for it).
# The release gate is keyed on the PR author, not the branch name (which any
# contributor controls), so a "release/…" branch can't bypass required checks.
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ github.actor != 'dependabot[bot]'
&& !(github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}

steps:
# Fetch project source with GitHub Actions Checkout.
Expand All @@ -57,4 +67,4 @@ jobs:
uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
sarif_file: semgrep.sarif
if: always()
if: always()
54 changes: 54 additions & 0 deletions .github/workflows/_changes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: changes

# Reusable detector shared by the PR-triggered CI workflows (lint, typecheck,
# test, windows, executable-check, semgrep). It reports a single output, `code`,
# which is 'true' when the triggering pull request changes anything other than
# documentation, and 'false' for docs-only PRs.
#
# Docs allowlist: *.md, *.txt, README*, LICENSE, docs/**. Anything else --
# source, lockfiles, Dockerfiles, Makefiles, the workflows themselves -- counts
# as code and forces the full suite to run.
#
# Callers gate their heavy jobs on `needs.changes.outputs.code == 'true'`. A
# docs-only PR therefore SKIPS those jobs, and GitHub branch protection treats a
# skipped required check as passing, so the PR can still merge with zero tests.
#
# Fail-safe by construction: only a clean docs-only `pull_request` yields
# 'false'. Any other event (push, workflow_dispatch, schedule), an empty or
# failed diff, or any non-docs path yields 'true' (full CI).
on:
workflow_call:
outputs:
code:
description: "true when non-docs files changed (or for non-PR events)"
value: ${{ jobs.detect.outputs.code }}

permissions:
contents: read

jobs:
detect:
name: Detect changes
runs-on: ubuntu-latest
outputs:
code: ${{ steps.detect.outputs.code }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
- id: detect
name: Detect non-docs changes
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
run: |
code=true
if [ "$EVENT_NAME" = "pull_request" ]; then
files="$(git diff --name-only "origin/${BASE_REF}...HEAD" || true)"
echo "Changed files:"; printf '%s\n' "$files"
if [ -n "$files" ] && ! printf '%s\n' "$files" | grep -qvE '(\.md|\.txt)$|(^|/)README[^/]*$|(^|/)LICENSE$|^docs/'; then
code=false
fi
fi
echo "Resolved code=$code (docs-only PR => code=false => skip CI)"
echo "code=$code" >> "$GITHUB_OUTPUT"
10 changes: 9 additions & 1 deletion .github/workflows/executable-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,25 @@ concurrency:
cancel-in-progress: true

jobs:
# Docs-only PRs skip the executable build; see .github/workflows/_changes.yml.
changes:
uses: ./.github/workflows/_changes.yml

verify:
name: Build & verify executable
needs: [changes]
# Skip CI only for the automated release PR: opened by github-actions[bot],
# from this same repo, on a release/* branch (the Create Release PR workflow
# posts the required checks as passing for it). Keyed on the PR author, not the
# branch name, so a "release/…" branch can't be used to bypass required checks.
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ !(github.event_name == 'pull_request'
&& github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}
runs-on: macos-latest
steps:
- uses: actions/checkout@v5
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,27 @@ permissions:
contents: read

jobs:
# Resolve whether this PR touches anything other than docs. Docs-only PRs skip
# the lint job below; a skipped required check is treated as passing by branch
# protection. See .github/workflows/_changes.yml.
changes:
uses: ./.github/workflows/_changes.yml

lint:
name: Lint
needs: [changes]
# Skip CI only for the automated release PR: opened by github-actions[bot],
# from this same repo, on a release/* branch (the Create Release PR workflow
# posts the required checks as passing for it). Keyed on the PR author, not the
# branch name, so a "release/…" branch can't be used to bypass required checks.
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ !(github.event_name == 'pull_request'
&& github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
Expand Down
25 changes: 20 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@
pull_request:
workflow_dispatch:
jobs:
# Docs-only PRs skip build/test/regression below; a skipped required check is
# treated as passing by branch protection. See .github/workflows/_changes.yml.
changes:
uses: ./.github/workflows/_changes.yml

build:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium test

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}
name: Build
needs: [changes]
# Skip CI only for the automated release PR: opened by github-actions[bot],
# from this same repo, on a release/* branch (the Create Release PR workflow
# posts the required checks as passing for it). The gate is keyed on the PR
# author — NOT the branch name, which any contributor controls — so a PR from
# a "release/…" branch can't be used to bypass required checks.
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ !(github.event_name == 'pull_request'
&& github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
Expand Down Expand Up @@ -46,12 +55,15 @@
test:
name: Test ${{ matrix.package }}
# Skip only for the automated release PR (see the build job for rationale).
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ !(github.event_name == 'pull_request'
&& github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
needs: [build]
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}
needs: [changes, build]
strategy:
matrix:
os: [ubuntu-latest]
Expand Down Expand Up @@ -154,12 +166,15 @@
regression:
name: Regression
# Skip only for the automated release PR (see the build job for rationale).
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ !(github.event_name == 'pull_request'
&& github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
needs: [build]
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}
needs: [changes, build]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,25 @@ permissions:
contents: read

jobs:
# Docs-only PRs skip typecheck; see .github/workflows/_changes.yml.
changes:
uses: ./.github/workflows/_changes.yml

typecheck:
name: Typecheck
needs: [changes]
# Skip CI only for the automated release PR: opened by github-actions[bot],
# from this same repo, on a release/* branch (the Create Release PR workflow
# posts the required checks as passing for it). Keyed on the PR author, not the
# branch name, so a "release/…" branch can't be used to bypass required checks.
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ !(github.event_name == 'pull_request'
&& github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
Expand Down
18 changes: 15 additions & 3 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,26 @@
pull_request:
workflow_dispatch:
jobs:
# Docs-only PRs skip build/test below; a skipped required check is treated as
# passing by branch protection. See .github/workflows/_changes.yml.
changes:
uses: ./.github/workflows/_changes.yml

build:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}
name: Build
needs: [changes]
# Skip CI only for the automated release PR: opened by github-actions[bot],
# from this same repo, on a release/* branch (the Create Release PR workflow
# posts the required checks as passing for it). Keyed on the PR author, not the
# branch name, so a "release/…" branch can't be used to bypass required checks.
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ !(github.event_name == 'pull_request'
&& github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}
runs-on: windows-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
Expand Down Expand Up @@ -45,12 +54,15 @@
test:
name: Test ${{ matrix.package }}
# Skip only for the automated release PR (see the build job for rationale).
# Also skipped for docs-only PRs (changes.code == 'false').
if: >-
${{ !(github.event_name == 'pull_request'
&& github.event.pull_request.user.login == 'github-actions[bot]'
&& github.event.pull_request.head.repo.full_name == github.repository
&& startsWith(github.head_ref, 'release/')) }}
needs: [build]
&& startsWith(github.head_ref, 'release/'))
&& (github.event_name != 'pull_request'
|| needs.changes.outputs.code == 'true') }}
needs: [changes, build]
strategy:
fail-fast: false
matrix:
Expand Down
Loading