From ed7590a193c3a7a3d40b98c108a246bea4ff4704 Mon Sep 17 00:00:00 2001 From: Tanmay Singh Date: Wed, 13 May 2026 22:09:51 -0400 Subject: [PATCH 1/6] Add Fern Replay docs and migration guide Introduces customer-facing documentation for Fern Replay ahead of the broader launch. Replay preserves SDK customizations across regenerations via 3-way merge, complementing `.fernignore`'s file-level protection. - custom-code.mdx: new "Preserving customizations with Replay" section, with the existing `.fernignore` section lightly reframed so the two tools coexist clearly. Early Access callout mirrors Autorelease's rollout pattern. - replay-migration.mdx: 5-phase guide for switching SDK generation from `release` or `push` mode to `pull-request` mode (a Replay requirement). Hidden in nav until launch. - sdks.yml: hidden nav entry for the migration page. - generators-yml-reference.mdx: `replay` (global + per-generator) opt-out config, mirroring the `autorelease` reference shape. --- fern/products/sdks/custom-code.mdx | 42 ++++- .../reference/generators-yml-reference.mdx | 31 ++++ fern/products/sdks/replay-migration.mdx | 166 ++++++++++++++++++ fern/products/sdks/sdks.yml | 10 +- 4 files changed, 243 insertions(+), 6 deletions(-) create mode 100644 fern/products/sdks/replay-migration.mdx diff --git a/fern/products/sdks/custom-code.mdx b/fern/products/sdks/custom-code.mdx index 4655c1866..933285e3b 100644 --- a/fern/products/sdks/custom-code.mdx +++ b/fern/products/sdks/custom-code.mdx @@ -1,7 +1,7 @@ --- title: Adding custom code headline: Adding custom code (overview) -description: Extend Fern-generated SDKs with custom methods, logic, and dependencies. Use .fernignore to protect your code from being overwritten during regeneration. +description: Extend Fern-generated SDKs with custom methods, logic, and dependencies. Use Fern Replay for line-level edits and .fernignore for files you own end-to-end. --- @@ -9,11 +9,47 @@ Fern-generated SDKs are designed to be extended with custom logic, methods, and You can also add custom methods by inheriting the Fern generated client and extending it, plus add any dependencies that your custom methods depend on in your `generators.yml` file. +Fern provides two complementary tools for keeping your customizations safe across regenerations: **Replay** for line-level edits to generated files, and **`.fernignore`** for files you own end-to-end. + +## Preserving customizations with Replay + + +Fern Replay is rolling out across customer orgs. [Contact us](https://buildwithfern.com/contact) to enable it for your organization. + + +Replay automatically preserves the edits you make to your generated SDK across regenerations. When you commit a change to generated code, Replay records it as a patch. On the next `fern generate`, the patch is reapplied to the freshly generated SDK using a 3-way merge. + +Replay handles the common case where `.fernignore` is too heavy-handed: you want to change a few lines, not own the whole file forever. + +### How it works + +1. Edit generated SDK code and commit to your SDK repo. +2. On the next `fern generate`, Replay scans your repo's history for customer commits since the last `[fern-generated]` commit and stores each one as a tracked patch in `.fern/replay.lock`. +3. The patches are reapplied on top of the freshly generated code via 3-way merge. +4. The result lands as a `[fern-replay]` commit on top of `[fern-generated]` in the regeneration PR. + +```text title="Regeneration PR" +* abc123 (HEAD -> main) [fern-replay] Apply customizations +* 789abc [fern-generated] Update SDK to spec rev 0451 +* 234bcd Add helpers on top of User type +* ... +``` + +If the generator and your customization changed the same lines, Replay reports the conflict in the PR body. Run `fern replay resolve` locally to walk through it. + +### Requirements + +Replay requires `pull-request` output mode. If your SDK uses `release` or `push` mode, see the [migration guide](/learn/sdks/overview/replay-migration) for the switch. + +### Disable Replay + +To disable Replay for a specific generator, set `replay.enabled: false` in `generators.yml`. See the [`replay` reference](/learn/sdks/reference/generators-yml#replay) for global and per-generator configuration. + ## Using `.fernignore` to preserve your customizations -Once you add files containing custom code, use `.fernignore` to protect your custom code from being overwritten when Fern regenerates your SDK. +Use `.fernignore` to protect files you own end-to-end from being overwritten during SDK regeneration. Where Replay reapplies line-level edits to generated files, `.fernignore` makes the file entirely yours. The generator won't touch it, but generator updates to that file also stop flowing. -Simply add your custom files to the SDK repository and list them in `.fernignore`. Fern won't override any files listed there. A `.fernignore` file is automatically created in your SDK repository when you use GitHub publishing. +Add your custom files to the SDK repository and list them in `.fernignore`. A `.fernignore` file is automatically created in your SDK repository when you use GitHub publishing. `.fernignore` applies only to SDK generation. It has no effect on [Fern Docs](/learn/docs/getting-started/overview) builds. diff --git a/fern/products/sdks/reference/generators-yml-reference.mdx b/fern/products/sdks/reference/generators-yml-reference.mdx index f18f7050f..5369ebe57 100644 --- a/fern/products/sdks/reference/generators-yml-reference.mdx +++ b/fern/products/sdks/reference/generators-yml-reference.mdx @@ -406,6 +406,19 @@ autorelease: true Set to `true` to enable Autorelease, `false` to disable it. [Per-generator settings](#autorelease-2) override this global configuration. +## `replay` + +Override [Fern Replay](/learn/sdks/overview/custom-code#preserving-customizations-with-replay) behavior globally. Replay runs based on your organization's configuration; use this setting to disable it across all generators in this `generators.yml`. Alternatively, you can [configure Replay for individual SDKs](#replay-2). + +```yaml title="generators.yml" +replay: + enabled: false +``` + + + Set to `false` to skip Replay for all SDK generations, even if Replay is enabled for your organization. [Per-generator settings](#replay-2) override this global configuration. + + ## `aliases` Define shortcuts that map to multiple generator groups, allowing you to run several groups with a single command. When you run `fern generate --group `, all groups in the alias run in parallel. You can also set an alias as your `default-group`. @@ -1019,6 +1032,24 @@ groups: Set to `true` to enable Autorelease for this generator, `false` to disable it. Per-generator settings override the global [`autorelease`](#autorelease-1) configuration. +#### `replay` + +Override [Fern Replay](/learn/sdks/overview/custom-code#preserving-customizations-with-replay) for an individual SDK. Alternatively, you can [configure Replay globally for all SDKs](#replay-1). + +```yml title="generators.yml" {6-7} +groups: + ts-sdk: + generators: + - name: fernapi/fern-typescript-sdk + ... + replay: + enabled: false +``` + + + Set to `false` to skip Replay for this generator, even if Replay is enabled for your organization or globally in this `generators.yml`. Per-generator settings override the global [`replay`](#replay-1) configuration. + + #### `snippets` Configures snippets for a particular generator. diff --git a/fern/products/sdks/replay-migration.mdx b/fern/products/sdks/replay-migration.mdx new file mode 100644 index 000000000..1821bdd0f --- /dev/null +++ b/fern/products/sdks/replay-migration.mdx @@ -0,0 +1,166 @@ +--- +title: Migrating to Replay +headline: Migrating from release or push mode to pull-request mode +description: Step-by-step guide for switching SDK generation from release or push mode to pull-request mode so Fern Replay can run. +--- + + +Fern Replay is rolling out across customer orgs. [Contact us](https://buildwithfern.com/contact) to enable it for your organization. + + +[Fern Replay](/learn/sdks/overview/custom-code#preserving-customizations-with-replay) requires `pull-request` output mode. This guide covers the migration from `release` or `push` mode in five phases. + +## What changes + +- SDK updates arrive as PRs instead of direct pushes to `main`. +- Customizations to generated code survive regeneration via 3-way merge. +- Version bumps are computed from pure generator output, never contaminated by customizations. +- Publishing is decoupled from generation, so you control when to release. + +## Before you start + +Make sure the [Fern GitHub App](https://github.com/apps/fern-api) is installed on your SDK repositories. [Contact us](https://buildwithfern.com/contact) to flip your organization onto the Replay-enabled pipeline. + +## Phase 1: Move publishing secrets to the SDK repo + +Fern sets GitHub Actions secrets in your SDK repo on every generation. When you switch to PR mode, that write would clobber any secrets you set directly. To avoid this, move secret ownership from the config repo to the SDK repo before flipping modes. + +For each SDK repo: + +1. Identify which secrets your `generators.yml` references for publishing: + - Python: `PYPI_USERNAME`, `PYPI_PASSWORD` + - TypeScript / npm: `NPM_TOKEN` + - Java / Maven: `MAVEN_USERNAME`, `MAVEN_PASSWORD`, `MAVEN_SIGNATURE_KID`, `MAVEN_SIGNATURE_PASSWORD`, `MAVEN_SIGNATURE_SECRET_KEY` + - Ruby: `RUBY_GEMS_API_KEY` + - C# / NuGet: `NUGET_API_KEY` + - Rust / Crates: `CRATES_TOKEN` +2. Add the real publish credentials directly to the SDK repo: **Settings → Secrets and variables → Actions → New repository secret**. +3. Remove the publish credential values from the config repo's `generators.yml`: + +```yaml title="generators.yml" +# BEFORE +groups: + python-sdk: + generators: + - name: fernapi/fern-python-sdk + version: "..." + github: + repository: customer/python-sdk + mode: pull-request + output: + pypi: + username: $PYPI_USERNAME + password: $PYPI_PASSWORD + +# AFTER (remove the output/pypi block entirely) +groups: + python-sdk: + generators: + - name: fernapi/fern-python-sdk + version: "..." + github: + repository: customer/python-sdk + mode: pull-request +``` + +4. Run a generation. Confirm the SDK repo's secrets weren't overwritten by checking the "Last updated" timestamp under **SDK repo → Settings → Secrets**. + +## Phase 2: Switch output mode + +In `generators.yml`: + +```yaml title="generators.yml" {3} +github: + repository: customer/python-sdk + mode: pull-request # was: mode: release (or: mode: push) +``` + +If you use `version: AUTO`, no other changes are needed. Autoversioning runs as part of the generator-cli pipeline and diffs pure generator output, so customer customizations never contaminate the version diff. + +## Phase 3: Decouple publishing from generation (optional) + +If you want to control publishing separately from generation, keep your publish workflow out of Fern's generation cycle: + +1. Add the workflow to `.fernignore`: + +```text title=".fernignore" +.github/workflows/ci.yml +``` + +2. Change the workflow trigger from push-to-main to manual or release-tagged: + +```yaml title=".github/workflows/ci.yml" +# Instead of: +on: + push: + branches: [main] + +# Use: +on: + workflow_dispatch: + release: + types: [published] +``` + +3. Update any secret references if you renamed credentials in Phase 1. + +## Phase 4: First generation with Replay + +1. Run `fern generate --group ` from the config repo (or trigger via your existing CI). +2. The first run auto-creates `.fern/replay.lock`. Replay tracks customer commits to generated files from this point on. +3. Review the resulting PR. It contains: + - `[fern-generated]`: pure generator output + - `[fern-replay]`: patches re-applied (empty on first run, since no patches exist yet) + - Updated `.fern/replay.lock` +4. Squash-merge the PR. The next generation re-anchors correctly even after the squash. + +## Phase 5: Validate + +After the first generation and merge: + +- A PR was created (not a direct push to `main`). +- No unintended package release was triggered. +- SDK code is correct and passes CI. +- `.fern/replay.lock` exists in the SDK repo. +- `.fernignore` contains `replay.lock` (and `replay.yml`). + +After your first real customization, verify Replay's behavior end-to-end: + +- You edit a generated file, commit, and merge to `main`. +- The next `fern generate` detects the patch (PR body shows `patches detected: 1`). +- The customization survives regeneration via a clean 3-way merge. +- `.fern/replay.lock` shows the patch stored. + +## Rollback + +Replay never modifies files destructively. Your `main` branch always has correct code, so rolling back is straightforward. + +**Quick disable.** If you only want to stop Replay and stay in `pull-request` mode, delete `.fern/replay.lock` from the SDK repo and commit. The next `fern generate` runs without Replay; PRs land in the same shape as before, just without the `[fern-replay]` commit. + +**Full revert to `release` or `push` mode:** + +1. Switch `mode` back in `generators.yml`: + +```yaml title="generators.yml" {3} +github: + repository: customer/python-sdk + mode: release # or mode: push +``` + +2. Re-add publish credentials to the config repo (if you removed them in Phase 1). +3. Clean up Replay state in the SDK repo: + - Delete `.fern/replay.lock` + - Delete `.fern/replay.yml` + - Delete the `fern-generation-base` tag if it exists +4. Close any open `fern-bot` PRs from PR mode. + +## Escape hatches + +- `fern generate --no-replay`: skip patch application for one generation. Generation still creates `[fern-generated]`; patches aren't applied. Useful for debugging. +- `fern replay forget `: untrack specific patches. The next generation overwrites those files. +- `fern replay reset`: delete the lockfile entirely. Your code stays, but Replay loses all state. Run `fern replay bootstrap` to start fresh. + +## Known caveats + +- **Closed-without-merge replay PRs.** If a replay PR is closed without merging, the next generation detects the abandoned state and re-anchors automatically. No manual cleanup needed. +- **Force pushes and rewritten history.** Replay handles force-pushed branches via a fallback detection path. Existing patches still apply correctly. diff --git a/fern/products/sdks/sdks.yml b/fern/products/sdks/sdks.yml index 2e9f95fc9..18aa5f54d 100644 --- a/fern/products/sdks/sdks.yml +++ b/fern/products/sdks/sdks.yml @@ -20,6 +20,10 @@ navigation: - page: Adding custom code path: ./custom-code.mdx slug: custom-code + - page: Migrating to Replay + hidden: true + path: ./replay-migration.mdx + slug: replay-migration - page: Capabilities path: ./capabilities.mdx slug: capabilities @@ -66,7 +70,7 @@ navigation: path: ./generators/python/configuration.mdx slug: configuration - page: Adding custom code - path: ./generators/python/custom-code.mdx + path: ./generators/python/custom-code.mdx slug: custom-code - page: Dynamic authentication path: ./generators/python/dynamic-authentication.mdx @@ -90,7 +94,7 @@ navigation: path: ./generators/go/configuration.mdx slug: configuration - page: Adding custom code - path: ./generators/go/custom-code.mdx + path: ./generators/go/custom-code.mdx slug: custom-code - changelog: ./generators/go/changelog slug: changelog @@ -264,7 +268,7 @@ navigation: path: ./deep-dives/self-hosted.mdx slug: self-hosted - section: Reference - contents: + contents: - page: generators.yml path: ./reference/generators-yml-reference.mdx slug: generators-yml From 8d35d23b0334475927b9c901847d4af8ca13fff2 Mon Sep 17 00:00:00 2001 From: Tanmay Singh Date: Wed, 13 May 2026 23:14:08 -0400 Subject: [PATCH 2/6] Address vale linter feedback - Lowercase 'replay' in three headings per FernStyles.Headings (matches the 'Disable autorelease' convention in autorelease.mdx) - Rephrase Phase 3 intro to drop the unnecessary 'separately' adverb flagged by FernStyles.Adverbs --- fern/products/sdks/custom-code.mdx | 4 ++-- fern/products/sdks/replay-migration.mdx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fern/products/sdks/custom-code.mdx b/fern/products/sdks/custom-code.mdx index 933285e3b..1d16a3be4 100644 --- a/fern/products/sdks/custom-code.mdx +++ b/fern/products/sdks/custom-code.mdx @@ -11,7 +11,7 @@ You can also add custom methods by inheriting the Fern generated client and exte Fern provides two complementary tools for keeping your customizations safe across regenerations: **Replay** for line-level edits to generated files, and **`.fernignore`** for files you own end-to-end. -## Preserving customizations with Replay +## Preserving customizations with replay Fern Replay is rolling out across customer orgs. [Contact us](https://buildwithfern.com/contact) to enable it for your organization. @@ -41,7 +41,7 @@ If the generator and your customization changed the same lines, Replay reports t Replay requires `pull-request` output mode. If your SDK uses `release` or `push` mode, see the [migration guide](/learn/sdks/overview/replay-migration) for the switch. -### Disable Replay +### Disable replay To disable Replay for a specific generator, set `replay.enabled: false` in `generators.yml`. See the [`replay` reference](/learn/sdks/reference/generators-yml#replay) for global and per-generator configuration. diff --git a/fern/products/sdks/replay-migration.mdx b/fern/products/sdks/replay-migration.mdx index 1821bdd0f..d6c3eab20 100644 --- a/fern/products/sdks/replay-migration.mdx +++ b/fern/products/sdks/replay-migration.mdx @@ -79,7 +79,7 @@ If you use `version: AUTO`, no other changes are needed. Autoversioning runs as ## Phase 3: Decouple publishing from generation (optional) -If you want to control publishing separately from generation, keep your publish workflow out of Fern's generation cycle: +To control publishing on your own schedule rather than on every Fern generation, keep your publish workflow out of Fern's generation cycle: 1. Add the workflow to `.fernignore`: @@ -104,7 +104,7 @@ on: 3. Update any secret references if you renamed credentials in Phase 1. -## Phase 4: First generation with Replay +## Phase 4: First generation with replay 1. Run `fern generate --group ` from the config repo (or trigger via your existing CI). 2. The first run auto-creates `.fern/replay.lock`. Replay tracks customer commits to generated files from this point on. From 78635828343a1fb9d98a2de0a7bad1861e93ec03 Mon Sep 17 00:00:00 2001 From: Tanmay Singh Date: Thu, 14 May 2026 15:22:32 -0400 Subject: [PATCH 3/6] Update Replay docs to GA messaging Replay is generally available at launch (org gate is being removed), not in early access. Update both pages accordingly: - Replace Early Access callouts with a Note stating the actual customer requirement: latest Fern CLI and SDK generator versions - Drop "Contact us" CTAs (Replay isn't opt-in) - Migration guide's "Before you start" becomes a 2-bullet checklist: install the Fern GitHub App + run `fern upgrade` --- fern/products/sdks/custom-code.mdx | 4 ++-- fern/products/sdks/replay-migration.mdx | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fern/products/sdks/custom-code.mdx b/fern/products/sdks/custom-code.mdx index 1d16a3be4..c1615b119 100644 --- a/fern/products/sdks/custom-code.mdx +++ b/fern/products/sdks/custom-code.mdx @@ -13,8 +13,8 @@ Fern provides two complementary tools for keeping your customizations safe acros ## Preserving customizations with replay - -Fern Replay is rolling out across customer orgs. [Contact us](https://buildwithfern.com/contact) to enable it for your organization. + +Replay requires the latest Fern CLI and SDK generator versions. Run `fern upgrade` to make sure you're current. Replay automatically preserves the edits you make to your generated SDK across regenerations. When you commit a change to generated code, Replay records it as a patch. On the next `fern generate`, the patch is reapplied to the freshly generated SDK using a 3-way merge. diff --git a/fern/products/sdks/replay-migration.mdx b/fern/products/sdks/replay-migration.mdx index d6c3eab20..eec88997f 100644 --- a/fern/products/sdks/replay-migration.mdx +++ b/fern/products/sdks/replay-migration.mdx @@ -4,8 +4,8 @@ headline: Migrating from release or push mode to pull-request mode description: Step-by-step guide for switching SDK generation from release or push mode to pull-request mode so Fern Replay can run. --- - -Fern Replay is rolling out across customer orgs. [Contact us](https://buildwithfern.com/contact) to enable it for your organization. + +Replay requires the latest Fern CLI and SDK generator versions. Run `fern upgrade` to make sure you're current. [Fern Replay](/learn/sdks/overview/custom-code#preserving-customizations-with-replay) requires `pull-request` output mode. This guide covers the migration from `release` or `push` mode in five phases. @@ -19,7 +19,8 @@ Fern Replay is rolling out across customer orgs. [Contact us](https://buildwithf ## Before you start -Make sure the [Fern GitHub App](https://github.com/apps/fern-api) is installed on your SDK repositories. [Contact us](https://buildwithfern.com/contact) to flip your organization onto the Replay-enabled pipeline. +- Install the [Fern GitHub App](https://github.com/apps/fern-api) on your SDK repositories. +- Update to the latest Fern CLI and SDK generator versions (`fern upgrade`). ## Phase 1: Move publishing secrets to the SDK repo From aed299f3dd00a023b55b18c9c6dbcf5adb81bda7 Mon Sep 17 00:00:00 2001 From: Devin Logan Date: Thu, 14 May 2026 17:18:54 -0400 Subject: [PATCH 4/6] make edits for structure and clarity --- .vale/styles/FernStyles/Headings.yml | 1 + .../cli-api-reference/pages/commands.mdx | 85 ++++++++++- fern/products/sdks/custom-code.mdx | 103 ++++++------- .../reference/generators-yml-reference.mdx | 8 +- fern/products/sdks/replay-migration.mdx | 141 +++++++++++++----- 5 files changed, 238 insertions(+), 100 deletions(-) diff --git a/.vale/styles/FernStyles/Headings.yml b/.vale/styles/FernStyles/Headings.yml index 856750e8d..50eced3e8 100644 --- a/.vale/styles/FernStyles/Headings.yml +++ b/.vale/styles/FernStyles/Headings.yml @@ -90,3 +90,4 @@ exceptions: - OG - BCP - ISO + - Replay diff --git a/fern/products/cli-api-reference/pages/commands.mdx b/fern/products/cli-api-reference/pages/commands.mdx index 0a5295d19..07f3c65fa 100644 --- a/fern/products/cli-api-reference/pages/commands.mdx +++ b/fern/products/cli-api-reference/pages/commands.mdx @@ -38,6 +38,8 @@ hideOnThisPage: true | [`fern generate`](#fern-generate) | Build & publish SDK updates | | [`fern write-overrides`](#fern-write-overrides) | Create OpenAPI customizations | | [`fern generator upgrade`](#fern-generator-upgrade) | Update SDK generators to latest versions | +| [`fern replay resolve`](#fern-replay-resolve) | Resolve patch conflicts left by [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay) | +| [`fern replay forget`](#fern-replay-forget) | Remove tracked customization patches from `.fern/replay.lock` | ## Detailed command documentation @@ -173,7 +175,7 @@ hideOnThisPage: true ```bash - fern generate [--group ] [--api ] [--version ] [--preview] [--fernignore ] [--local] [--force] + fern generate [--group ] [--api ] [--version ] [--preview] [--fernignore ] [--local] [--force] [--no-replay] ``` @@ -270,6 +272,18 @@ hideOnThisPage: true fern generate --group python-sdk --force ``` + ### no-replay + + Use `--no-replay` to skip [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay)'s patch detection and application phase for a single generation. The generation still produces the new SDK code, but tracked customization patches aren't applied on top. Useful for a clean regeneration — for example, to debug whether a conflict is caused by a specific patch. + + ```bash + fern generate --no-replay --local + ``` + + + `--no-replay` works only for local generation (`--local`). + + @@ -708,6 +722,75 @@ hideOnThisPage: true Use `--include-major` to include major version upgrades. Major versions are skipped by default to prevent breaking changes. + + + + Use `fern replay resolve` to work through patch conflicts that [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay) couldn't merge cleanly during `fern generate`. The command runs in two phases: the first run applies unresolved patches to your working tree with standard merge markers (`<<<<<<<` / `=======` / `>>>>>>>`) for you to edit; the second run verifies no markers remain and commits the resolved patches. + + + ```bash + fern replay resolve [directory] [--no-check-markers] + ``` + + + ### directory + + Path to the SDK directory containing `.fern/replay.lock`. Defaults to the current directory. + + ### no-check-markers + + Skip the conflict-marker check before committing. Use with caution — committing files that still contain merge markers will break the next regeneration. + + + + + + Use `fern replay forget` to remove tracked customization patches from `.fern/replay.lock`. Useful when the generator now supports a customization natively, or when you want to stop replaying a specific edit on future regenerations. + + + ```bash + fern replay forget ... [--all] [--dry-run] [--yes] + ``` + + + The command accepts three modes: + + - **By patch ID.** Pass one or more patch IDs to remove specific patches. + + ```bash + fern replay forget patch-abc123 patch-def456 + ``` + + - **By search pattern.** Pass a pattern to find matching patches. The command shows the matches with diff stats and prompts for confirmation before removing. + + ```bash + fern replay forget "src/utils" + ``` + + - **All patches.** Pass `--all` to remove every tracked patch. + + ```bash + fern replay forget --all + ``` + + ### dry-run + + Show what would be removed without actually removing. + + ```bash + fern replay forget "src/utils" --dry-run + ``` + + ### yes + + Skip confirmation prompts. Required in non-interactive or CI environments. + + ```bash + fern replay forget "src/utils" --yes + ``` + + + Pulls the latest OpenAPI spec from the specified `origin` in `generators.yml` and updates the local spec. Alternatively, you can [automate this process by setting up a GitHub Action](/api-definitions/openapi/sync-your-open-api-specification). diff --git a/fern/products/sdks/custom-code.mdx b/fern/products/sdks/custom-code.mdx index 1d16a3be4..8b18325fe 100644 --- a/fern/products/sdks/custom-code.mdx +++ b/fern/products/sdks/custom-code.mdx @@ -7,52 +7,19 @@ description: Extend Fern-generated SDKs with custom methods, logic, and dependen Fern-generated SDKs are designed to be extended with custom logic, methods, and dependencies. If you want your SDK to do more than just make basic API calls (like combining multiple calls, processing data, adding utilities), you can add custom code that lives in harmony with the generated code. -You can also add custom methods by inheriting the Fern generated client and extending it, plus add any dependencies that your custom methods depend on in your `generators.yml` file. +Fern provides two complementary tools for keeping your customizations safe across regenerations: -Fern provides two complementary tools for keeping your customizations safe across regenerations: **Replay** for line-level edits to generated files, and **`.fernignore`** for files you own end-to-end. +- **`.fernignore`** — Full-file ownership. The generator stops touching listed files entirely. Use for fully hand-written modules, READMEs, or custom workflows. Trade-off: you also stop receiving generator updates to those files. +- **Replay** — Line-level edits. Keep generated files under generator control, and reapply your edits via 3-way merge on every regeneration. -## Preserving customizations with replay +## Full-file ownership with `.fernignore` - -Fern Replay is rolling out across customer orgs. [Contact us](https://buildwithfern.com/contact) to enable it for your organization. - - -Replay automatically preserves the edits you make to your generated SDK across regenerations. When you commit a change to generated code, Replay records it as a patch. On the next `fern generate`, the patch is reapplied to the freshly generated SDK using a 3-way merge. - -Replay handles the common case where `.fernignore` is too heavy-handed: you want to change a few lines, not own the whole file forever. - -### How it works - -1. Edit generated SDK code and commit to your SDK repo. -2. On the next `fern generate`, Replay scans your repo's history for customer commits since the last `[fern-generated]` commit and stores each one as a tracked patch in `.fern/replay.lock`. -3. The patches are reapplied on top of the freshly generated code via 3-way merge. -4. The result lands as a `[fern-replay]` commit on top of `[fern-generated]` in the regeneration PR. - -```text title="Regeneration PR" -* abc123 (HEAD -> main) [fern-replay] Apply customizations -* 789abc [fern-generated] Update SDK to spec rev 0451 -* 234bcd Add helpers on top of User type -* ... -``` - -If the generator and your customization changed the same lines, Replay reports the conflict in the PR body. Run `fern replay resolve` locally to walk through it. - -### Requirements - -Replay requires `pull-request` output mode. If your SDK uses `release` or `push` mode, see the [migration guide](/learn/sdks/overview/replay-migration) for the switch. - -### Disable replay - -To disable Replay for a specific generator, set `replay.enabled: false` in `generators.yml`. See the [`replay` reference](/learn/sdks/reference/generators-yml#replay) for global and per-generator configuration. - -## Using `.fernignore` to preserve your customizations +`.fernignore` applies only to SDK generation. It has no effect on [Fern Docs](/learn/docs/getting-started/overview) builds. -Use `.fernignore` to protect files you own end-to-end from being overwritten during SDK regeneration. Where Replay reapplies line-level edits to generated files, `.fernignore` makes the file entirely yours. The generator won't touch it, but generator updates to that file also stop flowing. +Use `.fernignore` to protect files you own end-to-end from being overwritten during SDK regeneration. Add your custom files to the SDK repository and list them in `.fernignore`. A `.fernignore` file is automatically created in your SDK repository when you use GitHub publishing. -`.fernignore` applies only to SDK generation. It has no effect on [Fern Docs](/learn/docs/getting-started/overview) builds. - Your `.fernignore` file might look something like this: ```gitignore title=".fernignore" @@ -67,7 +34,8 @@ LICENSE # Custom code src/CustomClient.ts ``` -For another example, see Cohere's [`.fernignore` file for their TypeScript SDK](https://github.com/cohere-ai/cohere-typescript/blob/ad583e3003bd51e80a82317f9e16beec85881b86/.fernignore). + +For another real-world example, see Cohere's [`.fernignore` file for their TypeScript SDK](https://github.com/cohere-ai/cohere-typescript/blob/ad583e3003bd51e80a82317f9e16beec85881b86/.fernignore). You'll have a separate `.fernignore` file for each of your SDKs: @@ -97,24 +65,39 @@ You'll have a separate `.fernignore` file for each of your SDKs: +## Line-level edits with Replay + + +Replay is on by default for SDKs using [`pull-request` output mode](/learn/sdks/reference/generators-yml#pull-request). To enable Replay on an SDK that's currently on `release` or `push` mode, switch to `pull-request` mode via the [migration guide](/learn/sdks/overview/replay-migration). + +Replay requires the latest Fern CLI and SDK generator versions — run `fern upgrade` to make sure you're current. + + +Replay automatically preserves the edits you make to your generated SDK across regenerations. When you commit a change to generated code, Replay scans your repo's history since the last `[fern-generated]` commit and stores each customer commit as a tracked patch in `.fern/replay.lock`. On the next `fern generate`, those patches are reapplied to the freshly generated SDK via 3-way merge, and the result lands as a `[fern-replay]` commit on top of `[fern-generated]` in the regeneration PR: + +```text title="Regeneration PR" +* abc123 (HEAD -> main) [fern-replay] Apply customizations +* 789abc [fern-generated] Update SDK to spec rev 0451 +* 234bcd Add helpers on top of User type +* ... +``` + +If the generator and your customization changed the same lines, Replay reports the conflict in the PR body. Run [`fern replay resolve`](/learn/cli-api-reference/cli-reference/commands#fern-replay-resolve) locally to walk through it. + +### Disable Replay + +To disable Replay for a specific generator, set `replay.enabled: false` in `generators.yml`. See the [`replay` reference](/learn/sdks/reference/generators-yml#replay) for global and per-generator configuration. + +## Customization patterns by language + +Each SDK generator has its own conventions for adding custom code: how to extend the generated client, where helpers go, how to declare dependencies. See the guide for your language: - ## Augmenting your SDK with custom code - - Get started adding custom code to a specific SDK: - - - } href="/sdks/generators/typescript/custom-code"> - - } href="/sdks/generators/python/custom-code"> - - } href="/learn/sdks/generators/go/custom-code"> - - } href="/learn/sdks/generators/java/custom-code"> - - } href="/sdks/generators/csharp/custom-code"> - - } href="/sdks/generators/php/custom-code"> - - } href="/sdks/generators/ruby/custom-code"> - - + + } href="/sdks/generators/typescript/custom-code" /> + } href="/sdks/generators/python/custom-code" /> + } href="/learn/sdks/generators/go/custom-code" /> + } href="/learn/sdks/generators/java/custom-code" /> + } href="/sdks/generators/csharp/custom-code" /> + } href="/sdks/generators/php/custom-code" /> + } href="/sdks/generators/ruby/custom-code" /> + diff --git a/fern/products/sdks/reference/generators-yml-reference.mdx b/fern/products/sdks/reference/generators-yml-reference.mdx index 5369ebe57..c40ba300f 100644 --- a/fern/products/sdks/reference/generators-yml-reference.mdx +++ b/fern/products/sdks/reference/generators-yml-reference.mdx @@ -408,7 +408,7 @@ autorelease: true ## `replay` -Override [Fern Replay](/learn/sdks/overview/custom-code#preserving-customizations-with-replay) behavior globally. Replay runs based on your organization's configuration; use this setting to disable it across all generators in this `generators.yml`. Alternatively, you can [configure Replay for individual SDKs](#replay-2). +Override [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay) behavior globally. Replay runs based on your organization's configuration; use this setting to disable it across all generators in this `generators.yml`. Alternatively, you can [configure Replay for individual SDKs](#replay-1). ```yaml title="generators.yml" replay: @@ -416,7 +416,7 @@ replay: ``` - Set to `false` to skip Replay for all SDK generations, even if Replay is enabled for your organization. [Per-generator settings](#replay-2) override this global configuration. + Set to `false` to skip Replay for all SDK generations, even if Replay is enabled for your organization. [Per-generator settings](#replay-1) override this global configuration. ## `aliases` @@ -1034,7 +1034,7 @@ groups: #### `replay` -Override [Fern Replay](/learn/sdks/overview/custom-code#preserving-customizations-with-replay) for an individual SDK. Alternatively, you can [configure Replay globally for all SDKs](#replay-1). +Override [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay) for an individual SDK. Alternatively, you can [configure Replay globally for all SDKs](#replay). ```yml title="generators.yml" {6-7} groups: @@ -1047,7 +1047,7 @@ groups: ``` - Set to `false` to skip Replay for this generator, even if Replay is enabled for your organization or globally in this `generators.yml`. Per-generator settings override the global [`replay`](#replay-1) configuration. + Set to `false` to skip Replay for this generator, even if Replay is enabled for your organization or globally in this `generators.yml`. Per-generator settings override the global [`replay`](#replay) configuration. #### `snippets` diff --git a/fern/products/sdks/replay-migration.mdx b/fern/products/sdks/replay-migration.mdx index d6c3eab20..37971831e 100644 --- a/fern/products/sdks/replay-migration.mdx +++ b/fern/products/sdks/replay-migration.mdx @@ -4,11 +4,11 @@ headline: Migrating from release or push mode to pull-request mode description: Step-by-step guide for switching SDK generation from release or push mode to pull-request mode so Fern Replay can run. --- - -Fern Replay is rolling out across customer orgs. [Contact us](https://buildwithfern.com/contact) to enable it for your organization. + + Replay requires the latest Fern CLI and SDK generator versions. Run `fern upgrade` to make sure you're current. -[Fern Replay](/learn/sdks/overview/custom-code#preserving-customizations-with-replay) requires `pull-request` output mode. This guide covers the migration from `release` or `push` mode in five phases. +This guide migrates an SDK from `release` or `push` output mode to `pull-request` output mode — the mode required to enable [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay). Only follow it if your SDK is currently on `release` or `push` mode and you want to switch in order to use Replay. SDKs already using `pull-request` mode have Replay on by default; no migration is needed. ## What changes @@ -27,15 +27,29 @@ Fern sets GitHub Actions secrets in your SDK repo on every generation. When you For each SDK repo: -1. Identify which secrets your `generators.yml` references for publishing: - - Python: `PYPI_USERNAME`, `PYPI_PASSWORD` - - TypeScript / npm: `NPM_TOKEN` - - Java / Maven: `MAVEN_USERNAME`, `MAVEN_PASSWORD`, `MAVEN_SIGNATURE_KID`, `MAVEN_SIGNATURE_PASSWORD`, `MAVEN_SIGNATURE_SECRET_KEY` - - Ruby: `RUBY_GEMS_API_KEY` - - C# / NuGet: `NUGET_API_KEY` - - Rust / Crates: `CRATES_TOKEN` -2. Add the real publish credentials directly to the SDK repo: **Settings → Secrets and variables → Actions → New repository secret**. -3. Remove the publish credential values from the config repo's `generators.yml`: + + + +Find which secrets your `generators.yml` references for publishing: + +- Python: `PYPI_USERNAME`, `PYPI_PASSWORD` +- TypeScript / npm: `NPM_TOKEN` +- Java / Maven: `MAVEN_USERNAME`, `MAVEN_PASSWORD`, `MAVEN_SIGNATURE_KID`, `MAVEN_SIGNATURE_PASSWORD`, `MAVEN_SIGNATURE_SECRET_KEY` +- Ruby: `RUBY_GEMS_API_KEY` +- C# / NuGet: `NUGET_API_KEY` +- Rust / Crates: `CRATES_TOKEN` + + + + + +Add the real publish credentials directly to the SDK repo: **Settings → Secrets and variables → Actions → New repository secret**. + + + + + +Remove the publish credential values from the config repo's `generators.yml`: ```yaml title="generators.yml" # BEFORE @@ -63,7 +77,14 @@ groups: mode: pull-request ``` -4. Run a generation. Confirm the SDK repo's secrets weren't overwritten by checking the "Last updated" timestamp under **SDK repo → Settings → Secrets**. + + + + +Run a generation. Confirm the SDK repo's secrets weren't overwritten by checking the "Last updated" timestamp under **SDK repo → Settings → Secrets**. + + + ## Phase 2: Switch output mode @@ -81,13 +102,18 @@ If you use `version: AUTO`, no other changes are needed. Autoversioning runs as To control publishing on your own schedule rather than on every Fern generation, keep your publish workflow out of Fern's generation cycle: -1. Add the workflow to `.fernignore`: + + ```text title=".fernignore" .github/workflows/ci.yml ``` -2. Change the workflow trigger from push-to-main to manual or release-tagged: + + + + +Switch from push-to-main to manual or release-tagged: ```yaml title=".github/workflows/ci.yml" # Instead of: @@ -102,21 +128,50 @@ on: types: [published] ``` -3. Update any secret references if you renamed credentials in Phase 1. + + + + +Update any secret references if you renamed credentials in Phase 1. + + + ## Phase 4: First generation with replay -1. Run `fern generate --group ` from the config repo (or trigger via your existing CI). -2. The first run auto-creates `.fern/replay.lock`. Replay tracks customer commits to generated files from this point on. -3. Review the resulting PR. It contains: - - `[fern-generated]`: pure generator output - - `[fern-replay]`: patches re-applied (empty on first run, since no patches exist yet) - - Updated `.fern/replay.lock` -4. Squash-merge the PR. The next generation re-anchors correctly even after the squash. + + + +Run `fern generate --group ` from the config repo (or trigger via your existing CI). + + + + + +The first run auto-creates `.fern/replay.lock`. Replay tracks customer commits to generated files from this point on. + + + + + +The PR contains: + +- `[fern-generated]`: pure generator output +- `[fern-replay]`: patches re-applied (empty on first run, since no patches exist yet) +- Updated `.fern/replay.lock` + + + + + +The next generation re-anchors correctly even after the squash. + + + ## Phase 5: Validate -After the first generation and merge: +After the first generation and merge, confirm the following: - A PR was created (not a direct push to `main`). - No unintended package release was triggered. @@ -139,7 +194,8 @@ Replay never modifies files destructively. Your `main` branch always has correct **Full revert to `release` or `push` mode:** -1. Switch `mode` back in `generators.yml`: + + ```yaml title="generators.yml" {3} github: @@ -147,18 +203,33 @@ github: mode: release # or mode: push ``` -2. Re-add publish credentials to the config repo (if you removed them in Phase 1). -3. Clean up Replay state in the SDK repo: - - Delete `.fern/replay.lock` - - Delete `.fern/replay.yml` - - Delete the `fern-generation-base` tag if it exists -4. Close any open `fern-bot` PRs from PR mode. + + + + +Re-add the credentials you removed in Phase 1. + + + + + +- Delete `.fern/replay.lock` +- Delete `.fern/replay.yml` +- Delete the `fern-generation-base` tag if it exists + + + + + + + -## Escape hatches +## Troubleshooting -- `fern generate --no-replay`: skip patch application for one generation. Generation still creates `[fern-generated]`; patches aren't applied. Useful for debugging. -- `fern replay forget `: untrack specific patches. The next generation overwrites those files. -- `fern replay reset`: delete the lockfile entirely. Your code stays, but Replay loses all state. Run `fern replay bootstrap` to start fresh. +| Command | Effect | +|---------|--------| +| [`fern generate --no-replay`](/learn/cli-api-reference/cli-reference/commands#no-replay) | Skip patch application for one generation. Generation still creates `[fern-generated]`; patches aren't applied. Useful for debugging. | +| [`fern replay forget`](/learn/cli-api-reference/cli-reference/commands#fern-replay-forget) | Untrack patches by ID, by pattern, or all at once. The next generation overwrites those files. | ## Known caveats From ca8f399d86c7743f1f4c73f99bec1d9483bc2a7d Mon Sep 17 00:00:00 2001 From: Devin Logan Date: Thu, 14 May 2026 17:32:48 -0400 Subject: [PATCH 5/6] update lang-specific custom code pages --- fern/products/sdks/generators/csharp/custom-code.mdx | 4 ++-- fern/products/sdks/generators/go/custom-code.mdx | 4 ++-- fern/products/sdks/generators/java/custom-code.mdx | 4 ++-- fern/products/sdks/generators/php/custom-code.mdx | 4 ++-- fern/products/sdks/generators/python/custom-code.mdx | 4 ++-- fern/products/sdks/generators/ruby/custom-code.mdx | 4 ++-- fern/products/sdks/generators/typescript/custom-code.mdx | 4 ++-- fern/products/sdks/snippets/custom-code-callout.mdx | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/fern/products/sdks/generators/csharp/custom-code.mdx b/fern/products/sdks/generators/csharp/custom-code.mdx index a1ab27ffa..14da9cb94 100644 --- a/fern/products/sdks/generators/csharp/custom-code.mdx +++ b/fern/products/sdks/generators/csharp/custom-code.mdx @@ -7,12 +7,12 @@ description: Add custom logic and methods to your .NET SDK with Fern. Extend Bas This page covers how to add custom logic and methods to your .NET SDK. - - ## Adding custom logic To get started adding custom code: + + ```csharp title="src/YourNamespace/Helper.cs" diff --git a/fern/products/sdks/generators/go/custom-code.mdx b/fern/products/sdks/generators/go/custom-code.mdx index 59795942e..2480611d8 100644 --- a/fern/products/sdks/generators/go/custom-code.mdx +++ b/fern/products/sdks/generators/go/custom-code.mdx @@ -7,12 +7,12 @@ description: Learn how to add custom logic and methods to your Go SDK with Fern. This page covers how to add custom logic and methods to your Go SDK. - - ## Adding custom logic To get started adding custom code: + + ### Create a new file and add your custom logic diff --git a/fern/products/sdks/generators/java/custom-code.mdx b/fern/products/sdks/generators/java/custom-code.mdx index 1a88c9577..01ded5d18 100644 --- a/fern/products/sdks/generators/java/custom-code.mdx +++ b/fern/products/sdks/generators/java/custom-code.mdx @@ -7,12 +7,12 @@ description: Augment your Java SDK with custom utilities This page covers how to add custom logic, methods, and dependencies to your TypeScript SDK. - - ## Adding custom logic To get started adding custom code: + + ### Create a new file and add your custom logic diff --git a/fern/products/sdks/generators/php/custom-code.mdx b/fern/products/sdks/generators/php/custom-code.mdx index 848f1fa8d..b69f9873e 100644 --- a/fern/products/sdks/generators/php/custom-code.mdx +++ b/fern/products/sdks/generators/php/custom-code.mdx @@ -7,12 +7,12 @@ description: Learn how to add custom logic, methods, and dependencies to your PH This page covers how to add custom logic, methods, and dependencies to your PHP SDK. - - ## Adding custom logic To get started adding custom code: + + ```php title="src/Helper.php" diff --git a/fern/products/sdks/generators/python/custom-code.mdx b/fern/products/sdks/generators/python/custom-code.mdx index 3b8fcd6e0..b1c8e8cc5 100644 --- a/fern/products/sdks/generators/python/custom-code.mdx +++ b/fern/products/sdks/generators/python/custom-code.mdx @@ -7,12 +7,12 @@ description: Learn how to add custom logic, methods, and dependencies to your Py This page covers how to add custom logic, methods, and dependencies to your Python SDK. - - ## Adding custom logic To get started adding custom code: + + ### Create a new file and add your custom logic diff --git a/fern/products/sdks/generators/ruby/custom-code.mdx b/fern/products/sdks/generators/ruby/custom-code.mdx index fdb588a8b..64126ed79 100644 --- a/fern/products/sdks/generators/ruby/custom-code.mdx +++ b/fern/products/sdks/generators/ruby/custom-code.mdx @@ -7,12 +7,12 @@ description: Augment your Ruby SDK with custom utilities This page covers how to add custom logic, methods, and dependencies to your Ruby SDK. - - ## Adding custom logic To get started adding custom code: + + ```ruby title="lib//helper.rb" diff --git a/fern/products/sdks/generators/typescript/custom-code.mdx b/fern/products/sdks/generators/typescript/custom-code.mdx index 8a8b090e8..b4fadb488 100644 --- a/fern/products/sdks/generators/typescript/custom-code.mdx +++ b/fern/products/sdks/generators/typescript/custom-code.mdx @@ -7,12 +7,12 @@ description: Learn how to add custom logic, methods, and dependencies to your Ty This page covers how to add custom logic, methods, and dependencies to your TypeScript SDK. - - ## Adding custom logic To get started adding custom code: + + ### Create a new file and add your custom logic diff --git a/fern/products/sdks/snippets/custom-code-callout.mdx b/fern/products/sdks/snippets/custom-code-callout.mdx index 35db71efe..f450b4bb0 100644 --- a/fern/products/sdks/snippets/custom-code-callout.mdx +++ b/fern/products/sdks/snippets/custom-code-callout.mdx @@ -1,2 +1,2 @@ -Before getting started, [read about how Fern SDKs use custom code and learn about the `.fernignore` file](/sdks/overview/custom-code). \ No newline at end of file +These steps cover adding a new custom file to the SDK. To preserve line-level edits to a generated file, use [Replay](/sdks/overview/custom-code#line-level-edits-with-replay) instead. \ No newline at end of file From 2e882dc19437dd2508ca88b4767feee2b561cd65 Mon Sep 17 00:00:00 2001 From: Devin Logan Date: Thu, 14 May 2026 17:42:27 -0400 Subject: [PATCH 6/6] custom anchors --- fern/products/cli-api-reference/pages/commands.mdx | 6 +++--- fern/products/sdks/custom-code.mdx | 4 ++-- fern/products/sdks/reference/generators-yml-reference.mdx | 4 ++-- fern/products/sdks/replay-migration.mdx | 2 +- fern/products/sdks/snippets/custom-code-callout.mdx | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fern/products/cli-api-reference/pages/commands.mdx b/fern/products/cli-api-reference/pages/commands.mdx index 07f3c65fa..821e0ada0 100644 --- a/fern/products/cli-api-reference/pages/commands.mdx +++ b/fern/products/cli-api-reference/pages/commands.mdx @@ -38,7 +38,7 @@ hideOnThisPage: true | [`fern generate`](#fern-generate) | Build & publish SDK updates | | [`fern write-overrides`](#fern-write-overrides) | Create OpenAPI customizations | | [`fern generator upgrade`](#fern-generator-upgrade) | Update SDK generators to latest versions | -| [`fern replay resolve`](#fern-replay-resolve) | Resolve patch conflicts left by [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay) | +| [`fern replay resolve`](#fern-replay-resolve) | Resolve patch conflicts left by [Fern Replay](/learn/sdks/overview/custom-code#replay) | | [`fern replay forget`](#fern-replay-forget) | Remove tracked customization patches from `.fern/replay.lock` | ## Detailed command documentation @@ -274,7 +274,7 @@ hideOnThisPage: true ### no-replay - Use `--no-replay` to skip [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay)'s patch detection and application phase for a single generation. The generation still produces the new SDK code, but tracked customization patches aren't applied on top. Useful for a clean regeneration — for example, to debug whether a conflict is caused by a specific patch. + Use `--no-replay` to skip [Fern Replay](/learn/sdks/overview/custom-code#replay)'s patch detection and application phase for a single generation. The generation still produces the new SDK code, but tracked customization patches aren't applied on top. Useful for a clean regeneration — for example, to debug whether a conflict is caused by a specific patch. ```bash fern generate --no-replay --local @@ -725,7 +725,7 @@ hideOnThisPage: true - Use `fern replay resolve` to work through patch conflicts that [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay) couldn't merge cleanly during `fern generate`. The command runs in two phases: the first run applies unresolved patches to your working tree with standard merge markers (`<<<<<<<` / `=======` / `>>>>>>>`) for you to edit; the second run verifies no markers remain and commits the resolved patches. + Use `fern replay resolve` to work through patch conflicts that [Fern Replay](/learn/sdks/overview/custom-code#replay) couldn't merge cleanly during `fern generate`. The command runs in two phases: the first run applies unresolved patches to your working tree with standard merge markers (`<<<<<<<` / `=======` / `>>>>>>>`) for you to edit; the second run verifies no markers remain and commits the resolved patches. ```bash diff --git a/fern/products/sdks/custom-code.mdx b/fern/products/sdks/custom-code.mdx index 8b18325fe..dfdcdfa97 100644 --- a/fern/products/sdks/custom-code.mdx +++ b/fern/products/sdks/custom-code.mdx @@ -12,7 +12,7 @@ Fern provides two complementary tools for keeping your customizations safe acros - **`.fernignore`** — Full-file ownership. The generator stops touching listed files entirely. Use for fully hand-written modules, READMEs, or custom workflows. Trade-off: you also stop receiving generator updates to those files. - **Replay** — Line-level edits. Keep generated files under generator control, and reapply your edits via 3-way merge on every regeneration. -## Full-file ownership with `.fernignore` +## Full-file ownership with `.fernignore` [#fernignore] `.fernignore` applies only to SDK generation. It has no effect on [Fern Docs](/learn/docs/getting-started/overview) builds. @@ -65,7 +65,7 @@ You'll have a separate `.fernignore` file for each of your SDKs: -## Line-level edits with Replay +## Line-level edits with Replay [#replay] Replay is on by default for SDKs using [`pull-request` output mode](/learn/sdks/reference/generators-yml#pull-request). To enable Replay on an SDK that's currently on `release` or `push` mode, switch to `pull-request` mode via the [migration guide](/learn/sdks/overview/replay-migration). diff --git a/fern/products/sdks/reference/generators-yml-reference.mdx b/fern/products/sdks/reference/generators-yml-reference.mdx index c40ba300f..a2d09f116 100644 --- a/fern/products/sdks/reference/generators-yml-reference.mdx +++ b/fern/products/sdks/reference/generators-yml-reference.mdx @@ -408,7 +408,7 @@ autorelease: true ## `replay` -Override [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay) behavior globally. Replay runs based on your organization's configuration; use this setting to disable it across all generators in this `generators.yml`. Alternatively, you can [configure Replay for individual SDKs](#replay-1). +Override [Fern Replay](/learn/sdks/overview/custom-code#replay) behavior globally. Replay runs based on your organization's configuration; use this setting to disable it across all generators in this `generators.yml`. Alternatively, you can [configure Replay for individual SDKs](#replay-1). ```yaml title="generators.yml" replay: @@ -1034,7 +1034,7 @@ groups: #### `replay` -Override [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay) for an individual SDK. Alternatively, you can [configure Replay globally for all SDKs](#replay). +Override [Fern Replay](/learn/sdks/overview/custom-code#replay) for an individual SDK. Alternatively, you can [configure Replay globally for all SDKs](#replay). ```yml title="generators.yml" {6-7} groups: diff --git a/fern/products/sdks/replay-migration.mdx b/fern/products/sdks/replay-migration.mdx index 9fd01a222..6687b0280 100644 --- a/fern/products/sdks/replay-migration.mdx +++ b/fern/products/sdks/replay-migration.mdx @@ -8,7 +8,7 @@ description: Step-by-step guide for switching SDK generation from release or pus Replay requires the latest Fern CLI and SDK generator versions. Run `fern upgrade` to make sure you're current. -This guide migrates an SDK from `release` or `push` output mode to `pull-request` output mode — the mode required to enable [Fern Replay](/learn/sdks/overview/custom-code#line-level-edits-with-replay). Only follow it if your SDK is currently on `release` or `push` mode and you want to switch in order to use Replay. SDKs already using `pull-request` mode have Replay on by default; no migration is needed. +This guide migrates an SDK from `release` or `push` output mode to `pull-request` output mode — the mode required to enable [Fern Replay](/learn/sdks/overview/custom-code#replay). Only follow it if your SDK is currently on `release` or `push` mode and you want to switch in order to use Replay. SDKs already using `pull-request` mode have Replay on by default; no migration is needed. ## What changes diff --git a/fern/products/sdks/snippets/custom-code-callout.mdx b/fern/products/sdks/snippets/custom-code-callout.mdx index f450b4bb0..7cf2ce34a 100644 --- a/fern/products/sdks/snippets/custom-code-callout.mdx +++ b/fern/products/sdks/snippets/custom-code-callout.mdx @@ -1,2 +1,2 @@ -These steps cover adding a new custom file to the SDK. To preserve line-level edits to a generated file, use [Replay](/sdks/overview/custom-code#line-level-edits-with-replay) instead. \ No newline at end of file +These steps cover adding a new custom file to the SDK. To preserve line-level edits to a generated file, use [Replay](/sdks/overview/custom-code#replay) instead. \ No newline at end of file