Skip to content

Commit fa9a4cd

Browse files
committed
feat(taskless): enforce the workspace CLI in CI instead of asking for it
The check step added in the previous commit carried a comment asking a reader not to "fix" `pnpm cli check` back to `npx @taskless/cli check`. A request is not a boundary, and this repository lints its own prose precisely because comments do not hold. So the invariant becomes a rule. `ci-uses-workspace-cli` fails any `.github/workflows/*.yml` step whose `run:` invokes a published `@taskless/cli`. The repository IS the CLI: checking its rules with a published build would let a commit that breaks rule evaluation pass its own CI and surface a release later, in someone else's project. Three things the matcher has to get right, each measured rather than assumed: `npx @taskless/cli` already appears in PROSE in `validate.yml` and `release-cli-nightly.yml`, explaining this same decision. A rule that flagged its own rationale would be worse than no rule, so the matcher is anchored to a `run:` key's value rather than to file text. Verified: the clean tree reports nothing. A `run:` value is a `flow_node` inline and a `block_scalar` under `|`, and both spellings are ordinary here, so both are matched. The first attempt carried one `regex` with no sibling `kind` and was rejected by this repo's own `regex-requires-kind` check, which is the dogfooding working. `@taskless/cli-nightly` is deliberately excluded. The nightly is a published artifact by design and the release workflows reference it on purpose. The pattern ends at `($|[@\s])` rather than a negative lookahead, which the Rust regex engine ast-grep uses does not support. Proven both ways: with the step reverted to `npx @taskless/cli check`, `check` exits 1 naming this rule; restored, it exits 0.
1 parent bc6533d commit fa9a4cd

3 files changed

Lines changed: 89 additions & 9 deletions

File tree

‎.github/workflows/validate.yml‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,12 @@ jobs:
8787
# for anyone used to reading these logs. It only needs to be after
8888
# `Build`; nothing above it depends on it.
8989
#
90-
# `pnpm cli` is the workspace build (`packages/cli/dist/index.js`), NOT a
91-
# published release, and that is a deliberate divergence from `ci.txt`.
92-
# The recipe is written for consumers installing the published CLI; here
93-
# the repository IS the CLI, so running `npx @taskless/cli check` would
94-
# test the published artifact and exempt the engine at HEAD from its own
95-
# rules. Do NOT "fix" this back to `npx`: a commit that breaks rule
96-
# evaluation has to fail on the commit that breaks it, not one release
97-
# later. `Build` above ran `pnpm build` on this same commit, so `dist/`
98-
# here can be neither stale nor missing, and an absent `dist/` would fail
90+
# `pnpm cli` is the workspace build, NOT a published release. That is a
91+
# deliberate divergence from `ci.txt` and is ENFORCED, not requested:
92+
# the `ci-uses-workspace-cli` rule fails this very workflow if the
93+
# invocation is changed back, and carries the reasoning in its `note:`.
94+
# `Build` above ran `pnpm build` on this same commit, so `dist/` here
95+
# can be neither stale nor missing, and an absent `dist/` would fail
9996
# this step loudly rather than pass it empty.
10097
#
10198
# This blocks. `check` exits non-zero on any error-severity finding and
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
id: ci-uses-workspace-cli
2+
valid:
3+
- |
4+
steps:
5+
- name: Check house style
6+
run: pnpm cli check
7+
- |
8+
steps:
9+
- name: Install
10+
run: npx some-other-tool --version
11+
- |
12+
steps:
13+
- name: Nightly smoke test
14+
run: npx @taskless/cli-nightly@0.10.2 check
15+
- |
16+
steps:
17+
- name: Multi-line, workspace build
18+
run: |
19+
pnpm build
20+
pnpm cli check
21+
invalid:
22+
- |
23+
steps:
24+
- name: Check house style
25+
run: npx @taskless/cli check
26+
- |
27+
steps:
28+
- name: Check house style
29+
run: npx @taskless/cli@latest check
30+
- |
31+
steps:
32+
- name: Multi-line, published release
33+
run: |
34+
pnpm install
35+
npx @taskless/cli check
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
id: ci-uses-workspace-cli
2+
language: Yaml
3+
severity: error
4+
message: A workflow step must run the workspace CLI (`pnpm cli`), not a published release.
5+
note: |
6+
This repository IS the Taskless CLI, so a workflow that runs `npx
7+
@taskless/cli` checks the repository's rules with a PUBLISHED build rather
8+
than the one at HEAD. A commit that breaks rule evaluation would then pass
9+
its own CI and only fail a release later, in someone else's project.
10+
11+
`validate.yml` runs `pnpm build` before its check step, so the workspace
12+
build is already present and cannot be stale or missing.
13+
14+
`ci.txt`, the onboarding recipe, tells consumers to use `npx
15+
@taskless/cli`. That is correct for a project that merely depends on the
16+
CLI, and wrong here. This rule is the difference between the two, enforced
17+
rather than written down: the comment it replaces asked a reader not to
18+
"fix" the invocation back, which is a request, not a boundary.
19+
20+
`@taskless/cli-nightly` is deliberately not matched. The nightly IS a
21+
published artifact by design, and the release workflows reference it on
22+
purpose.
23+
24+
Only a `run:` value matches, never a comment: `npx @taskless/cli` appears
25+
in prose in `validate.yml` and `release-cli-nightly.yml`, explaining this
26+
very decision, and a rule that flagged its own rationale would be worse
27+
than no rule.
28+
files:
29+
- ".github/workflows/*.yml"
30+
- ".github/workflows/*.yaml"
31+
rule:
32+
all:
33+
- kind: block_mapping_pair
34+
- has:
35+
field: key
36+
kind: flow_node
37+
regex: '^run$'
38+
- has:
39+
field: value
40+
stopBy: end
41+
any:
42+
# A `run:` value is a flow_node when written inline and a
43+
# block_scalar under `|` or `>`. Both spellings are ordinary in
44+
# these workflows, so both have to match.
45+
- kind: flow_node
46+
regex: 'npx\s+@taskless/cli($|[@\s])'
47+
- kind: block_scalar
48+
regex: 'npx\s+@taskless/cli($|[@\s])'

0 commit comments

Comments
 (0)