Finish automated release notes generation

This commit is contained in:
David Wilson
2018-10-31 13:42:03 -07:00
parent cdc8a23bc3
commit 120a2b3771
3 changed files with 98 additions and 23 deletions

View File

@@ -1,8 +1,9 @@
const semver = require('semver')
const changelog = require('pr-changelog')
const octokit = require('@octokit/rest')()
const changelog = require('pr-changelog')
const childProcess = require('child_process')
module.exports.get = async function(releaseVersion, githubToken) {
module.exports.get = async function (releaseVersion, githubToken) {
if (githubToken) {
octokit.authenticate({
type: 'oauth',
@@ -10,12 +11,12 @@ module.exports.get = async function(releaseVersion, githubToken) {
})
}
let releases = await octokit.repos.getReleases({owner: 'atom', repo: 'atom'})
let release = releases.data.find(r => semver.eq(r.name, releaseVersion))
const releases = await octokit.repos.getReleases({owner: 'atom', repo: 'atom'})
const release = releases.data.find(r => semver.eq(r.name, releaseVersion))
return release ? release.body : undefined
}
module.exports.generate = async function(releaseVersion, githubToken) {
module.exports.generateForVersion = async function (releaseVersion, githubToken, oldReleaseNotes) {
let oldVersion = null
let oldVersionName = null
const parsedVersion = semver.parse(releaseVersion)
@@ -36,8 +37,7 @@ module.exports.generate = async function(releaseVersion, githubToken) {
oldVersionName = `v${parsedVersion.major}.${parsedVersion.minor - 1}.0`
} else {
let releases = await octokit.repos.getReleases({owner: 'atom', repo: 'atom'})
let versions = releases.data.map(r => r.name)
oldVersion = 'v' + getPreviousVersion(releaseVersion, versions)
oldVersion = 'v' + getPreviousRelease(releaseVersion, releases.data).name
oldVersionName = oldVersion
}
@@ -59,28 +59,61 @@ module.exports.generate = async function(releaseVersion, githubToken) {
}
})
return `## Notable Changes\n\n\
**TODO**: Pull relevant changes here!\n\n\
const writtenReleaseNotes =
extractWrittenReleaseNotes(oldReleaseNotes) ||
'**TODO**: Pull relevant changes here!'
return `## Notable Changes\n
${writtenReleaseNotes}\n
<details>
<summary>All Changes</summary>\n\n
${allChangesText}\n\n
<summary>All Changes</summary>\n
${allChangesText}
</details>
`
}
function getPreviousVersion (version, allVersions) {
module.exports.generateForNightly = async function(releaseVersion, githubToken) {
const releases = await octokit.repos.getReleases({owner: 'atom', repo: 'atom-nightly-releases'})
const previousRelease = getPreviousRelease(releaseVersion, releases.data)
const oldReleaseNotes = previousRelease ? previousRelease.body : undefined
const latestCommitResult = childProcess.spawnSync('git', ['rev-parse', '--short', 'HEAD'])
if (latestCommitResult && oldReleaseNotes) {
const latestCommit = latestCommitResult.stdout.toString().trim()
const extractMatch = oldReleaseNotes.match(/atom\/atom\/compare\/([0-9a-f]{5,40})\.\.\.([0-9a-f]{5,40})/)
if (extractMatch) {
return `### Click [here](https://github.com/atom/atom/compare/${extractMatch[2]}...${latestCommit}) to see the changes included with this release! :atom: :night_with_stars:`
}
}
return undefined
}
function extractWrittenReleaseNotes (oldReleaseNotes) {
if (oldReleaseNotes) {
const extractMatch = oldReleaseNotes.match(/^## Notable Changes\r\n([\s\S]*)<details>/)
if (extractMatch && extractMatch[1]) {
return extractMatch[1].trim()
}
}
return undefined
}
function getPreviousRelease (version, allReleases) {
const versionIsStable = semver.prerelease(version) === null
// Make sure versions are sorted before using them
allVersions.sort(semver.rcompare)
allReleases.sort((v1, v2) => semver.rcompare(v1.name, v2.name))
for (let otherVersion of allVersions) {
if (versionIsStable && semver.prerelease(otherVersion)) {
for (let release of allReleases) {
if (versionIsStable && semver.prerelease(release.name)) {
continue
}
if (semver.lt(otherVersion, version)) {
return otherVersion
if (semver.lt(release.name, version)) {
return release
}
}

View File

@@ -80,3 +80,12 @@ jobs:
ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET)
displayName: Upload CI Artifacts to S3
condition: and(succeeded(), eq(variables['IsSignedZipBranch'], 'true'))
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: $(Build.SourcesDirectory)/out/OLD_RELEASE_NOTES.md
ArtifactName: OLD_RELEASE_NOTES.md
ArtifactType: Container
displayName: Upload OLD_RELEASE_NOTES.md
condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true'), eq(variables['buildArch'], 'x86'))
continueOnError: true

View File

@@ -1,8 +1,10 @@
'use strict'
const fs = require('fs')
const path = require('path')
const glob = require('glob')
const publishRelease = require('publish-release')
const releaseNotes = require('./lib/release-notes')
const uploadToS3 = require('./lib/upload-to-s3')
const uploadLinuxPackages = require('./lib/upload-linux-packages')
@@ -19,11 +21,12 @@ const argv = yargs
.wrap(yargs.terminalWidth())
.argv
const releaseVersion = CONFIG.computedAppVersion
const isNightlyRelease = CONFIG.channel === 'nightly'
const assetsPath = argv.assetsPath || path.join(CONFIG.repositoryRootPath, 'out')
const assetsPath = argv.assetsPath || CONFIG.buildOutputPath
const assetsPattern = '/**/*(*.exe|*.zip|*.nupkg|*.tar.gz|*.rpm|*.deb|RELEASES*|atom-api.json)'
const assets = glob.sync(assetsPattern, { root: assetsPath, nodir: true })
const bucketPath = argv.s3Path || `releases/v${CONFIG.computedAppVersion}/`
const bucketPath = argv.s3Path || `releases/v${releaseVersion}/`
if (!assets || assets.length === 0) {
console.error(`No assets found under specified path: ${assetsPath}`)
@@ -31,7 +34,7 @@ if (!assets || assets.length === 0) {
}
async function uploadArtifacts() {
console.log(`Uploading ${assets.length} release assets for ${CONFIG.computedAppVersion} to S3 under '${bucketPath}'`)
console.log(`Uploading ${assets.length} release assets for ${releaseVersion} to S3 under '${bucketPath}'`)
await uploadToS3(
process.env.ATOM_RELEASES_S3_KEY,
@@ -44,20 +47,50 @@ async function uploadArtifacts() {
await uploadLinuxPackages(
argv.linuxRepoName,
process.env.PACKAGE_CLOUD_API_KEY,
CONFIG.computedAppVersion,
releaseVersion,
assets)
} else {
console.log("Skipping upload of Linux packages")
console.log("Skipping upload of Linux packages")
}
const oldReleaseNotes =
await releaseNotes.get(
releaseVersion,
process.env.GITHUB_TOKEN)
if (oldReleaseNotes) {
const oldReleaseNotesPath = path.resolve(CONFIG.buildOutputPath, "OLD_RELEASE_NOTES.md")
console.log(`Saving existing ${releaseVersion} release notes to ${oldReleaseNotesPath}`)
fs.writeFileSync(oldReleaseNotesPath, oldReleaseNotes, 'utf8')
}
if (argv.createGithubRelease) {
console.log(`Creating GitHub release v${CONFIG.computedAppVersion}`)
console.log(`\nGenerating new release notes for ${releaseVersion}`)
let newReleaseNotes = ''
if (isNightlyRelease) {
newReleaseNotes =
await releaseNotes.generateForNightly(
releaseVersion,
process.env.GITHUB_TOKEN,
oldReleaseNotes)
} else {
newReleaseNotes =
await releaseNotes.generateForVersion(
releaseVersion,
process.env.GITHUB_TOKEN,
oldReleaseNotes)
}
console.log(`New release notes:\n\n${newReleaseNotes}`)
console.log(`Creating GitHub release v${releaseVersion}`)
const release =
await publishReleaseAsync({
token: process.env.GITHUB_TOKEN,
owner: 'atom',
repo: !isNightlyRelease ? 'atom' : 'atom-nightly-releases',
name: CONFIG.computedAppVersion,
body: newReleaseNotes,
tag: `v${CONFIG.computedAppVersion}`,
draft: !isNightlyRelease,
prerelease: CONFIG.channel !== 'stable',