Skip to content

Commit e85b158

Browse files
committed
chore: add CLA contributor check workflow (enforce mode) [skip ci]
1 parent 4482d73 commit e85b158

1 file changed

Lines changed: 82 additions & 0 deletions

File tree

.github/workflows/cla-check.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: CLA contributor check
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, synchronize, reopened]
6+
7+
jobs:
8+
check-contributor-membership:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
pull-requests: write
12+
statuses: write
13+
14+
steps:
15+
- name: Generate App token
16+
id: app-token
17+
uses: actions/create-github-app-token@v2
18+
with:
19+
app-id: ${{ secrets.CLA_APP_ID }}
20+
private-key: ${{ secrets.CLA_APP_PRIVATE_KEY }}
21+
owner: openMF
22+
23+
- name: Check contributor team membership
24+
uses: actions/github-script@v7
25+
with:
26+
github-token: ${{ steps.app-token.outputs.token }}
27+
script: |
28+
const author = context.payload.pull_request.user.login;
29+
const org = 'openMF';
30+
const teams = ['contributors', 'contracted-contributors'];
31+
32+
if (context.payload.pull_request.user.type === 'Bot') {
33+
console.log('Bot PR — skipping CLA check');
34+
return;
35+
}
36+
37+
// App token has both members:read and pull-requests:write — one client for everything.
38+
// All actions appear as mifos-cla-check[bot], no personal account involved.
39+
let isMember = false;
40+
let matchedTeam = null;
41+
for (const team of teams) {
42+
try {
43+
const { data } = await github.rest.teams.getMembershipForUserInOrg({
44+
org, team_slug: team, username: author,
45+
});
46+
if (data.state === 'active') { isMember = true; matchedTeam = team; break; }
47+
} catch (e) { /* 404 = not in this team, try next */ }
48+
}
49+
50+
if (!isMember) {
51+
await github.rest.issues.createComment({
52+
owner: context.repo.owner,
53+
repo: context.repo.repo,
54+
issue_number: context.payload.pull_request.number,
55+
body:
56+
`👋 Hi @${author} — thank you for your pull request.\n\n` +
57+
`**This PR is currently blocked** because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.\n\n` +
58+
`To get unblocked:\n` +
59+
`1. Complete the form at https://mifos.org/about-us/financial-legal/mifos-contributor-agreement\n` +
60+
`2. Complete the CLA signing process\n` +
61+
`3. Once verified you will be added to the approved contributors list and this PR check will be cleared`
62+
});
63+
await github.rest.issues.addLabels({
64+
owner: context.repo.owner,
65+
repo: context.repo.repo,
66+
issue_number: context.payload.pull_request.number,
67+
labels: ['cla-required'],
68+
});
69+
core.setFailed(
70+
`@${author} is not a member of the 'contributors' or 'contracted-contributors' team. ` +
71+
`CLA signature required before this PR can be merged.`
72+
);
73+
} else {
74+
console.log(`@${author} is verified via '${matchedTeam}' team ✓`);
75+
try {
76+
await github.rest.issues.removeLabel({
77+
owner: context.repo.owner, repo: context.repo.repo,
78+
issue_number: context.payload.pull_request.number,
79+
name: 'cla-required',
80+
});
81+
} catch (e) { /* label may not be present — fine */ }
82+
}

0 commit comments

Comments
 (0)