Skip to content
Latchkey

Release Proposal Approval Tracker workflow (caddyserver/caddy)

The Release Proposal Approval Tracker workflow from caddyserver/caddy, explained and optimized by Latchkey.

C

CI health: C - fair

Point runs-on at Latchkey and get run de-duplication, job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: caddyserver/caddy.github/workflows/auto-release-pr.ymlLicense Apache-2.0View source

What it does

This is the Release Proposal Approval Tracker workflow from the caddyserver/caddy repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
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');
            

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

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
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  check-approvals:
    timeout-minutes: 30
    name: Track Maintainer Approvals
    runs-on: latchkey-small
    # 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:
    timeout-minutes: 30
    name: Handle PR Closed Without Tag
    runs-on: latchkey-small
    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');
            
 

What changed

This workflow runs 2 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow