Skip to content

Commit

Permalink
Verify pull request contents
Browse files Browse the repository at this point in the history
  • Loading branch information
willsawyerrrr committed Jan 21, 2024
1 parent 65fdecc commit 26ea56c
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 1 deletion.
64 changes: 63 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { components } from "@octokit/openapi-types";
import { EmitterWebhookEvent } from "@octokit/webhooks";
import { APIGatewayEvent, APIGatewayProxyResult } from "aws-lambda";
import { SpawnSyncReturns, execSync } from "child_process";
import { writeFileSync } from "fs";
import { App } from "octokit";

import { safeOctokitRequest } from "./utils";
import { safeOctokitRequest, verifyFiles } from "./utils";

const requiredFiles = ["script.sh", "config.yaml"];

const app = new App({
appId: process.env.APP_ID!,
Expand Down Expand Up @@ -76,6 +81,63 @@ async function verify({ payload }: EmitterWebhookEvent<"pull_request">) {
repo: payload.repository.name,
});

const fileDiffs = await safeOctokitRequest(octokit.rest.pulls.listFiles, {
owner: payload.repository.owner.login,
repo: payload.repository.name,
pull_number: payload.pull_request.number,
});

const errors = await verifyFiles(
octokit.rest.repos.getContent,
payload,
fileDiffs,
requiredFiles
);

if (errors.length) {
errors.unshift("Failed to verify pull request contents:");
await safeOctokitRequest(octokit.rest.issues.updateComment, {
body: errors.join("\n"),
issue_number: payload.pull_request.number,
owner: payload.repository.owner.login,
repo: payload.repository.name,
comment_id: commentId,
});

return;
}

fileDiffs.forEach(async (fileDiff) => {
const { name, content } = (await safeOctokitRequest(octokit.rest.repos.getContent, {
owner: payload.repository.owner.login,
repo: payload.repository.name,
path: fileDiff.filename,
ref: payload.pull_request.head.sha,
})) as components["schemas"]["content-file"];

writeFileSync(`/tmp/${name}`, content, {
mode: name === "script.sh" ? 0o755 : 0o644,
});
});

let result: Buffer;
try {
result = execSync("multi-gitter run /tmp/script.sh --config /tmp/config.yaml --dry-run");
} catch (error) {
const stdout = (error as SpawnSyncReturns<Buffer>).stdout.toString();
const stderr = (error as SpawnSyncReturns<Buffer>).stderr.toString();

await safeOctokitRequest(octokit.rest.issues.updateComment, {
body: "Failed to run `multi-gitter`.",
issue_number: payload.pull_request.number,
owner: payload.repository.owner.login,
repo: payload.repository.name,
comment_id: commentId,
});

return;
}

await safeOctokitRequest(octokit.rest.issues.updateComment, {
body: "Done verifying.",
issue_number: payload.pull_request.number,
Expand Down
46 changes: 46 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RestEndpointMethods } from "@octokit/plugin-rest-endpoint-methods/dist-types/generated/method-types.js";
import { EmitterWebhookEvent } from "@octokit/webhooks";
import { RequestError } from "octokit";

function camelCaseToSentenceCase(str: string) {
Expand Down Expand Up @@ -42,3 +43,48 @@ export async function safeOctokitRequest<
throw error;
}
}

/**
* Verifies that the pull request contains the desired non-empty files.
*
* @param getContent the `octokit.rest.repos.getContent` method
* @param payload the pull request webhook payload
* @param fileDiffs the files in the pull request
* @param desiredFilenames the filenames to check for
*
* @returns markdown-formatted error messages for missing or empty files
*/
export async function verifyFiles(
getContent: RestEndpointMethods["repos"]["getContent"],
payload: EmitterWebhookEvent<"pull_request">["payload"],
fileDiffs: Awaited<ReturnType<RestEndpointMethods["pulls"]["listFiles"]>>["data"],
desiredFilenames: string[]
): Promise<string[]> {
const errors: string[] = [];

desiredFilenames.forEach(async (desiredFilename) => {
const file = fileDiffs.find((file) => file.filename === desiredFilename);
if (!file) {
return errors.push(` - \`${desiredFilename}\` does not exist`);
}

const content = await safeOctokitRequest(getContent, {
owner: payload.repository.owner.login,
repo: payload.repository.name,
path: desiredFilename,
ref: payload.pull_request.head.ref,
});

if (content instanceof Array) {
return errors.push(` - \`${desiredFilename}\` is a directory`);
}

if (content.type !== "file") {
return errors.push(` - \`script.sh\` is not a file - is a \`${content.type}\``);
} else if (!content.content) {
return errors.push(" - `script.sh` is empty");
}
});

return errors;
}

0 comments on commit 26ea56c

Please sign in to comment.