Skip to content

Commit

Permalink
fix: external-prs better error messages (#1527)
Browse files Browse the repository at this point in the history
* Adds an .env.example so folks know how to configure this for local
  testing
* Reports in an error message what the valid commands are if you
  specified an invalid one
* Adds a bunch of comments in places
  • Loading branch information
ryscheng authored May 25, 2024
1 parent c1805fb commit 632af36
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 12 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test-deploy-owners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:

- name: Setup external pr tools
uses: ./.github/workflows/setup-external-pr-tools

- name: Initialize check
run: |
cd ops/external-prs &&
Expand All @@ -48,9 +48,9 @@ jobs:
echo "${{ github.event.pull_request.author_association }}"
- name: Login to google
uses: 'google-github-actions/auth@v2'
uses: "google-github-actions/auth@v2"
with:
credentials_json: '${{ secrets.GOOGLE_BQ_ADMIN_CREDENTIALS_JSON }}'
credentials_json: "${{ secrets.GOOGLE_BQ_ADMIN_CREDENTIALS_JSON }}"
create_credentials_file: true
if: ${{ contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR", "CONTRIBUTOR"]'), github.event.pull_request.author_association) }}

Expand Down
7 changes: 7 additions & 0 deletions ops/external-prs/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# .env

# GitHub App ID, which you can find here
# https://github.com/organizations/opensource-observer/settings/apps/oso-prs
PR_TOOLS_GITHUB_APP_ID=
# Base64 encoded private key for the GitHub App
PR_TOOLS_GITHUB_APP_PRIVATE_KEY=
19 changes: 18 additions & 1 deletion ops/external-prs/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ export class GHAppUtils {
});
}

/**
* Set a status comment on a PR
* This will try to keep updating the same comment if it exists
* You can have multiple comments by setting a `messageId`
**/
async setStatusComment(pr: number, message: string, messageId?: string) {
messageId = messageId || "external-pr-status-comment";
const messageIdText = `<!-- ${messageId} -->`;
Expand All @@ -92,6 +97,7 @@ export class GHAppUtils {
});
console.log(appCommentRefs);

// If this app has never commented on this PR, just create it
if (appCommentRefs.length === 0) {
await this.octo.rest.issues.createComment({
owner: this.repo.owner,
Expand All @@ -116,11 +122,13 @@ export class GHAppUtils {
comments.push(appComment);
}

// Look for the messageIdText
const matchingComments = comments.filter((c) => {
const body = c.data.body || "";
return body.trimStart().indexOf(messageIdText) === 0;
});

// Just create it if it doesn't exist yet
if (matchingComments.length === 0) {
await this.octo.rest.issues.createComment({
owner: this.repo.owner,
Expand All @@ -130,6 +138,8 @@ export class GHAppUtils {
});
return;
}

// Delete any duplicate comments with the same messageIdText
if (matchingComments.length > 1) {
logger.warn(
"multiple matching comments found. This isn't treated as an error. Deleting extra comments",
Expand All @@ -143,6 +153,7 @@ export class GHAppUtils {
});
}

// Update the existing comment
await this.octo.rest.issues.updateComment({
owner: this.repo.owner,
repo: this.repo.name,
Expand All @@ -169,6 +180,7 @@ export class GHAppUtils {
commentId: comment.data.id,
authorAssosication: comment.data.author_association,
author: comment.data.user?.login,
content: comment.data.body,
});

const login = comment.data.user?.login;
Expand Down Expand Up @@ -200,7 +212,12 @@ export class GHAppUtils {

const handler = handlers[match[1]];
if (!handler) {
throw new NoCommandError(`invalid command "${match[1]}`);
logger.warn(
`Valid commands include ${Object.keys(handlers)
.map((x) => `'${x}'`)
.join(", ")}`,
);
throw new NoCommandError(`invalid command /${match[1]}`);
}
const issueUrl = comment.data.issue_url;
const url = new URL(issueUrl);
Expand Down
18 changes: 14 additions & 4 deletions ops/external-prs/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@ type BeforeClientArgs = ArgumentsCamelCase<{
}>;

interface InitializePRCheck extends BaseArgs {
// Commit SHA
sha: string;
// GitHub user
login: string;
checkName: string;
}

/**
* Checks if the user has write access.
* If yes, we signal that we've already queued a job.
* Otherwise, signal that we need admin approval.
* This is typically run with the initiator of the pull request
**/
async function initializePrCheck(args: InitializePRCheck) {
logger.info({
message: "initializing the PR check",
Expand All @@ -49,8 +57,9 @@ async function initializePrCheck(args: InitializePRCheck) {
head_sha: args.sha,
status: CheckStatus.Queued,
output: {
title: "Test deployment queued",
summary: "Test deployment queued",
title: "Test workflow has been queued",
summary:
"Test workflow has been queued. Please check the corresponding owners workflow for the latest job status.",
},
});
} else {
Expand All @@ -62,7 +71,7 @@ async function initializePrCheck(args: InitializePRCheck) {
conclusion: CheckConclusion.ActionRequired,
output: {
title: `Approval required for ${args.checkName}`,
summary: `Approval required for the ${args.checkName} check.`,
summary: `Approval required for the ${args.checkName} check. Repo admins can run '/${args.checkName} LATEST_COMMIT_SHA'. Remember to use the latest commit SHA, or validation will fail.`,
},
});
}
Expand Down Expand Up @@ -96,8 +105,9 @@ const cli = yargs(hideBin(process.argv))
demandOption: true,
})
.middleware(async (args: BeforeClientArgs) => {
// Get base64-encoded private key from the environment
const buf = Buffer.from(args.githubAppPrivateKey as string, "base64"); // Ta-da

// Log into GitHub Octokit
const app = new App({
appId: args.githubAppId as string,
privateKey: buf.toString("utf-8"),
Expand Down
19 changes: 15 additions & 4 deletions ops/external-prs/src/ossd/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ function jsonlExport<T>(path: string, arr: Array<T>): Promise<void> {
}

interface ParseCommentArgs extends BaseArgs {
// Comment ID
comment: number;
// Output filename
output: string;
login: string;
}
Expand Down Expand Up @@ -686,6 +688,11 @@ async function validatePR(args: ValidatePRArgs) {
await pr.validate(args);
}

/**
* This command is called by external-prs-handle-comment as a check
* for whether we should run the validation logic,
* based on whether a valid command was called.
**/
async function parseOSSDirectoryComments(args: ParseCommentArgs) {
const enableValidation: CommmentCommandHandler<GithubOutput> = async (
command,
Expand All @@ -712,15 +719,19 @@ async function parseOSSDirectoryComments(args: ParseCommentArgs) {
});
};

const commandHandlers = {
// /validate <sha>
validate: enableValidation,
};

try {
const output = await args.appUtils.parseCommentForCommand<GithubOutput>(
args.comment,
{
validate: enableValidation,
},
commandHandlers,
);
await output.commit(args.output);
} catch (_e) {
} catch (e) {
logger.debug("Error", e);
await GithubOutput.write(args.output, {
deploy: "false",
});
Expand Down

0 comments on commit 632af36

Please sign in to comment.