Skip to content
Merged
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
26 changes: 15 additions & 11 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const {
} = require('./utils/needs-manual-prs');
const {
fetchInitiator,
getReleaseBranches,
getSemverForCommitRange,
getSupportedBranches,
isInvalidBranch,
Expand Down Expand Up @@ -49,7 +50,7 @@ app.get('/verify-semver', async (req, res) => {

const { branch } = req.query;

const branches = await getSupportedBranches();
const branches = await getReleaseBranches();
if (isInvalidBranch(branches, branch)) {
res.status(400).json({ error: `${branch} is not a valid branch` });
return;
Expand All @@ -76,7 +77,7 @@ app.get('/verify-semver', async (req, res) => {
app.post('/verify-semver', verifySlackRequest, async (req, res) => {
res.status(200).end();

const branches = await getSupportedBranches();
const branches = await getReleaseBranches();
const branch = req.body.text;

const initiator = await fetchInitiator(req);
Expand Down Expand Up @@ -127,15 +128,14 @@ app.post('/verify-semver', verifySlackRequest, async (req, res) => {
// Check for pull requests targeting a specified release branch
// that have not yet been merged.
app.post('/unmerged', verifySlackRequest, async (req, res) => {
const branches = await getSupportedBranches();
const branch = req.body.text;

const initiator = await fetchInitiator(req);
console.log(
`${initiator.name} initiated unmerged audit for branch: ${branch}`,
);

if (branch !== 'all' && isInvalidBranch(branches, branch)) {
if (branch !== 'all' && isInvalidBranch(await getReleaseBranches(), branch)) {
console.error(`${branch} is not a valid branch`);
await postToSlack(
{
Expand All @@ -150,7 +150,8 @@ app.post('/unmerged', verifySlackRequest, async (req, res) => {
console.log(`Auditing unmerged PRs on branch: ${branch}`);

try {
const branchesToCheck = branch === 'all' ? branches : [branch];
const branchesToCheck =
branch === 'all' ? await getSupportedBranches() : [branch];

let messages = [];
for (const branch of branchesToCheck) {
Expand Down Expand Up @@ -191,7 +192,6 @@ app.post('/unmerged', verifySlackRequest, async (req, res) => {
// Check for pull requests which have been merged to main and labeled
// with target/BRANCH_NAME that trop failed for and which still need manual backports.
app.post('/needs-manual', verifySlackRequest, async (req, res) => {
const branches = await getSupportedBranches();
const REMIND = 'remind';

let [branch, author, remind] = req.body.text.split(' ');
Expand All @@ -209,7 +209,7 @@ app.post('/needs-manual', verifySlackRequest, async (req, res) => {
`${initiator.name} initiated needs-manual audit for branch: ${branch}`,
);

if (branch !== 'all' && isInvalidBranch(branches, branch)) {
if (branch !== 'all' && isInvalidBranch(await getReleaseBranches(), branch)) {
console.error(`${branch} is not a valid branch`);
await postToSlack(
{
Expand Down Expand Up @@ -241,7 +241,8 @@ app.post('/needs-manual', verifySlackRequest, async (req, res) => {
}

try {
const branchesToCheck = branch === 'all' ? branches : [branch];
const branchesToCheck =
branch === 'all' ? await getSupportedBranches() : [branch];

let messages = [];
for (const branch of branchesToCheck) {
Expand Down Expand Up @@ -296,15 +297,16 @@ app.get('/unreleased', async (req, res) => {
const { branch } = req.query;

try {
const branches = await getSupportedBranches();
const result = {};

if (branch === 'all') {
const branches = await getSupportedBranches();
for (const b of branches) {
const { commits } = await fetchUnreleasedCommits(b);
result[b] = commits;
}
} else {
const branches = await getReleaseBranches();
if (isInvalidBranch(branches, branch)) {
return res
.status(400)
Expand All @@ -326,13 +328,13 @@ app.get('/unreleased', async (req, res) => {
// Check for commits which have been merged to a release branch but
// not been released in a beta or stable.
app.post('/unreleased', verifySlackRequest, async (req, res) => {
const branches = await getSupportedBranches();
const branch = req.body.text;

const initiator = await fetchInitiator(req);

// Allow for manual batch audit of all supported release branches.
if (branch === 'all') {
const branches = await getSupportedBranches();
console.log(
`${initiator.name} triggered audit for all supported release branches`,
);
Expand Down Expand Up @@ -368,6 +370,8 @@ app.post('/unreleased', verifySlackRequest, async (req, res) => {
`${initiator.name} initiated unreleased commit audit for branch: ${branch}`,
);

const branches = await getReleaseBranches();

if (isInvalidBranch(branches, branch)) {
console.error(`${branch} is not a valid branch`);
await postToSlack(
Expand Down Expand Up @@ -408,7 +412,7 @@ app.post('/unreleased', verifySlackRequest, async (req, res) => {
// Combines checks for all PRs that either need manual backport to a given
// release line or which are targeting said line and haven't been merged.
app.post('/audit-pre-release', verifySlackRequest, async (req, res) => {
const branches = await getSupportedBranches();
const branches = await getReleaseBranches();
const branch = req.body.text;

const initiator = await fetchInitiator(req);
Expand Down
13 changes: 13 additions & 0 deletions test/gh-api-calls.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const { parseArgs } = require('node:util');

const { fetchUnreleasedCommits } = require('../utils/unreleased-commits');
const {
getReleaseBranches,
getSemverForCommitRange,
getSupportedBranches,
releaseIsDraft,
Expand Down Expand Up @@ -52,6 +53,18 @@ describe('API tests', () => {
assert.ok(Array.isArray(prs));
});

it('can get release branches', async () => {
const branches = await getReleaseBranches();
assert.ok(Array.isArray(branches));
assert.strictEqual(branches.length, 10);
});

it('can get more release branches', async () => {
const branches = await getReleaseBranches(15);
assert.ok(Array.isArray(branches));
assert.strictEqual(branches.length, 15);
});

it('can get supported branches', async () => {
const branches = await getSupportedBranches();
assert.ok(Array.isArray(branches));
Expand Down
72 changes: 65 additions & 7 deletions utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,21 +122,78 @@ async function releaseIsDraft(tag) {
}
}

// Fetch an array of the currently supported branches.
async function getSupportedBranches() {
const resp = await fetch(
'https://releases.electronjs.org/schedule.json?eolGracePeriod=7',
);
async function fetchSchedule() {
const resp = await fetch('https://releases.electronjs.org/schedule.json');
if (!resp.ok) {
throw new Error(
`Failed to fetch supported branches: ${resp.status} ${resp.statusText}`,
`Failed to fetch release branches: ${resp.status} ${resp.statusText}`,
);
}
const schedule = await resp.json();
return Object.values(schedule);
}

async function branchExists(branch) {
const octokit = await getOctokit();

try {
await octokit.rest.repos.getBranch({
owner: ORGANIZATION_NAME,
repo: REPO_NAME,
branch,
});
} catch (err) {
// A 404 means the branch genuinely doesn't exist
if (err.status === 404) {
return false;
}
throw err;
}

return true;
}

return Object.values(schedule)
async function checkForNewReleaseBranch(latestVersion) {
const latestMajor = latestVersion.split('.')[0];
const nextMajorBranch = `${latestMajor}-x-y`;

if (await branchExists(nextMajorBranch)) {
return nextMajorBranch;
}

return null;
}

// Fetch an array of the currently supported branches.
async function getSupportedBranches() {
const schedule = await fetchSchedule();
const branches = schedule
.filter(({ status }) => ['prerelease', 'stable'].includes(status))
.map(({ branch }) => branch);

// Schedule is cached, so check GH for a new release branch
const newReleaseBranch = await checkForNewReleaseBranch(schedule[0].version);
if (newReleaseBranch) {
branches.unshift(newReleaseBranch);
branches.pop(); // Knock out the last stable branch if we have a new release branch
}

return branches;
}

// Fetch an array of newest N release branches
async function getReleaseBranches(n = 10) {
const schedule = await fetchSchedule();
const branches = schedule.slice(0, n).map(({ branch }) => branch);

// Schedule is cached, so check GH for a new release branch
const newReleaseBranch = await checkForNewReleaseBranch(schedule[0].version);
if (newReleaseBranch) {
branches.splice(1, 0, newReleaseBranch);
branches.pop();
}

return branches;
}

// Post a message to a Slack workspace.
Expand Down Expand Up @@ -215,6 +272,7 @@ function verifySlackRequest(req, res, next) {
module.exports = {
escapeSlackText,
fetchInitiator,
getReleaseBranches,
getSemverForCommitRange,
getSupportedBranches,
isInvalidBranch,
Expand Down