diff --git a/.github/workflows/release-preview.yaml b/.github/workflows/release-preview.yaml new file mode 100644 index 0000000..583112e --- /dev/null +++ b/.github/workflows/release-preview.yaml @@ -0,0 +1,177 @@ +name: Release Preview + +on: + workflow_call: + +jobs: + preview: + name: Preview Release + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + + - name: Setup branch for semantic-release + run: | + # Explicitly checkout to the PR branch by name + git checkout -B ${{ github.event.pull_request.head.ref }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Setup preview config + run: | + # Update preview config with the PR branch name + sed -i.bak "s/BRANCH_PLACEHOLDER/${{ github.event.pull_request.head.ref }}/g" .preview-releaserc.json + rm .preview-releaserc.json.bak + + - name: Run semantic-release (dry-run) + id: semantic + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GIT_COMMITTER_NAME: "github-actions[bot]" + GIT_COMMITTER_EMAIL: "github-actions[bot]@users.noreply.github.com" + GIT_AUTHOR_NAME: "github-actions[bot]" + GIT_AUTHOR_EMAIL: "github-actions[bot]@users.noreply.github.com" + run: | + # Unset GitHub Actions environment variables that interfere with semantic-release + unset GITHUB_REF + unset GITHUB_REF_NAME + unset GITHUB_HEAD_REF + unset GITHUB_BASE_REF + + # Set them to what we want + export GITHUB_REF="refs/heads/${{ github.event.pull_request.head.ref }}" + export GITHUB_REF_NAME="${{ github.event.pull_request.head.ref }}" + + # Temporarily use preview config + mv .releaserc.json .releaserc.json.main + cp .preview-releaserc.json .releaserc.json + + # Run semantic-release with inline package installation (same as your local command) + OUTPUT=$(npx --package semantic-release --package @semantic-release/exec --package conventional-changelog-conventionalcommits semantic-release 2>&1 || true) + echo "$OUTPUT" + + # Restore original config + rm .releaserc.json + mv .releaserc.json.main .releaserc.json + + # Extract version information + NEW_VERSION=$(echo "$OUTPUT" | grep -Eo "The next release version is [0-9]+\.[0-9]+\.[0-9]+" | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+" || echo "") + RELEASE_TYPE=$(echo "$OUTPUT" | grep -Eo "Analysis of [0-9]+ commits complete: [a-z]+ release" | grep -Eo "(major|minor|patch) release" | sed 's/ release//' || echo "") + + # Extract release notes (everything after "Release note for version") + RELEASE_NOTES=$(echo "$OUTPUT" | sed -n '/Release note for version/,$p' | tail -n +2 || echo "") + + # Save to outputs + echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT + echo "release_type=$RELEASE_TYPE" >> $GITHUB_OUTPUT + + # Save release notes for comment + echo "release_notes<> $GITHUB_OUTPUT + echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Display Preview + run: | + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " RELEASE PREVIEW" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + if [ -n "${{ steps.semantic.outputs.new_version }}" ]; then + echo "Version: v${{ steps.semantic.outputs.new_version }}" + echo "Release Type: ${{ steps.semantic.outputs.release_type }}" + echo "Status: Release will be published" + else + echo "Status: No release will be published" + echo "Reason: No relevant changes detected" + fi + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + - name: Comment on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const newVersion = '${{ steps.semantic.outputs.new_version }}'; + const releaseType = '${{ steps.semantic.outputs.release_type }}'; + + const releaseNotes = `${{ steps.semantic.outputs.release_notes }}`; + + let body; + if (newVersion) { + body = `## Release Preview + + **Version:** \`v${newVersion}\` + **Release Type:** \`${releaseType}\` + **Status:** Release will be published when merged to main + + --- + + ### Release Notes + + ${releaseNotes} + + --- + + *This preview is generated by semantic-release dry-run mode*`; + } + else { + body = `## Release Preview + + **Status:** No release will be published + **Reason:** No relevant changes detected + + --- + +
+ View full semantic-release log + + \`\`\` + ${{ steps.semantic.outputs.full_output }} + \`\`\` +
+ + --- + + *This preview is generated by semantic-release dry-run mode*`; + } + + // Find existing comment + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('Release Preview') + ); + + // Update or create comment + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + } diff --git a/.github/workflows/terraform.yaml b/.github/workflows/terraform.yaml index 3c55029..6b05110 100644 --- a/.github/workflows/terraform.yaml +++ b/.github/workflows/terraform.yaml @@ -15,6 +15,14 @@ on: - main - master jobs: + releasePreview: + name: Release Preview + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }} + uses: ./.github/workflows/release-preview.yaml + permissions: + contents: write + pull-requests: write + preCommitCheck: name: Terraform Checks uses: ./.github/workflows/terraform-checks.yaml diff --git a/.preview-releaserc.json b/.preview-releaserc.json new file mode 100644 index 0000000..d000f8a --- /dev/null +++ b/.preview-releaserc.json @@ -0,0 +1,11 @@ +{ + "branches": ["BRANCH_PLACEHOLDER"], + "debug": true, + "ci": false, + "dryRun": true, + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/github" + ] +} diff --git a/examples/cross-account/README.md b/examples/cross-account/README.md index 4a809d3..70ba35e 100644 --- a/examples/cross-account/README.md +++ b/examples/cross-account/README.md @@ -26,7 +26,7 @@ route53:ChangeResourceRecordSets route53:ListHostedZonesByName route53:ListResourceRecordSets -And a trust policy allowing Account A to assume the role. +And a trust policy which allows Account A to assume the role. ## Example `tfvars` Configuration