Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: automated cla flow with robust cla requirement check #20

Merged
merged 4 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading