Migrating to Replay
This guide migrates an SDK from release or push output mode to pull-request output mode — the mode required to enable Fern 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
- 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
- Install the Fern GitHub App on your SDK repositories.
- Update to the latest Fern CLI:
fern upgrade. - Update to the latest SDK generator versions:
fern generator upgrade.
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 pull-request output 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:
Identify the publishing secrets
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 credentials to the SDK repo
Add the real publish credentials directly to the SDK repo: Settings → Secrets and variables → Actions → New repository secret.
Phase 2: Switch output mode
In generators.yml:
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)
To control publishing on your own schedule rather than on every Fern generation, keep your publish workflow out of Fern’s generation cycle:
Phase 4: First generation with Replay
Run fern generate
Run fern generate --group <group-name> from the config repo (or trigger via your existing CI).
Confirm the lockfile was created
The first run auto-creates .fern/replay.lock. Replay tracks customer commits to generated files from this point on.
Phase 5: Validate
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.
- SDK code is correct and passes CI.
.fern/replay.lockexists in the SDK repo..fernignorecontainsreplay.lock(andreplay.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 generatedetects the patch (PR body showspatches detected: 1). - The customization survives regeneration via a clean 3-way merge.
.fern/replay.lockshows the patch stored.
Rollback
Replay never modifies files destructively. Your main branch always has correct code, so rolling back is straightforward.
Quick disable. To stop Replay while staying in pull-request mode, set replay.enabled: false in your generators.yml (see the replay reference). The next fern generate skips the patch detection and application phase; PRs land in the same shape as before, just without the [fern-replay] commit.
Full revert to release or push mode:
Troubleshooting
Known caveats
- Closed-without-merge replay PRs. If a replay PR is closed without merging, the next generation re-derives its anchor from the current branch history. No manual cleanup needed.
- Force pushes and rewritten history. Replay re-derives its scan anchor from
git logon every run, so force-pushed branches continue to work and existing patches still apply correctly.