-
Notifications
You must be signed in to change notification settings - Fork 7
Add Fern Replay docs and migration guide #5497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ed7590a
8d35d23
7863582
aed299f
64c717c
ca8f399
2e882dc
0873572
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -90,3 +90,4 @@ exceptions: | |
| - OG | ||
| - BCP | ||
| - ISO | ||
| - Replay | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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#replay) | | ||
| | [`fern replay forget`](#fern-replay-forget) | Remove tracked customization patches from `.fern/replay.lock` | | ||
|
|
||
| ## Detailed command documentation | ||
|
|
||
|
|
@@ -173,7 +175,7 @@ hideOnThisPage: true | |
|
|
||
| <CodeBlock title="terminal"> | ||
| ```bash | ||
| fern generate [--group <group>] [--api <api>] [--version <version>] [--preview] [--fernignore <path>] [--local] [--force] | ||
| fern generate [--group <group>] [--api <api>] [--version <version>] [--preview] [--fernignore <path>] [--local] [--force] [--no-replay] | ||
| ``` | ||
| </CodeBlock> | ||
|
|
||
|
|
@@ -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#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 | ||
| ``` | ||
|
|
||
| <Note> | ||
| `--no-replay` works only for local generation (`--local`). | ||
| </Note> | ||
|
|
||
| </Accordion> | ||
|
|
||
| <Accordion title="fern check"> | ||
|
|
@@ -708,6 +722,75 @@ hideOnThisPage: true | |
| Use `--include-major` to include major version upgrades. Major versions are skipped by default to prevent breaking changes. | ||
|
|
||
| </Accordion> | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| <Accordion title="fern replay resolve"> | ||
|
|
||
| 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. | ||
|
|
||
| <CodeBlock title="terminal"> | ||
| ```bash | ||
| fern replay resolve [directory] [--no-check-markers] | ||
| ``` | ||
| </CodeBlock> | ||
|
|
||
| ### 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. | ||
|
|
||
| </Accordion> | ||
|
|
||
| <Accordion title="fern replay forget"> | ||
|
|
||
| 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. | ||
|
|
||
| <CodeBlock title="terminal"> | ||
| ```bash | ||
| fern replay forget <patch-id-or-pattern>... [--all] [--dry-run] [--yes] | ||
| ``` | ||
| </CodeBlock> | ||
|
|
||
| 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 | ||
| ``` | ||
|
|
||
| </Accordion> | ||
|
|
||
| <Accordion title="fern api update"> | ||
| 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). | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,25 @@ | ||
| --- | ||
| 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. | ||
| --- | ||
|
|
||
|
|
||
| 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: | ||
|
|
||
| ## Using `.fernignore` to preserve your customizations | ||
| - **`.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. | ||
|
|
||
| Once you add files containing custom code, use `.fernignore` to protect your custom code from being overwritten when Fern regenerates your SDK. | ||
|
|
||
| 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. | ||
| ## Full-file ownership with `.fernignore` [#fernignore] | ||
|
|
||
| <Note>`.fernignore` applies only to SDK generation. It has no effect on [Fern Docs](/learn/docs/getting-started/overview) builds.</Note> | ||
|
|
||
| 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. | ||
|
|
||
| Your `.fernignore` file might look something like this: | ||
|
|
||
| ```gitignore title=".fernignore" | ||
|
|
@@ -31,7 +34,8 @@ LICENSE | |
| # Custom code | ||
| src/CustomClient.ts | ||
| ``` | ||
| <Note>For another example, see Cohere's [`.fernignore` file for their TypeScript SDK](https://github.com/cohere-ai/cohere-typescript/blob/ad583e3003bd51e80a82317f9e16beec85881b86/.fernignore).</Note> | ||
|
|
||
| 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: | ||
|
|
||
|
|
@@ -61,24 +65,37 @@ You'll have a separate `.fernignore` file for each of your SDKs: | |
| </Folder> | ||
| </Files> | ||
|
|
||
| ## Line-level edits with Replay [#replay] | ||
|
|
||
| <Note title="Requirements"> | ||
| 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). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| </Note> | ||
|
|
||
| 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: | ||
|
|
||
| <CardGroup cols={3}> | ||
| <Card title="TypeScript" icon={<img src="./images/icons/ts-light.svg" alt="TypeScript logo" className="h-6 w-6" noZoom />} href="/sdks/generators/typescript/custom-code"> | ||
| </Card> | ||
| <Card title="Python" icon={<img src="./images/icons/python-light.svg" className="h-6 w-6" noZoom />} href="/sdks/generators/python/custom-code"> | ||
| </Card> | ||
| <Card title="Go" icon={<img src="./images/icons/go-light.svg" className="h-6 w-6" noZoom />} href="/learn/sdks/generators/go/custom-code"> | ||
| </Card> | ||
| <Card title="Java" icon={<img src="./images/icons/java-light.svg" className="h-6 w-6" noZoom />} href="/learn/sdks/generators/java/custom-code"> | ||
| </Card> | ||
| <Card title=".NET" icon={<img src="./images/icons/csharp-light.svg" className="h-6 w-6" noZoom />} href="/sdks/generators/csharp/custom-code"> | ||
| </Card> | ||
| <Card title="PHP" icon={<img src="./images/icons/php-light.svg" className="h-6 w-6" noZoom />} href="/sdks/generators/php/custom-code"> | ||
| </Card> | ||
| <Card title="Ruby" icon={<img src="./images/icons/ruby-light.svg" className="h-6 w-6" noZoom />} href="/sdks/generators/ruby/custom-code"> | ||
| </Card> | ||
| </CardGroup> | ||
| <CardGroup cols={4}> | ||
| <Card title="TypeScript" icon={<img src="./images/icons/ts-light.svg" alt="TypeScript logo" className="h-6 w-6" noZoom />} href="/sdks/generators/typescript/custom-code" /> | ||
| <Card title="Python" icon={<img src="./images/icons/python-light.svg" className="h-6 w-6" noZoom />} href="/sdks/generators/python/custom-code" /> | ||
| <Card title="Go" icon={<img src="./images/icons/go-light.svg" className="h-6 w-6" noZoom />} href="/learn/sdks/generators/go/custom-code" /> | ||
| <Card title="Java" icon={<img src="./images/icons/java-light.svg" className="h-6 w-6" noZoom />} href="/learn/sdks/generators/java/custom-code" /> | ||
| <Card title=".NET" icon={<img src="./images/icons/csharp-light.svg" className="h-6 w-6" noZoom />} href="/sdks/generators/csharp/custom-code" /> | ||
| <Card title="PHP" icon={<img src="./images/icons/php-light.svg" className="h-6 w-6" noZoom />} href="/sdks/generators/php/custom-code" /> | ||
| <Card title="Ruby" icon={<img src="./images/icons/ruby-light.svg" className="h-6 w-6" noZoom />} href="/sdks/generators/ruby/custom-code" /> | ||
| </CardGroup> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[FernStyles.Current] Avoid time-relative terms like 'latest' that become outdated