Skip to content

Commit e46063b

Browse files
Barryclaude
authored andcommitted
refactor(upgrade): rewrite as composite action
Replace the Node.js action (TypeScript + tsup bundle) with a composite action to align with the repo's architectural pattern where actions are thin shell wrappers around CLI commands. - Rewrite action.yml from `using: node20` to `using: composite` - Add scripts/snapshot.js for version capture (plain JS, no npm deps) - Add scripts/diff.js for diff computation + PR content generation - Reuse resolve-cli composite action for CLI version resolution - Use gh CLI for PR management instead of @actions/github - Delete all TypeScript source, tests, dist bundle, and build config - Remove upgrade from pnpm workspace (no package.json needed) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 86bbed4 commit e46063b

15 files changed

Lines changed: 620 additions & 28249 deletions

actions/upgrade/action.yml

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,133 @@ inputs:
1818
outputs:
1919
run-id:
2020
description: "UUIDv4 for this upgrade run"
21+
value: ${{ steps.run-id.outputs.run-id }}
2122
pr-url:
2223
description: "URL of the created or updated upgrade PR (empty if no changes)"
24+
value: ${{ steps.manage-pr.outputs.pr-url }}
2325
cli-upgraded:
2426
description: "Whether the Fern CLI version was upgraded (true/false)"
27+
value: ${{ steps.diff.outputs.cli-upgraded }}
2528
generators-upgraded:
2629
description: "JSON array of {generator, from, to} for each upgraded generator"
30+
value: ${{ steps.diff.outputs.generators-upgraded }}
2731

2832
runs:
29-
using: "node20"
30-
main: "dist/index.js"
33+
using: composite
34+
steps:
35+
- name: Resolve Fern CLI
36+
id: cli
37+
uses: fern-api/actions/resolve-cli@main
38+
with:
39+
version: ${{ inputs.version }}
40+
41+
- name: Set run ID
42+
id: run-id
43+
shell: bash
44+
run: |
45+
if [ -z "${FERN_RUN_ID}" ]; then
46+
FERN_RUN_ID=$(node -e "console.log(require('crypto').randomUUID())")
47+
echo "FERN_RUN_ID=${FERN_RUN_ID}" >> "$GITHUB_ENV"
48+
echo "Generated new FERN_RUN_ID: ${FERN_RUN_ID}"
49+
else
50+
echo "Inheriting existing FERN_RUN_ID: ${FERN_RUN_ID}"
51+
fi
52+
echo "run-id=${FERN_RUN_ID}" >> "$GITHUB_OUTPUT"
53+
54+
- name: Capture before versions
55+
id: before
56+
shell: bash
57+
run: |
58+
SNAPSHOT=$(node "${{ github.action_path }}/scripts/snapshot.js")
59+
echo "snapshot=$SNAPSHOT" >> "$GITHUB_OUTPUT"
60+
echo "Captured before snapshot: $SNAPSHOT"
61+
62+
- name: Run fern upgrade
63+
shell: bash
64+
env:
65+
FERN_TOKEN: ${{ inputs.fern-token }}
66+
run: |
67+
echo "Running fern upgrade..."
68+
${{ steps.cli.outputs.fern-cmd }} upgrade --yes || \
69+
echo "::warning::fern upgrade exited with non-zero code"
70+
71+
- name: Run fern generator upgrade
72+
shell: bash
73+
env:
74+
FERN_TOKEN: ${{ inputs.fern-token }}
75+
run: |
76+
echo "Running fern generator upgrade..."
77+
${{ steps.cli.outputs.fern-cmd }} generator upgrade --yes || \
78+
echo "::warning::fern generator upgrade exited with non-zero code"
79+
# TODO: FER-9669 — Check automation config before running upgrades
80+
81+
- name: Compute upgrade diff
82+
id: diff
83+
shell: bash
84+
env:
85+
BEFORE_SNAPSHOT: ${{ steps.before.outputs.snapshot }}
86+
run: node "${{ github.action_path }}/scripts/diff.js"
87+
88+
- name: Push changes and manage PR
89+
id: manage-pr
90+
if: steps.diff.outputs.has-changes == 'true'
91+
shell: bash
92+
env:
93+
GH_TOKEN: ${{ inputs.github-token }}
94+
GITHUB_TOKEN: ${{ inputs.github-token }}
95+
PR_TITLE: ${{ steps.diff.outputs.pr-title }}
96+
COMMIT_MSG: ${{ steps.diff.outputs.commit-msg }}
97+
run: |
98+
BRANCH="fern/upgrade"
99+
100+
# Configure git
101+
git config user.name "github-actions[bot]"
102+
git config user.email "github-actions[bot]@users.noreply.github.com"
103+
104+
# Get default branch
105+
DEFAULT_BRANCH=$(gh api "repos/${{ github.repository }}" --jq '.default_branch')
106+
echo "Default branch: $DEFAULT_BRANCH"
107+
108+
# Warn if overwriting existing branch
109+
if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then
110+
echo "::warning::Existing $BRANCH branch will be overwritten (clean-slate strategy)"
111+
fi
112+
113+
# Clean-slate branch from default branch HEAD
114+
git fetch origin "$DEFAULT_BRANCH"
115+
git checkout -B "$BRANCH" "origin/$DEFAULT_BRANCH"
116+
117+
# Stage all changes
118+
git add -A
119+
if git diff --cached --quiet; then
120+
echo "No changes to commit"
121+
echo "pr-url=" >> "$GITHUB_OUTPUT"
122+
exit 0
123+
fi
124+
125+
# Commit and force push
126+
git commit -m "$COMMIT_MSG"
127+
git push --force origin "$BRANCH"
128+
129+
# Write PR body to temp file to avoid shell escaping issues
130+
cat > /tmp/pr-body.md << 'PREOF'
131+
${{ steps.diff.outputs.pr-body }}
132+
PREOF
133+
134+
# Create or update PR
135+
EXISTING_PR=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number // empty')
136+
if [ -n "$EXISTING_PR" ]; then
137+
gh pr edit "$EXISTING_PR" --title "$PR_TITLE" --body-file /tmp/pr-body.md
138+
PR_URL=$(gh pr view "$EXISTING_PR" --json url --jq '.url')
139+
echo "Updated existing PR: $PR_URL"
140+
else
141+
PR_URL=$(gh pr create --head "$BRANCH" --base "$DEFAULT_BRANCH" \
142+
--title "$PR_TITLE" --body-file /tmp/pr-body.md)
143+
echo "Created new PR: $PR_URL"
144+
fi
145+
146+
echo "pr-url=$PR_URL" >> "$GITHUB_OUTPUT"
147+
# TODO: FER-9668 — Emit upgrade telemetry
31148
32149
branding:
33150
icon: "refresh-cw"

0 commit comments

Comments
 (0)