Unverified Commit cc58249b authored by Robert Knight's avatar Robert Knight Committed by GitHub

Merge pull request #1099 from hypothesis/fix-changelog-script

Fix GitHub release generation script
parents b2ad6fb3 8833cb99
...@@ -6,19 +6,30 @@ const wrapText = require('wrap-text'); ...@@ -6,19 +6,30 @@ const wrapText = require('wrap-text');
/** /**
* Return a `Date` indicating when a Git tag was created. * Return a `Date` indicating when a Git tag was created.
*
* The tag creation date is used for annotated tags, otherwise there is a
* fallback to the date of the tagged commit.
*/ */
function getTagDate(tag) { function getTagDate(tag) {
const result = execSync(`git tag --list "${tag}" "--format=%(taggerdate)"`, { const result = execSync(
`git tag --list "${tag}" "--format=%(committerdate) -- %(taggerdate)"`,
{
encoding: 'utf-8', encoding: 'utf-8',
}); }
return new Date(result.trim()); );
const [commitDate, tagDate] = result.trim().split('--');
const date = new Date(tagDate || commitDate);
if (isNaN(date)) {
throw new Error(`Unable to determine tag date of tag "${tag}"`);
}
return date;
} }
/** /**
* Return the name of the most recently created Git tag. * Return the name of the tag with the highest version number.
*/ */
function getLastTag() { function getHighestVersionTag() {
const result = execSync('git tag --list --sort=-taggerdate', { const result = execSync('git tag --list --sort=-version:refname', {
encoding: 'utf-8', encoding: 'utf-8',
}); });
const tags = result.split('\n').map(line => line.trim()); const tags = result.split('\n').map(line => line.trim());
...@@ -33,14 +44,11 @@ function getLastTag() { ...@@ -33,14 +44,11 @@ function getLastTag() {
* Iterate over items in a GitHub API response and yield each item, fetching * Iterate over items in a GitHub API response and yield each item, fetching
* additional pages of results as necessary. * additional pages of results as necessary.
*/ */
async function* itemsInGitHubAPIResponse(octokit, response) { async function* itemsInGitHubAPIResponse(octokit, options) {
let isFirstPage = true; for await (const page of octokit.paginate.iterator(options)) {
while (isFirstPage || octokit.hasNextPage(response)) { for (let item of page.data) {
isFirstPage = false;
for (let item of response.data) {
yield item; yield item;
} }
response = await octokit.getNextPage(response);
} }
} }
...@@ -50,7 +58,7 @@ async function* itemsInGitHubAPIResponse(octokit, response) { ...@@ -50,7 +58,7 @@ async function* itemsInGitHubAPIResponse(octokit, response) {
async function getPRsMergedSince(octokit, org, repo, tag) { async function getPRsMergedSince(octokit, org, repo, tag) {
const tagDate = getTagDate(tag); const tagDate = getTagDate(tag);
let response = await octokit.pullRequests.list({ const options = await octokit.pullRequests.list.endpoint.merge({
owner: org, owner: org,
repo, repo,
state: 'closed', state: 'closed',
...@@ -59,7 +67,7 @@ async function getPRsMergedSince(octokit, org, repo, tag) { ...@@ -59,7 +67,7 @@ async function getPRsMergedSince(octokit, org, repo, tag) {
}); });
const prs = []; const prs = [];
for await (const pr of itemsInGitHubAPIResponse(octokit, response)) { for await (const pr of itemsInGitHubAPIResponse(octokit, options)) {
if (!pr.merged_at) { if (!pr.merged_at) {
// This PR was closed without being merged. // This PR was closed without being merged.
continue; continue;
...@@ -100,12 +108,16 @@ async function getPRsMergedSince(octokit, org, repo, tag) { ...@@ -100,12 +108,16 @@ async function getPRsMergedSince(octokit, org, repo, tag) {
* ``` * ```
*/ */
function formatChangeList(pullRequests) { function formatChangeList(pullRequests) {
return pullRequests return (
.map(pr => `- ${pr.title} [#${pr.number}](${pr.url})`) pullRequests
// Skip automated dependency update PRs.
.filter(pr => !pr.labels.some(label => label.name === 'dependencies'))
.map(pr => `- ${pr.title} [#${pr.number}](${pr.html_url})`)
.map(item => wrapText(item, 90)) .map(item => wrapText(item, 90))
// Align the start of lines after the first with the text in the first line. // Align the start of lines after the first with the text in the first line.
.map(item => item.replace(/\n/mg, '\n ')) .map(item => item.replace(/\n/gm, '\n '))
.join('\n\n'); .join('\n\n')
);
} }
/** /**
...@@ -115,7 +127,7 @@ function formatChangeList(pullRequests) { ...@@ -115,7 +127,7 @@ function formatChangeList(pullRequests) {
* *
* Tag names are usually `vX.Y.Z` where `X.Y.Z` is the package version. * Tag names are usually `vX.Y.Z` where `X.Y.Z` is the package version.
*/ */
async function changelistSinceTag(octokit, tag=getLastTag()) { async function changelistSinceTag(octokit, tag = getHighestVersionTag()) {
const org = 'hypothesis'; const org = 'hypothesis';
const repo = 'client'; const repo = 'client';
...@@ -123,6 +135,20 @@ async function changelistSinceTag(octokit, tag=getLastTag()) { ...@@ -123,6 +135,20 @@ async function changelistSinceTag(octokit, tag=getLastTag()) {
return formatChangeList(mergedPRs); return formatChangeList(mergedPRs);
} }
if (require.main === module) {
const Octokit = require('@octokit/rest');
const octokit = new Octokit({ auth: `token ${process.env.GITHUB_TOKEN}` });
const tag = process.argv[2] || getHighestVersionTag();
changelistSinceTag(octokit, tag)
.then(changes => {
console.log(changes);
})
.catch(err => {
console.error('Unable to generate change list:', err);
});
}
module.exports = { module.exports = {
changelistSinceTag, changelistSinceTag,
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment