Skip to content

Commit

Permalink
update release notes script (#4212)
Browse files Browse the repository at this point in the history
  • Loading branch information
jtoar authored Jan 20, 2022
1 parent 4c69711 commit 9841c31
Showing 1 changed file with 126 additions and 60 deletions.
186 changes: 126 additions & 60 deletions tasks/release-notes/release-notes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import yargs from 'yargs'
import { hideBin } from 'yargs/helpers'

/**
* If the user didn't set a GitHub token, exit early.
* If the user didn't provide a GitHub token, exit early.
*/
if (!process.env.GITHUB_TOKEN) {
console.log()
console.error(
` You have to provide a github token. Make sure there's a var named GITHUB_TOKEN in your env.`
` You have to provide a GitHub personal-access token (PAT) by setting it to an env var named "GITHUB_TOKEN"`
)
console.error(
` You can provision an personal access token here: https://github.com/settings/tokens`
` You can provision a PAT here: https://github.com/settings/tokens`
)
console.log()

Expand All @@ -44,29 +44,97 @@ function builder(yargs) {
.example('$0 v0.40.0', 'Build release notes for v0.40.0')
}

const milestonesQuery = `
query($title: String) {
const GET_MILESTONE_IDS = `
query GetMilestoneIds($title: String) {
repository(owner: "redwoodjs", name: "redwood") {
milestones(query: $title, first: 3, orderBy: { field: NUMBER, direction: DESC }) {
milestones(
query: $title
first: 100
orderBy: { field: NUMBER, direction: DESC }
) {
nodes {
title
id
pullRequests(first: 100) {
nodes {
number
title
author {
login
}
}
}
}
`

/**
* @param {string} title
*/
async function getMilestoneId(title) {
const {
repository: { milestones },
} = await octokit.graphql(GET_MILESTONE_IDS, { title })

let milestone = milestones.nodes.find(
(milestone) => milestone.title === title
)

if (!milestone) {
const [latestMilestone] = milestones.nodes
console.log(
`No milestone was provided; using the latest: ${latestMilestone.title}`
)
milestone = latestMilestone
}

return milestone
}

const GET_PRS_WITH_MILESTONE = `
query GetPRsWithMilestone($milestoneId: ID!, $after: String) {
node(id: $milestoneId) {
... on Milestone {
pullRequests(first: 100, after: $after) {
pageInfo {
hasNextPage
endCursor
}
nodes {
number
title
author {
login
}
labels(first: 100) {
nodes {
name
}
}
totalCount
}
totalCount
}
}
}
}
`

/**
* @param {{ milestoneId: string, after?: string }}
*/
async function getPRsWithMilestone({ milestoneId, after }) {
const {
node: { pullRequests },
} = await octokit.graphql(GET_PRS_WITH_MILESTONE, {
milestoneId,
after,
})

if (!pullRequests.pageInfo.hasNextPage) {
return pullRequests.nodes
}

const prs = await getPRsWithMilestone({
milestoneId,
after: pullRequests.pageInfo.endCursor,
})

return [...pullRequests.nodes, ...prs]
}

/**
* This function does pretty much all the work.
*
Expand All @@ -78,56 +146,19 @@ async function handler(argv) {
/**
* Get the milestone's title, id, and PRs.
*/
let title = argv.milestone
let prs

const {
repository: { milestones },
} = await octokit.graphql(milestonesQuery, { title })

/**
* If no milestone was provided, use the latest.
*/
if (title === undefined) {
console.log(milestones.nodes)
const [latestMilestone] = milestones.nodes

title = latestMilestone.title
prs = latestMilestone.pullRequests.nodes
} else {
const milestone = milestones.nodes.find(
(milestone) => milestone.title === title
)

prs = milestone.pullRequests.nodes
}

/**
* Interpolate the template and write to `${cwd}/${milestone}-release-notes.md`.
*
* @see {@link https://nodejs.org/docs/latest-v15.x/api/esm.html#esm_no_filename_or_dirname}
*/
const interpolate = template(
fs.readFileSync(
new URL('release-notes.md.template', import.meta.url),
'utf8'
)
)
const { title, id } = await getMilestoneId(argv.milestone)
const prs = await getPRsWithMilestone({ milestoneId: id })

const filename = new URL(`${title}-release-notes.md`, import.meta.url)
const filedata = interpolate({
uniqueContributors: getNoOfUniqueContributors(prs),
prsMerged: prs.length,
prsMerged: prs.filter((pr) => pr.author.login !== 'renovate').length,
...sortPRs(prs),
})

const filename = new URL(`${title}-release-notes.md`, import.meta.url)

fs.writeFileSync(filename, filedata)

console.log()
console.log(`Written to ${url.fileURLToPath(filename)}`)
console.log('Done')
console.log()
}

yargs(hideBin(process.argv))
Expand All @@ -145,6 +176,15 @@ yargs(hideBin(process.argv))
* Helper functions.
*/

/**
* Interpolate the template and write to `${cwd}/${milestone}-release-notes.md`.
*
* @see {@link https://nodejs.org/docs/latest-v15.x/api/esm.html#esm_no_filename_or_dirname}
*/
const interpolate = template(
fs.readFileSync(new URL('release-notes.md.template', import.meta.url), 'utf8')
)

/**
* A helper function for formatting PRs.
* A `pr` looks like:
Expand Down Expand Up @@ -172,19 +212,19 @@ function formatPR(pr) {
}

function getNoOfUniqueContributors(prs) {
const logins = prs.map((pr) => pr.author.login)
const logins = prs
.map((pr) => pr.author.login)
.filter((login) => login !== 'renovate')

return new Set(logins).size
}

/**
* This is just a stub till we have some kind of changesets integration.
*
* @param {Array<{
* number: number,
* title: string,
* author: {
* login: string,
* }
* author: { login: string }
* labels: { nodes: Array<{ name: string }> }
* }>} prs
*/
function sortPRs(prs) {
Expand All @@ -195,11 +235,37 @@ function sortPRs(prs) {
const manual = []

for (const pr of prs) {
/**
* Sort `packageDependencies` by author (i.e. renovate bot).
*/
if (pr.author.login === 'renovate') {
packageDependencies.push(`<li>${formatPR(pr)}</li>`)
continue
}

/**
* Sort the rest by label.
*/
const labels = pr.labels.nodes.map((label) => label.name)

if (labels.includes('release:feature')) {
features.push(`<li>${formatPR(pr)}</li>`)
continue
}

if (labels.includes('release:fix')) {
fixed.push(`<li>${formatPR(pr)}</li>`)
continue
}

if (labels.includes('release:chore')) {
chore.push(`<li>${formatPR(pr)}</li>`)
continue
}

/**
* Those that can't be sorted.
*/
manual.push(`- ${formatPR(pr)}`)
}

Expand Down

0 comments on commit 9841c31

Please sign in to comment.