Skip to content

release: lace-extension@2.0.4 #6

release: lace-extension@2.0.4

release: lace-extension@2.0.4 #6

name: Publish release on merge
# Companion to the lace-platform publish workflow.
#
# When a release/<tag> PR merges (into the default branch), flip the corresponding draft
# GH Release from draft → published, retargeting the tag at the merge
# commit. When one closes without merging, delete the orphan draft.
#
# Uses the built-in GITHUB_TOKEN — no extra secrets required.
on:
pull_request:
types: [closed]
permissions:
contents: write
pull-requests: read
jobs:
# Both jobs guard on:
# - base.ref == default branch — release/<tag> PRs always target it,
# so a non-default-branch PR with a release/* head is unrelated.
# - head.repo.fork == false — a fork PR named release/<existing-tag>
# could otherwise publish or delete the corresponding draft release.
# The publish workflow only ever pushes release/* to the public repo
# itself (never from a fork), so this is safe to require.
flip:
if: >
github.event.pull_request.merged == true &&
github.event.pull_request.head.repo.fork == false &&
github.event.pull_request.base.ref == github.event.repository.default_branch &&
startsWith(github.event.pull_request.head.ref, 'release/')
runs-on: ubuntu-latest
steps:
- name: Derive tag from branch name
id: tag
env:
HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: echo "value=${HEAD_REF#release/}" >> "$GITHUB_OUTPUT"
- name: Publish the draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.value }}
MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
# Look up the draft via the LIST endpoint (which returns
# drafts) rather than `gh release view <tag>` (which goes
# through /releases/tags/<tag> — that endpoint only returns
# published releases since drafts don't have a real git tag).
release_id=$(gh api --paginate "/repos/$GITHUB_REPOSITORY/releases" \
--jq "[.[] | select(.tag_name == \"$TAG\" and .draft == true)][0].id")
if [[ -z "$release_id" || "$release_id" == "null" ]]; then
echo "::error::no draft release found for tag '$TAG' — merged PR #$PR_NUMBER. Existing releases:"
gh api "/repos/$GITHUB_REPOSITORY/releases" \
--jq '.[] | " tag=\(.tag_name) draft=\(.draft) published_at=\(.published_at // "(draft)")"'
exit 1
fi
# Retarget the draft at the merge commit and publish. The tag
# is created against MERGE_SHA at publish time, so squash /
# rebase / merge-commit strategies all behave correctly.
gh api -X PATCH "/repos/$GITHUB_REPOSITORY/releases/$release_id" \
-F draft=false -f target_commitish="$MERGE_SHA" > /dev/null
echo "Published $TAG (release id $release_id) targeting merge commit $MERGE_SHA"
cleanup-orphan-draft:
if: >
github.event.pull_request.merged == false &&
github.event.pull_request.head.repo.fork == false &&
github.event.pull_request.base.ref == github.event.repository.default_branch &&
startsWith(github.event.pull_request.head.ref, 'release/')
runs-on: ubuntu-latest
steps:
- name: Delete orphan draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
set -euo pipefail
tag="${HEAD_REF#release/}"
# Same lookup mode as the flip job — list endpoint, not
# /releases/tags/ (which doesn't return drafts).
release_id=$(gh api --paginate "/repos/$GITHUB_REPOSITORY/releases" \
--jq "[.[] | select(.tag_name == \"$tag\" and .draft == true)][0].id")
if [[ -n "$release_id" && "$release_id" != "null" ]]; then
gh api -X DELETE "/repos/$GITHUB_REPOSITORY/releases/$release_id"
echo "Deleted orphan draft $tag (release id $release_id)"
else
echo "No orphan draft for $tag; nothing to clean up."
fi