Skip to content

Create Release from main #5

Create Release from main

Create Release from main #5

name: Create Release
run-name: "Create Release from ${{ github.ref_name }}"
on:
workflow_run:
workflows: ["Run Tests"]
types: [completed]
branches: [main]
workflow_dispatch:
inputs:
pr_number:
description: "PR number to pull release notes from (e.g. 123)"
required: true
type: string
permissions:
contents: write
issues: write
pull-requests: write
jobs:
create-release:
name: Create release
runs-on: ubuntu-latest
timeout-minutes: 10
if: |
github.event.workflow_run.conclusion == 'success' ||
github.event_name == 'workflow_dispatch'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Find merged PR that triggered this push (or use dispatch input)
id: find-pr
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
PR_NUMBER="${{ inputs.pr_number }}"
echo "Manual dispatch — fetching PR #${PR_NUMBER}"
else
HEAD_SHA="${{ github.event.workflow_run.head_sha }}"
echo "Looking up PR for commit ${HEAD_SHA}"
PR_NUMBER=$(gh pr list \
--search "${HEAD_SHA}" \
--state merged \
--json number \
--limit 1 \
--jq '.[0].number')
fi
if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" = "null" ]; then
echo "::notice::No PR found. Not a release-eligible push."
echo "IS_RELEASE=false" >> "$GITHUB_OUTPUT"
exit 0
fi
PR_JSON=$(gh pr view "$PR_NUMBER" --json number,body,labels,title,state --jq '.')
PR_TITLE=$(echo "$PR_JSON" | jq -r '.title')
HAS_RC_LABEL=$(echo "$PR_JSON" | jq -r '[.labels[].name] | index("release candidate") != null')
PR_STATE=$(echo "$PR_JSON" | jq -r '.state')
echo "Found PR #${PR_NUMBER}: ${PR_TITLE} (state: ${PR_STATE})"
echo "PR_NUMBER=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
if [ "$HAS_RC_LABEL" != "true" ]; then
echo "::notice::PR #${PR_NUMBER} does not have 'release candidate' label. Skipping release."
echo "IS_RELEASE=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Write PR body to temp file for safe handling later
echo "$PR_JSON" | jq -r '.body' > /tmp/pr-body.md
echo "IS_RELEASE=true" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}
- name: Validate release notes (PR body is non-empty)
id: validate
if: steps.find-pr.outputs.IS_RELEASE == 'true'
run: |
if [ ! -s /tmp/pr-body.md ]; then
echo "::error::PR body is empty. Release notes are required in the PR description."
exit 1
fi
echo "Release notes found ($(wc -c < /tmp/pr-body.md) bytes)."
- name: Read version from package.json
id: version
if: steps.find-pr.outputs.IS_RELEASE == 'true'
run: |
VERSION=$(jq -r '.version' package.json)
if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then
echo "::error::Could not read version from package.json"
exit 1
fi
echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT"
TAG="v${VERSION}"
echo "TAG=${TAG}" >> "$GITHUB_OUTPUT"
echo "Version: ${VERSION} → Tag: ${TAG}"
- name: Check tag doesn't already exist
id: check-tag
if: steps.find-pr.outputs.IS_RELEASE == 'true'
run: |
TAG="${{ steps.version.outputs.TAG }}"
if git rev-parse "${TAG}" >/dev/null 2>&1; then
echo "::error::Tag ${TAG} already exists. Version must be bumped."
exit 1
fi
echo "Tag ${TAG} is available."
- name: Validate version bump against latest tag
if: steps.find-pr.outputs.IS_RELEASE == 'true'
run: |
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
LATEST_VERSION=$(echo "$LATEST_TAG" | sed 's/^v//')
NEW_VERSION="${{ steps.version.outputs.VERSION }}"
# Simple semver comparison (handles X.Y.Z format)
compare_versions() {
echo "$1" | awk -F. '{ printf "%d%03d%03d", $1, $2, $3 }'
}
OLD_CMP=$(compare_versions "$LATEST_VERSION")
NEW_CMP=$(compare_versions "$NEW_VERSION")
if [ "$OLD_CMP" -ge "$NEW_CMP" ]; then
echo "::error::Version $NEW_VERSION is not greater than latest release $LATEST_VERSION. Version must be bumped."
exit 1
fi
echo "Version bump validated: ${LATEST_VERSION} → ${NEW_VERSION}"
- name: Create git tag
if: steps.find-pr.outputs.IS_RELEASE == 'true'
run: |
TAG="${{ steps.version.outputs.TAG }}"
git tag "$TAG"
git push origin "$TAG"
echo "Pushed tag ${TAG}"
- name: Create GitHub Release
if: steps.find-pr.outputs.IS_RELEASE == 'true'
run: |
TAG="${{ steps.version.outputs.TAG }}"
VERSION="${{ steps.version.outputs.VERSION }}"
TITLE="LootLocker_UnitySDKv${VERSION}"
gh release create "$TAG" \
--title "$TITLE" \
--notes-file /tmp/pr-body.md \
--target main
echo "Created release ${TAG}"
env:
GH_TOKEN: ${{ github.token }}
- name: Post release notification on PR
if: steps.find-pr.outputs.IS_RELEASE == 'true'
run: |
TAG="${{ steps.version.outputs.TAG }}"
PR_NUMBER="${{ steps.find-pr.outputs.PR_NUMBER }}"
REPO="${{ github.repository }}"
printf -v BODY '✅ **Release [%s](https://github.com/%s/releases/tag/%s) created.**\n\nWorkflows now running:\n- [Package SDK](https://github.com/%s/actions/workflows/package-sdk.yml) — builds and attaches .unitypackage\n- [Sign Package](https://github.com/%s/actions/workflows/sign-package.yml) — signs and attaches UPM package\n- [Generate Docs](https://github.com/%s/actions/workflows/generate-docs.yml) — deploys reference docs' \
"$TAG" "$REPO" "$TAG" "$REPO" "$REPO" "$REPO"
gh pr comment "$PR_NUMBER" --body "$BODY"
env:
GH_TOKEN: ${{ github.token }}