caddyhttp: Fix logging on wildcard sites when SkipUnmappedHosts is true #64
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: Release Proposal Approval Tracker | |
| on: | |
| pull_request_review: | |
| types: [submitted, dismissed] | |
| pull_request: | |
| types: [labeled, unlabeled, synchronize, closed] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| check-approvals: | |
| name: Track Maintainer Approvals | |
| runs-on: ubuntu-latest | |
| # Only run on PRs with release-proposal label | |
| if: contains(github.event.pull_request.labels.*.name, 'release-proposal') && github.event.pull_request.state == 'open' | |
| steps: | |
| - name: Check approvals and update PR | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| MAINTAINER_LOGINS: ${{ secrets.MAINTAINER_LOGINS }} | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| // Extract version from PR title (e.g., "Release Proposal: v1.2.3") | |
| const versionMatch = pr.title.match(/Release Proposal:\s*(v[\d.]+(?:-[\w.]+)?)/); | |
| const commitMatch = pr.body.match(/\*\*Target Commit:\*\*\s*`([a-f0-9]+)`/); | |
| if (!versionMatch || !commitMatch) { | |
| console.log('Could not extract version from title or commit from body'); | |
| return; | |
| } | |
| const version = versionMatch[1]; | |
| const targetCommit = commitMatch[1]; | |
| console.log(`Version: ${version}, Target Commit: ${targetCommit}`); | |
| // Get all reviews | |
| const reviews = await github.rest.pulls.listReviews({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number | |
| }); | |
| // Get list of maintainers | |
| const maintainerLoginsRaw = process.env.MAINTAINER_LOGINS || ''; | |
| const maintainerLogins = maintainerLoginsRaw | |
| .split(/[,;]/) | |
| .map(login => login.trim()) | |
| .filter(login => login.length > 0); | |
| console.log(`Maintainer logins: ${maintainerLogins.join(', ')}`); | |
| // Get the latest review from each user | |
| const latestReviewsByUser = {}; | |
| reviews.data.forEach(review => { | |
| const username = review.user.login; | |
| if (!latestReviewsByUser[username] || new Date(review.submitted_at) > new Date(latestReviewsByUser[username].submitted_at)) { | |
| latestReviewsByUser[username] = review; | |
| } | |
| }); | |
| // Count approvals from maintainers | |
| const maintainerApprovals = Object.entries(latestReviewsByUser) | |
| .filter(([username, review]) => | |
| maintainerLogins.includes(username) && | |
| review.state === 'APPROVED' | |
| ) | |
| .map(([username, review]) => username); | |
| const approvalCount = maintainerApprovals.length; | |
| console.log(`Found ${approvalCount} maintainer approvals from: ${maintainerApprovals.join(', ')}`); | |
| // Get current labels | |
| const currentLabels = pr.labels.map(label => label.name); | |
| const hasApprovedLabel = currentLabels.includes('approved'); | |
| const hasAwaitingApprovalLabel = currentLabels.includes('awaiting-approval'); | |
| if (approvalCount >= 2 && !hasApprovedLabel) { | |
| console.log('✅ Quorum reached! Updating PR...'); | |
| // Remove awaiting-approval label if present | |
| if (hasAwaitingApprovalLabel) { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| name: 'awaiting-approval' | |
| }).catch(e => console.log('Label not found:', e.message)); | |
| } | |
| // Add approved label | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| labels: ['approved'] | |
| }); | |
| // Add comment with tagging instructions | |
| const approversList = maintainerApprovals.map(u => `@${u}`).join(', '); | |
| const commentBody = [ | |
| '## ✅ Approval Quorum Reached', | |
| '', | |
| `This release proposal has been approved by ${approvalCount} maintainers: ${approversList}`, | |
| '', | |
| '### Tagging Instructions', | |
| '', | |
| 'A maintainer should now create and push the signed tag:', | |
| '', | |
| '```bash', | |
| `git checkout ${targetCommit}`, | |
| `git tag -s ${version} -m "Release ${version}"`, | |
| `git push origin ${version}`, | |
| `git checkout -`, | |
| '```', | |
| '', | |
| 'The release workflow will automatically start when the tag is pushed.' | |
| ].join('\n'); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: commentBody | |
| }); | |
| console.log('Posted tagging instructions'); | |
| } else if (approvalCount < 2 && hasApprovedLabel) { | |
| console.log('⚠️ Approval count dropped below quorum, removing approved label'); | |
| // Remove approved label | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| name: 'approved' | |
| }).catch(e => console.log('Label not found:', e.message)); | |
| // Add awaiting-approval label | |
| if (!hasAwaitingApprovalLabel) { | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| labels: ['awaiting-approval'] | |
| }); | |
| } | |
| } else { | |
| console.log(`⏳ Waiting for more approvals (${approvalCount}/2 required)`); | |
| } | |
| handle-pr-closed: | |
| name: Handle PR Closed Without Tag | |
| runs-on: ubuntu-latest | |
| if: | | |
| contains(github.event.pull_request.labels.*.name, 'release-proposal') && | |
| github.event.action == 'closed' && !contains(github.event.pull_request.labels.*.name, 'released') | |
| steps: | |
| - name: Add cancelled label and comment | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| // Check if the release-in-progress label is present | |
| const hasReleaseInProgress = pr.labels.some(label => label.name === 'release-in-progress'); | |
| if (hasReleaseInProgress) { | |
| // PR was closed while release was in progress - this is unusual | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: '⚠️ **Warning:** This PR was closed while a release was in progress. This may indicate an error. Please verify the release status.' | |
| }); | |
| } else { | |
| // PR was closed before tag was created - this is normal cancellation | |
| const versionMatch = pr.title.match(/Release Proposal:\s*(v[\d.]+(?:-[\w.]+)?)/); | |
| const version = versionMatch ? versionMatch[1] : 'unknown'; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: `## 🚫 Release Proposal Cancelled\n\nThis release proposal for ${version} was closed without creating the tag.\n\nIf you want to proceed with this release later, you can create a new release proposal.` | |
| }); | |
| } | |
| // Add cancelled label | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| labels: ['cancelled'] | |
| }); | |
| // Remove other workflow labels if present | |
| const labelsToRemove = ['awaiting-approval', 'approved', 'release-in-progress']; | |
| for (const label of labelsToRemove) { | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| name: label | |
| }); | |
| } catch (e) { | |
| console.log(`Label ${label} not found or already removed`); | |
| } | |
| } | |
| console.log('Added cancelled label and cleaned up workflow labels'); | |