Update description for WireMock Runner section #13
Workflow file for this run
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: Auto-merge .NET docs PRs | |
| on: | |
| issue_comment: | |
| types: [created] | |
| pull_request_target: | |
| types: [opened, synchronize, reopened] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| auto-merge: | |
| runs-on: ubuntu-latest | |
| # Only run on PRs (not regular issues) and non-draft PRs | |
| if: | | |
| (github.event_name == 'pull_request_target' && github.event.pull_request.draft == false) || | |
| (github.event_name == 'issue_comment' && github.event.issue.pull_request != null) | |
| steps: | |
| - name: Get PR details | |
| id: pr-details | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| let prNumber; | |
| if (context.eventName === 'pull_request_target') { | |
| prNumber = context.payload.pull_request.number; | |
| } else if (context.eventName === 'issue_comment') { | |
| prNumber = context.payload.issue.number; | |
| } | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| }); | |
| console.log(`PR #${prNumber}: ${pr.title}`); | |
| console.log(`Head SHA: ${pr.head.sha}`); | |
| console.log(`Base SHA: ${pr.base.sha}`); | |
| console.log(`Draft: ${pr.draft}`); | |
| return { | |
| number: prNumber, | |
| head_sha: pr.head.sha, | |
| base_sha: pr.base.sha, | |
| draft: pr.draft | |
| }; | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ fromJson(steps.pr-details.outputs.result).head_sha }} | |
| fetch-depth: 0 | |
| - name: Get changed files | |
| id: changed-files | |
| uses: tj-actions/changed-files@v45 | |
| with: | |
| base_sha: ${{ fromJson(steps.pr-details.outputs.result).base_sha }} | |
| sha: ${{ fromJson(steps.pr-details.outputs.result).head_sha }} | |
| - name: Check if PR only affects dotnet paths | |
| id: check-files | |
| run: | | |
| echo "Extracting dotnet-related paths from CODEOWNERS" | |
| # Get all paths from CODEOWNERS that contain 'dotnet' (excluding the default owner line) | |
| DOTNET_PATHS=$(grep -E '^\s*/.*dotnet.*/' .github/CODEOWNERS | awk '{print $1}') | |
| echo "Dotnet paths found in CODEOWNERS:" | |
| echo "$DOTNET_PATHS" | |
| echo "Checking if all changed files match dotnet paths..." | |
| ALL_DOTNET=true | |
| MATCHED_PATHS="" | |
| for file in ${{ steps.changed-files.outputs.all_changed_files }}; do | |
| echo "Checking file: $file" | |
| FILE_MATCHED=false | |
| # Check if file matches any dotnet path | |
| while IFS= read -r path; do | |
| # Remove leading slash and trailing slash for pattern matching | |
| pattern="${path#/}" | |
| if [[ "$file" =~ ^${pattern} ]]; then | |
| echo " ✓ Matches path: $path" | |
| FILE_MATCHED=true | |
| # Add to matched paths if not already there | |
| if [[ ! "$MATCHED_PATHS" =~ "$path" ]]; then | |
| MATCHED_PATHS="$MATCHED_PATHS$path"$'\n' | |
| fi | |
| break | |
| fi | |
| done <<< "$DOTNET_PATHS" | |
| if [ "$FILE_MATCHED" = "false" ]; then | |
| echo " ✗ File $file does not match any dotnet path" | |
| ALL_DOTNET=false | |
| break | |
| fi | |
| done | |
| echo "all_dotnet_docs=$ALL_DOTNET" >> $GITHUB_OUTPUT | |
| if [ "$ALL_DOTNET" = "true" ]; then | |
| echo "✅ All changed files match dotnet paths" | |
| echo "Matched paths:" | |
| echo "$MATCHED_PATHS" | |
| # Extract all unique code owners from matched paths | |
| ALL_OWNERS="" | |
| while IFS= read -r path; do | |
| if [ -n "$path" ]; then | |
| # Get owners for this specific path | |
| OWNERS=$(grep "^$path" .github/CODEOWNERS | sed "s|^$path||" | sed 's/@//g') | |
| ALL_OWNERS="$ALL_OWNERS $OWNERS" | |
| fi | |
| done <<< "$MATCHED_PATHS" | |
| # Remove duplicates and convert to JSON array | |
| UNIQUE_OWNERS=$(echo "$ALL_OWNERS" | tr ' ' '\n' | grep -v '^$' | sort -u) | |
| OWNERS_JSON=$(echo "$UNIQUE_OWNERS" | jq -R -s -c 'split("\n") | map(select(length > 0))') | |
| echo "Code owners: $OWNERS_JSON" | |
| echo "owners=$OWNERS_JSON" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Some files are outside dotnet paths" | |
| fi | |
| - name: Check for code owner thumbs-up comment | |
| id: check-approval | |
| if: steps.check-files.outputs.all_dotnet_docs == 'true' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const codeOwners = ${{ steps.check-files.outputs.owners }}; | |
| const prNumber = ${{ fromJson(steps.pr-details.outputs.result).number }}; | |
| console.log(`Code owners for affected dotnet paths: ${codeOwners.join(', ')}`); | |
| // Get all comments on the PR | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| }); | |
| console.log(`Found ${comments.length} comments on PR #${prNumber}`); | |
| // Check for a comment containing only a thumbs-up emoji from a code owner | |
| let approvedBy = null; | |
| for (const comment of comments) { | |
| const commentBody = comment.body.trim(); | |
| console.log(`Checking comment ${comment.id} by ${comment.user.login}: "${commentBody}"`); | |
| // Check if comment is just a thumbs-up emoji and from a code owner | |
| if (commentBody === '👍' && codeOwners.includes(comment.user.login)) { | |
| approvedBy = comment.user.login; | |
| console.log(`✅ Found thumbs-up comment from code owner: ${approvedBy}`); | |
| break; | |
| } | |
| } | |
| if (approvedBy) { | |
| console.log(`✅ Approved by code owner: ${approvedBy}`); | |
| return { approved: true, approver: approvedBy }; | |
| } else { | |
| console.log(`⏳ No thumbs-up comment from code owners yet. Waiting for comment from: ${codeOwners.join(', ')}`); | |
| return { approved: false, approver: null }; | |
| } | |
| - name: Enable auto-merge | |
| if: | | |
| steps.check-files.outputs.all_dotnet_docs == 'true' && | |
| fromJson(steps.check-approval.outputs.result).approved == true | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh pr merge ${{ fromJson(steps.pr-details.outputs.result).number }} --squash --auto | |
| echo '✅ Auto-merge enabled successfully!' | |
| - name: Add comment on success | |
| if: | | |
| steps.check-files.outputs.all_dotnet_docs == 'true' && | |
| fromJson(steps.check-approval.outputs.result).approved == true | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const prNumber = ${{ fromJson(steps.pr-details.outputs.result).number }}; | |
| const approvalResult = ${{ steps.check-approval.outputs.result }}; | |
| const approver = approvalResult.approver; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body: `✅ Auto-merge enabled: This PR only affects .NET-related paths and was approved by @${approver} with a 👍 comment. The PR will be merged automatically once all checks pass.` | |
| }); | |
| - name: Add comment if not eligible | |
| if: | | |
| steps.check-files.outputs.all_dotnet_docs == 'false' || | |
| (steps.check-files.outputs.all_dotnet_docs == 'true' && fromJson(steps.check-approval.outputs.result).approved == false) | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const prNumber = ${{ fromJson(steps.pr-details.outputs.result).number }}; | |
| let reason = ''; | |
| if ('${{ steps.check-files.outputs.all_dotnet_docs }}' !== 'true') { | |
| reason = '❌ This PR includes files outside of .NET-related paths and cannot be auto-merged.'; | |
| } else { | |
| const approvalResult = ${{ steps.check-approval.outputs.result }}; | |
| if (!approvalResult.approved) { | |
| const codeOwners = ${{ steps.check-files.outputs.owners }}; | |
| const ownerMentions = codeOwners.map(owner => `@${owner}`).join(', '); | |
| reason = `⏳ Waiting for a 👍 comment from a code owner (${ownerMentions}) for auto-merge.`; | |
| } | |
| } | |
| if (reason) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body: reason | |
| }); | |
| } |