Skip to content

Commit

Permalink
Merge pull request #11783 from CesiumGS/cla-checking-final
Browse files Browse the repository at this point in the history
Automate CLA checking
  • Loading branch information
ggetz authored Feb 23, 2024
2 parents ba6f5a3 + 874cad7 commit f3a3dfa
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 1 deletion.
156 changes: 156 additions & 0 deletions .github/actions/check-for-CLA/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { Octokit } from "@octokit/core";
import { google } from "googleapis";
import Handlebars from "handlebars";
import fs from "fs-extra";

const PULL_REQUST_INFO = {
id: process.env.PULL_REQUEST_ID,
owner: process.env.GITHUB_REPOSITORY.split("/")[0],
repoName: process.env.GITHUB_REPOSITORY.split("/")[1],
username: process.env.GITHUB_ACTOR,
gitHubToken: process.env.GITHUB_TOKEN,
};

const GOOGLE_SHEETS_INFO = {
APIKeys: process.env.GOOGLE_KEYS,
individualCLASheetId: process.env.INDIVIDUAL_CLA_SHEET_ID,
corporateCLASheetId: process.env.CORPORATE_CLA_SHEET_ID,
};

const CONTRIBUTORS_URL =
"https://github.com/CesiumGS/cesium/blob/main/CONTRIBUTORS.md";

const main = async () => {
let hasSignedCLA;
let errorFoundOnCLACheck;

try {
hasSignedCLA = await checkIfUserHasSignedAnyCLA();
} catch (error) {
errorFoundOnCLACheck = error.toString();
}

const response = await postCommentOnPullRequest(
hasSignedCLA,
errorFoundOnCLACheck
);
};

const checkIfUserHasSignedAnyCLA = async () => {
let foundIndividualCLA = await checkIfIndividualCLAFound();
if (foundIndividualCLA) {
return true;
}

let foundCorporateCLA = await checkIfCorporateCLAFound();
return foundCorporateCLA;
};

const checkIfIndividualCLAFound = async () => {
const response = await getValuesFromGoogleSheet(
GOOGLE_SHEETS_INFO.individualCLASheetId,
"D2:D"
);

const rows = response.data.values;
for (let i = 0; i < rows.length; i++) {
if (rows[i].length === 0) {
continue;
}

const rowUsername = rows[i][0].toLowerCase();
if (PULL_REQUST_INFO.username.toLowerCase() === rowUsername) {
return true;
}
}

return false;
};

const checkIfCorporateCLAFound = async () => {
const response = await getValuesFromGoogleSheet(
GOOGLE_SHEETS_INFO.corporateCLASheetId,
"H2:H"
);

const rows = response.data.values;
for (let i = 0; i < rows.length; i++) {
if (rows[i].length === 0) {
continue;
}

// We're more lenient with the ScheduleA username check since it's an unformatted text field.
let rowScheduleA = rows[i][0].toLowerCase();
rowScheduleA = rowScheduleA.replace(/\n/g, " ");
const words = rowScheduleA.split(" ");

for (let j = 0; j < words.length; j++) {
// Checking for substrings because many GitHub usernames added as "github.com/username".
if (words[j].includes(PULL_REQUST_INFO.username.toLowerCase())) {
return true;
}
}
}

return false;
};

const getValuesFromGoogleSheet = async (sheetId, cellRanges) => {
const googleSheetsApi = await getGoogleSheetsApiClient();

return googleSheetsApi.spreadsheets.values.get({
spreadsheetId: sheetId,
range: cellRanges,
});
};

const getGoogleSheetsApiClient = async () => {
const googleConfigFilePath = "GoogleConfig.json";
fs.writeFileSync(googleConfigFilePath, GOOGLE_SHEETS_INFO.APIKeys);

const auth = new google.auth.GoogleAuth({
keyFile: googleConfigFilePath,
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
});
const googleAuthClient = await auth.getClient();

return google.sheets({ version: "v4", auth: googleAuthClient });
};

const postCommentOnPullRequest = async (hasSignedCLA, errorFoundOnCLACheck) => {
const octokit = new Octokit();

return octokit.request(
`POST /repos/${PULL_REQUST_INFO.owner}/${PULL_REQUST_INFO.repoName}/issues/${PULL_REQUST_INFO.id}/comments`,
{
owner: PULL_REQUST_INFO.username,
repo: PULL_REQUST_INFO.repoName,
issue_number: PULL_REQUST_INFO.id,
body: getCommentBody(hasSignedCLA, errorFoundOnCLACheck),
headers: {
authorization: `bearer ${PULL_REQUST_INFO.gitHubToken}`,
accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
}
);
};

const getCommentBody = (hasSignedCLA, errorFoundOnCLACheck) => {
const commentTemplate = fs.readFileSync(
"./.github/actions/check-for-CLA/templates/pullRequestComment.hbs",
"utf-8"
);

const getCommentFromTemplate = Handlebars.compile(commentTemplate);
const commentBody = getCommentFromTemplate({
errorCla: errorFoundOnCLACheck,
hasCla: hasSignedCLA,
username: PULL_REQUST_INFO.username,
contributorsUrl: CONTRIBUTORS_URL,
});

return commentBody;
};

main();
20 changes: 20 additions & 0 deletions .github/actions/check-for-CLA/templates/pullRequestComment.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{#if errorCla}}
:red_circle: There was an error checking the CLA! If this is your first contribution, please send in a [Contributor License Agreement](https://github.com/CesiumGS/cesium/blob/main/CONTRIBUTING.md#contributor-license-agreement-cla).
```
{{ errorCla }}
```
{{else}}
{{#if hasCla}}
Thank you for the pull request, @{{ username }}!

:white_check_mark: We can confirm we have a CLA on file for you.
{{else}}
Thank you for the pull request, @{{ username }}! Welcome to the Cesium community!

In order for us to review your PR, please complete the following steps:
- [ ] Send in a [Contributor License Agreement](https://github.com/CesiumGS/cesium/blob/main/CONTRIBUTING.md#contributor-license-agreement-cla) (CLA)
- [ ] Add yourself to the [contributors]({{ contributorsUrl }}) file

Review [Pull Request Guidelines](https://github.com/CesiumGS/cesium/blob/main/CONTRIBUTING.md#pull-request-guidelines) to make sure your PR gets accepted quickly.
{{/if}}
{{/if}}
27 changes: 27 additions & 0 deletions .github/workflows/cla.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CLA Checking
on:
pull_request:
types: [opened]

jobs:
check-cla:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/checkout@v3
- name: install node 20
uses: actions/setup-node@v3
with:
node-version: '20'
- name: install npm packages
run: npm install googleapis @octokit/core handlebars fs-extra
- name: run script
run: node .github/actions/check-for-CLA/index.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULL_REQUEST_ID: ${{ github.event.number }}
GOOGLE_KEYS: ${{ secrets.GOOGLE_KEYS }}
INDIVIDUAL_CLA_SHEET_ID: ${{ secrets.INDIVIDUAL_CLA_SHEET_ID }}
CORPORATE_CLA_SHEET_ID: ${{ secrets.CORPORATE_CLA_SHEET_ID }}
2 changes: 1 addition & 1 deletion .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,4 @@ jobs:
run: npm pack &> /dev/null
- name: package workspace modules
run: npm pack --workspaces &> /dev/null
- uses: ./.github/actions/verify-package
- uses: ./.github/actions/verify-package
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ yarn.lock
.idea/workspace.xml
.idea/tasks.xml
.idea/shelf

# Used in the CLA checking GitHub workflow
GoogleConfig.json

0 comments on commit f3a3dfa

Please sign in to comment.