2017-10-23 15:02:50 +00:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
2018-07-19 18:48:13 +00:00
|
|
|
if (!process.env.CI) require('dotenv-safe').load()
|
2018-05-14 21:21:51 +00:00
|
|
|
const args = require('minimist')(process.argv.slice(2), {
|
|
|
|
boolean: ['automaticRelease', 'notesOnly', 'stable']
|
|
|
|
})
|
2017-11-17 20:13:30 +00:00
|
|
|
const ciReleaseBuild = require('./ci-release-build')
|
2019-05-08 01:48:40 +00:00
|
|
|
const octokit = require('@octokit/rest')({
|
|
|
|
auth: process.env.ELECTRON_GITHUB_TOKEN
|
|
|
|
})
|
2017-10-23 15:02:50 +00:00
|
|
|
const { execSync } = require('child_process')
|
2018-02-12 16:01:50 +00:00
|
|
|
const { GitProcess } = require('dugite')
|
2018-12-03 21:28:10 +00:00
|
|
|
|
2017-10-23 15:02:50 +00:00
|
|
|
const path = require('path')
|
2018-01-31 23:40:38 +00:00
|
|
|
const readline = require('readline')
|
2019-06-24 17:18:04 +00:00
|
|
|
const releaseNotesGenerator = require('./notes/index.js')
|
|
|
|
const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js')
|
2018-12-06 19:00:10 +00:00
|
|
|
const bumpType = args._[0]
|
|
|
|
const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron'
|
2017-10-23 15:02:50 +00:00
|
|
|
|
2018-12-03 21:28:10 +00:00
|
|
|
require('colors')
|
2019-08-29 14:46:54 +00:00
|
|
|
const pass = '✓'.green
|
|
|
|
const fail = '✗'.red
|
2018-12-03 21:28:10 +00:00
|
|
|
|
2018-12-06 19:00:10 +00:00
|
|
|
if (!bumpType && !args.notesOnly) {
|
2020-03-20 15:12:18 +00:00
|
|
|
console.log('Usage: prepare-release [stable | minor | beta | nightly]' +
|
|
|
|
' (--stable) (--notesOnly) (--automaticRelease) (--branch)')
|
2017-10-23 15:02:50 +00:00
|
|
|
process.exit(1)
|
|
|
|
}
|
|
|
|
|
2018-08-16 15:57:12 +00:00
|
|
|
async function getNewVersion (dryRun) {
|
2018-08-20 15:17:47 +00:00
|
|
|
if (!dryRun) {
|
2018-12-06 19:00:10 +00:00
|
|
|
console.log(`Bumping for new "${bumpType}" version.`)
|
2018-01-31 23:40:38 +00:00
|
|
|
}
|
2019-06-24 17:18:04 +00:00
|
|
|
const bumpScript = path.join(__dirname, 'version-bumper.js')
|
2018-12-06 19:00:10 +00:00
|
|
|
const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`]
|
2019-01-09 02:09:42 +00:00
|
|
|
if (dryRun) scriptArgs.push('--dryRun')
|
2017-10-23 15:02:50 +00:00
|
|
|
try {
|
2018-09-13 16:10:51 +00:00
|
|
|
let bumpVersion = execSync(scriptArgs.join(' '), { encoding: 'UTF-8' })
|
2017-10-23 15:02:50 +00:00
|
|
|
bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim()
|
2018-10-02 01:56:31 +00:00
|
|
|
const newVersion = `v${bumpVersion}`
|
2018-01-31 23:40:38 +00:00
|
|
|
if (!dryRun) {
|
|
|
|
console.log(`${pass} Successfully bumped version to ${newVersion}`)
|
|
|
|
}
|
2017-10-23 15:02:50 +00:00
|
|
|
return newVersion
|
|
|
|
} catch (err) {
|
|
|
|
console.log(`${fail} Could not bump version, error was:`, err)
|
2018-08-16 15:57:12 +00:00
|
|
|
throw err
|
2017-10-23 15:02:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-10 20:01:38 +00:00
|
|
|
async function getReleaseNotes (currentBranch, newVersion) {
|
2018-12-06 19:00:10 +00:00
|
|
|
if (bumpType === 'nightly') {
|
2019-01-08 00:00:36 +00:00
|
|
|
return { text: 'Nightlies do not get release notes, please compare tags for info.' }
|
2018-08-16 15:57:12 +00:00
|
|
|
}
|
2017-10-23 15:02:50 +00:00
|
|
|
console.log(`Generating release notes for ${currentBranch}.`)
|
2019-01-10 20:01:38 +00:00
|
|
|
const releaseNotes = await releaseNotesGenerator(currentBranch, newVersion)
|
2018-11-06 20:06:11 +00:00
|
|
|
if (releaseNotes.warning) {
|
|
|
|
console.warn(releaseNotes.warning)
|
2018-05-14 21:21:51 +00:00
|
|
|
}
|
2017-10-23 15:02:50 +00:00
|
|
|
return releaseNotes
|
|
|
|
}
|
|
|
|
|
|
|
|
async function createRelease (branchToTarget, isBeta) {
|
2018-10-02 01:56:31 +00:00
|
|
|
const newVersion = await getNewVersion()
|
2019-01-10 20:01:38 +00:00
|
|
|
const releaseNotes = await getReleaseNotes(branchToTarget, newVersion)
|
2018-02-12 16:01:50 +00:00
|
|
|
await tagRelease(newVersion)
|
2019-01-08 20:05:58 +00:00
|
|
|
|
2020-03-20 15:12:18 +00:00
|
|
|
console.log('Checking for existing draft release.')
|
2019-01-08 20:05:58 +00:00
|
|
|
const releases = await octokit.repos.listReleases({
|
2017-10-23 15:02:50 +00:00
|
|
|
owner: 'electron',
|
2018-08-16 16:12:06 +00:00
|
|
|
repo: targetRepo
|
2019-01-08 20:05:58 +00:00
|
|
|
}).catch(err => {
|
|
|
|
console.log(`${fail} Could not get releases. Error was: `, err)
|
|
|
|
})
|
|
|
|
|
2018-10-02 01:56:31 +00:00
|
|
|
const drafts = releases.data.filter(release => release.draft &&
|
2018-01-31 00:35:16 +00:00
|
|
|
release.tag_name === newVersion)
|
2017-10-23 15:02:50 +00:00
|
|
|
if (drafts.length > 0) {
|
|
|
|
console.log(`${fail} Aborting because draft release for
|
2017-11-06 21:11:34 +00:00
|
|
|
${drafts[0].tag_name} already exists.`)
|
2017-10-23 15:02:50 +00:00
|
|
|
process.exit(1)
|
|
|
|
}
|
|
|
|
console.log(`${pass} A draft release does not exist; creating one.`)
|
2019-01-08 20:05:58 +00:00
|
|
|
|
|
|
|
let releaseBody
|
|
|
|
let releaseIsPrelease = false
|
2017-10-23 15:02:50 +00:00
|
|
|
if (isBeta) {
|
2018-08-17 17:42:45 +00:00
|
|
|
if (newVersion.indexOf('nightly') > 0) {
|
2020-03-20 15:12:18 +00:00
|
|
|
releaseBody = 'Note: This is a nightly release. Please file new issues ' +
|
|
|
|
'for any bugs you find in it.\n \n This release is published to npm ' +
|
|
|
|
'under the nightly tag and can be installed via npm install electron@nightly, ' +
|
2019-08-12 20:01:52 +00:00
|
|
|
`or npm i electron-nightly@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`
|
2018-08-16 16:12:06 +00:00
|
|
|
} else {
|
2020-03-20 15:12:18 +00:00
|
|
|
releaseBody = 'Note: This is a beta release. Please file new issues ' +
|
|
|
|
'for any bugs you find in it.\n \n This release is published to npm ' +
|
|
|
|
'under the beta tag and can be installed via npm install electron@beta, ' +
|
2018-11-06 20:06:11 +00:00
|
|
|
`or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`
|
2018-08-16 16:12:06 +00:00
|
|
|
}
|
2019-01-08 20:05:58 +00:00
|
|
|
releaseIsPrelease = true
|
2018-01-31 23:40:38 +00:00
|
|
|
} else {
|
2019-04-30 18:36:39 +00:00
|
|
|
releaseBody = releaseNotes.text
|
2017-10-23 15:02:50 +00:00
|
|
|
}
|
2019-01-08 20:05:58 +00:00
|
|
|
|
|
|
|
const release = await octokit.repos.createRelease({
|
|
|
|
owner: 'electron',
|
|
|
|
repo: targetRepo,
|
|
|
|
tag_name: newVersion,
|
|
|
|
draft: true,
|
|
|
|
name: `electron ${newVersion}`,
|
|
|
|
body: releaseBody,
|
|
|
|
prerelease: releaseIsPrelease,
|
|
|
|
target_commitish: newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget
|
|
|
|
}).catch(err => {
|
|
|
|
console.log(`${fail} Error creating new release: `, err)
|
|
|
|
process.exit(1)
|
|
|
|
})
|
|
|
|
|
2018-12-19 02:51:38 +00:00
|
|
|
console.log(`Release has been created with id: ${release.data.id}.`)
|
2018-11-28 04:12:01 +00:00
|
|
|
console.log(`${pass} Draft release for ${newVersion} successful.`)
|
2017-10-23 15:02:50 +00:00
|
|
|
}
|
|
|
|
|
2018-05-14 21:21:51 +00:00
|
|
|
async function pushRelease (branch) {
|
2019-06-24 17:18:04 +00:00
|
|
|
const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR)
|
2017-10-23 15:02:50 +00:00
|
|
|
if (pushDetails.exitCode === 0) {
|
2018-02-12 16:01:50 +00:00
|
|
|
console.log(`${pass} Successfully pushed the release. Wait for ` +
|
2020-03-20 15:12:18 +00:00
|
|
|
'release builds to finish before running "npm run release".')
|
2017-10-23 15:02:50 +00:00
|
|
|
} else {
|
2019-01-08 20:05:58 +00:00
|
|
|
console.log(`${fail} Error pushing the release: ${pushDetails.stderr}`)
|
2017-10-23 15:02:50 +00:00
|
|
|
process.exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 16:01:50 +00:00
|
|
|
async function runReleaseBuilds (branch) {
|
|
|
|
await ciReleaseBuild(branch, {
|
2018-05-14 21:21:51 +00:00
|
|
|
ghRelease: true,
|
|
|
|
automaticRelease: args.automaticRelease
|
2017-11-17 20:13:30 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-02-12 16:01:50 +00:00
|
|
|
async function tagRelease (version) {
|
|
|
|
console.log(`Tagging release ${version}.`)
|
2020-03-20 15:12:18 +00:00
|
|
|
const checkoutDetails = await GitProcess.exec(['tag', '-a', '-m', version, version], ELECTRON_DIR)
|
2018-02-12 16:01:50 +00:00
|
|
|
if (checkoutDetails.exitCode === 0) {
|
|
|
|
console.log(`${pass} Successfully tagged ${version}.`)
|
|
|
|
} else {
|
|
|
|
console.log(`${fail} Error tagging ${version}: ` +
|
|
|
|
`${checkoutDetails.stderr}`)
|
|
|
|
process.exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-31 23:40:38 +00:00
|
|
|
async function verifyNewVersion () {
|
2018-10-02 01:56:31 +00:00
|
|
|
const newVersion = await getNewVersion(true)
|
2018-05-14 21:21:51 +00:00
|
|
|
let response
|
|
|
|
if (args.automaticRelease) {
|
|
|
|
response = 'y'
|
|
|
|
} else {
|
|
|
|
response = await promptForVersion(newVersion)
|
|
|
|
}
|
2018-01-31 23:40:38 +00:00
|
|
|
if (response.match(/^y/i)) {
|
|
|
|
console.log(`${pass} Starting release of ${newVersion}`)
|
|
|
|
} else {
|
|
|
|
console.log(`${fail} Aborting release of ${newVersion}`)
|
|
|
|
process.exit()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function promptForVersion (version) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const rl = readline.createInterface({
|
|
|
|
input: process.stdin,
|
|
|
|
output: process.stdout
|
|
|
|
})
|
|
|
|
rl.question(`Do you want to create the release ${version.green} (y/N)? `, (answer) => {
|
|
|
|
rl.close()
|
|
|
|
resolve(answer)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-08-19 00:36:16 +00:00
|
|
|
// function to determine if there have been commits to master since the last release
|
|
|
|
async function changesToRelease () {
|
2020-03-20 15:12:18 +00:00
|
|
|
const lastCommitWasRelease = new RegExp('^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$', 'g')
|
|
|
|
const lastCommit = await GitProcess.exec(['log', '-n', '1', '--pretty=format:\'%s\''], ELECTRON_DIR)
|
2018-08-19 00:36:16 +00:00
|
|
|
return !lastCommitWasRelease.test(lastCommit.stdout)
|
|
|
|
}
|
|
|
|
|
2017-10-23 15:02:50 +00:00
|
|
|
async function prepareRelease (isBeta, notesOnly) {
|
2018-08-20 15:17:47 +00:00
|
|
|
if (args.dryRun) {
|
2018-10-02 01:56:31 +00:00
|
|
|
const newVersion = await getNewVersion(true)
|
2018-08-20 15:17:47 +00:00
|
|
|
console.log(newVersion)
|
2017-10-23 15:02:50 +00:00
|
|
|
} else {
|
2019-06-24 17:18:04 +00:00
|
|
|
const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(ELECTRON_DIR)
|
2018-08-20 15:17:47 +00:00
|
|
|
if (notesOnly) {
|
2019-01-10 20:01:38 +00:00
|
|
|
const newVersion = await getNewVersion(true)
|
|
|
|
const releaseNotes = await getReleaseNotes(currentBranch, newVersion)
|
2018-11-06 20:06:11 +00:00
|
|
|
console.log(`Draft release notes are: \n${releaseNotes.text}`)
|
2018-08-19 00:36:16 +00:00
|
|
|
} else {
|
2018-08-20 15:17:47 +00:00
|
|
|
const changes = await changesToRelease(currentBranch)
|
|
|
|
if (changes) {
|
|
|
|
await verifyNewVersion()
|
|
|
|
await createRelease(currentBranch, isBeta)
|
|
|
|
await pushRelease(currentBranch)
|
|
|
|
await runReleaseBuilds(currentBranch)
|
|
|
|
} else {
|
2020-03-20 15:12:18 +00:00
|
|
|
console.log('There are no new changes to this branch since the last release, aborting release.')
|
2018-08-20 15:17:47 +00:00
|
|
|
process.exit(1)
|
|
|
|
}
|
2018-08-19 00:36:16 +00:00
|
|
|
}
|
2017-10-23 15:02:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
prepareRelease(!args.stable, args.notesOnly)
|