Skip to content

Commit

Permalink
[New] add --commit to check status of commits instead of just PRs
Browse files Browse the repository at this point in the history
  • Loading branch information
Meghal Bisht authored and Green-Ranger11 committed Jan 30, 2022
1 parent 3a6fa74 commit 9e65a6a
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 60 deletions.
28 changes: 24 additions & 4 deletions bin/can-merge
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const parsePullRequest = require('../utils/parsePullRequest');
const parseRemoteUrl = require('../utils/parseRemoteUrl');
const pullRequestStatus = require('../utils/models/pullRequestStatus');
const getMessage = require('../utils/getMessage');
const evaluateCommitStatus = require('../utils/evaluateCommitStatus');
const commitStatus = require('../utils/models/commitStatus');

const {
GITHUB_TOKEN,
Expand All @@ -35,6 +37,13 @@ const args = Yargs
.help()
.strict()
.options({
commit: {
alias: 'c',
default: false,
demandOption: false,
describe: 'check commit status',
type: 'boolean',
},
pr: {
alias: 'p',
demandOption: false,
Expand Down Expand Up @@ -72,7 +81,6 @@ const args = Yargs
},
watch: {
alias: 'w',
default: false,
describe: 'watch pending checks',
type: 'boolean',
},
Expand Down Expand Up @@ -110,22 +118,33 @@ const args = Yargs

const token = args.token || GITHUB_TOKEN || GH_TOKEN;

// eslint-disable-next-line max-lines-per-function
function outputStatus(response) {
if (NODE_ENV === 'DEBUG') {
console.log(JSON.stringify(response, null, 2));
}
if (args.commit) {
if (response.repository.commit === null) {
console.info(chalk.redBright(`⚠ This remote repository does not contain any commits matching sha: ${args.sha}`));
} else {
const status = evaluateCommitStatus(response.repository.commit);
console.info(getMessage(status));
if (status !== commitStatus.STATUS_SUCCESS) {
process.exitCode = (process.exitCode ?? 0) + 1;
}
}
}
const prs = parsePullRequest(response);
if (prs.length === 0) {
if (prs.length === 0 && !args.commit) {
if (args.pr) {
console.info(chalk.redBright(`⚠ This remote repository does not contain any pull requests matching number: ${args.pr}`));
} else {
console.info(chalk.redBright(`⚠ This remote repository does not contain any pull requests matching sha: ${args.sha}`));
}
process.exitCode = 1;
} else {
prs.forEach((pr) => {
const status = evaluatePullRequest(pr);
console.info('PR:', pr.number, getMessage(status));
console.info('PR status:', pr.number, getMessage(status));
if (status !== pullRequestStatus.MERGEABLE) {
process.exitCode = (process.exitCode ?? 0) + 1;
}
Expand All @@ -152,6 +171,7 @@ const options = {

https.request(options, (response) => {
const params = {
commit: args.commit,
repo: parseRemoteUrl(response.headers?.location) ?? String(repo),
pr,
sha: pr ? null : sha,
Expand Down
130 changes: 84 additions & 46 deletions utils/buildQuery.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,103 @@
/* eslint-disable max-lines-per-function */

'use strict';

const pullRequestQuery = () => `
state
url
title
number
merged
mergeable
reviewDecision
potentialMergeCommit {
commitUrl
}
commits(last: 1) {
nodes {
commit {
statusCheckRollup {
state
contexts(last: 100) {
totalCount
pageInfo {
endCursor
hasNextPage
}
nodes {
__typename
... on CheckRun {
status
name
conclusion
// eslint-disable-next-line max-params
const pullRequestQuery = (name, owner, pr, sha) => `
search(query: "is:pr repo:${owner}/${name} ${sha ? `sha:${sha}` : ''} ${pr}", type:ISSUE, first: 100) {
issueCount
edges {
node {
... on PullRequest {
state
url
title
number
merged
mergeable
reviewDecision
potentialMergeCommit {
commitUrl
}
commits(last: 1) {
nodes {
commit {
statusCheckRollup {
state
contexts(last: 100) {
totalCount
pageInfo {
endCursor
hasNextPage
}
... on StatusContext {
state
context
description
nodes {
__typename
... on CheckRun {
status
name
conclusion
}
... on StatusContext {
state
context
description
}
}
}
}
}
}
}
}
}
}
}
`;

const commitStatusQuery = (name, owner, sha) => `
repository(name: "${name}", owner: "${owner}") {
commit: object(expression: "${sha}") {
... on Commit {
statusCheckRollup {
state
contexts(last: 100) {
totalCount
pageInfo {
endCursor
hasNextPage
}
nodes {
__typename
... on CheckRun {
status
name
conclusion
}
... on StatusContext {
state
context
description
}
}
}
}
}
}
}
`;

module.exports = function buildQuery({
name,
owner,
pr,
sha,
commit,
}) {
return `
{
search(query: "is:pr repo:${owner}/${name} ${sha ? `sha:${sha}` : ''} ${pr}", type:ISSUE, first: 100) {
issueCount
edges {
node {
... on PullRequest {
${pullRequestQuery()}
}
}
}
}
}
`;
return `${commit
? `{
${pullRequestQuery(name, owner, pr, sha)}
${commitStatusQuery(name, owner, sha)}
}`
: `{${pullRequestQuery(name, owner, pr, sha)}}`
}`;
};
20 changes: 20 additions & 0 deletions utils/evaluateCommitStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

const commitStatus = require('./models/commitStatus');

const success = (commit) => commit?.statusCheckRollup?.state === 'SUCCESS';
const failure = (commit) => commit?.statusCheckRollup?.state === 'ERROR' || commit?.statusCheckRollup?.state === 'FAILURE';
const pending = (commit) => commit?.statusCheckRollup?.state === 'PENDING';

module.exports = function evaluateCommitStatus(commit) {
if (success(commit)) {
return commitStatus.STATUS_SUCCESS;
}
if (failure(commit)) {
return commitStatus.STATUS_FAILURE;
}
if (pending(commit)) {
return commitStatus.STATUS_PENDING;
}
return commitStatus.STATUS_NOT_FOUND;
};
8 changes: 3 additions & 5 deletions utils/evaluatePending.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
'use strict';

module.exports = function evaluatePending(response) {
const { search: { edges: [{ node: { commits: { nodes: [{ commit: { statusCheckRollup } }] } } }] } } = response;
const pullRequestPending = response.search?.edges[0].node.commits.nodes[0].commit.statusCheckRollup.state === 'PENDING';
const commitRequestPending = response.repository?.commit.statusCheckRollup.state === 'PENDING';

// eslint-disable-next-line no-underscore-dangle
const pending = statusCheckRollup.contexts.nodes.filter((ctx) => (ctx.__typename === 'CheckRun' && ctx.status !== 'COMPLETED') || (ctx.__typename === 'StatusContext' && ctx.state === 'PENDING'));

return pending.length;
return pullRequestPending || commitRequestPending;
};
7 changes: 6 additions & 1 deletion utils/getMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const chalk = require('chalk');
const pullRequestStatus = require('./models/pullRequestStatus');
const commitStatus = require('./models/commitStatus');

const messages = {
[pullRequestStatus.CLOSED]: chalk.redBright(pullRequestStatus.CLOSED),
Expand All @@ -12,7 +13,11 @@ const messages = {
[pullRequestStatus.REVIEW_DISAPPROVED]: chalk.yellowBright(pullRequestStatus.REVIEW_DISAPPROVED),
[pullRequestStatus.REVIEW_REQUIRED]: chalk.redBright(pullRequestStatus.REVIEW_REQUIRED),
[pullRequestStatus.STATUS_FAILURE]: chalk.blueBright(pullRequestStatus.STATUS_FAILURE),
[pullRequestStatus.STATUS_PENDING]: chalk.yellowBright(pullRequestStatus.STATUS_PENDING),
[pullRequestStatus.STATUSCHECK_PENDING]: chalk.yellowBright(pullRequestStatus.STATUSCHECK_PENDING),
[commitStatus.STATUS_FAILURE]: chalk.redBright(commitStatus.STATUS_FAILURE),
[commitStatus.STATUS_PENDING]: chalk.yellowBright(commitStatus.STATUS_PENDING),
[commitStatus.STATUS_SUCCESS]: chalk.greenBright(commitStatus.STATUS_SUCCESS),
[commitStatus.STATUS_NOT_FOUND]: chalk.yellowBright(commitStatus.STATUS_NOT_FOUND),
};

module.exports = function getMessage(response) {
Expand Down
8 changes: 8 additions & 0 deletions utils/models/commitStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';

module.exports = {
STATUS_FAILURE: '⚠ Some commit checks were not successful',
STATUS_NOT_FOUND: 'ℹ No commit checks were found',
STATUS_PENDING: '⌛ Some commit checks are pending',
STATUS_SUCCESS: '✔ All commit checks are successful',
};
4 changes: 2 additions & 2 deletions utils/runQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
const { graphql } = require('@octokit/graphql');
const buildQuery = require('./buildQuery');

module.exports = async function runQuery({ repo, pr, sha, token }) {
module.exports = async function runQuery({ commit, repo, pr, sha, token }) {
const [owner, name] = repo.split('/');
let response = null;
try {
response = await graphql(buildQuery({ name, owner, pr, sha }), {
response = await graphql(buildQuery({ commit, name, owner, pr, sha }), {
headers: {
authorization: `token ${token}`,
},
Expand Down
4 changes: 2 additions & 2 deletions utils/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ const evaluatePending = require('../utils/evaluatePending');
module.exports = async function watch(retryDelay, getResponse) {
const response = await getResponse();

const pendingChecks = evaluatePending(response);
const isPendingChecks = evaluatePending(response);

if (pendingChecks === 0) {
if (!isPendingChecks) {
return response;
}

Expand Down

0 comments on commit 9e65a6a

Please sign in to comment.