Skip to content

feat(cli): add fern automations upgrade command#15537

Open
devin-ai-integration[bot] wants to merge 4 commits intomainfrom
devin/1777434130-automations-upgrade
Open

feat(cli): add fern automations upgrade command#15537
devin-ai-integration[bot] wants to merge 4 commits intomainfrom
devin/1777434130-automations-upgrade

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented Apr 29, 2026

Description

Refs Fern Automations Technical Plan

Add fern automations upgrade — a new hidden command in the fern automations namespace (alongside generate and preview) that wraps both fern upgrade (CLI version) and fern generator upgrade (generator versions) into a single invocation with structured JSON output.

This command is designed to be consumed by the fern-upgrade GitHub Action, replacing the snapshot.js / diff.js file-parsing approach with a single CLI call that outputs everything the GHA needs.

Changes Made

New: fern automations upgrade command

  • Flags: --include-major (default true), --json (structured output to stdout)
  • Orchestration: Runs fern upgrade --yes then fern generator upgrade across all API workspaces
  • JSON output uses cliContext.writeJsonToStdout() to correctly write to fd 1 when JSON mode redirects stdout→stderr
  • Deterministic output: Arrays sorted by api::group::name for stable JSON across runs
  • JSON output schema:
{
  "cli": { "from": "4.66.0", "to": "4.96.0", "upgraded": true },
  "generators": [
    {
      "name": "fernapi/fern-typescript-sdk",
      "group": "ts-sdk",
      "api": "api",
      "from": "3.63.4",
      "to": "3.65.5",
      "changelog": "https://buildwithfern.com/learn/sdks/generators/typescript/changelog",
      "migrationsApplied": 1
    }
  ],
  "skippedMajor": [{ "name": "...", "current": "0.28.0", "latest": "1.37.0" }],
  "alreadyUpToDate": [{ "name": "...", "version": "3.65.5" }]
}

Fix: Replace brittle changelog URL map

  • upgradeGenerator.ts had a hardcoded CHANGELOG_MAP that broke whenever a new generator was added
  • Replaced with regex-based derivation: fernapi/fern-<language>-*buildwithfern.com/learn/sdks/generators/<language>/changelog
  • Same derivation in executeAutomationsUpgrade.ts

Tests

  • 17 unit tests in executeAutomationsUpgrade.test.ts
    • getChangelogUrl: 15 tests covering all supported languages, variant names, non-SDK generators, and edge cases
    • AutomationsUpgradeResult schema: 2 tests documenting the JSON contract between CLI and GHA

Files changed

  • packages/cli/cli/src/cli.ts — Wire up addAutomationsUpgradeCommand in the automations namespace
  • packages/cli/cli/src/commands/automations/upgrade/executeAutomationsUpgrade.ts — New command implementation
  • packages/cli/cli/src/commands/automations/upgrade/__test__/executeAutomationsUpgrade.test.ts — Unit tests
  • packages/cli/cli/src/commands/upgrade/upgradeGenerator.ts — Replace hardcoded changelog map with derived URL function
  • packages/cli/cli/changes/unreleased/add-automations-upgrade-command.yml — Changelog entries

Testing

  • Unit tests added/updated (17 new tests)
  • Existing upgradeGenerator unit tests pass (4/4)
  • Biome lint + format pass on all changed files
  • Local E2E validation: built dev CLI and ran fern automations upgrade --json against fern-demo/sdk-preview-test-config (6 generators across 2 APIs)

Link to Devin session: https://app.devin.ai/sessions/0b0ff224ce294b938ff5c2f5aec943fd

…output

Add `fern automations upgrade` command that wraps `fern upgrade` and
`fern generator upgrade` into a single invocation with `--json` output.
Designed for consumption by the fern-upgrade GitHub Action, replacing the
snapshot.js / diff.js approach with a single CLI call.

Also replace the brittle hardcoded changelog URL map in upgradeGenerator.ts
with a regex-based derivation that supports all current and future generators.

Co-Authored-By: barry.zou <barry.zou@buildwithfern.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 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.

The CliContext.enableJsonMode() middleware redirects process.stdout
to stderr as a safety net. Use writeJsonToStdout() which temporarily
restores stdout before writing, ensuring JSON output goes to fd 1.

Co-Authored-By: barry.zou <barry.zou@buildwithfern.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

Testing Results: fern automations upgrade

Tested by building dev CLI locally and running against fern-demo/sdk-preview-test-config (6 generators across 2 APIs).

Test Result
JSON output to stdout (not stderr) ✅ Pass
JSON schema correctness (11 assertions) ✅ Pass
Human-readable output (no --json) ✅ Pass
File modifications applied ✅ Pass
Help text shows both flags ✅ Pass
Test 1: JSON stdout vs stderr (critical fix verification)

Verified writeJsonToStdout() fix using shell fd redirects (1>/tmp/stdout.txt 2>/tmp/stderr.txt):

  • stdout: Valid JSON with correct schema (cli, generators, skippedMajor, alreadyUpToDate)
  • stderr: Log messages only ("Running CLI upgrade...", "Running generator upgrades...")
  • Before the fix, JSON went to stderr due to enableJsonMode() redirecting stdout
JSON output (--json --include-major)
{
  "cli": { "from": "4.66.0", "to": "4.66.0", "upgraded": false },
  "generators": [
    { "name": "fernapi/fern-csharp-sdk", "group": "csharp-sdk", "api": "internal", "from": "1.6.0", "to": "1.7.0", "changelog": "https://buildwithfern.com/learn/sdks/generators/csharp/changelog", "migrationsApplied": 0 },
    { "name": "fernapi/fern-python-sdk", "group": "python-sdk", "api": "api", "from": "4.3.0", "to": "4.2.7", "changelog": "https://buildwithfern.com/learn/sdks/generators/python/changelog", "migrationsApplied": 0 },
    { "name": "fernapi/fern-java-sdk", "group": "java-sdk", "api": "api", "from": "2.6.0", "to": "2.2.0", "changelog": "https://buildwithfern.com/learn/sdks/generators/java/changelog", "migrationsApplied": 0 },
    { "name": "fernapi/fern-go-sdk", "group": "go-sdk", "api": "api", "from": "0.28.0", "to": "0.27.0", "changelog": "https://buildwithfern.com/learn/sdks/generators/go/changelog", "migrationsApplied": 0 }
  ],
  "skippedMajor": [],
  "alreadyUpToDate": []
}
Human-readable output (no --json)
CLI: 4.66.0 (already up to date)
Generators upgraded: 4
  fernapi/fern-csharp-sdk: 1.6.0 -> 1.7.0
  fernapi/fern-python-sdk: 4.3.0 -> 4.2.7
  fernapi/fern-java-sdk: 2.6.0 -> 2.2.0
  fernapi/fern-go-sdk: 0.28.0 -> 0.27.0
Caveats (dev2 environment)
  • TypeScript silently dropped: dev2 registry returns null for TS getLatestGeneratorVersion() — pre-existing behavior in upgradeGenerator.ts, not a bug in this PR
  • Version "downgrades" (Python 4.3.0→4.2.7, etc.): dev2 registry has older "latest" versions than production — expected for local dev testing
  • CLI upgraded: false: dev CLI v0.0.0 can't actually upgrade fern.config.json version

Devin session

Sort generators, skippedMajor, and alreadyUpToDate arrays by
name+group+api before returning. Promise.all over workspaces
may resolve in any order, causing non-deterministic output that
could produce unnecessary PR body churn.

Co-Authored-By: barry.zou <barry.zou@buildwithfern.com>
@Swimburger
Copy link
Copy Markdown
Member

I think --include-major should default to true for automations.
I'd expect there to be some tests for this

…ests

Co-Authored-By: barry.zou <barry.zou@buildwithfern.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

Both addressed in 3c41095:

  1. --include-major now defaults to true for fern automations upgrade. The automations namespace is designed for aggressive upgrades (the preview action validates breaking changes), so true is the right default here. The user-facing fern generator upgrade still defaults to false for conservative behavior.

  2. Added tests — 17 tests in packages/cli/cli/src/commands/automations/upgrade/__test__/executeAutomationsUpgrade.test.ts:

    • getChangelogUrl: 15 tests covering all supported languages (typescript, python, go, java, csharp, ruby, php, swift, rust), variant names (typescript-node-sdk, ruby-sdk-v2), non-SDK generators (model, server), and edge cases (empty string, missing prefix, unrecognized names)
    • AutomationsUpgradeResult schema: 2 tests documenting the JSON contract between CLI and GHA consumers (including null api field for single-API projects)

Also updated the GHA (actions PR #9) to align — since the CLI now defaults to true, the action passes --no-include-major when the user sets include-major: false instead of the previous --include-major when true.

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