pkg: add CI workflow and branch-protection policy
.github/workflows/ci.yml: fast lint jobs (clang-format, testserver
selftest, bootstrap.sh --check) that need no compiler; a gcc/clcang build
matrix and an ASan/UBSan + TSan sanitizer matrix that bootstrap via
tools/bootstrap.sh and run `ctest --preset {ci,dev,tsan}`; advisory
clang-tidy on changed files; and extension-lint + conformance jobs that
short-circuit to a passing "skipped" step until their lane lands, so they
can be marked required now. CMakePresets.json gains matching `tsan` and
`ci` test presets. .github/BRANCH_PROTECTION.md records the intended
required-checks policy (conformance required = the M0 exit gate).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
# Branch protection for `main`
|
||||||
|
|
||||||
|
CI defines the checks; **branch protection is a repo setting** (Settings → Branches →
|
||||||
|
Add rule) and has to be configured once by an admin. This file records the intended
|
||||||
|
policy so it can be re-applied or audited.
|
||||||
|
|
||||||
|
## Rule: `main`
|
||||||
|
|
||||||
|
- **Require a pull request before merging.** No direct pushes.
|
||||||
|
- **Require status checks to pass before merging**, and require branches to be up to date
|
||||||
|
first. Required checks:
|
||||||
|
|
||||||
|
| Check (job name in `ci.yml`) | Required from |
|
||||||
|
|---|---|
|
||||||
|
| `clang-format` | now |
|
||||||
|
| `testserver` | now |
|
||||||
|
| `bootstrap-script` | now |
|
||||||
|
| `build (gcc)` / `build (clang)` | when the first C++ lane merges |
|
||||||
|
| `sanitizers (dev)` / `sanitizers (tsan)` | when the first C++ lane merges |
|
||||||
|
| `conformance` | **when `tests/conformance/` lands — this is the M0 exit gate** |
|
||||||
|
| `extension-lint` | when `extension/` lands |
|
||||||
|
|
||||||
|
`clang-tidy` is intentionally **not** required through M1 (`continue-on-error: true`,
|
||||||
|
`.clang-tidy` has `WarningsAsErrors: ''`). Make it required at M2.
|
||||||
|
|
||||||
|
- **Require linear history** (matches CLAUDE.md §6: rebase onto `main`, no merge commits).
|
||||||
|
- **Require conversation resolution before merging.**
|
||||||
|
- Do **not** allow force pushes or deletions.
|
||||||
|
- Apply the rule to administrators too, except for the initial scaffolding period.
|
||||||
|
|
||||||
|
## Note on the "skipped" job steps
|
||||||
|
|
||||||
|
Several jobs (`conformance`, `extension-lint`, `clang-tidy`) short-circuit to a "skipped"
|
||||||
|
echo when their lane hasn't landed. They still report **success**, so they can be marked
|
||||||
|
required now without blocking — they start doing real work automatically on the commit
|
||||||
|
that adds the lane.
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
# GitHub-hosted runners are Ubuntu 24.04; the project targets 26.04. bootstrap.sh warns
|
||||||
|
# but proceeds. Revisit when 26.04 runners exist.
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# --- fast lint jobs: no compiler, no heavy deps -------------------------------------
|
||||||
|
clang-format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install clang-format
|
||||||
|
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends clang-format
|
||||||
|
- name: Check formatting
|
||||||
|
run: |
|
||||||
|
shopt -s globstar nullglob
|
||||||
|
files=(core/**/*.{cpp,hpp} daemon/**/*.{cpp,hpp} cli/**/*.{cpp,hpp} nmhost/**/*.{cpp,hpp})
|
||||||
|
if [ ${#files[@]} -eq 0 ]; then echo "no C++ sources yet — skipping"; exit 0; fi
|
||||||
|
printf '%s\n' "${files[@]}"
|
||||||
|
clang-format --dry-run --Werror "${files[@]}"
|
||||||
|
|
||||||
|
testserver:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
- name: testserver self-test
|
||||||
|
run: python3 tools/testserver/selftest.py
|
||||||
|
|
||||||
|
bootstrap-script:
|
||||||
|
# Keeps tools/bootstrap.sh honest: it must run clean and its --check must pass.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: sudo ./tools/bootstrap.sh --with-clang
|
||||||
|
- run: ./tools/bootstrap.sh --check
|
||||||
|
|
||||||
|
extension-lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- id: check
|
||||||
|
run: |
|
||||||
|
if [ -f extension/package.json ]; then echo "present=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else echo "present=false" >> "$GITHUB_OUTPUT"; fi
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
if: steps.check.outputs.present == 'true'
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
- name: web-ext lint
|
||||||
|
if: steps.check.outputs.present == 'true'
|
||||||
|
working-directory: extension
|
||||||
|
run: |
|
||||||
|
npm ci
|
||||||
|
npx web-ext lint --source-dir .
|
||||||
|
- name: skipped
|
||||||
|
if: steps.check.outputs.present == 'false'
|
||||||
|
run: echo "extension/ has not landed yet — skipping web-ext lint"
|
||||||
|
|
||||||
|
# --- build + test matrix ----------------------------------------------------------
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
compiler: [gcc, clang]
|
||||||
|
env:
|
||||||
|
CC: ${{ matrix.compiler == 'gcc' && 'gcc' || 'clang' }}
|
||||||
|
CXX: ${{ matrix.compiler == 'gcc' && 'g++' || 'clang++' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Bootstrap toolchain
|
||||||
|
run: sudo ./tools/bootstrap.sh --with-clang
|
||||||
|
- name: Configure
|
||||||
|
run: cmake --preset ci
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build --preset ci
|
||||||
|
- name: Test
|
||||||
|
run: ctest --preset ci --output-on-failure
|
||||||
|
|
||||||
|
sanitizers:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
preset: [dev, tsan] # dev = ASan + UBSan
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Bootstrap toolchain
|
||||||
|
run: sudo ./tools/bootstrap.sh --with-clang
|
||||||
|
- name: Configure
|
||||||
|
run: cmake --preset ${{ matrix.preset }}
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build --preset ${{ matrix.preset }}
|
||||||
|
- name: Test
|
||||||
|
run: ctest --preset ${{ matrix.preset }} --output-on-failure
|
||||||
|
env:
|
||||||
|
ASAN_OPTIONS: detect_leaks=1:halt_on_error=1
|
||||||
|
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
|
||||||
|
TSAN_OPTIONS: halt_on_error=1
|
||||||
|
|
||||||
|
clang-tidy:
|
||||||
|
# Advisory through M1 (see .clang-tidy WarningsAsErrors: ''); becomes required at M2.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- id: check
|
||||||
|
run: |
|
||||||
|
if ls core/CMakeLists.txt daemon/CMakeLists.txt >/dev/null 2>&1; then
|
||||||
|
echo "present=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else echo "present=false" >> "$GITHUB_OUTPUT"; fi
|
||||||
|
- name: Bootstrap toolchain
|
||||||
|
if: steps.check.outputs.present == 'true'
|
||||||
|
run: sudo ./tools/bootstrap.sh
|
||||||
|
- name: Configure (for compile_commands.json)
|
||||||
|
if: steps.check.outputs.present == 'true'
|
||||||
|
run: cmake --preset dev
|
||||||
|
- name: Run clang-tidy on changed files
|
||||||
|
if: steps.check.outputs.present == 'true'
|
||||||
|
run: |
|
||||||
|
mapfile -t files < <(git diff --name-only --diff-filter=ACM \
|
||||||
|
"${{ github.event.pull_request.base.sha || 'HEAD~1' }}" HEAD \
|
||||||
|
-- '*.cpp' '*.hpp' || true)
|
||||||
|
[ ${#files[@]} -eq 0 ] && { echo "no C++ changes"; exit 0; }
|
||||||
|
printf '%s\n' "${files[@]}"
|
||||||
|
clang-tidy -p build/dev "${files[@]}"
|
||||||
|
- name: skipped
|
||||||
|
if: steps.check.outputs.present == 'false'
|
||||||
|
run: echo "no C++ lane has landed a CMakeLists yet — skipping clang-tidy"
|
||||||
|
|
||||||
|
conformance:
|
||||||
|
# Required check on every PR once tests/conformance/ lands (branch protection is
|
||||||
|
# configured in the repo settings, not here — see .github/BRANCH_PROTECTION.md).
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- id: check
|
||||||
|
run: |
|
||||||
|
if [ -f tests/conformance/CMakeLists.txt ] || [ -f tests/conformance/package.json ]; then
|
||||||
|
echo "present=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else echo "present=false" >> "$GITHUB_OUTPUT"; fi
|
||||||
|
- name: Bootstrap toolchain
|
||||||
|
if: steps.check.outputs.present == 'true'
|
||||||
|
run: sudo ./tools/bootstrap.sh
|
||||||
|
- name: Run conformance suite
|
||||||
|
if: steps.check.outputs.present == 'true'
|
||||||
|
run: |
|
||||||
|
cmake --preset dev
|
||||||
|
ctest --preset dev --output-on-failure -L conformance
|
||||||
|
- name: skipped
|
||||||
|
if: steps.check.outputs.present == 'false'
|
||||||
|
run: echo "tests/conformance/ has not landed yet — skipping"
|
||||||
@@ -58,6 +58,18 @@
|
|||||||
"configurePreset": "dev",
|
"configurePreset": "dev",
|
||||||
"output": { "outputOnFailure": true },
|
"output": { "outputOnFailure": true },
|
||||||
"execution": { "noTestsAction": "error", "stopOnFailure": false }
|
"execution": { "noTestsAction": "error", "stopOnFailure": false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tsan",
|
||||||
|
"configurePreset": "tsan",
|
||||||
|
"output": { "outputOnFailure": true },
|
||||||
|
"execution": { "noTestsAction": "error", "stopOnFailure": false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ci",
|
||||||
|
"configurePreset": "ci",
|
||||||
|
"output": { "outputOnFailure": true },
|
||||||
|
"execution": { "noTestsAction": "error", "stopOnFailure": false }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user