Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

chore: add workflow visualization #19

chore: add workflow visualization

chore: add workflow visualization #19

name: Release Management
on:
# Using pull_request_target instead of pull_request for security reasons:
# - Runs in the context of the BASE repository, not the fork
# - Has access to repository secrets
# - Can commit changes to protected branches
# - SECURITY NOTE: Be careful when checking out PR code with this event type
pull_request_target:
types: [closed]
branches:
- main
workflow_dispatch:
inputs:
release_type:
description: 'Force a specific release type (leave empty for auto-detection)'
required: false
type: choice
options:
- auto
- major
- minor
- patch
default: 'auto'
target_branch:
description: 'Target branch for manual release (usually develop)'
required: false
default: 'develop'
schedule:
# Run on the 1st and 15th of each month
- cron: '0 0 1,15 * *'
jobs:
prepare-release:
# Only run if:
# 1. PR from develop to main is merged, OR
# 2. Manually triggered, OR
# 3. Scheduled run
if: (github.event_name == 'pull_request_target' && github.event.pull_request.merged == true && github.event.pull_request.head.ref == 'develop') || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- name: Set checkout ref
id: set_ref
run: |
if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then
echo "ref=main" >> $GITHUB_OUTPUT
else
echo "ref=${{ github.event.inputs.target_branch || 'develop' }}" >> $GITHUB_OUTPUT
fi
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
# For PR events, check out the main branch
# For manual/scheduled events, check out the specified target branch or develop
ref: ${{ steps.set_ref.outputs.ref }}
# Use a personal access token with repo scope for better permissions
token: ${{ secrets.REPO_PAT }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
# Add caching for npm dependencies
- name: Cache npm dependencies
uses: actions/cache@v3
id: npm-cache
with:
path: |
**/node_modules
~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install dependencies
# Only run full install if cache miss
if: steps.npm-cache.outputs.cache-hit != 'true'
run: npm ci
# If cache hit, just check for any missing dependencies
- name: Check dependencies
if: steps.npm-cache.outputs.cache-hit == 'true'
run: npm ci --prefer-offline --no-audit
- name: Check for changesets
id: check_changesets
run: |
if [ -d ".changesets" ] && [ "$(ls -A .changesets)" ]; then
echo "has_changesets=true" >> $GITHUB_OUTPUT
else
echo "has_changesets=false" >> $GITHUB_OUTPUT
echo "No changesets found. Exiting."
exit 1
fi
# Generate release notes in parallel with version bump
- name: Generate release notes
id: release_notes
run: |
# Create a temporary directory outside the repository with proper permissions
mkdir -p /tmp/release-notes
chmod 777 /tmp/release-notes
# Check if this is a PR from develop to main
if [[ "${{ github.event_name }}" == "pull_request_target" && "${{ github.event.pull_request.head.ref }}" == "develop" ]]; then
# Extract release notes from the PR body
PR_BODY="${{ github.event.pull_request.body }}"
# Extract the content between "## Upcoming Changes" and the disclaimer
RELEASE_NOTES=$(echo "$PR_BODY" | sed -n '/## Upcoming Changes/,/This PR contains all changes/p' | sed '1d;$d')
# Save to file for use in later steps (in temporary directory)
echo "$RELEASE_NOTES" > /tmp/release-notes/release_notes.md
else
# Generate release notes in markdown format for release body
# Skip the npm command output by redirecting stderr to /dev/null and grep out the command line
npm run release:notes 2>/dev/null | grep -v "^>" > /tmp/release-notes/release_notes.md
fi
# Check if the file has content
if [ ! -s /tmp/release-notes/release_notes.md ]; then
# If empty, provide a default message
echo "## Release Notes" > /tmp/release-notes/release_notes.md
echo "" >> /tmp/release-notes/release_notes.md
echo "No changesets found. No changes to release at this time." >> /tmp/release-notes/release_notes.md
else
# If there is content, replace "Upcoming Changes" with "Release Notes" if present
sed -i 's/## Upcoming Changes/## Release Notes/g' /tmp/release-notes/release_notes.md
# Remove the note about PR updates if present
sed -i '/This PR contains all changes that will be included in the next release/d' /tmp/release-notes/release_notes.md
fi
# For debugging
echo "Generated release notes:"
cat /tmp/release-notes/release_notes.md
# Set the content for GitHub Actions output
# Properly escape the content for GitHub Actions
RELEASE_NOTES=$(cat /tmp/release-notes/release_notes.md)
RELEASE_NOTES="${RELEASE_NOTES//'%'/'%25'}"
RELEASE_NOTES="${RELEASE_NOTES//$'\n'/'%0A'}"
RELEASE_NOTES="${RELEASE_NOTES//$'\r'/'%0D'}"
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Ensure the temporary directory and files are readable by subsequent steps
chmod -R 755 /tmp/release-notes
# List the contents of the directory to verify the file exists
echo "Verifying release notes file exists:"
ls -la /tmp/release-notes/
# Copy the release notes to a more permanent location in the workspace
# This ensures it's available even if the temporary directory is cleaned up
cp /tmp/release-notes/release_notes.md $GITHUB_WORKSPACE/release_notes.md
# Run version bump in parallel with release notes generation
- name: Determine version bump
id: version_bump
run: |
if [[ "${{ github.event.inputs.release_type }}" != "auto" && "${{ github.event.inputs.release_type }}" != "" ]]; then
# Use the specified release type
npm run version:bump -- --type=${{ github.event.inputs.release_type }}
else
# Auto-detect release type from changesets
npm run version:bump
fi
# Get the new version after bump
NEW_VERSION=$(grep -oP "define\('AUTOMATION_TESTS_VERSION', '\K[^']+" constants.php)
echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
# Update changelogs after both release notes and version bump are complete
- name: Update changelogs
run: |
# First, check if this is a breaking change release by using our new script
# This will automatically update the upgrade notice section if breaking changes are found
npm run upgrade-notice:update -- --version=${{ steps.version_bump.outputs.version }} --notes-file=/tmp/release-notes/release_notes.md
# Now update the changelogs as usual
npm run changelogs:update -- --version=${{ steps.version_bump.outputs.version }}
- name: Commit changes to develop
if: github.event_name != 'pull_request_target'
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "release: prepare v${{ steps.version_bump.outputs.version }}"
file_pattern: "*.php *.md *.txt package.json"
# For manual/scheduled events, commit to the specified target branch or develop
branch: ${{ github.event.inputs.target_branch || 'develop' }}
env:
GITHUB_TOKEN: ${{ secrets.REPO_PAT }}
- name: Commit changes to main
if: github.event_name == 'pull_request_target'
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
# Check if there are any changes to commit
if [[ -n "$(git status --porcelain)" ]]; then
echo "Changes detected, committing to main branch"
git add *.php *.md *.txt package.json
git commit -m "release: prepare v${{ steps.version_bump.outputs.version }}"
git push origin main
else
echo "No changes to commit"
fi
- name: Create and push tag
if: github.event_name == 'pull_request_target'
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
# Check if tag already exists
if git rev-parse "v${{ steps.version_bump.outputs.version }}" >/dev/null 2>&1; then
echo "Tag v${{ steps.version_bump.outputs.version }} already exists. Skipping tag creation."
else
# Create an annotated tag with the changelog as the message
git tag -a "v${{ steps.version_bump.outputs.version }}" -m "Release v${{ steps.version_bump.outputs.version }}"
# Push the tag
git push origin "v${{ steps.version_bump.outputs.version }}"
fi
- name: Create GitHub Release
if: github.event_name == 'pull_request_target'
uses: actions/create-release@v1
id: create_release
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.REPO_PAT }}
with:
tag_name: v${{ steps.version_bump.outputs.version }}
release_name: Release v${{ steps.version_bump.outputs.version }}
body_path: ${{ github.workspace }}/release_notes.md
draft: false
prerelease: false
- name: Handle release creation failure
if: github.event_name == 'pull_request_target' && steps.create_release.outcome == 'failure'
run: |
echo "Failed to create release. This could be because the tag already exists."
echo "Checking if release exists..."
RELEASE_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${{ secrets.REPO_PAT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${{ github.repository }}/releases/tags/v${{ steps.version_bump.outputs.version }}")
if [[ "$RELEASE_EXISTS" == "200" ]]; then
echo "Release for v${{ steps.version_bump.outputs.version }} already exists. Skipping release creation."
else
echo "Release creation failed for an unknown reason."
# Check for rate limiting
RATE_LIMIT=$(curl -s \
-H "Authorization: token ${{ secrets.REPO_PAT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/rate_limit")
REMAINING=$(echo "$RATE_LIMIT" | jq -r '.resources.core.remaining')
RESET_TIME=$(echo "$RATE_LIMIT" | jq -r '.resources.core.reset')
RESET_TIME_HUMAN=$(date -d @$RESET_TIME)
if [[ "$REMAINING" -le 10 ]]; then
echo "::warning::GitHub API rate limit is low: $REMAINING requests remaining. Resets at $RESET_TIME_HUMAN"
fi
# Try again with a different approach
echo "Attempting to create release using GitHub CLI..."
# Install GitHub CLI if not already installed
if ! command -v gh &> /dev/null; then
echo "Installing GitHub CLI..."
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install gh
fi
# Authenticate with GitHub CLI
echo "${{ secrets.REPO_PAT }}" | gh auth login --with-token
# Verify the release notes file exists
if [ -f "${{ github.workspace }}/release_notes.md" ]; then
echo "Using release notes from workspace:"
cat "${{ github.workspace }}/release_notes.md"
# Try to create the release with GitHub CLI
if gh release create "v${{ steps.version_bump.outputs.version }}" \
--title "Release v${{ steps.version_bump.outputs.version }}" \
--notes-file "${{ github.workspace }}/release_notes.md"; then
echo "Successfully created release using GitHub CLI"
else
echo "::error::Failed to create release using GitHub CLI with notes file."
# Try one more time with inline notes from the output
echo "Trying with inline notes..."
if gh release create "v${{ steps.version_bump.outputs.version }}" \
--title "Release v${{ steps.version_bump.outputs.version }}" \
--notes "${{ steps.release_notes.outputs.content }}"; then
echo "Successfully created release using GitHub CLI with inline notes"
else
echo "::error::Failed to create release using all methods. Please check logs for details."
exit 1
fi
fi
else
echo "Release notes file not found in workspace. Using inline notes..."
# Try with inline notes
if gh release create "v${{ steps.version_bump.outputs.version }}" \
--title "Release v${{ steps.version_bump.outputs.version }}" \
--notes "${{ steps.release_notes.outputs.content }}"; then
echo "Successfully created release using GitHub CLI with inline notes"
else
echo "::error::Failed to create release using all methods. Please check logs for details."
exit 1
fi
fi
fi
# Add a cleanup step to ensure temporary files are removed
# This step runs AFTER the GitHub release is created
- name: Cleanup temporary files
if: always()
run: |
# Check if the temporary directory exists before attempting to remove it
if [ -d "/tmp/release-notes" ]; then
echo "Cleaning up temporary files..."
rm -rf /tmp/release-notes
echo "Temporary files removed."
else
echo "No temporary files to clean up."
fi
# Also remove the release notes file from the workspace
# This prevents it from being versioned in the repository
if [ -f "$GITHUB_WORKSPACE/release_notes.md" ]; then
echo "Removing release notes file from workspace..."
rm -f "$GITHUB_WORKSPACE/release_notes.md"
echo "Workspace release notes file removed."
fi
- name: Delete processed changesets
if: github.event_name == 'pull_request_target'
run: |
# Configure Git
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
# Check if the .changesets directory exists and contains files
if [ -d ".changesets" ]; then
# List all changesets before deleting (for logging purposes)
echo "Changesets found in directory:"
ls -la .changesets/
# Remove all .md files in the .changesets directory
find .changesets -type f -name "*.md" -exec rm -f {} \;
# Check if any files were removed
if [ $? -eq 0 ]; then
echo "Changesets removed successfully"
# Stage the deletions
git add -A .changesets/
# Also make sure the release_notes.md file is not staged
git reset -- release_notes.md || true
# Commit the deleted changesets
git commit -m "chore: delete changesets after release v${{ steps.version_bump.outputs.version }}"
git push origin main
echo "Deleted changesets for v${{ steps.version_bump.outputs.version }}"
else
echo "Error removing changesets"
exit 1
fi
else
echo "No .changesets directory found"
fi
- name: Update develop branch
if: github.event_name == 'pull_request_target'
run: |
# Configure Git
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
# Fetch all branches
git fetch --unshallow || git fetch
# Checkout develop branch
git checkout develop
git pull
# Merge main into develop with a descriptive message
git merge --no-ff origin/main -m "chore: sync main back to develop after release v${{ steps.version_bump.outputs.version }} [skip ci]"
# Push changes to develop
git push origin develop
echo "Successfully synced main back to develop branch"