|
| 1 | +name: CLA |
| 2 | +on: |
| 3 | + workflow_call: |
| 4 | + inputs: |
| 5 | + runner: |
| 6 | + description: 'The runner to use for the job' |
| 7 | + required: false |
| 8 | + type: string |
| 9 | + default: 'ubuntu-24.04' |
| 10 | + |
| 11 | +permissions: |
| 12 | + contents: read |
| 13 | + pull-requests: write # Required to add labels and comments |
| 14 | + |
| 15 | +jobs: |
| 16 | + check-cla: |
| 17 | + runs-on: ${{ inputs.runner }} |
| 18 | + steps: |
| 19 | + # --- Step 1: Check Base Branch --- |
| 20 | + - name: Checkout base branch and check for contributor status |
| 21 | + uses: actions/checkout@v5 |
| 22 | + with: |
| 23 | + token: ${{ secrets.GITHUB_TOKEN }} |
| 24 | + ref: ${{ github.event.pull_request.base.sha }} |
| 25 | + |
| 26 | + - name: Determine if contributor exists in base |
| 27 | + id: check_contributor_base |
| 28 | + run: | |
| 29 | + AUTHOR="${{ github.event.pull_request.user.login }}" |
| 30 | + if [ -f "CONTRIBUTORS" ]; then |
| 31 | + if grep -q "^$AUTHOR" CONTRIBUTORS; then |
| 32 | + echo "on_base=true" >> $GITHUB_OUTPUT |
| 33 | + echo "🎉 $AUTHOR has already signed the CLA on base branch." |
| 34 | + else |
| 35 | + echo "on_base=false" >> $GITHUB_OUTPUT |
| 36 | + echo "⚠️ $AUTHOR not on base. Proceeding to check PR branch." |
| 37 | + fi |
| 38 | + else |
| 39 | + # If CONTRIBUTORS file doesn't exist, we must check PR branch |
| 40 | + echo "on_base=undefined" >> $GITHUB_OUTPUT |
| 41 | + echo "🔴 CONTRIBUTORS file does not exist on base. Proceeding to check PR branch." |
| 42 | + fi |
| 43 | +
|
| 44 | + # --- Step 2: Check PR Branch --- |
| 45 | + - name: Checkout PR branch and check for contributor status |
| 46 | + # Only run if contributor wasn't found on the base branch |
| 47 | + if: steps.check_contributor_base.outputs.on_base != 'true' |
| 48 | + uses: actions/checkout@v5 |
| 49 | + with: |
| 50 | + token: ${{ secrets.GITHUB_TOKEN }} |
| 51 | + ref: ${{ github.event.pull_request.head.sha }} |
| 52 | + |
| 53 | + - name: Determine if contributor exists in PR branch |
| 54 | + id: check_contributor_pr |
| 55 | + # Only run if contributor wasn't found on the base branch |
| 56 | + if: steps.check_contributor_base.outputs.on_base != 'true' |
| 57 | + run: | |
| 58 | + AUTHOR="${{ github.event.pull_request.user.login }}" |
| 59 | + if grep -q "^$AUTHOR" CONTRIBUTORS; then |
| 60 | + echo "signed=true" >> $GITHUB_OUTPUT |
| 61 | + echo "✅ $AUTHOR has updated their CLA signature in CONTRIBUTORS file." |
| 62 | + else |
| 63 | + echo "signed=false" >> $GITHUB_OUTPUT |
| 64 | + echo "❌ $AUTHOR has not signed the CLA." |
| 65 | + fi |
| 66 | +
|
| 67 | + # --- Step 3: Manage PR Labels, Comments, and Final Status (Consolidated) --- |
| 68 | + - name: Manage CLA Status, Labels, and Comments |
| 69 | + uses: actions/github-script@v8 |
| 70 | + # Using 'always()' here so this step runs regardless of previous |
| 71 | + # success/failure to manage labels correctly |
| 72 | + if: always() |
| 73 | + with: |
| 74 | + github-token: ${{ secrets.GITHUB_TOKEN }} |
| 75 | + script: | |
| 76 | + const signedOnBase = '${{ steps.check_contributor_base.outputs.on_base }}' === 'true'; |
| 77 | + // Default signed status is false if the second check wasn't run |
| 78 | + const signedOnPr = '${{ steps.check_contributor_pr.outputs.signed }}' === 'true'; |
| 79 | + const cla_met = signedOnBase || signedOnPr; |
| 80 | + const issue_number = context.issue.number; |
| 81 | + const owner = context.repo.owner; |
| 82 | + const repo = context.repo.repo; |
| 83 | + const author = context.payload.pull_request.user.login; |
| 84 | +
|
| 85 | + // Helper function to create or update a label with a specific color |
| 86 | + async function ensureLabel(name, color, description) { |
| 87 | + try { |
| 88 | + await github.rest.issues.updateLabel({ owner, repo, name, color, description }); |
| 89 | + console.log(`Updated label: ${name} with color ${color}`); |
| 90 | + } catch (error) { |
| 91 | + // If update fails (label doesn't exist), create it |
| 92 | + await github.rest.issues.createLabel({ owner, repo, name, color, description }); |
| 93 | + console.log(`Created new label: ${name} with color ${color}`); |
| 94 | + } |
| 95 | + } |
| 96 | +
|
| 97 | + // Define desired colors and descriptions for consistency |
| 98 | + const COLOR_SIGNED = '0052cc'; // Blue |
| 99 | + const COLOR_REQUIRED = 'b60205'; // Red |
| 100 | +
|
| 101 | + console.log(`CLA Met: ${cla_met} (Base: ${signedOnBase}, PR: ${signedOnPr})`); |
| 102 | +
|
| 103 | + if (cla_met) { |
| 104 | + await ensureLabel('cla-signed', COLOR_SIGNED, 'This contributor has signed the CLA.'); |
| 105 | + console.log('✅ CLA condition met. Removing required label and adding signed label.'); |
| 106 | + // Use Promise.allSettled for robust label management |
| 107 | + await Promise.allSettled([ |
| 108 | + github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'cla-required' }), |
| 109 | + ]); |
| 110 | + if ( signedOnBase === false ) { |
| 111 | + await Promise.allSettled([ |
| 112 | + github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['cla-signed'] }) |
| 113 | + ]); |
| 114 | + } |
| 115 | +
|
| 116 | + } else { |
| 117 | + await ensureLabel('cla-required', COLOR_REQUIRED, 'CLA signature is required for this PR.'); |
| 118 | + console.log('❌ CLA condition NOT met. Adding required label and ensuring signed label is absent.'); |
| 119 | +
|
| 120 | + // Ensure labels are correct |
| 121 | + await Promise.allSettled([ |
| 122 | + github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'cla-signed' }), |
| 123 | + github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['cla-required'] }) |
| 124 | + ]); |
| 125 | +
|
| 126 | + // Post CLA comment |
| 127 | + const commentBody = `Hello @${author}! 👋\n\nThank you for your contribution. Since this is your first time contributing to this repository, we ask that you sign our Contributor Licence Agreement (CLA).\n\n📄 [You can read the CLA here](https://github.com/MetOffice/simulation-systems/blob/github_wps/Momentum-CLA.md).\n\nTo agree to the CLA, please add your details (**GitHub username**, real name, organisation, email, and date) to the _CONTRIBUTORS_ file (create one, if required) in the development branch for this PR. After signing the CLA, you won't need to do this again for future PRs.`; |
| 128 | +
|
| 129 | + await github.rest.issues.createComment({ owner, repo, issue_number, body: commentBody }); |
| 130 | +
|
| 131 | + // Fail the GitHub Action run |
| 132 | + console.error("⚠️ Please add yourself to the CONTRIBUTORS file to sign the CLA."); |
| 133 | + process.exit(1); |
| 134 | + } |
0 commit comments