Add logic to support automatic releases (#12177)

This commit is contained in:
John Kleinschmidt
2018-03-08 12:46:06 -05:00
committed by Charles Kerr
parent 76fb7ee87c
commit bb40bc4aca
4 changed files with 92 additions and 30 deletions

View File

@@ -58,6 +58,29 @@ jobs:
else
echo 'Skipping upload distribution because build is not for release'
fi
- run:
name: Optionally finish release
shell: /bin/sh
command: |
if [ "$ELECTRON_RELEASE" == "1" ] && [ "$AUTO_RELEASE" == "true" ]; then
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm install 8
nvm use 8
node script/release.js --automaticRelease
releaseExitCode=$?
if [ $? -eq 0 ]; then
echo 'Release successful, now publishing to npm'
echo "//registry.npmjs.org/:_authToken=$ELECTRON_NPM_TOKEN" >> ~/.npmrc
nvm use system
npm run publish
else
echo 'Release is not complete, skipping publish for now.'
fi
else
echo 'Skipping release check'
fi
- run:
name: Zip out directory
command: |

View File

@@ -37,7 +37,7 @@ async function makeRequest (requestOptions, parseResponse) {
})
}
async function circleCIcall (buildUrl, targetBranch, job, ghRelease) {
async function circleCIcall (buildUrl, targetBranch, job, options) {
assert(process.env.CIRCLE_TOKEN, 'CIRCLE_TOKEN not found in environment')
console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`)
let buildRequest = {
@@ -46,12 +46,16 @@ async function circleCIcall (buildUrl, targetBranch, job, ghRelease) {
}
}
if (ghRelease) {
if (options.ghRelease) {
buildRequest.build_parameters.ELECTRON_RELEASE = 1
} else {
buildRequest.build_parameters.RUN_RELEASE_BUILD = 'true'
}
if (options.automaticRelease) {
buildRequest.build_parameters.AUTO_RELEASE = 'true'
}
let circleResponse = await makeRequest({
method: 'POST',
url: buildUrl,
@@ -66,17 +70,21 @@ async function circleCIcall (buildUrl, targetBranch, job, ghRelease) {
console.log(`Check ${circleResponse.build_url} for status. (${job})`)
}
async function buildAppVeyor (targetBranch, ghRelease) {
async function buildAppVeyor (targetBranch, options) {
console.log(`Triggering AppVeyor to run build on branch: ${targetBranch} with release flag.`)
assert(process.env.APPVEYOR_TOKEN, 'APPVEYOR_TOKEN not found in environment')
let environmentVariables = {}
if (ghRelease) {
if (options.ghRelease) {
environmentVariables.ELECTRON_RELEASE = 1
} else {
environmentVariables.RUN_RELEASE_BUILD = 'true'
}
if (options.automaticRelease) {
environmentVariables.AUTO_RELEASE = 'true'
}
const requestOpts = {
url: buildAppVeyorURL,
auth: {
@@ -100,27 +108,27 @@ async function buildAppVeyor (targetBranch, ghRelease) {
console.log(`AppVeyor release build request successful. Check build status at ${buildUrl}`)
}
function buildCircleCI (targetBranch, ghRelease, job) {
function buildCircleCI (targetBranch, options) {
const circleBuildUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/tree/${targetBranch}?circle-token=${process.env.CIRCLE_TOKEN}`
if (job) {
assert(circleCIJobs.includes(job), `Unknown CI job name: ${job}.`)
circleCIcall(circleBuildUrl, targetBranch, job, ghRelease)
if (options.job) {
assert(circleCIJobs.includes(options.job), `Unknown CI job name: ${options.job}.`)
circleCIcall(circleBuildUrl, targetBranch, options.job, options)
} else {
circleCIJobs.forEach((job) => circleCIcall(circleBuildUrl, targetBranch, job, ghRelease))
circleCIJobs.forEach((job) => circleCIcall(circleBuildUrl, targetBranch, job, options))
}
}
async function buildJenkins (targetBranch, ghRelease, job) {
async function buildJenkins (targetBranch, options) {
assert(process.env.JENKINS_AUTH_TOKEN, 'JENKINS_AUTH_TOKEN not found in environment')
assert(process.env.JENKINS_BUILD_TOKEN, 'JENKINS_BUILD_TOKEN not found in environment')
let jenkinsCrumb = await getJenkinsCrumb()
if (job) {
assert(jenkinsJobs.includes(job), `Unknown CI job name: ${job}.`)
callJenkinsBuild(job, jenkinsCrumb, targetBranch, ghRelease)
if (options.job) {
assert(jenkinsJobs.includes(options.job), `Unknown CI job name: ${options.job}.`)
callJenkinsBuild(options.job, jenkinsCrumb, targetBranch, options)
} else {
jenkinsJobs.forEach((job) => {
callJenkinsBuild(job, jenkinsCrumb, targetBranch, ghRelease)
callJenkinsBuild(job, jenkinsCrumb, targetBranch, options)
})
}
}
@@ -143,15 +151,18 @@ async function callJenkins (path, requestParameters, requestHeaders) {
return jenkinsResponse
}
async function callJenkinsBuild (job, jenkinsCrumb, targetBranch, ghRelease) {
async function callJenkinsBuild (job, jenkinsCrumb, targetBranch, options) {
console.log(`Triggering Jenkins to run build job: ${job} on branch: ${targetBranch} with release flag.`)
let jenkinsParams = {
token: process.env.JENKINS_BUILD_TOKEN,
BRANCH: targetBranch
}
if (!ghRelease) {
if (!options.ghRelease) {
jenkinsParams.RUN_RELEASE_BUILD = 1
}
if (options.automaticRelease) {
jenkinsParams.AUTO_RELEASE = 'true'
}
await callJenkins(`job/${job}/buildWithParameters`, jenkinsParams, jenkinsCrumb)
.catch(err => {
console.log(`Error calling Jenkins build`, err)
@@ -176,33 +187,35 @@ function runRelease (targetBranch, options) {
if (options.ci) {
switch (options.ci) {
case 'CircleCI': {
buildCircleCI(targetBranch, options.ghRelease, options.job)
buildCircleCI(targetBranch, options)
break
}
case 'AppVeyor': {
buildAppVeyor(targetBranch, options.ghRelease)
buildAppVeyor(targetBranch, options)
break
}
case 'Jenkins': {
buildJenkins(targetBranch, options.ghRelease, options.job)
buildJenkins(targetBranch, options)
break
}
}
} else {
buildCircleCI(targetBranch, options.ghRelease, options.job)
buildAppVeyor(targetBranch, options.ghRelease)
buildJenkins(targetBranch, options.ghRelease, options.job)
buildCircleCI(targetBranch, options)
buildAppVeyor(targetBranch, options)
buildJenkins(targetBranch, options)
}
}
module.exports = runRelease
if (require.main === module) {
const args = require('minimist')(process.argv.slice(2), { boolean: 'ghRelease' })
const args = require('minimist')(process.argv.slice(2), {
boolean: ['ghRelease', 'automaticRelease']
})
const targetBranch = args._[0]
if (args._.length < 1) {
console.log(`Trigger CI to build release builds of electron.
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor|Jenkins] [--ghRelease] TARGET_BRANCH
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor|Jenkins] [--ghRelease] [--automaticRelease] TARGET_BRANCH
`)
process.exit(0)
}

View File

@@ -1,7 +1,9 @@
#!/usr/bin/env node
require('colors')
const args = require('minimist')(process.argv.slice(2))
const args = require('minimist')(process.argv.slice(2), {
boolean: ['automaticRelease', 'notesOnly', 'stable']
})
const assert = require('assert')
const ciReleaseBuild = require('./ci-release-build')
const { execSync } = require('child_process')
@@ -20,7 +22,7 @@ const versionType = args._[0]
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
if (!versionType && !args.notesOnly) {
console.log(`Usage: prepare-release versionType [major | minor | patch | beta]` +
` (--stable) (--notesOnly)`)
` (--stable) (--notesOnly) (--automaticRelease)`)
process.exit(1)
}
@@ -76,7 +78,10 @@ async function getReleaseNotes (currentBranch) {
base: `v${pkg.version}`,
head: currentBranch
}
let releaseNotes = '(placeholder)\n'
let releaseNotes = ''
if (!args.automaticRelease) {
releaseNotes = '(placeholder)\n'
}
console.log(`Checking for commits from ${pkg.version} to ${currentBranch}`)
let commitComparison = await github.repos.compareCommits(githubOpts)
.catch(err => {
@@ -85,10 +90,21 @@ async function getReleaseNotes (currentBranch) {
process.exit(1)
})
if (commitComparison.data.commits.length === 0) {
console.log(`${pass} There are no commits from ${pkg.version} to ` +
`${currentBranch}, skipping release.`)
process.exit(0)
}
commitComparison.data.commits.forEach(commitEntry => {
let commitMessage = commitEntry.commit.message
if (commitMessage.toLowerCase().indexOf('merge') > -1) {
releaseNotes += `${commitMessage} \n`
let mergeRE = /Merge pull request (#\d+) from .*\n/
let prMatch = commitMessage.match(mergeRE)
commitMessage = commitMessage.replace(mergeRE, '').replace('\n', '')
if (commitMessage.substr(commitMessage.length - 1, commitMessage.length) !== '.') {
commitMessage += '.'
}
releaseNotes += `${commitMessage} ${prMatch[1]} \n\n`
}
})
console.log(`${pass} Done generating release notes for ${currentBranch}.`)
@@ -152,7 +168,8 @@ async function pushRelease () {
async function runReleaseBuilds (branch) {
await ciReleaseBuild(branch, {
ghRelease: true
ghRelease: true,
automaticRelease: args.automaticRelease
})
}
@@ -193,6 +210,11 @@ async function promptForVersion (version) {
}
async function prepareRelease (isBeta, notesOnly) {
if (args.automaticRelease && (pkg.version.indexOf('beta') === -1 ||
versionType !== 'beta')) {
console.log(`${fail} Automatic release is only supported for beta releases`)
process.exit(1)
}
let currentBranch = await getCurrentBranch(gitDir)
if (notesOnly) {
let releaseNotes = await getReleaseNotes(currentBranch)

View File

@@ -148,7 +148,11 @@ function s3UrlsForVersion (version) {
function checkVersion () {
console.log(`Verifying that app version matches package version ${pkgVersion}.`)
let startScript = path.join(__dirname, 'start.py')
let appVersion = runScript(startScript, ['--version']).trim()
let scriptArgs = ['--version']
if (args.automaticRelease) {
scriptArgs.unshift('-R')
}
let appVersion = runScript(startScript, scriptArgs).trim()
check((pkgVersion.indexOf(appVersion) === 0), `App version ${appVersion} matches ` +
`package version ${pkgVersion}.`, true)
}