-
Notifications
You must be signed in to change notification settings - Fork 0
124 lines (112 loc) · 6.8 KB
/
Copy pathci.yml
File metadata and controls
124 lines (112 loc) · 6.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
name: CI
on:
pull_request:
push:
branches: [main]
concurrency:
# PR events share a per-PR group so new pushes supersede stale runs; non-PR
# events (push to main) get a unique per-run group so no pending main run is
# ever replaced or canceled.
group: ci-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
quality:
# Actions pinned to commit SHAs, like every other tool here (invariant 5): a
# major-only `@v4` moves under us, which is the irreproducibility the pinned
# shellcheck image and claude CLI avoid. The runner is pinned to an OS release
# rather than `ubuntu-latest` — GitHub still refreshes that image's contents
# weekly, so this bounds the drift, it does not eliminate it. Full image
# reproducibility would need a container, which this check battery
# doesn't warrant.
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# checkout writes the job token into .git/config by default, where any
# later step — or anything a step runs — can read it. Nothing here needs
# authenticated git after the clone, so don't leave it lying around.
persist-credentials: false
# The version-bump check diffs against the merge-base with the PR's base
# branch, and the default depth-1 clone has neither the base branch nor a
# merge-base. ~50 commits, so the full history costs nothing here.
fetch-depth: 0
# The hook, the two checkers and their three suites are the only executables here,
# so lint plus those suites are the only mechanical gates we have. Everything else
# here is a prompt, and prompts have no typechecker (see README, "Contributing").
# POSIX sh, for two different reasons that must not be blurred: the HOOK is bound
# by invariant 4, because it runs under whatever /bin/sh a user has, so a bash-ism
# there is a portability bug rather than a style opinion; the two repo-local
# checkers are bound by this workflow invoking them with `sh`, which is their own
# requirement and not invariant 4's reach.
# Pinned to an exact image tag — a moving linter makes the gate irreproducible.
- name: Lint shell scripts (POSIX sh)
run: |
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:v0.11.0 \
--shell=sh plugins/dev-workflow/hooks/codex-gate.sh
# The test file's only findings are SC2015 on `[ c ] && pass "x" || fail "x"`.
# `pass` is a bare printf whose sole failure mode is a broken stdout, which
# runs `fail` too — a spurious FAIL, never a false pass. Excluded by code, so
# every other check still applies to this file.
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:v0.11.0 \
--shell=sh --exclude=SC2015 plugins/dev-workflow/hooks/codex-gate.test.sh
# The two repo-local checkers are CI gates themselves, so they get the same
# POSIX lint as the hook.
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:v0.11.0 \
--shell=sh scripts/check-invariants.sh
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:v0.11.0 \
--shell=sh --exclude=SC2015 scripts/check-invariants.test.sh
# No exclusion on this pair: unlike the two suites above, it has no
# `[ c ] && pass || fail` lines, so every rule applies as-is.
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:v0.11.0 \
--shell=sh scripts/check-version-bump.sh
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:v0.11.0 \
--shell=sh scripts/check-version-bump.test.sh
# Two runs, because the hook has to be correct under both shells and the runner's
# /bin/sh is dash while a contributor's may be bash. HOOK_SH selects the shell the
# HOOK runs under; without it, running the file under dash would only exercise the
# harness under dash. Ubuntu also gives mawk here, where a developer machine gives
# BWK or GNU awk — this step is the only place the locator meets a second awk.
- name: Hook state-machine tests (sh)
run: HOOK_SH=sh sh plugins/dev-workflow/hooks/codex-gate.test.sh
- name: Hook state-machine tests (dash)
run: HOOK_SH=dash dash plugins/dev-workflow/hooks/codex-gate.test.sh
# Invariants 5 and 6 plus three prompt-conformance checks, mechanically, and BOTH
# checkers' regression suites. The
# invariant-12 checker itself is not here — it needs a PR base and runs in the
# step below, so naming this step "version bump" would show a green version-bump
# label on a push that was never version-bump checked.
# Each rule was prose first and each was violated anyway — a floating action ref
# shipped in this very workflow, a duplicate hooks manifest key stopped the plugin
# loading in 0.2.1, and the plugin shipped changes without a bump twice.
- name: Invariant checks (pinning, manifest, prompt conformance) + both checker suites
run: |
sh scripts/check-invariants.test.sh
sh scripts/check-invariants.sh
sh scripts/check-version-bump.test.sh
# Invariant 12 itself needs a base to diff against, so it runs only on pull
# requests — a push to main has no PR base, and diffing the push range would just
# re-check what the PR run already gated. Consequence, stated rather than implied:
# a commit pushed straight to main bypasses this check entirely.
#
# base_ref goes through env, never into the run script via ${{ }}: git ref names
# may contain shell metacharacters, and direct interpolation would splice a branch
# name in as code rather than pass it as data.
- name: Version bump required for plugin changes (invariant 12)
if: github.event_name == 'pull_request'
env:
BASE_REF: ${{ github.base_ref }}
run: sh scripts/check-version-bump.sh "origin/$BASE_REF"
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.23.1
# `plugin validate` needs no auth and no API key — verified against a clean
# HOME — so it runs in CI unchanged: manifest schema, skill/command
# frontmatter, and hooks.json syntax.
- name: Validate marketplace + plugin manifests
run: |
# Pinned exactly, never floating: a moving validator makes this check
# irreproducible. Bump deliberately (repo's own `dependency-unpinned` class).
npm install -g @anthropic-ai/claude-code@2.1.207
claude plugin validate . --strict