Skip to content

Commit

Permalink
feat: automated cla flow with robust cla requirement check
Browse files Browse the repository at this point in the history
  • Loading branch information
gitcommitshow authored Jun 22, 2024
2 parents 0dc09c0 + 84185b2 commit 40a138d
Show file tree
Hide file tree
Showing 15 changed files with 458 additions and 134 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ APP_ID="11"
GITHUB_APP_PRIVATE_KEY_BASE64="base64 encoded private key"
PRIVATE_KEY_PATH="very/secure/location/gh_app_key.pem"
WEBHOOK_SECRET="secret"
WEBSITE_ADDRESS="https://github.app.home"
82 changes: 73 additions & 9 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import dotenv from "dotenv";
import fs from "fs";
import http from "http";
import url from "url";
import { Octokit, App } from "octokit";
import { createNodeMiddleware } from "@octokit/webhooks";
import { routes } from "./routes.js";
import { routes } from "./src/routes.js";
import {
getMessage,
isCLARequired,
isMessageAfterMergeRequired,
} from "./src/helpers.js";

// Load environment variables from .env file
dotenv.config();
Expand All @@ -26,7 +32,6 @@ const privateKey =
);
const secret = process.env.WEBHOOK_SECRET;
const enterpriseHostname = process.env.ENTERPRISE_HOSTNAME;
const messageForNewPRs = fs.readFileSync("./message.md", "utf8");

// Create an authenticated Octokit client authenticated as a GitHub App
const app = new App({
Expand Down Expand Up @@ -54,11 +59,63 @@ app.webhooks.on("pull_request.opened", async ({ octokit, payload }) => {
`Received a pull request event for #${payload.pull_request.number}`,
);
try {
if (!isCLARequired(payload.pull_request)) {
return;
}
// If the user is not a member of the organization and haven't yet signed CLA,
// ask them to sign the CLA
const comment = getMessage("ask-to-sign-cla", {
username: payload.pull_request.user.login,
org: payload.repository.owner.login,
repo: payload.repository.name,
pr_number: payload.pull_request.number,
});
await octokit.rest.issues.createComment({
owner: payload.repository.owner.login,
repo: payload.repository.name,
issue_number: payload.pull_request.number,
body: messageForNewPRs,
body: comment,
});
// Add a label to the PR
octokit.rest.issues.addLabels({
owner: payload.repository.owner.login,
repo: payload.repository.name,
issue_number: payload.pull_request.number,
labels: ["Pending CLA"],
});
} catch (error) {
if (error.response) {
console.error(
`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`,
);
} else {
console.error(error);
}
}
});

app.webhooks.on("pull_request.closed", async ({ octokit, payload }) => {
console.log(
`Closed a pull request event for #${payload.pull_request.number}`,
);
if (!payload.pull_request.merged) return;
console.log(`This PR is merged`);
try {
if (!isMessageAfterMergeRequired()) {
return;
}
console.log(`Going to notify the PR author...`);
const comment = getMessage("message-after-merge", {
username: payload.pull_request.user.login,
org: payload.repository.owner.login,
repo: payload.repository.name,
pr_number: payload.pull_request.number,
});
await octokit.rest.issues.createComment({
owner: payload.repository.owner,
repo: payload.repository.name,
issue_number: payload.pull_request.number,
body: comment,
});
} catch (error) {
if (error.response) {
Expand All @@ -72,7 +129,9 @@ app.webhooks.on("pull_request.opened", async ({ octokit, payload }) => {
});

app.webhooks.on("issues.opened", async ({ octokit, payload }) => {
console.log(`Received a new issue event for #${payload.issue.number}`);
console.log(
`Received a new issue event for #${payload.issue.number} by ${pull_request.user.type}: ${pull_request.user.login}`,
);
try {
await octokit.rest.issues.createComment({
owner: payload.repository.owner.login,
Expand Down Expand Up @@ -118,15 +177,20 @@ const middleware = createNodeMiddleware(app.webhooks, { path });

http
.createServer((req, res) => {
switch (req.method + " " + req.url) {
const parsedUrl = url.parse(req.url);
const pathWithoutQuery = parsedUrl.pathname;
const queryString = parsedUrl.query;
console.log(req.method + " " + pathWithoutQuery);
if (queryString) console.log(queryString.substring(0, 20) + "...");
switch (req.method + " " + pathWithoutQuery) {
case "GET /":
routes.home(req, res);
break;
case "GET /form":
routes.form(req, res);
case "GET /cla":
routes.cla(req, res);
break;
case "POST /form":
routes.submitForm(req, res);
case "POST /cla":
routes.submitCla(req, res, app);
break;
case "POST /api/webhook":
middleware(req, res);
Expand Down
45 changes: 0 additions & 45 deletions form.html

This file was deleted.

15 changes: 0 additions & 15 deletions helpers.js

This file was deleted.

34 changes: 0 additions & 34 deletions home.html

This file was deleted.

5 changes: 0 additions & 5 deletions message.md

This file was deleted.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "starter_github_app",
"private": true,
"version": "0.0.1",
"name": "rudder_github_app",
"private": false,
"version": "0.0.2",
"type": "module",
"scripts": {
"lint": "standard",
Expand All @@ -15,5 +15,8 @@
"dependencies": {
"dotenv": "^16.0.3",
"octokit": "^3.1.2"
},
"engines": {
"node": ">=20"
}
}
Loading

0 comments on commit 40a138d

Please sign in to comment.