Skip to content

ci: bootstrap build/lint/test workflow and wire eslint - #6

Merged
IronTony merged 2 commits into
mainfrom
chore/ci-bootstrap
Aug 26, 2026
Merged

IronTony merged 2 commits into
mainfrom
chore/ci-bootstrap

Conversation

@IronTony

Copy link
Copy Markdown
Owner

Bootstraps CI. This PR needs the ruleset bypass to merge, and it is the only one that should.

Why

The protect-main ruleset requires status checks named build, lint and test, plus CodeQL code scanning results. The repo had no .github/workflows directory at all and CodeQL default setup reports not-configured, so none of those four have ever been reported by anything. Every PR is permanently blocked on checks with no producer. #3 is sitting in exactly that state right now with zero check runs on its head commit.

That is a bootstrap deadlock: the CI that satisfies the rules has to land on main, but landing on main requires satisfying the rules. Hence the one-time bypass. Once this merges, every subsequent PR gets real checks and no bypass is needed again.

Lint had never actually run

.eslintrc.js extended universe/native and universe/web, but neither eslint nor eslint-config-universe was in package.json, and ESLint 9+ ignores .eslintrc.* regardless. The config was vestigial.

Wiring it up surfaced 161 problems on the first run. .prettierrc pinning singleQuote took that to 13, by matching the config to the source rather than reformatting every string in the repo to universe's double-quote default. Nine of the rest were auto-fixable import order and wrapping.

Two needed a judgement call, both suppressed at the line rather than switched off in config:

  • no-void on the single void endSigningSession() in the repo. It is the idiom for a deliberately unawaited promise.
  • react-hooks/set-state-in-effect on the auto-initialize effect. doInitialize sets state on its first line, which the rule reads as a cascading render. It is deliberate: that effect brings the DocuSign SDK up, and the INITIALIZING transition reports its progress. Cost is one extra render on mount. Reworking the hook's init lifecycle is a behaviour change and does not belong in the commit that turns lint on.

Notes

Job ids are build / lint / test deliberately. With no name: override the job id becomes the status check context, which is what the ruleset matches on. Renaming a job silently makes its required check unsatisfiable again.

The lockfile's root version was stale at 1.0.3 against a package.json already at 1.0.5. Installing the new devDependencies corrects it. npm ci would have required that anyway, so it is a prerequisite rather than scope creep. Not a version bump.

CodeQL default setup is enabled separately via the API, since it is repo configuration rather than a file in the tree.

Verification

npm ci && npm run build && npm run lint && npm test all green locally. 13 tests passing.

.eslintrc.js extended universe/native and universe/web, but neither eslint nor
eslint-config-universe was ever a dependency, and eslint 9 ignores .eslintrc.*
regardless. The config was vestigial and had never run against this repo.

Adds eslint, eslint-config-universe and prettier as devDependencies, replaces
.eslintrc.js with eslint.config.js, and adds a lint script gated at zero
warnings.

.prettierrc pins single quotes to match the existing source rather than
reformatting every string in the repo to universe's double-quote default. That
alone took the first run from 161 problems to 13.

Of the remainder, nine were auto-fixable import order and wrapping. Two needed
a decision, both suppressed at the line rather than in config:

no-void, on the one `void endSigningSession()` in the repo. It is the idiom for
a deliberately unawaited promise and reads better than the `undefined` the rule
asks for.

react-hooks/set-state-in-effect, on the auto-initialize effect. doInitialize
sets state on its first line, which the rule reads as a cascading render. It is
deliberate: that effect exists to bring the DocuSign SDK up, and the
INITIALIZING transition is how the progress is reported. The cost is one extra
render on mount. Reworking the hook's init lifecycle is a behaviour change and
does not belong in the commit that turns lint on.

The lockfile's root version was also stale at 1.0.3 against a package.json
already at 1.0.5. Installing the new devDependencies corrects it, which npm ci
would have required anyway.
The protect-main ruleset requires status checks named build, lint and test, and
also CodeQL results. The repo had no workflows at all, so none of those were
ever reported and every PR sat permanently blocked on checks that had no
producer. #3 is currently stuck exactly this way.

Job ids are build / lint / test deliberately: with no name override, the job id
becomes the status check context, which is what the ruleset matches on.
Renaming a job here silently makes its required check unsatisfiable again.

Three separate jobs rather than one with three steps, so each context reports
independently and a lint failure does not mask a test failure.

permissions is contents: read, there are no secrets, and no untrusted context
values reach any run step. Actions are tag-pinned rather than SHA-pinned, which
is worth revisiting if the repo standardises on stricter supply-chain rules.
@IronTony
IronTony merged commit f3ff612 into main Aug 26, 2026
3 checks passed
@IronTony
IronTony deleted the chore/ci-bootstrap branch August 26, 2026 19:19
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.

1 participant