diff --git a/script/vsts/lib/release-notes.js b/script/vsts/lib/release-notes.js
index 640ebd2cf..d19df9c17 100644
--- a/script/vsts/lib/release-notes.js
+++ b/script/vsts/lib/release-notes.js
@@ -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
-All Changes
\n\n
-${allChangesText}\n\n
+All Changes
\n
+${allChangesText}
`
}
-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]*)/)
+ 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
}
}
diff --git a/script/vsts/release-branch-build.yml b/script/vsts/release-branch-build.yml
index b48afebce..e3dfdf16e 100644
--- a/script/vsts/release-branch-build.yml
+++ b/script/vsts/release-branch-build.yml
@@ -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
diff --git a/script/vsts/upload-artifacts.js b/script/vsts/upload-artifacts.js
index ad5fa8d05..ea568e8e6 100644
--- a/script/vsts/upload-artifacts.js
+++ b/script/vsts/upload-artifacts.js
@@ -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',