Skip to content

feat(cli): add ci command for managing CI#955

Open
rayhanadev wants to merge 8 commits into
mainfrom
ray/cli-ci-setup-tool
Open

feat(cli): add ci command for managing CI#955
rayhanadev wants to merge 8 commits into
mainfrom
ray/cli-ci-setup-tool

Conversation

@rayhanadev

@rayhanadev rayhanadev commented Jun 24, 2026

Copy link
Copy Markdown
Member

Why

react-doctor install bundles CI setup together with skills, hooks, and the package script, so there was no focused way to set up, tune, or upgrade React Doctor in CI on its own. This adds react-doctor ci <install|upgrade|config> as the dedicated home for CI, while leaving install unchanged.

ci config edits any workflow that contains the React Doctor action — surgically, in place, preserving your other steps, jobs, inputs, and comments — rather than only a pristine scaffold.

Usage

Set up CI in one command — auto-detects the provider (GitHub Actions or GitLab CI), scaffolds an advisory workflow that scans every PR:

npx react-doctor@latest ci install
✔ Added .github/workflows/react-doctor.yml.
  Review and commit it to start scanning every pull request.

  React Doctor will now:
    • Report findings without failing the check
    • Report only the issues a change introduces
    • Post a summary comment on each pull request
    • Add inline review comments on changed lines
    • Report a commit status with the health score

Tune the gate later — interactively, or with flags for scripts/CI:

react-doctor ci config --blocking error --scope full --no-comment
✔ Updated .github/workflows/react-doctor.yml.

  React Doctor will now:
    • Fail the check on new error-level findings
    • Scan the whole project on every run
    • Skip the summary comment
    • Add inline review comments on changed lines
    • Report a commit status with the health score

It edits your real workflow in place. Given a hand-customized file:

# our team's custom CI
jobs:
  react-doctor:
    steps:
      - uses: actions/checkout@v5
      - uses: millionco/react-doctor@v2 # pinned by us
        with:
          node-version: 24
          directory: apps/web
          blocking: none

ci config --blocking error --scope full --no-comment touches only the gate, preserving the rest:

# our team's custom CI
jobs:
  react-doctor:
    steps:
      - uses: actions/checkout@v5
      - uses: millionco/react-doctor@v2 # pinned by us
        with:
          node-version: 24
          directory: apps/web
          blocking: error      # changed
          scope: full          # added
          comment: false        # added

Bake the gate at install time, or open a PR with the change:

react-doctor ci install --blocking error          # advisory off from day one
react-doctor ci install --pr                       # open a PR instead of writing to the tree
react-doctor ci install --provider gitlab-ci       # GitLab gate-only scaffold
react-doctor ci upgrade                            # bump the action to its current major

What changed

  • New ci command group (commands/ci.ts, wired in cli/index.ts) with install, config, upgrade, a help epilog, and flag-strip routing.
  • Provider abstraction (cli/utils/ci/): githubActionsProvider (full support) + gitlabCiProvider (gate-only scaffold), auto-detected from the git remote host then on-disk CI files, --provider to override.
  • ci config edits in place. GitHub uses the yaml AST to set only the managed gate keys (blocking/scope/comment/review-comments/commit-status) on the React Doctor step's with: map — preserving comments, the action ref (incl. SHA pins), and other inputs (directory/project/node-version/version); keys that return to the action default are removed. A pristine scaffold still gets the clean template rebuild; only a file with no React Doctor step is refused. GitLab splices the gate flags on its scan line. yaml is added and inlined into the bundle (no extra user install).
  • Telemetry: ci.scaffolded / ci.configured / ci.upgraded (anonymized: provider id, mode, gate enums, booleans).
  • install unchanged; its help points to ci. No action.yml / JSON-report / schemaVersion change. Minor changeset.

Test plan

  • pnpm --filter react-doctor run typecheck · pnpm lint · pnpm format:check — clean
  • pnpm --filter react-doctor exec vp test run tests/ci-providers.test.ts tests/manage-ci.test.ts tests/strip-unknown-cli-flags.test.ts tests/install-github-workflow.test.ts tests/install-react-doctor.test.ts tests/open-workflow-pull-request.test.ts113 passing, incl. surgical edit preserving extra steps/inputs/comments, no-with: insertion, remove-on-default, flow-style with: {…}, no-step → snippet, GitLab splice in a larger pipeline, --base add/remove, provider detection, flag routing
  • Full react-doctor suite green in isolation (the only workspace failures are pre-existing: the .env*-gitignore-trap tests in core, a deslop-js fixture, and flaky e2e cli-and-output under parallel load)
  • Built the CLI, confirmed yaml is bundled into dist/cli.js, and smoke-tested the built bin end to end (ci install, ci config on a customized workflow, GitLab scaffold)

🤖 Generated with Claude Code


Note

Medium Risk
The CLI writes and rewrites real CI config files (workflows and .gitlab-ci.yml); mistakes in YAML editing could break pipelines, though surgical edits and broad tests reduce that risk.

Overview
Adds react-doctor ci with install, config, and upgrade as the dedicated way to set up and tune React Doctor in CI (the bundled install flow is unchanged).

ci install auto-detects GitHub Actions or GitLab CI, scaffolds an advisory PR-scan workflow, accepts gate flags (--blocking, --scope, PR reporting toggles), and optional --pr to open a setup PR. GitHub gets full workflow + action inputs; GitLab is a merge-request job scaffold only (blocking/scope on the CLI).

ci config reads the current gate from disk, applies flags or interactive prompts, then edits workflows in place—GitHub via yaml AST on the React Doctor step’s with: map (preserving other steps, pins, and custom inputs); GitLab by splicing flags on the scan line. Customized files get surgical edits; missing React Doctor wiring falls back to a paste snippet.

ci upgrade bumps a floating @v1 GitHub Action ref to @v2. Provider resolution uses existing workflow, origin remote, then on-disk CI files. README CI steps now point at ci install; yaml is bundled; telemetry adds ci.scaffolded / ci.configured / ci.upgraded.

Reviewed by Cursor Bugbot for commit e58e01f. Bugbot is set up for automated code reviews on this repo. Configure here.

`install` bundles CI setup with skills, hooks, and the package script, so
there was no focused way to set up, tune, or upgrade React Doctor in CI on
its own. `react-doctor ci <install|upgrade|config>` is that home.

- `ci install` detects the provider (GitHub Actions or GitLab CI), scaffolds
  a pull-request-scanning workflow, bakes a gate from the flags, and can open
  a PR with `--pr`.
- `ci config` walks the gate / scope / reporting settings (interactively or
  via flags) with a plain-language recap, editing in place only when the file
  round-trips to what React Doctor generates so a hand-edited workflow is
  never clobbered.
- `ci upgrade` bumps the action to its current floating major.

GitHub Actions is fully supported; GitLab CI is a gate-only scaffold. Reuses
the existing workflow template, PR-opening flow, branch detection, and gate
guards; `install`'s behavior is unchanged. Telemetry: ci.scaffolded /
ci.configured / ci.upgraded (anonymized).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Jun 24, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/eslint-plugin-react-doctor@955
npm i https://pkg.pr.new/oxlint-plugin-react-doctor@955
npm i https://pkg.pr.new/react-doctor@955

commit: e58e01f

Comment thread packages/react-doctor/src/cli/utils/ci/detect-ci-provider.ts Outdated
Comment thread packages/react-doctor/src/cli/utils/ci/gitlab-ci-provider.ts
Comment thread packages/react-doctor/src/cli/utils/ci/manage-ci.ts

@devin-ai-integration devin-ai-integration Bot 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.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

- detect: check the git remote host before the generic on-disk CI files, so a
  GitHub repo carrying a stray/legacy `.gitlab-ci.yml` is no longer misread as
  GitLab.
- gitlab: parse the gate off React Doctor's own scan line, not file-wide, so a
  merged pipeline running other jobs with `--blocking`/`--scope` can't be
  mistaken for the gate (mirrors the GitHub provider's anchored parse).
- ci install: when a GitHub workflow already exists, say the gate flags weren't
  applied and point at `ci config`, instead of silently ignoring them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/react-doctor/src/cli/utils/ci/github-actions-provider.ts Outdated
rayhanadev and others added 2 commits June 24, 2026 17:12
A `#` comment that mentions `millionco/react-doctor@` (GitHub) or
`react-doctor … --blocking` (GitLab) could be read as the action step, so the
real `with:` block / scan flags were skipped and `ci config` fell back to the
advisory defaults. Anchor the GitHub parse to a `- uses:` line and the GitLab
parse to a `- ` script item (stripping any trailing comment) so only the real
step is read.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ffolds

`ci config` previously rebuilt the whole workflow and wrote only when the file
was byte-identical to a React Doctor scaffold, so a workflow with extra
steps/jobs/comments, other action inputs, or generated by another version got a
paste snippet instead of an edit.

Now the GitHub provider edits the workflow via the `yaml` AST: it finds the
React Doctor step (matching `millionco/react-doctor@…`, any ref/SHA pin) and
sets only the managed gate keys on its `with:` map, deleting keys that return
to the action default. Comments, the action ref, and other inputs (directory /
project / node-version / version) are preserved. A pristine scaffold still
takes the clean template-rebuild path; only a file with no React Doctor step
(or unparseable YAML) is refused. The GitLab provider splices the gate flags on
its scan line in place. `yaml` is added as a dependency and inlined into the
bundle, so end users get no extra install.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/react-doctor/src/cli/utils/ci/manage-ci.ts
Comment thread packages/react-doctor/src/cli/utils/ci/gitlab-ci-provider.ts
Two GitLab gaps from the in-place editor:

- `ci config` treated any existing `.gitlab-ci.yml` as a React Doctor workflow.
  A file with no scan job reported the advisory defaults as "current" and blamed
  a failed edit on "customization". Add a `containsReactDoctor` provider check so
  `ci config` says the file has no React Doctor job and points at `ci install`.
- The GitLab gate splice only replaced existing `--blocking`/`--scope` tokens, so
  a flag the user had removed was silently skipped while the recap claimed it
  applied. Upsert the flags (add when missing) and anchor the scan line on the
  `react-doctor@` npx spec so a flagless line is still found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/react-doctor/src/cli/utils/ci/gitlab-ci-provider.ts Outdated
…ention

`isScanLine` matched any `- …react-doctor@…` script item, so a setup step like
`- npm install react-doctor@latest` could be read or edited as the scan command.
Match an actual run instead — a runner (`npx`/`pnpm dlx`/`yarn dlx`/`bunx`) or a
bare `react-doctor` command — so install steps and comments are skipped and the
real scan line is found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/react-doctor/src/cli/utils/ci/manage-ci.ts
Comment thread packages/react-doctor/src/cli/utils/ci/gitlab-ci-provider.ts Outdated
Comment thread packages/react-doctor/src/cli/utils/ci/github-actions-provider.ts Outdated
rayhanadev and others added 2 commits June 24, 2026 22:04
`ci upgrade` claimed the workflow "already uses @v2" whenever nothing changed,
including exact-tag and SHA-pinned refs that deliberately skip the floating
`@v1`→`@v2` bump. Report that there's no floating `@v1` ref to upgrade instead
of asserting a version it may not be on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… block scripts

- GitHub: `ci config` / `ci upgrade` now find the React Doctor action step in any
  `.github/workflows/*.yml` (preferring the canonical `react-doctor.yml`), not
  just `react-doctor.yml`, so a step added to an existing CI workflow is managed
  in place. `ci install` treats the action existing anywhere as "already
  configured" and won't add a duplicate workflow.
- GitLab: the scan-line matcher now matches the React Doctor run whether it's a
  `- npx react-doctor@…` item or a command inside a multiline `script: - |`
  block scalar, while still excluding the `react-doctor:` job key, comments, and
  `npm install react-doctor` steps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e58e01f. Configure here.

.option("--color", "force colored output")
.option("--no-color", "disable colored output (also honors NO_COLOR)")
.addHelpText("after", renderCiHelpEpilog)
.action((_options, command) => ciInstallAction(command.optsWithGlobals()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Root scan flags affect CI

Medium Severity

ci install and ci config merge options with command.optsWithGlobals(), sharing --blocking and --scope with the default scan command. Flags given before the ci subcommand (e.g. react-doctor --scope full ci install or a shell alias that always passes scan flags) are applied to the CI gate, not only to local scans, which can silently bake the wrong blocking level or PR scope into the workflow.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e58e01f. Configure here.

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