Skip to content
Merged
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
74 changes: 68 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ name: CI
# stage-1 executable, and stage 1 rebuilds a clean export (stage 2). Bootstrap test results never count for the
# sources under test. See docs/guides/development.md#continuous-integration.

# Documentation-only changes (Markdown or docs/) skip the build matrix inside the workflow instead of through
# `paths-ignore`: a workflow that never starts leaves a required check pending forever and would block every
# documentation-only pull request. `CI gate` always runs and is the single required status check.
on:
pull_request:
paths-ignore:
- "**/*.md"
- "docs/**"
push:
branches: [main]
paths-ignore:
- "**/*.md"
- "docs/**"
workflow_dispatch:

permissions:
Expand All @@ -25,8 +22,48 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
changes:
name: changes
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
code: ${{ steps.classify.outputs.code }}
steps:
- name: Check out the history needed to classify the change
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false

- name: Classify the change
id: classify
shell: bash
env:
EVENT: ${{ github.event_name }}
PR_BASE: ${{ github.event.pull_request.base.sha }}
PR_HEAD: ${{ github.event.pull_request.head.sha }}
PUSH_BEFORE: ${{ github.event.before }}
PUSH_HEAD: ${{ github.sha }}
run: |
set -euo pipefail
# Anything that cannot be classified runs the full matrix: skipping is only for a proven documentation-only change.
code=true
case "$EVENT" in
pull_request) range="$PR_BASE...$PR_HEAD" ;;
push) range="$PUSH_BEFORE..$PUSH_HEAD" ;;
*) range= ;;
esac
if [ -n "$range" ] && files="$(git diff --name-only "$range" 2>/dev/null)" && [ -n "$files" ]; then
if printf '%s\n' "$files" | grep -qvE '(\.md$|^docs/)'; then code=true; else code=false; fi
fi
echo "event=$EVENT range=${range:-none} code=$code"
printf '%s\n' "${files:-}" | sed 's/^/changed: /'
echo "code=$code" >> "$GITHUB_OUTPUT"

native:
name: native (${{ matrix.label || matrix.host }})
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
# Keep both old-LTS and new Linux loader coverage required after the producer's TLSDESC repair.
Expand Down Expand Up @@ -122,3 +159,28 @@ jobs:
${{ runner.temp }}/crt-cli-ci/sdk-source.json
if-no-files-found: warn
retention-days: 14

# The only required status check. It always runs: a documentation-only change legitimately skips the matrix, but a
# code change whose matrix failed, was cancelled or never ran must not pass.
gate:
name: CI gate
needs: [changes, native]
if: ${{ always() }}
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Decide
shell: bash
env:
CHANGES: ${{ needs.changes.result }}
NATIVE: ${{ needs.native.result }}
CODE: ${{ needs.changes.outputs.code }}
run: |
set -euo pipefail
echo "changes=$CHANGES native=$NATIVE code=$CODE"
[ "$CHANGES" = success ] || { echo "change classification did not succeed"; exit 1; }
if [ "$CODE" = true ]; then
[ "$NATIVE" = success ] || { echo "code changed but the native matrix result is '$NATIVE'"; exit 1; }
else
[ "$NATIVE" = skipped ] || { echo "documentation-only change but the native matrix result is '$NATIVE'"; exit 1; }
fi
13 changes: 8 additions & 5 deletions docs/guides/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,14 @@ unsupported for programs that import a DLL beside them.
repository permissions and no secrets. It builds nothing from CRT: it consumes published artifacts.
The repository carries no executables (`bin/` was removed); CI bootstraps from a release.

Push and PR events use `paths-ignore: ["**/*.md", "docs/**"]`, following the
CRT workflow's documentation exclusions. A change consisting only of Markdown
or files under `docs/` skips automatic CI; a mixed documentation/code change
still runs. Sources, headers, tests, tools, manifests, CI pins and workflow
configuration remain covered. Manual dispatch is available regardless of paths.
Push and PR events always start the workflow. A `changes` job diffs the pull request (merge base to head) or the push
(`before..sha`) and sets `code=false` only when every changed file is Markdown or under `docs/`; anything it cannot classify
(new branch, unreachable range, manual dispatch) runs the full matrix. The build matrix is skipped for `code=false`.
`CI gate` always runs and is the one required status check: it passes when the matrix succeeded for a code change or was
skipped for a documentation-only change, and fails when the matrix failed, was cancelled or never ran. The workflow does
not use `paths-ignore`, because a workflow that never starts would leave a required check pending and block
documentation-only pull requests. Sources, headers, tests, tools, manifests, CI pins and workflow configuration remain
covered. Manual dispatch is available regardless of paths.

1. **Bootstrap.** `tools/ci_bootstrap.py` downloads the host's published CRT-CLI Preview and checks its size and
SHA-256 against `.github/ci/pins.json`. The pin verifies byte identity;
Expand Down
Loading