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: implement requiredStatusChecks for GitHub and GitLab #4597

Closed
wants to merge 2 commits into from
Closed
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
59 changes: 43 additions & 16 deletions lib/platform/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,23 +561,26 @@ export async function getBranchPr(branchName: string) {
// Returns the combined status for a branch.
export async function getBranchStatus(
branchName: string,
requiredStatusChecks: any
requiredStatusChecks: string[] | null
) {
logger.debug(`getBranchStatus(${branchName})`);
if (!requiredStatusChecks) {
// null means disable status checks, so it always succeeds
logger.debug('Status checks disabled = returning "success"');
return 'success';
}
if (requiredStatusChecks.length) {
// This is Unsupported
logger.warn({ requiredStatusChecks }, `Unsupported requiredStatusChecks`);
return 'failed';
}
const commitStatusUrl = `repos/${config.repository}/commits/${branchName}/status`;
let commitStatus;
const commitStatusUrl = `repos/${config.repository}/commits/${branchName}/statuses`;
let commitStatuses: {
context: string;
state: string;
}[];
try {
commitStatus = (await api.get(commitStatusUrl)).body;
commitStatuses = (await api.get<
{
context: string;
state: string;
}[]
>(commitStatusUrl)).body;
} catch (err) /* istanbul ignore next */ {
if (err.statusCode === 404) {
logger.info(
Expand All @@ -588,10 +591,7 @@ export async function getBranchStatus(
logger.info('Unknown error when checking branch status');
throw err;
}
logger.debug(
{ state: commitStatus.state, statuses: commitStatus.statuses },
'branch status check result'
);
logger.debug({ statuses: commitStatuses }, 'branch status check result');
let checkRuns: { name: string; status: string; conclusion: string }[] = [];
if (!config.isGhe) {
try {
Expand Down Expand Up @@ -629,17 +629,44 @@ export async function getBranchStatus(
}
}
}

const filteredStatuses = commitStatuses.filter(
status =>
requiredStatusChecks.includes(status.context) ||
requiredStatusChecks.length === 0
);
const contexts = filteredStatuses.map(s => s.context);
const states = filteredStatuses.map(s => s.state);
const allStatusesExists = requiredStatusChecks.every(s =>
contexts.includes(s)
);

let filteredStatusesCombined: string;
if (states.includes('failure') || states.includes('error')) {
filteredStatusesCombined = 'failed';
} else if (
states.includes('pending') ||
!allStatusesExists ||
(requiredStatusChecks.length === 0 && filteredStatuses.length === 0)
) {
filteredStatusesCombined = 'pending';
} else {
filteredStatusesCombined = 'success';
}

if (checkRuns.length === 0) {
return commitStatus.state;
return filteredStatusesCombined;
}

if (
commitStatus.state === 'failed' ||
filteredStatusesCombined === 'failed' ||
checkRuns.some(run => run.conclusion === 'failed')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be applying the same logic to check runs? What happens if the user applies requiredStatusChecks while also there are some check runs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see what I can do.

) {
return 'failed';
}

if (
(commitStatus.state === 'success' || commitStatus.statuses.length === 0) &&
(filteredStatusesCombined === 'success' || filteredStatuses.length === 0) &&
checkRuns.every(run => ['neutral', 'success'].includes(run.conclusion))
) {
return 'success';
Expand Down
60 changes: 34 additions & 26 deletions lib/platform/gitlab/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,6 @@ export async function getBranchStatus(
// null means disable status checks, so it always succeeds
return 'success';
}
if (Array.isArray(requiredStatusChecks) && requiredStatusChecks.length) {
// This is Unsupported
logger.warn({ requiredStatusChecks }, `Unsupported requiredStatusChecks`);
return 'failed';
}

if (!(await branchExists(branchName))) {
throw new Error('repository-changed');
Expand All @@ -336,27 +331,40 @@ export async function getBranchStatus(
const branchSha = await config.storage.getBranchCommit(branchName);
// Now, check the statuses for that commit
const url = `projects/${config.repository}/repository/commits/${branchSha}/statuses`;
const res = await api.get(url, { paginate: true });
logger.debug(`Got res with ${res.body.length} results`);
if (res.body.length === 0) {
// Return 'pending' if we have no status checks
return 'pending';
}
let status = 'success';
// Return 'success' if all are success
res.body.forEach((check: { status: string; allow_failure?: boolean }) => {
// If one is failed then don't overwrite that
if (status !== 'failure') {
if (!check.allow_failure) {
if (check.status === 'failed') {
status = 'failure';
} else if (check.status !== 'success') {
({ status } = check);
}
}
}
});
return status;
const res = await api.get<
{ status: string; name: string; allow_failure?: boolean }[]
>(url, { paginate: true });
const commitStatuses = res.body;
logger.debug(`Got res with ${commitStatuses.length} results`);
const filteredCommitStatuses = commitStatuses.filter(
status =>
requiredStatusChecks.length === 0 ||
requiredStatusChecks.includes(status.name)
);

const names = filteredCommitStatuses.map(s => s.name);
const statuses = filteredCommitStatuses
.filter(s => !s.allow_failure)
.map(s => s.status);
const allRequiredStatusExists = requiredStatusChecks.every(s =>
names.includes(s)
);

let combinedStatuses: string;
if (statuses.includes('failed') || statuses.includes('canceled')) {
combinedStatuses = 'failed';
} else if (
statuses.includes('pending') ||
statuses.includes('running') ||
!allRequiredStatusExists ||
(requiredStatusChecks.length === 0 && filteredCommitStatuses.length === 0)
) {
combinedStatuses = 'pending';
} else {
combinedStatuses = 'success';
}

return combinedStatuses;
}

export async function getBranchStatusCheck(
Expand Down
Loading