Publish release workflow (radix-ui/primitives)
The Publish release workflow from radix-ui/primitives, explained and optimized by Latchkey.
A
CI health: A - excellent
Point runs-on at Latchkey and get job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Publish release workflow from the radix-ui/primitives repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
workflow (.yml)
name: Publish release
on:
push:
branches:
- main
- stable
workflow_dispatch:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
detect-snapshot-changes:
if: github.repository == 'radix-ui/primitives' && github.ref == 'refs/heads/main'
name: Detect snapshot changes
runs-on: ubuntu-latest
outputs:
should_publish: ${{ steps.detect.outputs.should_publish }}
reason: ${{ steps.detect.outputs.reason }}
last_snapshot_version: ${{ steps.detect.outputs.last_snapshot_version }}
last_snapshot_published_at: ${{ steps.detect.outputs.last_snapshot_published_at }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Detect snapshot changes
id: detect
uses: ./.github/actions/detect-snapshot-changes
publish-snapshot:
if: github.repository == 'radix-ui/primitives' && github.ref == 'refs/heads/main' && needs.detect-snapshot-changes.outputs.should_publish == 'true'
name: Publish snapshot release
needs: detect-snapshot-changes
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
pull-requests: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Publish snapshot
uses: ./.github/actions/publish-snapshot
publish-stable:
if: github.repository == 'radix-ui/primitives' && github.ref == 'refs/heads/stable'
name: Publish stable release
timeout-minutes: 15
runs-on: ubuntu-latest
outputs:
published: ${{ steps.publish-stable.outputs.published }}
permissions:
contents: write
pull-requests: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Publish stable
id: publish-stable
uses: ./.github/actions/publish-stable
open-stable-backport-pr:
if: github.repository == 'radix-ui/primitives' && github.ref == 'refs/heads/stable' && needs.publish-stable.result == 'success' && needs.publish-stable.outputs.published == 'true'
name: Open stable backport PR
needs: publish-stable
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Open PR from stable to main
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git fetch origin main stable
AHEAD_COUNT=$(git rev-list --right-only --count origin/main...origin/stable)
if [ "$AHEAD_COUNT" -eq 0 ]; then
echo "stable has no changes to merge back into main"
exit 0
fi
# Use `// empty` so jq emits nothing (instead of the string "null")
# when there is no open backport PR. Otherwise `[ -n "$PR_NUMBER" ]`
# would treat "null" as a truthy match and skip creation.
PR_NUMBER=$(gh pr list --base main --head stable --state open --json number --jq '.[0].number // empty')
if [ -n "$PR_NUMBER" ]; then
echo "PR #$PR_NUMBER from stable to main already exists"
else
PR_TITLE=$(git log -1 --pretty=%s origin/stable)
PR_BODY="$(cat <<'EOF'
## Summary
- Sync the latest published changes from `stable` back into `main`.
- Opened automatically after a successful stable publish.
EOF
)"
# Capture `gh pr create`'s output and exit code separately from the
# sed extraction so a failure in `gh pr create` fails the step
# instead of being masked by sed's success on empty input.
PR_URL=$(gh pr create \
--base main \
--head stable \
--title "$PR_TITLE" \
--body "$PR_BODY")
PR_NUMBER=$(echo "$PR_URL" | sed -E 's|.*/([0-9]+)$|\1|')
fi
# Skip enablement if auto-merge is already on (re-runs on a PR that was
# found via the existing-PR branch above can land here a second time).
AUTO_MERGE_STATE=$(gh pr view "$PR_NUMBER" --json autoMergeRequest --jq '.autoMergeRequest')
if [ "$AUTO_MERGE_STATE" != "null" ] && [ -n "$AUTO_MERGE_STATE" ]; then
echo "Auto-merge is already enabled on PR #$PR_NUMBER"
exit 0
fi
# Enable auto-merge so the PR merges itself once required checks pass.
# If auto-merge is disabled at the repository level, log it and continue
# (a human will need to merge the backport PR manually). Any other
# failure - auth, permissions, merge conflicts, etc. - is real and
# should fail the job rather than silently leaving the backport unmerged.
if ! AUTO_MERGE_OUTPUT=$(gh pr merge --auto --squash "$PR_NUMBER" 2>&1); then
if echo "$AUTO_MERGE_OUTPUT" | grep -qiE 'auto[- ]merge is not allowed|auto[- ]merge is not enabled'; then
echo "Auto-merge is not enabled on this repository; backport PR #$PR_NUMBER must be merged manually."
echo "$AUTO_MERGE_OUTPUT"
else
echo "Failed to enable auto-merge on PR #$PR_NUMBER:" >&2
echo "$AUTO_MERGE_OUTPUT" >&2
exit 1
fi
fi
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: Publish release on: push: branches: - main - stable workflow_dispatch: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: detect-snapshot-changes: timeout-minutes: 30 if: github.repository == 'radix-ui/primitives' && github.ref == 'refs/heads/main' name: Detect snapshot changes runs-on: latchkey-small outputs: should_publish: ${{ steps.detect.outputs.should_publish }} reason: ${{ steps.detect.outputs.reason }} last_snapshot_version: ${{ steps.detect.outputs.last_snapshot_version }} last_snapshot_published_at: ${{ steps.detect.outputs.last_snapshot_published_at }} steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - name: Detect snapshot changes id: detect uses: ./.github/actions/detect-snapshot-changes publish-snapshot: if: github.repository == 'radix-ui/primitives' && github.ref == 'refs/heads/main' && needs.detect-snapshot-changes.outputs.should_publish == 'true' name: Publish snapshot release needs: detect-snapshot-changes runs-on: latchkey-small timeout-minutes: 15 permissions: contents: read pull-requests: read id-token: write steps: - name: Checkout uses: actions/checkout@v6 - name: Publish snapshot uses: ./.github/actions/publish-snapshot publish-stable: if: github.repository == 'radix-ui/primitives' && github.ref == 'refs/heads/stable' name: Publish stable release timeout-minutes: 15 runs-on: latchkey-small outputs: published: ${{ steps.publish-stable.outputs.published }} permissions: contents: write pull-requests: write id-token: write steps: - name: Checkout uses: actions/checkout@v6 - name: Publish stable id: publish-stable uses: ./.github/actions/publish-stable open-stable-backport-pr: timeout-minutes: 30 if: github.repository == 'radix-ui/primitives' && github.ref == 'refs/heads/stable' && needs.publish-stable.result == 'success' && needs.publish-stable.outputs.published == 'true' name: Open stable backport PR needs: publish-stable runs-on: latchkey-small permissions: contents: read pull-requests: write steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - name: Open PR from stable to main env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git fetch origin main stable AHEAD_COUNT=$(git rev-list --right-only --count origin/main...origin/stable) if [ "$AHEAD_COUNT" -eq 0 ]; then echo "stable has no changes to merge back into main" exit 0 fi # Use `// empty` so jq emits nothing (instead of the string "null") # when there is no open backport PR. Otherwise `[ -n "$PR_NUMBER" ]` # would treat "null" as a truthy match and skip creation. PR_NUMBER=$(gh pr list --base main --head stable --state open --json number --jq '.[0].number // empty') if [ -n "$PR_NUMBER" ]; then echo "PR #$PR_NUMBER from stable to main already exists" else PR_TITLE=$(git log -1 --pretty=%s origin/stable) PR_BODY="$(cat <<'EOF' ## Summary - Sync the latest published changes from `stable` back into `main`. - Opened automatically after a successful stable publish. EOF )" # Capture `gh pr create`'s output and exit code separately from the # sed extraction so a failure in `gh pr create` fails the step # instead of being masked by sed's success on empty input. PR_URL=$(gh pr create \ --base main \ --head stable \ --title "$PR_TITLE" \ --body "$PR_BODY") PR_NUMBER=$(echo "$PR_URL" | sed -E 's|.*/([0-9]+)$|\1|') fi # Skip enablement if auto-merge is already on (re-runs on a PR that was # found via the existing-PR branch above can land here a second time). AUTO_MERGE_STATE=$(gh pr view "$PR_NUMBER" --json autoMergeRequest --jq '.autoMergeRequest') if [ "$AUTO_MERGE_STATE" != "null" ] && [ -n "$AUTO_MERGE_STATE" ]; then echo "Auto-merge is already enabled on PR #$PR_NUMBER" exit 0 fi # Enable auto-merge so the PR merges itself once required checks pass. # If auto-merge is disabled at the repository level, log it and continue # (a human will need to merge the backport PR manually). Any other # failure - auth, permissions, merge conflicts, etc. - is real and # should fail the job rather than silently leaving the backport unmerged. if ! AUTO_MERGE_OUTPUT=$(gh pr merge --auto --squash "$PR_NUMBER" 2>&1); then if echo "$AUTO_MERGE_OUTPUT" | grep -qiE 'auto[- ]merge is not allowed|auto[- ]merge is not enabled'; then echo "Auto-merge is not enabled on this repository; backport PR #$PR_NUMBER must be merged manually." echo "$AUTO_MERGE_OUTPUT" else echo "Failed to enable auto-merge on PR #$PR_NUMBER:" >&2 echo "$AUTO_MERGE_OUTPUT" >&2 exit 1 fi fi
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Add a job timeout so a hung step cannot burn hours of runner time.
This workflow runs 4 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.