From 32797ceef4af34ac2548dff9bf9d6de502186d39 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 30 Apr 2026 20:53:56 +0930 Subject: [PATCH 1/6] Create cla-check.yml --- .github/workflows/cla-check.yml | 91 +++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 .github/workflows/cla-check.yml diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml new file mode 100644 index 0000000..37061f1 --- /dev/null +++ b/.github/workflows/cla-check.yml @@ -0,0 +1,91 @@ +name: CLA contributor check + +on: + pull_request_target: + types: [opened, synchronize, reopened] + +jobs: + check-contributor-membership: + runs-on: ubuntu-latest + permissions: + pull-requests: write + statuses: write + + steps: + - name: Check contributor team membership + id: check + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ORG_PAT }} + script: | + const author = context.payload.pull_request.user.login; + const org = 'openMF'; + const teams = ['contributors', 'contracted-contributors']; + + // Skip bots + if (context.payload.pull_request.user.type === 'Bot') { + console.log('Bot PR — skipping check'); + return; + } + + // Check membership in either team + let isMember = false; + let matchedTeam = null; + for (const team of teams) { + try { + const resp = await github.rest.teams.getMembershipForUserInOrg({ + org, + team_slug: team, + username: author, + }); + if (resp.data.state === 'active') { + isMember = true; + matchedTeam = team; + break; + } + } catch (e) { + // 404 = not a member, continue + } + } + + if (!isMember) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: `👋 Hi @${author} — thank you for your pull request.\n\n` + + `**This PR is currently blocked** because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.\n\n` + + `To get unblocked:\n` + + `1. Contact the Mifos team at contribute@mifos.org\n` + + `2. Complete the CLA signing process\n` + + `3. Once verified, you will be added to the approved contributors list and this PR will be cleared\n\n` + + `_No action is needed from you on this PR until that step is complete._` + }); + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: ['cla-required'] + }); + + core.setFailed( + `@${author} is not a member of the 'contributors' or 'contracted-contributors' team. ` + + `CLA signature required before this PR can be merged.` + ); + + } else { + console.log(`@${author} is verified via '${matchedTeam}' team ✓`); + + // Remove cla-required label if it was previously added + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + name: 'cla-required' + }); + } catch (e) { + // Label may not exist — that's fine + } + } From 27d4e23c028402e11cabca72f2a2ef4dc9627208 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 1 May 2026 14:00:53 +0930 Subject: [PATCH 2/6] chore: update CLA contributor check workflow [skip ci] --- .github/workflows/cla-check.yml | 52 +++++++++++---------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml index 37061f1..16235bc 100644 --- a/.github/workflows/cla-check.yml +++ b/.github/workflows/cla-check.yml @@ -13,7 +13,6 @@ jobs: steps: - name: Check contributor team membership - id: check uses: actions/github-script@v7 with: github-token: ${{ secrets.ORG_PAT }} @@ -22,30 +21,20 @@ jobs: const org = 'openMF'; const teams = ['contributors', 'contracted-contributors']; - // Skip bots if (context.payload.pull_request.user.type === 'Bot') { - console.log('Bot PR — skipping check'); + console.log('Bot PR — skipping CLA check'); return; } - // Check membership in either team let isMember = false; let matchedTeam = null; for (const team of teams) { try { - const resp = await github.rest.teams.getMembershipForUserInOrg({ - org, - team_slug: team, - username: author, + const { data } = await github.rest.teams.getMembershipForUserInOrg({ + org, team_slug: team, username: author, }); - if (resp.data.state === 'active') { - isMember = true; - matchedTeam = team; - break; - } - } catch (e) { - // 404 = not a member, continue - } + if (data.state === 'active') { isMember = true; matchedTeam = team; break; } + } catch (e) { /* 404 = not in this team, try next */ } } if (!isMember) { @@ -53,39 +42,32 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, - body: `👋 Hi @${author} — thank you for your pull request.\n\n` + - `**This PR is currently blocked** because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.\n\n` + - `To get unblocked:\n` + - `1. Contact the Mifos team at contribute@mifos.org\n` + - `2. Complete the CLA signing process\n` + - `3. Once verified, you will be added to the approved contributors list and this PR will be cleared\n\n` + - `_No action is needed from you on this PR until that step is complete._` + body: + `👋 Hi @${author} — thank you for your pull request.\n\n` + + `**This PR is currently blocked** because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.\n\n` + + `To get unblocked:\n` + + `1. Contact the Mifos team at contribute@mifos.org\n` + + `2. Complete the CLA signing process\n` + + `3. Once verified you will be added to the approved contributors list and this PR will be cleared\n\n` + + `_No action is needed from you on this PR until that step is complete._` }); - await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, - labels: ['cla-required'] + labels: ['cla-required'], }); - core.setFailed( `@${author} is not a member of the 'contributors' or 'contracted-contributors' team. ` + `CLA signature required before this PR can be merged.` ); - } else { console.log(`@${author} is verified via '${matchedTeam}' team ✓`); - - // Remove cla-required label if it was previously added try { await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, - name: 'cla-required' + name: 'cla-required', }); - } catch (e) { - // Label may not exist — that's fine - } + } catch (e) { /* label may not be present — fine */ } } From d96693c1fc2192df28193b65dbb14c170e0751da Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 4 May 2026 11:15:57 +0930 Subject: [PATCH 3/6] chore: update CLA contributor check workflow [skip ci] --- .github/workflows/cla-check.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml index 16235bc..29da97b 100644 --- a/.github/workflows/cla-check.yml +++ b/.github/workflows/cla-check.yml @@ -46,10 +46,9 @@ jobs: `👋 Hi @${author} — thank you for your pull request.\n\n` + `**This PR is currently blocked** because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.\n\n` + `To get unblocked:\n` + - `1. Contact the Mifos team at contribute@mifos.org\n` + + `1. Complete the form at https://mifos.org/about-us/financial-legal/mifos-contributor-agreement\n` + `2. Complete the CLA signing process\n` + - `3. Once verified you will be added to the approved contributors list and this PR will be cleared\n\n` + - `_No action is needed from you on this PR until that step is complete._` + `3. Once verified you will be added to the approved contributors list and this PR check will be cleared` }); await github.rest.issues.addLabels({ owner: context.repo.owner, From 2d75ca6f07bd5fdfb425acc38a7cb976d96f436e Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 21 May 2026 11:24:16 +0930 Subject: [PATCH 4/6] chore: update CLA contributor check workflow (warn mode) [skip ci] --- .github/workflows/cla-check.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml index 29da97b..5de2cbf 100644 --- a/.github/workflows/cla-check.yml +++ b/.github/workflows/cla-check.yml @@ -44,11 +44,13 @@ jobs: issue_number: context.payload.pull_request.number, body: `👋 Hi @${author} — thank you for your pull request.\n\n` + - `**This PR is currently blocked** because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.\n\n` + - `To get unblocked:\n` + + `**Note:** We do not have a Contributor License Agreement (CLA) on file for your GitHub account. ` + + `Merging is **not blocked** at this time — this repository is in a CLA grace period. ` + + `However, a signed CLA will be required once enforcement is enabled.\n\n` + + `To get your CLA on file:\n` + `1. Complete the form at https://mifos.org/about-us/financial-legal/mifos-contributor-agreement\n` + `2. Complete the CLA signing process\n` + - `3. Once verified you will be added to the approved contributors list and this PR check will be cleared` + `3. Once verified you will be added to the approved contributors list` }); await github.rest.issues.addLabels({ owner: context.repo.owner, @@ -56,9 +58,9 @@ jobs: issue_number: context.payload.pull_request.number, labels: ['cla-required'], }); - core.setFailed( - `@${author} is not a member of the 'contributors' or 'contracted-contributors' team. ` + - `CLA signature required before this PR can be merged.` + core.warning( + `@${author} is not a member of the 'contributors' or 'contracted-contributors' team — ` + + `CLA required (grace period active, merge not blocked).` ); } else { console.log(`@${author} is verified via '${matchedTeam}' team ✓`); From 0a0be688e22c16ed65350370d6e85743c1b158d2 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 4 Jul 2026 21:55:55 +0930 Subject: [PATCH 5/6] chore: update CLA contributor check workflow (warn mode) [skip ci] --- .github/workflows/cla-check.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml index 5de2cbf..2781066 100644 --- a/.github/workflows/cla-check.yml +++ b/.github/workflows/cla-check.yml @@ -12,10 +12,18 @@ jobs: statuses: write steps: + - name: Generate App token + id: app-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.CLA_APP_ID }} + private-key: ${{ secrets.CLA_APP_PRIVATE_KEY }} + owner: openMF + - name: Check contributor team membership uses: actions/github-script@v7 with: - github-token: ${{ secrets.ORG_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const author = context.payload.pull_request.user.login; const org = 'openMF'; @@ -26,6 +34,8 @@ jobs: return; } + // App token has both members:read and pull-requests:write — one client for everything. + // All actions appear as mifos-cla-check[bot], no personal account involved. let isMember = false; let matchedTeam = null; for (const team of teams) { From f894195bd0aeef9ccdc04dfd62b4618779ef28f4 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 4 Jul 2026 23:26:12 +0930 Subject: [PATCH 6/6] chore: update CLA contributor check workflow (enforce mode) [skip ci] --- .github/workflows/cla-check.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml index 2781066..445cd09 100644 --- a/.github/workflows/cla-check.yml +++ b/.github/workflows/cla-check.yml @@ -54,13 +54,11 @@ jobs: issue_number: context.payload.pull_request.number, body: `👋 Hi @${author} — thank you for your pull request.\n\n` + - `**Note:** We do not have a Contributor License Agreement (CLA) on file for your GitHub account. ` + - `Merging is **not blocked** at this time — this repository is in a CLA grace period. ` + - `However, a signed CLA will be required once enforcement is enabled.\n\n` + - `To get your CLA on file:\n` + + `**This PR is currently blocked** because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.\n\n` + + `To get unblocked:\n` + `1. Complete the form at https://mifos.org/about-us/financial-legal/mifos-contributor-agreement\n` + `2. Complete the CLA signing process\n` + - `3. Once verified you will be added to the approved contributors list` + `3. Once verified you will be added to the approved contributors list and this PR check will be cleared` }); await github.rest.issues.addLabels({ owner: context.repo.owner, @@ -68,9 +66,9 @@ jobs: issue_number: context.payload.pull_request.number, labels: ['cla-required'], }); - core.warning( - `@${author} is not a member of the 'contributors' or 'contracted-contributors' team — ` + - `CLA required (grace period active, merge not blocked).` + core.setFailed( + `@${author} is not a member of the 'contributors' or 'contracted-contributors' team. ` + + `CLA signature required before this PR can be merged.` ); } else { console.log(`@${author} is verified via '${matchedTeam}' team ✓`);