Skip to content

fix(ci): gate every extension's tests, not just levelcode-ai - #25

Merged
ndemianc merged 2 commits into
developfrom
fix/ci-gate-all-extension-tests
Jul 20, 2026
Merged

fix(ci): gate every extension's tests, not just levelcode-ai#25
ndemianc merged 2 commits into
developfrom
fix/ci-gate-all-extension-tests

Conversation

@ndemianc

Copy link
Copy Markdown
Contributor

The gap

The pre-build test job did this:

cd extensions/levelcode-ai
for t in test/*.test.js; do node "$t"; done

The cd meant extensions/levelcode-updater/test/*.test.js never ran in CI — including the regression test added in the auto-update S3 fix, the one thing standing between a feed change and the Download button handing users a raw .app.zip. It protected the release it shipped in and nothing after that.

The fix

Glob every extension instead, so a new suite is gated the moment it's added and there's no list here to drift out of sync. Requires in these tests are file-relative, so running from the repo root needs no cd.

It also guards the vacuous pass: a zero-match glob previously reported success while gating nothing — the same class of bug being fixed. It now fails with a ::error:: annotation rather than going quietly green.

Verification

Run locally under bash -e, the Actions default shell:

Case Result
Real run 14 test files pass, up from 13
Zero-match glob exits 1 with the annotation
A deliberately failing suite aborts the job, exit 1
release.yml parses; test, build, draft-release intact

Note

release.yml is the only workflow in the repo, so this gate runs on tag push. It does not run on PRs — worth considering separately, but out of scope here.

🤖 Generated with Claude Code

The pre-build test job did `cd extensions/levelcode-ai` before globbing, so
extensions/levelcode-updater/test/*.test.js never ran in CI — including the
regression test added in the S3 fix, which is the only thing standing between a
feed change and the updater's Download button handing users a raw .app.zip.
It protected the release it shipped in and nothing after that.

The step now globs extensions/*/test/*.test.js, so a new suite is gated the
moment it is added and there is no list here to drift out of sync. Requires in
these tests are file-relative, so running from the repo root needs no cd.

Also guards the vacuous pass: a zero-match glob previously reported success
while gating nothing, which is the same class of bug as the one being fixed.
It now fails with a ::error:: annotation.

Verified locally under `bash -e` (the Actions default shell):
  - real run: 14 test files pass, up from 13
  - zero-match glob: exits 1 with the annotation
  - a deliberately failing suite: aborts the job, exit 1
  - release.yml still parses; all 3 jobs intact

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 05:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens the release workflow’s pre-build gate by ensuring all extension unit tests run before spending macOS build minutes, preventing silent gaps where new extension test suites are not executed in CI.

Changes:

  • Run node tests across extensions/*/test/*.test.js instead of only extensions/levelcode-ai.
  • Add a “vacuous pass” guard: fail the job if the glob matches zero test files, with a ::error:: annotation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

# ran here, including the one guarding the updater's Download button against serving a raw
# .app.zip. Globbing every extension means a new suite is gated the moment it is added, with no
# list here to keep in sync. Requires are file-relative, so running from the repo root is fine.
run: |
Addresses the PR #25 review: the step uses `shopt`, a bash builtin, but relied
on the runner default rather than declaring the shell. True today on
ubuntu-latest; pwsh on a Windows runner, where the step would break if the job
were ever copied.

Worth noting the fix is not purely defensive — `shell: bash` is not the same as
the default. It runs `bash --noprofile --norc -eo pipefail` rather than
`bash -e`, so the gate also stops inheriting profile files and gains pipefail.
Both are improvements here, and neither changes this step's behaviour (it has no
pipes). Updated the inline comment that credited "the Actions default" for the
abort, since `-e` now comes from the pinned shell.

Re-verified under the exact flags `shell: bash` invokes
(`bash --noprofile --norc -eo pipefail`):
  - real run: 14 test files pass, exit 0
  - zero-match glob: exit 1 with the ::error:: annotation
  - a deliberately failing suite: aborts, exit 1
  - release.yml parses; test/build/draft-release intact; shell resolves to bash

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ndemianc
ndemianc merged commit 07b7341 into develop Jul 20, 2026
1 check passed
@ndemianc
ndemianc deleted the fix/ci-gate-all-extension-tests branch July 21, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants