Create Release from main #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} |