Skip to content

Commit

Permalink
chore: added green CI check
Browse files Browse the repository at this point in the history
  • Loading branch information
gentlementlegen committed Jul 7, 2024
1 parent ccf831a commit f419e76
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 18 deletions.
33 changes: 31 additions & 2 deletions src/helpers/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,37 @@ export async function getApprovalCount({ octokit, logger }: Context, { owner, re
pull_number: pullNumber,
});
return reviews.filter((review) => review.state === "APPROVED").length;
} catch (error) {
logger.error(`Error fetching reviews: ${error}`);
} catch (e) {
logger.error(`Error fetching reviews' approvals: ${e}`);
return 0;
}
}

export async function isCiGreen({ octokit, logger }: Context, sha: string, { owner, repo }: IssueParams) {
try {
const ref = sha;

const { data: checkSuites } = await octokit.checks.listSuitesForRef({
owner,
repo,
ref,
});

const checkSuitePromises = checkSuites.check_suites.map(async (suite) => {
const { data: checkRuns } = await octokit.checks.listForSuite({
owner,
repo,
check_suite_id: suite.id,
});

return checkRuns.check_runs.every((run) => run.conclusion === "success");
});

const checkResults = await Promise.all(checkSuitePromises);

return checkResults.every((result) => result);
} catch (e) {
logger.error(`Error checking CI status: ${e}`);
return false;
}
}
26 changes: 14 additions & 12 deletions src/helpers/update-pull-requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import ms from "ms";
import { PullRequest } from "../adapters/sqlite/entities/pull-request";
import { getAllTimelineEvents } from "../handlers/github-events";
import { Context } from "../types";
import { getApprovalCount, getMergeTimeoutAndApprovalRequiredCount, IssueParams, parseGitHubUrl } from "./github";
import { getApprovalCount, getMergeTimeoutAndApprovalRequiredCount, isCiGreen, IssueParams, parseGitHubUrl } from "./github";

type IssueEvent = {
created_at?: string;
Expand All @@ -11,17 +11,14 @@ type IssueEvent = {
commented_at?: string;
};

async function isPullMerged(context: Context, { repo, owner, issue_number: pullNumber }: IssueParams) {
try {
const res = await context.octokit.pulls.checkIfMerged({
async function getPullRequestDetails(context: Context, { repo, owner, issue_number: pullNumber }: IssueParams) {
return (
await context.octokit.pulls.get({
repo,
owner,
pull_number: pullNumber,
});
return res.status === 204;
} catch (e) {
return false;
}
})
).data;
}

export async function updatePullRequests(context: Context) {
Expand All @@ -33,8 +30,9 @@ export async function updatePullRequests(context: Context) {
for (const pullRequest of pullRequests) {
try {
const gitHubUrl = parseGitHubUrl(pullRequest.url);
const pullRequestDetails = await getPullRequestDetails(context, gitHubUrl);
context.logger.debug(`Processing pull-request ${pullRequest.url}...`);
if (await isPullMerged(context, gitHubUrl)) {
if (pullRequestDetails.merged) {
context.logger.info(`The pull request ${pullRequest.url} is already merged, nothing to do.`);
try {
await context.adapters.sqlite.pullRequest.delete(pullRequest.url);
Expand All @@ -56,8 +54,12 @@ export async function updatePullRequests(context: Context) {
const requirements = await getMergeTimeoutAndApprovalRequiredCount(context, gitHubUrl);
if (isNaN(lastActivityDate.getTime()) || isPastOffset(lastActivityDate, requirements.mergeTimeout)) {
if ((await getApprovalCount(context, gitHubUrl)) > 0) {
context.logger.info(`Pull-request ${pullRequest.url} is past its due date (${requirements} after ${lastActivityDate}), will merge.`);
await mergePullRequest(context, pullRequest, gitHubUrl);
if (await isCiGreen(context, pullRequestDetails.head.sha, gitHubUrl)) {
context.logger.info(`Pull-request ${pullRequest.url} is past its due date (${requirements} after ${lastActivityDate}), will merge.`);
await mergePullRequest(context, pullRequest, gitHubUrl);
} else {
context.logger.info(`Pull-request ${pullRequest.url} does not pass all CI tests, won't merge.`);
}
} else {
context.logger.info(`Pull-request ${pullRequest.url} does not have sufficient reviewer approvals to be merged.`);
}
Expand Down
10 changes: 10 additions & 0 deletions tests/__mocks__/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,14 @@ export const db = factory({
id: primaryKey(Number),
name: String,
},
pullRequests: {
id: primaryKey(Number),
head: {
sha: String,
},
},
ci: {
id: primaryKey(Number),
conclusion: String,
},
});
13 changes: 13 additions & 0 deletions tests/__mocks__/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { http, HttpResponse } from "msw";
import { db } from "./db";

/**
* Intercepts the routes and returns a custom payload
Expand All @@ -10,6 +11,9 @@ export const handlers = [
http.get("https://api.github.com/repos/:org/:repo/pulls/:id/merge", () => {
return HttpResponse.json();
}),
http.get("https://api.github.com/repos/:org/:repo/pulls/:id/reviews", () => {
return HttpResponse.json([]);
}),
http.get("https://api.github.com/repos/:org/:repo/issues/:id/timeline", () => {
return HttpResponse.json();
}),
Expand All @@ -19,4 +23,13 @@ export const handlers = [
http.get("https://api.github.com/repos/:org/:repo/collaborators/:login", () => {
return HttpResponse.json();
}),
http.get("https://api.github.com/repos/:org/:repo/commits/:id/check-suites", () => {
return HttpResponse.json({ check_suites: db.ci.getAll() });
}),
http.get("https://api.github.com/repos/:org/:repo/check-suites/:id/check-runs", () => {
return HttpResponse.json({ check_runs: db.ci.getAll() });
}),
http.get("https://api.github.com/repos/:org/:repo/pulls/:id", ({ params: { id } }) => {
return HttpResponse.json(db.pullRequests.findFirst({ where: { id: { equals: Number(id) } } }));
}),
];
16 changes: 16 additions & 0 deletions tests/__mocks__/seed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"pullRequests": [
{
"id": 1,
"head": {
"sha": "1"
}
}
],
"ci": [
{
"id": 1,
"conclusion": "success"
}
]
}
61 changes: 57 additions & 4 deletions tests/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { drop } from "@mswjs/data";
import { Octokit } from "@octokit/rest";
import { http, HttpResponse } from "msw";
import * as fs from "node:fs";
import { initializeDataSource } from "../src/adapters/sqlite/data-source";
import { PullRequest } from "../src/adapters/sqlite/entities/pull-request";
import { getMergeTimeoutAndApprovalRequiredCount } from "../src/helpers/github";
import { getMergeTimeoutAndApprovalRequiredCount, isCiGreen } from "../src/helpers/github";
import { db } from "./__mocks__/db";
import { server } from "./__mocks__/node";
import { expect, describe, beforeAll, beforeEach, afterAll, afterEach, it, jest } from "@jest/globals";
import { Context } from "../src/types";
import seed from "./__mocks__/seed.json";

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

const htmlUrl = "https://github.com/ubiquibot/automated-merging/pull/1";
const actionsGithubPackage = "@actions/github";
const issueParams = { owner: "ubiquibot", repo: "automated-merging", issue_number: 1 };

describe("Action tests", () => {
let dbName = `database/tests/test.db`;
Expand All @@ -23,6 +27,13 @@ describe("Action tests", () => {
jest.resetModules();
dbName = `database/tests/${expect.getState().currentTestName}.db`;
fs.rmSync(dbName, { force: true });
drop(db);
for (const table of Object.keys(seed)) {
const tableName = table as keyof typeof seed;
for (const row of seed[tableName]) {
db[tableName].create(row);
}
}
});

it("Should add a pull request in the DB on PR opened", async () => {
Expand Down Expand Up @@ -67,6 +78,15 @@ describe("Action tests", () => {
pr.url = htmlUrl;
pr.lastActivity = new Date();
await pr.save();
server.use(
http.get(
"https://api.github.com/repos/:org/:repo/pulls/:id/reviews",
() => {
return HttpResponse.json([{ state: "APPROVED" }, { state: "APPROVED" }]);
},
{ once: true }
)
);
jest.mock(actionsGithubPackage, () => ({
context: {
repo: {
Expand Down Expand Up @@ -146,7 +166,6 @@ describe("Action tests", () => {
pr.url = htmlUrl;
const lastActivityDate = new Date();
lastActivityDate.setDate(new Date().getDate() - 8);
console.log(lastActivityDate);
pr.lastActivity = lastActivityDate;
await pr.save();
server.use(
Expand Down Expand Up @@ -209,7 +228,7 @@ describe("Action tests", () => {
},
octokit: new Octokit(),
} as unknown as Context;
await expect(getMergeTimeoutAndApprovalRequiredCount(context, { owner: "ubiquibot", repo: "automated-merging", issue_number: 1 })).resolves.toEqual({
await expect(getMergeTimeoutAndApprovalRequiredCount(context, issueParams)).resolves.toEqual({
mergeTimeout: collaboratorMergeTimeout,
requiredApprovalCount: collaboratorMinimumApprovalsRequired,
});
Expand All @@ -222,9 +241,43 @@ describe("Action tests", () => {
{ once: true }
)
);
await expect(getMergeTimeoutAndApprovalRequiredCount(context, { owner: "ubiquibot", repo: "automated-merging", issue_number: 1 })).resolves.toEqual({
await expect(getMergeTimeoutAndApprovalRequiredCount(context, issueParams)).resolves.toEqual({
mergeTimeout: contributorMergeTimeout,
requiredApprovalCount: contributorMinimumApprovalsRequired,
});
});

it("Should check if the CI tests are all passing", async () => {
server.use(
http.get(
"https://api.github.com/repos/:org/:repo/commits/:id/check-suites",
() => {
return HttpResponse.json({ check_suites: [{ id: 1 }] });
},
{ once: true }
)
);
server.use(
http.get(
"https://api.github.com/repos/:org/:repo/check-suites/:id/check-runs",
() => {
return HttpResponse.json({ check_runs: [{ id: 1, conclusion: "success" }] });
},
{ once: true }
)
);
const context = {
logger: {
debug: console.log,
},
payload: {
pull_request: {
assignees: [{ login: "ubiquibot" }],
},
},
config: {},
octokit: new Octokit(),
} as unknown as Context;
await expect(isCiGreen(context, "1", issueParams)).resolves.toEqual(true);
});
});

0 comments on commit f419e76

Please sign in to comment.