Skip to content

Commit

Permalink
Add automation to close outdated pull requests
Browse files Browse the repository at this point in the history
Some error reports are pending regeneration of the spec (and labeled as such). This GitHub workflow automatically closes their pull requests (and deletes the underlying branch) once it detects the relevant spec has been updated.

At that point, either the errors will have been fixed by the regeneration, or a new report will be generated at the next analysis.
  • Loading branch information
dontcallmedom committed Jun 19, 2024
1 parent 88bf933 commit 73aedd1
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/clean-pending-reports.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Close potentially outdated report pull requests

on:
schedule:
- cron: '30 4 * * *'
workflow_dispatch:
jobs:
clean:
runs-on: ubuntu-latest
steps:
- name: Setup node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Checkout strudy
uses: actions/checkout@v4
- name: Install dependencies
run: |
npm ci
- name: Checkout webref
uses: actions/checkout@v4
with:
repository: w3c/webref
path: webref
- name: Close pull requests if needed
run: node src/reporting/clean-pending-regeneration.js webref/ed/
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

128 changes: 128 additions & 0 deletions src/reporting/clean-pending-regeneration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**
* Check GitHub pull requests labeled "pending-spec-regeneration"
* and close them if the relevant spec has been regenerated
* since the PR was created.
*/

const core = require('@actions/core');
const Octokit = require('../lib/octokit');
const fs = require('fs');
const path = require('path');
const matter = require('gray-matter');
const { loadCrawlResults } = require('../lib/util');

const owner = 'w3c';
const repo = 'strudy';



/**
* Check GitHub issues and PR referenced by patch files and drop patch files
* that only reference closed issues and PR.
*
* @function
* @return {String} A GitHub flavored markdown string that describes what
* patches got dropped and why. To be used in a possible PR. Returns an
* empty string when there are no patches to drop.
*/
async function dropPendingReportsWhenPossible (edCrawlResultsPath) {
// Find PRs labeled pending-spec-regenetation
const { data: issues } = (await octokit.rest.issues.listForRepo({
owner,
repo,
labels: "pending-spec-regeneration"
}));

const pull_requests = issues.filter(i => i.pull_request);
if (!pull_requests.length) {
console.log("No open pull requests labeled pending-spec-regeneration");
return [];
}
const crawl = await loadCrawlResults(edCrawlResultsPath);

const dropped = [];
for (const pr of pull_requests) {
// find relevant spec URL from issue body (the first markdown link there)
const m = pr.body.match(/\]\(([^\)]+)\)/);
if (!m) {
console.error(`Could not find link to spec in body of PR ${pr.number}, aborting`);
continue;
}
const url = m[1];
const spec = crawl.ed.find(s => s.nightly?.url === url);
if (!spec) {
console.error(`Could not find ${url} in webref crawl data, aborting`);
continue;
}
const lastModified = new Date(spec.crawlCacheInfo.lastModified);
// check if the last modified date in webref is later than the PR creation
if (lastModified.toJSON() > pr.created_at) {
console.log(`PR ${pr.number} may no longer be relevant, closing it`);
// if it is, delete the branch, and close the PR
const prData = await octokit.rest.pulls.get({
owner,
repo,
pull_number: pr.number
});
await octokit.rest.git.deleteRef({
owner,
repo,
ref: prData.head.ref
});
await octokit.rest.issues.update({
owner,
repo,
issue_number: pr.number,
state: "closed"
});
dropped.push(pr.pull_request.html_url);
}
}
return dropped;
}

/*******************************************************************************
Retrieve GH_TOKEN from environment, prepare Octokit and kick things off
*******************************************************************************/
const GH_TOKEN = (() => {
try {
return require('../../config.json').GH_TOKEN;
} catch {
return process.env.GH_TOKEN;
}
})();
if (!GH_TOKEN) {
console.error('GH_TOKEN must be set to some personal access token as an env variable or in a config.json file');
process.exit(1);
}

const octokit = new Octokit({
auth: GH_TOKEN
// log: console
});

let edCrawlResultsPath = process.argv[2];

if (!edCrawlResultsPath) {
console.error('Path to the webref crawl of editors draft needs to be provided as argument');
process.exit(1);
}

// Target the index file if needed
if (!edCrawlResultsPath.endsWith('index.json')) {
edCrawlResultsPath = path.join(edCrawlResultsPath, 'index.json');
}


dropPendingReportsWhenPossible(edCrawlResultsPath)
.then(res => {
core.exportVariable('dropped_reports', res);
console.log();
console.log('Set dropped_reports env variable');
console.log(res);
console.log('== The end ==');
})
.catch(err => {
console.error(err);
process.exit(1);
});

0 comments on commit 73aedd1

Please sign in to comment.