Skip to content

feat(cli): add --skip-if-no-diff flag to fern generate#15137

Open
Swimburger wants to merge 1 commit intomainfrom
devin/1776454919-skip-if-no-diff-flag
Open

feat(cli): add --skip-if-no-diff flag to fern generate#15137
Swimburger wants to merge 1 commit intomainfrom
devin/1776454919-skip-if-no-diff-flag

Conversation

@Swimburger
Copy link
Copy Markdown
Member

@Swimburger Swimburger commented Apr 17, 2026

Description

Adds an opt-in --skip-if-no-diff flag to fern generate and decouples the existing no-diff skip behavior from automationMode.

Previously, the tree-hash check in GithubStep that skips PR creation / push when generated output has no diff vs. the base branch was gated behind if (this.config.automationMode). That conflated two concepts (automation PR shape vs. no-diff skip). This PR introduces a dedicated skipIfNoDiff config so non-automation callers can opt into the skip without inheriting automation's other side effects (separate PRs per generation, automerge, run_id in body, etc.).

Behavior for fern automations generate is preserved — it now explicitly sets skipIfNoDiff: true alongside automationMode: true.

Changes Made

  • GithubStepConfig (packages/generator-cli/src/pipeline/types.ts): added skipIfNoDiff?: boolean; updated the automationMode doc comment to drop the "no-diff skip" responsibility.
  • GithubStep.ts: both no-diff gates (PR mode and push mode) now call a new exported helper shouldCheckNoDiff(config) instead of reading automationMode. The existing-PR lookup (line 102), fresh-branch naming, and automerge logic remain gated on automationMode and are untouched.
  • fern automations generate (packages/cli/cli/src/cli.ts): sets skipIfNoDiff: true to preserve today's behavior exactly.
  • fern generate (packages/cli/cli/src/cli.ts): new yargs option --skip-if-no-diff (boolean, default: false, strictly opt-in). Threaded through both the API and docs branches.
  • Plumbing, mirroring automationMode:
    • Local path: generateAPIWorkspacesgenerateAPIWorkspacerunLocalGenerationForWorkspace → into GithubStepConfig.
    • Remote path: runRemoteGenerationForAPIWorkspacerunRemoteGenerationForGeneratorcreateAndStartJob.
  • Tests (packages/generator-cli/src/__test__/skip-if-no-diff.test.ts): unit tests for the new shouldCheckNoDiff helper covering the decoupling requirements:
    • skipIfNoDiff: true, automationMode: false → skips
    • skipIfNoDiff: false, automationMode: true → does NOT skip (proves decoupling)
    • undefined defaults to no skip
  • Changelog: packages/cli/cli/changes/unreleased/add-skip-if-no-diff-flag.yml (feat).

What's specifically worth a careful look

  • Behavior preservation for fern automations generate: the gate swap from automationModeskipIfNoDiff at GithubStep.ts:161 and :304 relies on the call site in addAutomationsGenerateCommand also setting skipIfNoDiff: true. Please confirm there are no other in-tree callers that construct a GithubStepConfig with automationMode: true but without skipIfNoDiff: true — they would silently stop skipping no-diff PRs.
  • Remote (Fiddle) wiring: createAndStartJob now accepts skipIfNoDiff but does not forward it to the Fiddle API (the inner createJob takes the param but doesn't include it in the generator config). This mirrors the existing handling of automationMode (there's a pre-existing TODO around forwarding automation flags once Fiddle's API is updated). As a result, --skip-if-no-diff is fully wired for the local generation path but is a no-op on the remote path until Fiddle learns about it. Flagging in case reviewers want a Fiddle-side change in this PR instead of a follow-up.
  • Test depth: the new tests cover the pure helper, matching the style of the existing shouldEnableAutomerge / resolveBranchAction tests. The runtime flow through GithubStep.executePullRequestMode / executePushMode is not exercised by integration tests (the one existing GithubStep CLI test in pr.test.ts is skipped). If heavier coverage is desired here, happy to add it.

Testing

  • Unit tests added (shouldCheckNoDiff)
  • pnpm turbo run test --filter @fern-api/generator-cli → 184 passed, 1 pre-existing skipped
  • pnpm run check (biome) → clean
  • pnpm turbo run compile on @fern-api/generator-cli, @fern-api/local-workspace-runner, @fern-api/remote-workspace-runner, @fern-api/cli → all pass (tsc)
  • Manual end-to-end of fern generate --skip-if-no-diff against a real repo (not run locally in this session)

Link to Devin session: https://app.devin.ai/sessions/f4dcbe307826491cb7d3a7b15d2f4691
Requested by: @Swimburger


Open with Devin

Decouples the no-diff skip behavior from automationMode by introducing a
new skipIfNoDiff field on GithubStepConfig and a --skip-if-no-diff yargs
option on fern generate (default false, strictly opt-in).

- GithubStep.ts: both gates (PR mode + push mode) now check skipIfNoDiff
  via a new exported shouldCheckNoDiff helper; existing-PR lookup,
  fresh-branch naming, and automerge behavior remain gated on automationMode.
- fern automations generate: sets skipIfNoDiff: true to preserve today's
  behavior exactly.
- Threads skipIfNoDiff through generateAPIWorkspaces, generateAPIWorkspace,
  runLocalGenerationForWorkspace, and the remote path
  (runRemoteGenerationForAPIWorkspace -> runRemoteGenerationForGenerator
  -> createAndStartJob) mirroring the automationMode plumbing.
- Adds unit tests proving the decoupling (skipIfNoDiff=true + automationMode=false
  skips; skipIfNoDiff=false + automationMode=true does NOT skip).

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

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 potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-17T04:58:39Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 98s 141s 41s -57s (-58.2%)
go-sdk square 114s 148s 53s -61s (-53.5%)
java-sdk square 170s 187s 121s -49s (-28.8%)
php-sdk square 99s 127s 40s -59s (-59.6%)
python-sdk square 114s 152s 67s -47s (-41.2%)
ruby-sdk-v2 square 102s 156s 70s -32s (-31.4%)
rust-sdk square 98s 94s 46s -52s (-53.1%)
swift-sdk square 84s 130s 39s -45s (-53.6%)
ts-sdk square 100s 138s 51s -49s (-49.0%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-17T04:58:39Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-04-17 21:31 UTC

@github-actions
Copy link
Copy Markdown
Contributor

Docs Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-17T04:58:39Z).

Fixture main PR Delta
docs 289.7s 308.6s (35 versions) +18.9s (+6.5%)

Docs generation runs fern generate --docs --preview end-to-end against the benchmark fixture with 35 API versions (each version: markdown processing + OpenAPI-to-IR + FDR upload).
Delta is computed against the nightly baseline on main.
Baseline from nightly run(s) on main (latest: 2026-04-17T04:58:39Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-04-17 21:33 UTC

retryRateLimited: argv["retry-rate-limited"],
requireEnvVars: argv["require-env-vars"]
requireEnvVars: argv["require-env-vars"],
skipIfNoDiff: argv["skip-if-no-diff"]
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.

Self-review nit: skipIfNoDiff is threaded into the API branch but NOT into the generateDocsWorkspace call below (line 895). That's semantically correct — docs generation doesn't go through GithubStep — but fern generate --docs --skip-if-no-diff silently accepts and ignores the flag. Option A: leave as-is (matches how --retry-rate-limited etc. behave for docs). Option B: add a validation error like the --dynamic-ir-only + --docs check at line 831. Leaning A; flagging for visibility.

boolean: true,
default: false,
description:
"Skip opening a PR / pushing when the generated output has no diff from the base branch."
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.

Self-review nit (description clarity): The description "Skip opening a PR / pushing when the generated output has no diff from the base branch" is accurate but only applies when the generator has a github output mode (pull-request or push). For generators with local-file-system output or publish-to-registry output, the flag is a no-op. Could tighten to something like: "For generators with GitHub output (pull-request/push), skip opening the PR or pushing when the generated output has no diff from the base branch." Not a must-fix.

fernignoreContents: string | undefined;
automationMode?: boolean;
autoMerge?: boolean;
skipIfNoDiff?: boolean;
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.

Self-review: The inner createJob now accepts skipIfNoDiff but doesn't forward it to Fiddle — matches how automationMode / autoMerge / runId are currently handled (threaded in, parked behind the existing TODO at lines 181–186). When FER-9671 is actioned, add skipIfNoDiff to the forwarded flags and update the TODO comment here. Happy to do a follow-up once Fiddle's API is ready.

// pipeline from running at all. This tree-hash check is a safety net for edge cases
// where the version changes but the generated output doesn't.
if (this.config.automationMode) {
if (shouldCheckNoDiff(this.config)) {
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.

Self-review (confirming intent): For automationMode: false + skipIfNoDiff: true + an existing PR found by the pre-check at line 102, we still reach this gate post-commit on the existing PR's branch. That means: "update the existing PR, but if the commit produced no diff vs. base, skip the push." Matches the task requirement and the existing-PR pre-check is untouched.

Worth flagging in release notes if this ever lands on a non-automation path: previously those callers would push an empty commit to the existing PR; with --skip-if-no-diff they won't. Intended, but a subtle behavior change.

/** When true: separate PRs per generation, automerge support, run_id in body */
automationMode?: boolean;
/** When true: skip opening a PR / pushing when the generated output has no diff from the base branch. */
skipIfNoDiff?: boolean;
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.

Self-review nit: I inserted skipIfNoDiff between automationMode and autoMerge, splitting the automation-related grouping. Considered moving it after autoMerge or to its own section, but left it here since the fields read top-to-bottom as "what gates PR/push behavior." Open to rearranging. The autoMerge docstring below still says "only effective when automationMode && !hasBreakingChanges" — accurate, but now that skipIfNoDiff doesn't share automationMode's coupling, maybe worth a brief note that the two are independent gates.

generated output has no diff from the base branch. The existing no-diff skip behavior is
decoupled from `automationMode` and now gated behind its own `skipIfNoDiff` config.
`fern automations generate` continues to skip no-diff PRs by default.
type: feat
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.

Self-review: Typed as feat → minor bump. This is strictly additive (new flag, default false) and preserves fern automations generate behavior exactly, so chore (patch bump) would also be defensible. I went with feat because the flag is user-facing — but if you prefer a patch release, flip this to chore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant