Skip to content

Commit 2fead8d

Browse files
authored
fix(sdk-release-finalize): preserve Fern generation commits (#40)
1 parent 6054a2d commit 2fead8d

2 files changed

Lines changed: 82 additions & 2 deletions

File tree

.github/workflows/sdk-release-finalize.yml

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
name: sdk-release-finalize
22

33
# Reusable workflow: create release-discovery tags after registry publishing
4-
# succeeds. Call this from SDK repos after npm/PyPI/Maven publish completes,
4+
# succeeds and preserve Fern's current replay generation commit for the next
5+
# autorelease. Call this from SDK repos after npm/PyPI/Maven publish completes,
56
# passing the version_tag, origin_commit, and origin_tag outputs from
67
# sdk-release-gate. Keeping these side effects post-publish means failed
78
# registry publishes can be retried without a pre-created tag blocking them.
@@ -24,6 +25,10 @@ on:
2425
description: "Git revision to tag. Defaults to the caller workflow commit."
2526
type: string
2627
default: "HEAD"
28+
replay-lock-path:
29+
description: "Path to Fern's replay.lock within the caller repo."
30+
type: string
31+
default: ".fern/replay.lock"
2732
create-github-release:
2833
description: "Whether to create a GitHub release for the version tag."
2934
type: boolean
@@ -41,6 +46,12 @@ on:
4146
origin_tag_created:
4247
description: "true when a new origin release tag was pushed; false when it already existed."
4348
value: ${{ jobs.finalize.outputs.origin_tag_created }}
49+
fern_generation_tag:
50+
description: "The Fern replay generation tag created or verified by this workflow, or empty when replay.lock is absent."
51+
value: ${{ jobs.finalize.outputs.fern_generation_tag }}
52+
fern_generation_tag_created:
53+
description: "true when a new Fern replay generation tag was pushed; false when it already existed or replay.lock is absent."
54+
value: ${{ jobs.finalize.outputs.fern_generation_tag_created }}
4455

4556
permissions:
4657
contents: write
@@ -53,11 +64,14 @@ jobs:
5364
version_tag_created: ${{ steps.version-tag-output.outputs.created }}
5465
origin_tag: ${{ steps.inputs.outputs.origin_tag }}
5566
origin_tag_created: ${{ steps.origin-tag.outputs.created }}
67+
fern_generation_tag: ${{ steps.fern-generation-tag.outputs.tag }}
68+
fern_generation_tag_created: ${{ steps.fern-generation-tag.outputs.created }}
5669
env:
5770
VERSION_TAG: ${{ inputs.version-tag }}
5871
ORIGIN_COMMIT: ${{ inputs.origin-commit }}
5972
ORIGIN_TAG: ${{ inputs.origin-tag }}
6073
TAG_TARGET: ${{ inputs.tag-target }}
74+
REPLAY_LOCK_PATH: ${{ inputs.replay-lock-path }}
6175
steps:
6276
- name: Checkout release commit
6377
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
@@ -198,3 +212,67 @@ jobs:
198212
git push origin "refs/tags/$ORIGIN_TAG"
199213
200214
echo "created=true" >> "$GITHUB_OUTPUT"
215+
216+
- name: Preserve Fern replay generation
217+
id: fern-generation-tag
218+
shell: bash
219+
run: |
220+
set -euo pipefail
221+
222+
release_commit=$(git rev-parse --verify "${TAG_TARGET}^{commit}")
223+
if ! replay_lock=$(git show "${release_commit}:${REPLAY_LOCK_PATH}" 2>/dev/null); then
224+
echo "::notice::$REPLAY_LOCK_PATH does not exist at $release_commit; skipping Fern replay generation tag."
225+
{
226+
echo "tag="
227+
echo "created=false"
228+
} >> "$GITHUB_OUTPUT"
229+
exit 0
230+
fi
231+
232+
generation_commit=$(printf '%s\n' "$replay_lock" | sed -n 's/^current_generation:[[:space:]]*//p' | head -n1)
233+
if [[ -z "$generation_commit" ]]; then
234+
echo "::error::$REPLAY_LOCK_PATH at $release_commit has no current_generation field."
235+
exit 1
236+
fi
237+
if ! printf '%s' "$generation_commit" | grep -Eq '^[0-9a-f]{40}$'; then
238+
echo "::error::Invalid Fern current_generation SHA: $generation_commit"
239+
exit 1
240+
fi
241+
242+
generation_tag="fern-generation/$generation_commit"
243+
if ! git check-ref-format "refs/tags/$generation_tag"; then
244+
echo "::error::Invalid Fern generation tag name: $generation_tag"
245+
exit 1
246+
fi
247+
248+
if ! target_commit=$(git rev-parse --verify "${generation_commit}^{commit}" 2>/dev/null); then
249+
if ! git fetch --no-tags --depth=1 origin "$generation_commit"; then
250+
echo "::error::Fern current_generation commit $generation_commit is not present in the checkout and could not be fetched from origin. Ensure it is reachable from a remote ref before finalizing."
251+
exit 1
252+
fi
253+
target_commit=$(git rev-parse --verify "${generation_commit}^{commit}")
254+
fi
255+
256+
existing_commit=$(git rev-parse -q --verify "refs/tags/${generation_tag}^{commit}" || true)
257+
258+
if [[ -n "$existing_commit" ]]; then
259+
if [[ "$existing_commit" == "$target_commit" ]]; then
260+
echo "Tag $generation_tag already points at $target_commit."
261+
{
262+
echo "tag=$generation_tag"
263+
echo "created=false"
264+
} >> "$GITHUB_OUTPUT"
265+
exit 0
266+
fi
267+
268+
echo "::error::Tag $generation_tag already points at $existing_commit, not $target_commit."
269+
exit 1
270+
fi
271+
272+
git tag "$generation_tag" "$target_commit"
273+
git push origin "refs/tags/$generation_tag"
274+
275+
{
276+
echo "tag=$generation_tag"
277+
echo "created=true"
278+
} >> "$GITHUB_OUTPUT"

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ Shared GitHub Actions and reusable workflows for the Phenoml SDK repos.
2727
jobs stay in each SDK repo.
2828
- [`sdk-release-finalize`](.github/workflows/sdk-release-finalize.yml)
2929
creates release-discovery tags after an SDK package publish succeeds: the
30-
normal SDK version tag/release and a Fern origin release tag.
30+
normal SDK version tag/release, a Fern origin release tag, and, when Fern
31+
Replay is configured, a stable tag preserving Fern's replay generation commit
32+
for the next autorelease.
3133
- [`sync-fern-artifacts`](.github/workflows/sync-fern-artifacts.yml) — runs
3234
bundle → extract → commit in a single ordered run, so the bundled spec and
3335
its derived `code-examples.json` are always generated together and land in

0 commit comments

Comments
 (0)