feat(WO-6): Test #42
This file contains 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: Auto-Merge PRs | |
on: | |
pull_request: | |
types: [opened, labeled, synchronize, reopened, edited] | |
pull_request_review: | |
types: [ submitted ] | |
jobs: | |
validate-pr-title-and-description: | |
runs-on: ubuntu-22.04 | |
strategy: | |
fail-fast: true | |
steps: | |
- name: Validate PR Title | |
run: | | |
PR_TITLE="${{ github.event.pull_request.title }}" | |
if [[ ! "$PR_TITLE" =~ ^(feat|fix|docs|style|refactor|perf|test|build|chore)\(WO-[0-9]+\):\ .+$ ]]; then | |
echo "❌ ERROR: PR title does not follow the required format!" | |
exit 1 | |
fi | |
- name: Validate PR Description | |
run: | | |
PR_DESCRIPTION="${{ github.event.pull_request.body }}" | |
# Check if description is empty | |
if [[ -z "$PR_DESCRIPTION" ]]; then | |
echo "❌ ERROR: PR description cannot be empty!" | |
exit 1 | |
fi | |
# Check if description follows the commit message convention (e.g., imperative tone) | |
if [[ ! "$PR_DESCRIPTION" =~ ^(Add|Fix|Improve|Update|Remove|Refactor|Implement|Optimize|Enhance|Correct|Revert|Modify|Deprecate|Restructure|Format|Document)\ .+ ]]; then | |
echo "❌ ERROR: PR description does not follow the standard commit message convention!" | |
exit 1 | |
fi | |
# Check if description has at least 3 lines (adjust number as needed) | |
LINE_COUNT=$(echo "$PR_DESCRIPTION" | awk 'NF {count++} END {print count}') | |
if [[ "$LINE_COUNT" -lt 3 ]]; then | |
echo "❌ ERROR: PR description must contain at least 3 meaningful lines!" | |
exit 1 | |
fi | |
echo "✅ PR description is valid." | |
shell: bash | |
# Check for reviewers and their approvals | |
check-if-pr-approve: | |
runs-on: ubuntu-22.04 | |
strategy: | |
fail-fast: true | |
needs: validate-pr-title-and-description | |
if: success() | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v4 | |
- name: Install GitHub CLI | |
run: sudo apt update && sudo apt install -y gh | |
- name: Authenticate with GitHub CLI | |
run: gh auth status | |
- name: Fetch PR Details | |
run: | | |
PR_NUMBER="${{ github.event.pull_request.number }}" | |
BASE_BRANCH=$(gh pr view "$PR_NUMBER" --json baseRefName --jq '.baseRefName') | |
HEAD_BRANCH=$(gh pr view "$PR_NUMBER" --json headRefName --jq '.headRefName') | |
echo "Base Branch: $BASE_BRANCH" | |
echo "Head Branch: $HEAD_BRANCH" | |
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV | |
echo "BASE_BRANCH=$BASE_BRANCH" >> $GITHUB_ENV | |
echo "HEAD_BRANCH=$HEAD_BRANCH" >> $GITHUB_ENV | |
- name: Check if PR is Approved | |
run: | | |
PR_NUMBER="${{ github.event.pull_request.number }}" | |
# Fetch assigned reviewers (those explicitly assigned) | |
ASSIGNED_REVIEWERS=$(gh pr view "$PR_NUMBER" --json reviewRequests --jq '[.reviewRequests[].users[].login]') | |
# Fetch latest reviewers (who actually reviewed the PR) | |
LATEST_REVIEWERS=$(gh pr view "$PR_NUMBER" --json latestReviews --jq '[.latestReviews[].author.login]') | |
# Merge both lists and remove duplicates | |
ALL_REVIEWERS=$(jq -n --argjson a "$ASSIGNED_REVIEWERS" --argjson b "$LATEST_REVIEWERS" '$a + $b | unique') | |
# Fetch approvals | |
APPROVED_REVIEWERS=$(gh pr view "$PR_NUMBER" --json reviews --jq '[.reviews[] | select(.state == "APPROVED") | .author.login]') | |
# Count reviewers | |
REVIEWER_COUNT=$(echo "$ALL_REVIEWERS" | jq 'length') | |
# Fail if no reviewers exist | |
if [[ "$REVIEWER_COUNT" -eq 0 ]]; then | |
echo "❌ ERROR: No reviews found. At least one reviewer approval is required before merging." | |
exit 1 | |
fi | |
# Ensure all reviewers have approved | |
for reviewer in $(echo "$ALL_REVIEWERS" | jq -r '.[]'); do | |
if ! echo "$APPROVED_REVIEWERS" | jq -r '.[]' | grep -q "^$reviewer$"; then | |
echo "❌ PR #$PR_NUMBER is NOT fully approved. Missing approval from: $reviewer" | |
exit 1 | |
fi | |
done | |
echo "✅ All assigned and reviewed users have approved. Proceeding with squash merge..." | |
# Run Tests Before Merging | |
test-before-merge: | |
runs-on: ubuntu-22.04 | |
strategy: | |
fail-fast: true | |
needs: check-if-pr-approve | |
if: success() | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v4 | |
- name: Setup Test Environment | |
uses: ./.github/actions/setup-environment/ | |
with: | |
env_type: "test" | |
- name: Run Tests | |
uses: ./.github/actions/run-tests/ | |
# Prepare for Merge (Squash but Don't Push Yet) | |
prepare-merge: | |
runs-on: ubuntu-22.04 | |
strategy: | |
fail-fast: true | |
needs: test-before-merge | |
if: success() | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v4 | |
- name: Install GitHub CLI | |
run: sudo apt update && sudo apt install -y gh | |
- name: Authenticate with GitHub CLI | |
run: gh auth status | |
- name: Fetch and Checkout PR Branch | |
run: | | |
git fetch origin $HEAD_BRANCH | |
git checkout $HEAD_BRANCH | |
env: | |
HEAD_BRANCH: ${{ github.event.pull_request.head.ref }} | |
- name: Set Merge Message | |
run: | | |
if [[ "$COMMIT_COUNT" -eq 1 ]]; then | |
COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD) | |
else | |
COMMIT_MESSAGE="${{ github.event.pull_request.title }} - ${{ github.event.pull_request.body }}" | |
fi | |
# Write the multi-line commit message safely to GITHUB_ENV | |
{ | |
echo "COMMIT_MESSAGE<<EOF" | |
echo "$COMMIT_MESSAGE" | |
echo "EOF" | |
} >> "$GITHUB_ENV" | |
- name: Debug Token Permissions | |
run: | | |
echo "Checking token permissions..." | |
gh auth status || echo "GitHub CLI is not authenticated" | |
gh api user || echo "GitHub API request failed" | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
- name: Squash Merge PR | |
run: | | |
gh pr merge "$PR_NUMBER" --squash --subject "$COMMIT_MESSAGE" | |
- name: Squash Merge PR (But Don't Push Yet) | |
run: | | |
gh pr merge "$PR_NUMBER" --squash --subject "$COMMIT_MESSAGE" | |
# Run Tests on Merged Code Before Pushing | |
test-after-merge: | |
runs-on: ubuntu-22.04 | |
strategy: | |
fail-fast: true | |
needs: prepare-merge | |
if: success() | |
steps: | |
- name: Checkout Production Branch | |
uses: actions/checkout@v4 | |
with: | |
ref: production | |
- name: Setup Test Environment | |
uses: ./.github/actions/setup-environment/ | |
with: | |
env_type: "test" | |
- name: Run Tests on Merged Code | |
uses: ./.github/actions/run-tests/ | |
# Push to Production If All Tests Pass | |
push-to-production: | |
runs-on: ubuntu-22.04 | |
strategy: | |
fail-fast: true | |
needs: test-after-merge | |
if: success() | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
steps: | |
- name: Fetch PR Details | |
run: | | |
HEAD_BRANCH=$(gh pr view "$PR_NUMBER" --json headRefName --jq '.headRefName') | |
echo "Head Branch: $HEAD_BRANCH" | |
echo "HEAD_BRANCH=$HEAD_BRANCH" >> $GITHUB_ENV | |
- name: Checkout Production Branch | |
uses: actions/checkout@v4 | |
with: | |
ref: production | |
- name: Push Merged Code to Production | |
run: git push origin production | |
- name: Delete Feature Branch After Merge | |
run: | | |
if [[ "$HEAD_BRANCH" != "production" && "$HEAD_BRANCH" != "staging" ]]; then | |
echo "🗑️ Deleting feature branch: $HEAD_BRANCH" | |
gh api -X DELETE repos/${{ github.repository }}/git/refs/heads/$HEAD_BRANCH | |
else | |
echo "❌ Not deleting protected branch: $HEAD_BRANCH" | |
fi |