ci: run identical checks on PRs and main to keep them in lockstep#261
Merged
Conversation
PRs were validated by CircleCI (lint + ci:build + test) while main was validated by the GitHub Actions release workflow (ci:build only, then changesets). Because the changeset version/release step only ever ran on main, a PR could go green and then break on merge -- e.g. #247 left a changeset pointing at the deleted react-native-mlkit-docs package, which passed PR CI but failed `changeset version` on main. - Add .github/workflows/ci.yml: a single `verify` job (install, lint, changeset status, ci:build, test) that runs on every pull_request and is exposed via workflow_call. - release.yml now calls that workflow as a `verify` job and gates the version/release job behind it (`needs: verify`), so main runs the exact same checks as PRs before anything is versioned or published. - `changeset status` catches changesets that reference missing/renamed packages at PR time (verified: it fails on a docs-package reference). - Drop the now-duplicate test_and_build job from CircleCI; CircleCI keeps only docs publishing, which depends on its orb + ir-docs SSH access. Closes #250 https://claude.ai/code/session_01HWaRWqpMHzzeTSJeqTeV75
|
- `changeset status` failed on PRs with "Failed to find where HEAD diverged from main" because a fresh Actions checkout has no local `main` branch. Pass `--since=origin/main` so it diffs against the remote-tracking ref (fetch-depth: 0 already provides the history). Verified it still fails on a changeset referencing a missing package. - Add an explicit `permissions: contents: read` block, resolving the CodeQL "Workflow does not contain permissions" finding. The verify job only reads the repo; publishing stays in release.yml. https://claude.ai/code/session_01HWaRWqpMHzzeTSJeqTeV75
This PR only touches CI configuration (no published package changes), so it carries an intentionally empty changeset. This satisfies the new `changeset status` gate, which requires every PR with changes to either declare a release or explicitly opt out via an empty changeset. https://claude.ai/code/session_01HWaRWqpMHzzeTSJeqTeV75
`changeset status` couples two behaviors: validating that changesets reference real packages, and requiring every PR with changes to include a changeset. Only the former is needed for #250, and the latter forced empty changesets on CI/docs/chore PRs (and behaved inconsistently between local and the PR merge-ref checkout). changesets has no config flag to disable the "changed but no changeset" error, so replace the step with scripts/validate-changesets.mjs, which uses @changesets/get-release-plan (already a devDependency) to assemble the release plan. That throws on a changeset referencing a missing package (catching #247) but does not require a changeset to exist and needs no git history -- so fetch-depth: 0 and the empty changeset are both dropped. https://claude.ai/code/session_01HWaRWqpMHzzeTSJeqTeV75
Revert the custom validation script in favor of `changeset status`, which requires a changeset when a versionable package changes (the behavior we want) and still fails on changesets referencing a missing package (#247). Two adjustments make it robust: - Set `privatePackages.version: false` in .changeset/config.json. By default changesets treats private packages as versionable, so changes to the private demo app (example-app) and config packages demanded a changeset. Those are never published, so they are now exempt; the six published @infinitered/* modules still require one. - Run the changeset check before `yarn lint`. Lint runs `prettier --write`, which reformats drifted files in place and made changesets think example-app changed. Checking the pristine, just-checked-out tree avoids that phantom change. Note: root-level files (.github, .circleci, etc.) are not part of any workspace package, so CI/config-only PRs never require a changeset. https://claude.ai/code/session_01HWaRWqpMHzzeTSJeqTeV75
CI ran `yarn lint`, whose example-app script does `eslint --fix` and `prettier --write`, silently reformatting files in the runner instead of failing on drift. Make CI verify instead of fix: - Add `lint:ci` to example-app (eslint without --fix, prettier --check) and a matching `format:check` script. - Add root `ci:lint` that lints the modules via turbo (their `expo-module lint` is already non-mutating) and runs example-app's check. No --no-cache: the example-app check runs outside turbo and module lint is non-mutating and input-keyed, so turbo's cache stays correct. - Point the CI workflow at `yarn ci:lint`. `yarn lint` still auto-fixes locally. - Format the two drifted example-app files so the check passes. https://claude.ai/code/session_01HWaRWqpMHzzeTSJeqTeV75
The job name is what branch protection's required-status-check rule matches on. Use a short, stable 'verify' instead of a descriptive sentence so the rule doesn't need re-updating if the description changes. https://claude.ai/code/session_01HWaRWqpMHzzeTSJeqTeV75
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #250.
The problem
PRs and
mainwere validated by two different pipelines running different checks, so a PR could go green and then break on merge:main/ release checkstest_and_build)release.yml)ci:build→ testci:build→ changesets (yarn version/yarn release)Two gaps fall out of this:
main, so errors in it were invisible until after merge.lint, notest).That's exactly how #247 broke: it deleted the
@infinitered/react-native-mlkit-docspackage but left a changeset referencing it. CircleCI never runs changesets, so the PR was green; on merge,changeset versionfailed onmainand needed follow-up fixes (#251, #252).The fix — one source of truth
.github/workflows/ci.ymlwith a singleverifyjob: install → lint →changeset status→ci:build→ test. It runs on everypull_requestand is exposed viaworkflow_call.release.ymlnow calls that workflow as averifyjob and gates the version/release job behind it (needs: verify), somainruns the exact sameverifyjob as PRs before anything is versioned or published.changeset statusvalidates that every changeset references a real workspace package. Verified locally: it passes on the current changeset and fails (exit 1) when pointed at the deletedreact-native-mlkit-docspackage — i.e. it would have caught docs: update contribution docs and remove docusaurus #247 at PR time.test_and_buildjob and keeps only docs publishing, which depends on the Infinite Redpublish-docsorb + CircleCI-managed SSH access toir-docs.If
main's required status checks currently require the CircleCItest_and_buildcheck, that check no longer runs. Update the branch protection rules to require the new GitHub Actionsverify/ "Lint, validate changesets, build, and test" check instead, otherwise PRs may hang waiting on a check that never reports.Notes / open questions
lint+changeset status+testinto it instead — happy to flip the approach.https://claude.ai/code/session_01HWaRWqpMHzzeTSJeqTeV75
Generated by Claude Code