Skip to content

Commit

Permalink
Merge pull request #18 from bobvanderlinden/pr-graphql
Browse files Browse the repository at this point in the history
use GraphQL to fetch all PR information at once
  • Loading branch information
probot-auto-merge[bot] committed Aug 11, 2018
2 parents f1efd96 + 811c083 commit a7c8178
Show file tree
Hide file tree
Showing 12 changed files with 848 additions and 698 deletions.
6 changes: 3 additions & 3 deletions src/association.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Association } from "./github-models";
import { CommentAuthorAssociation } from "./github-models";

export const associations: Association[] = [
export const associations: CommentAuthorAssociation[] = [
'NONE',
'FIRST_TIMER',
'FIRST_TIME_CONTRIBUTOR',
Expand All @@ -10,7 +10,7 @@ export const associations: Association[] = [
'OWNER'
]

export function getAssociationPriority(association: Association): number {
export function getAssociationPriority(association: CommentAuthorAssociation): number {
// Note: this will return -1 for any association that is not found.
// This will prioritise unknown associations lowest.
return associations.indexOf(association);
Expand Down
135 changes: 71 additions & 64 deletions src/github-models.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,15 @@
import { ElementOf } from './utils';
export type PullRequestState = 'OPEN' | 'CLOSED' | 'MERGED'
export type MergeableState = 'MERGEABLE' | 'CONFLICTING' | 'UNKNOWN'
export type CommentAuthorAssociation = 'MEMBER' | 'OWNER' | 'COLLABORATOR' | 'CONTRIBUTOR' | 'FIRST_TIME_CONTRIBUTOR' | 'FIRST_TIMER' | 'NONE'
export type PullRequestReviewState = 'PENDING' | 'COMMENTED' | 'APPROVED' | 'CHANGES_REQUESTED' | 'DISMISSED'

export interface PullRequestInfo {
export interface PullRequestReference {
owner: string;
repo: string;
number: number;
}

export interface CheckPullRequest {
number: number;
}

export interface PullRequest {
base: {
ref: string
sha: string
repo: {
name: string
}
user: {
login: string
}
}
head: {
ref: string
sha: string
repo: {
name: string
}
user: {
login: string
}
}
number: number
state: 'open' | 'closed'
merged: boolean
mergeable: boolean,
labels: {
name: string
}[]
}

export type ReviewState = 'APPROVED' | 'CHANGES_REQUESTED'
export type Association = 'COLLABORATOR' | 'CONTRIBUTOR' | 'FIRST_TIMER' | 'FIRST_TIME_CONTRIBUTOR' | 'MEMBER' | 'NONE' | 'OWNER'
export interface Review {
submitted_at: string
state: ReviewState
user: {
login: string
}
author_association: Association
}

export interface CheckRun {
name: string
head_sha: string
Expand All @@ -58,25 +18,72 @@ export interface CheckRun {
conclusion: 'success' | 'failure' | 'neutral' | 'cancelled' | 'timed_out' | 'action_required'
}

export interface BranchProtection {
required_status_checks: {
strict: boolean
}
enforce_admins: {
enabled: boolean
}
required_pull_request_reviews: {
dismiss_stale_reviews: boolean
require_code_owner_reviews: boolean
required_approving_review_count: number
export interface PullRequestQueryResult {
repository: {
pullRequest: {
number: number,
state: PullRequestState,
mergeable: MergeableState,
potentialMergeCommit: {
oid: string
},
reviews: {
nodes: Array<{
authorAssociation: CommentAuthorAssociation,
author: {
login: string
}
submittedAt: string,
state: PullRequestReviewState
}>
},
labels: {
nodes: Array<{
name: string
}>
},
authorAssociation: CommentAuthorAssociation,
baseRef: {
repository: {
owner: {
login: string
},
name: string
},
target: {
oid: string
}
name: string
},
baseRefOid: string,
headRef: {
repository: {
owner: {
login: string
},
name: string
},
target: {
oid: string
}
name: string
},
headRefOid: string,
repository: {
protectedBranches: {
nodes: Array<{
name: string,
hasRestrictedPushes: boolean,
hasStrictRequiredStatusChecks: boolean
}>
}
}
}
}
restrictions: {}
}

export interface Branch {
name: string;
commit: {
sha: string
}
protected: boolean
export type PullRequestInfo = PullRequestQueryResult['repository']['pullRequest'] & {
checkRuns: CheckRun[]
}

export type Review = ElementOf<PullRequestInfo["reviews"]["nodes"]>
70 changes: 37 additions & 33 deletions src/pull-request-handler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { TaskScheduler } from "./task-scheduler";
import { PullRequest, HandlerContext, PullRequestInfo } from "./models";
import { HandlerContext, PullRequestReference, PullRequestInfo } from "./models";
import { result } from "./utils";
import { getPullRequestStatus, PullRequestStatus } from "./pull-request-status";
import { queryPullRequest } from "./pull-request-query";
const debug = require("debug")("pull-request-handler");

interface PullRequestTask {
context: HandlerContext;
pullRequestInfo: PullRequestInfo;
PullRequestReference: PullRequestReference;
}

const taskScheduler = new TaskScheduler<PullRequestTask>({
Expand All @@ -19,35 +20,35 @@ const pullRequestTimeouts: {

export function schedulePullRequestTrigger(
context: HandlerContext,
pullRequestInfo: PullRequestInfo
PullRequestReference: PullRequestReference
) {
const queueName = getRepositoryKey(pullRequestInfo);
const queueName = getRepositoryKey(PullRequestReference);
if (!taskScheduler.hasQueued(queueName)) {
taskScheduler.queue(queueName, { context, pullRequestInfo });
taskScheduler.queue(queueName, { context, PullRequestReference });
}
}

function getRepositoryKey({ owner, repo }: { owner: string, repo: string }) {
return `${owner}/${repo}`
}

function getPullRequestKey({ owner, repo, number }: PullRequestInfo) {
function getPullRequestKey({ owner, repo, number }: PullRequestReference) {
return `${owner}/${repo}#${number}`;
}

async function pullRequestWorker({
context,
pullRequestInfo
PullRequestReference
}: PullRequestTask) {
await handlePullRequestTrigger(context, pullRequestInfo);
await handlePullRequestTrigger(context, PullRequestReference);
}

async function handlePullRequestTrigger(
context: HandlerContext,
pullRequestInfo: PullRequestInfo
PullRequestReference: PullRequestReference
) {
const { log: appLog } = context;
const pullRequestKey = getPullRequestKey(pullRequestInfo);
const pullRequestKey = getPullRequestKey(PullRequestReference);

function log(msg: string) {
appLog(`${pullRequestKey}: ${msg}`);
Expand All @@ -61,14 +62,19 @@ async function handlePullRequestTrigger(
...context,
log
};
await doPullRequestWork(pullRequestContext, pullRequestInfo);
await doPullRequestWork(pullRequestContext, PullRequestReference);
}

async function doPullRequestWork(
context: HandlerContext,
pullRequestInfo: PullRequestInfo
PullRequestReference: PullRequestReference
) {
const { log } = context;
const pullRequestInfo = await queryPullRequest(
context.github,
PullRequestReference
);

const pullRequestStatus = await getPullRequestStatus(
context,
pullRequestInfo
Expand All @@ -83,40 +89,33 @@ export async function handlePullRequestStatus(
pullRequestStatus: PullRequestStatus
) {
const { log, github, config } = context;
const { owner, repo, number } = pullRequestInfo;

const pullRequestReference: PullRequestReference = {
owner: pullRequestInfo.baseRef.repository.owner.login,
repo: pullRequestInfo.baseRef.repository.name,
number: pullRequestInfo.number
}
switch (pullRequestStatus.code) {
case "ready_for_merge":
// We're ready for merging!
// This presses the merge button.
result(
await github.pullRequests.merge({
owner,
repo,
number,
...pullRequestReference,
merge_method: config.mergeMethod
})
);
if (config.deleteBranchAfterMerge) {
const pullRequest = result<PullRequest>(
await github.pullRequests.get({
owner,
repo,
number
})
);

// Check whether the pull request's branch was actually part of the same repo, as
// we do not want to (or rather do not have permission to) alter forks of this repo.
if (
pullRequest.head.user.login === owner &&
pullRequest.head.repo.name === repo
pullRequestInfo.headRef.repository.owner.login === pullRequestInfo.baseRef.repository.owner.login &&
pullRequestInfo.headRef.repository.name === pullRequestInfo.baseRef.repository.name
) {
result(
await github.gitdata.deleteReference({
owner,
repo,
ref: `heads/${pullRequest.head.ref}`
owner: pullRequestInfo.headRef.repository.owner.login,
repo: pullRequestInfo.headRef.repository.name,
ref: `heads/${pullRequestInfo.headRef.name}`
})
);
}
Expand All @@ -125,7 +124,12 @@ export async function handlePullRequestStatus(
case "out_of_date_branch":
if (config.updateBranch) {
// This merges the baseRef on top of headRef of the PR.
result(await github.repos.merge(pullRequestStatus.merge));
result(await github.repos.merge({
owner: pullRequestInfo.headRef.repository.owner.login,
repo: pullRequestInfo.headRef.repository.name,
base: pullRequestInfo.headRef.name,
head: pullRequestInfo.baseRef.name
}));
}
return;
case "pending_checks":
Expand All @@ -135,13 +139,13 @@ export async function handlePullRequestStatus(
// 1 minutes. The recheck is cancelled once another pull
// request event comes by.
log("Scheduling pull request trigger after 1 minutes");
const pullRequestKey = getPullRequestKey(pullRequestInfo);
const pullRequestKey = getPullRequestKey(pullRequestReference);
debug(`Setting timeout for ${pullRequestKey}`);
pullRequestTimeouts[pullRequestKey] = setTimeout(() => {
/* istanbul ignore next */
debug(`Timeout triggered for ${pullRequestKey}`);
/* istanbul ignore next */
schedulePullRequestTrigger(context, pullRequestInfo);
schedulePullRequestTrigger(context, pullRequestReference);
}, 1 * 60 * 1000);
return;
default:
Expand Down

0 comments on commit a7c8178

Please sign in to comment.