diff --git a/script/ci-release-build.js b/script/ci-release-build.js index 60c3682ac7..de4574ca1f 100644 --- a/script/ci-release-build.js +++ b/script/ci-release-build.js @@ -6,7 +6,7 @@ const circleCIJobs = [ 'electron-linux-arm', 'electron-linux-arm64', 'electron-linux-ia32', - 'electron-linux-mips64el', +// 'electron-linux-mips64el', 'electron-linux-x64' ] @@ -32,7 +32,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 = { @@ -41,12 +41,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, @@ -61,17 +65,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: { @@ -95,13 +103,13 @@ 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)) } } @@ -109,28 +117,30 @@ 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 } } } else { - buildCircleCI(targetBranch, options.ghRelease, options.job) - buildAppVeyor(targetBranch, options.ghRelease) + buildCircleCI(targetBranch, options) + buildAppVeyor(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] [--ghRelease] TARGET_BRANCH + Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor] [--ghRelease] [--automaticRelease] TARGET_BRANCH `) process.exit(0) } diff --git a/script/prepare-release.js b/script/prepare-release.js index 6988292876..8021fa331d 100755 --- a/script/prepare-release.js +++ b/script/prepare-release.js @@ -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) (--branch)`) process.exit(1) } @@ -76,7 +78,12 @@ async function getReleaseNotes (currentBranch) { base: `v${pkg.version}`, head: currentBranch } - let releaseNotes = '(placeholder)\n' + let releaseNotes + if (args.automaticRelease) { + releaseNotes = '## Bug Fixes/Changes \n\n' + } else { + releaseNotes = '(placeholder)\n' + } console.log(`Checking for commits from ${pkg.version} to ${currentBranch}`) let commitComparison = await github.repos.compareCommits(githubOpts) .catch(err => { @@ -85,13 +92,45 @@ 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) + } + + let prCount = 0 + const mergeRE = /Merge pull request #(\d+) from .*\n/ + const newlineRE = /(.*)\n*.*/ + const prRE = /(.* )\(#(\d+)\)(?:.*)/ commitComparison.data.commits.forEach(commitEntry => { let commitMessage = commitEntry.commit.message - if (commitMessage.toLowerCase().indexOf('merge') > -1) { - releaseNotes += `${commitMessage} \n` + if (commitMessage.indexOf('#') > -1) { + let prMatch = commitMessage.match(mergeRE) + let prNumber + if (prMatch) { + commitMessage = commitMessage.replace(mergeRE, '').replace('\n', '') + let newlineMatch = commitMessage.match(newlineRE) + if (newlineMatch) { + commitMessage = newlineMatch[1] + } + prNumber = prMatch[1] + } else { + prMatch = commitMessage.match(prRE) + if (prMatch) { + commitMessage = prMatch[1].trim() + prNumber = prMatch[2] + } + } + if (prMatch) { + if (commitMessage.substr(commitMessage.length - 1, commitMessage.length) !== '.') { + commitMessage += '.' + } + releaseNotes += `* ${commitMessage} #${prNumber} \n\n` + prCount++ + } } }) - console.log(`${pass} Done generating release notes for ${currentBranch}.`) + console.log(`${pass} Done generating release notes for ${currentBranch}. Found ${prCount} PRs.`) return releaseNotes } @@ -138,8 +177,8 @@ async function createRelease (branchToTarget, isBeta) { console.log(`${pass} Draft release for ${newVersion} has been created.`) } -async function pushRelease () { - let pushDetails = await GitProcess.exec(['push', 'origin', 'HEAD', '--follow-tags'], gitDir) +async function pushRelease (branch) { + let pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], gitDir) if (pushDetails.exitCode === 0) { console.log(`${pass} Successfully pushed the release. Wait for ` + `release builds to finish before running "npm run release".`) @@ -152,7 +191,8 @@ async function pushRelease () { async function runReleaseBuilds (branch) { await ciReleaseBuild(branch, { - ghRelease: true + ghRelease: true, + automaticRelease: args.automaticRelease }) } @@ -170,7 +210,12 @@ async function tagRelease (version) { async function verifyNewVersion () { let newVersion = getNewVersion(true) - let response = await promptForVersion(newVersion) + let response + if (args.automaticRelease) { + response = 'y' + } else { + response = await promptForVersion(newVersion) + } if (response.match(/^y/i)) { console.log(`${pass} Starting release of ${newVersion}`) } else { @@ -193,14 +238,24 @@ async function promptForVersion (version) { } async function prepareRelease (isBeta, notesOnly) { - let currentBranch = await getCurrentBranch(gitDir) + 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 + if (args.branch) { + currentBranch = args.branch + } else { + currentBranch = await getCurrentBranch(gitDir) + } if (notesOnly) { let releaseNotes = await getReleaseNotes(currentBranch) - console.log(`Draft release notes are: ${releaseNotes}`) + console.log(`Draft release notes are: \n${releaseNotes}`) } else { await verifyNewVersion() await createRelease(currentBranch, isBeta) - await pushRelease() + await pushRelease(currentBranch) await runReleaseBuilds(currentBranch) } } diff --git a/script/release.js b/script/release.js index 7ddd069e21..5e694366dc 100755 --- a/script/release.js +++ b/script/release.js @@ -97,7 +97,7 @@ function assetsForVersion (version, validatingRelease) { `electron-${version}-linux-armv7l.zip`, `electron-${version}-linux-ia32-symbols.zip`, `electron-${version}-linux-ia32.zip`, - `electron-${version}-linux-mips64el.zip`, +// `electron-${version}-linux-mips64el.zip`, `electron-${version}-linux-x64-symbols.zip`, `electron-${version}-linux-x64.zip`, `electron-${version}-mas-x64-dsym.zip`, @@ -116,7 +116,7 @@ function assetsForVersion (version, validatingRelease) { `ffmpeg-${version}-linux-arm64.zip`, `ffmpeg-${version}-linux-armv7l.zip`, `ffmpeg-${version}-linux-ia32.zip`, - `ffmpeg-${version}-linux-mips64el.zip`, +// `ffmpeg-${version}-linux-mips64el.zip`, `ffmpeg-${version}-linux-x64.zip`, `ffmpeg-${version}-mas-x64.zip`, `ffmpeg-${version}-win32-ia32.zip`, @@ -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) } @@ -179,7 +183,11 @@ function uploadNodeShasums () { function uploadIndexJson () { console.log('Uploading index.json to S3.') let scriptPath = path.join(__dirname, 'upload-index-json.py') - runScript(scriptPath, []) + let scriptArgs = [] + if (args.automaticRelease) { + scriptArgs.push('-R') + } + runScript(scriptPath, scriptArgs) console.log(`${pass} Done uploading index.json to S3.`) } diff --git a/script/upload.py b/script/upload.py index ee7a027af0..ccbbe26e54 100755 --- a/script/upload.py +++ b/script/upload.py @@ -50,6 +50,7 @@ def main(): github = GitHub(auth_token()) releases = github.repos(ELECTRON_REPO).releases.get() tag_exists = False + release = None for r in releases: if not r['draft'] and r['tag_name'] == args.version: release = r @@ -62,6 +63,9 @@ def main(): if not args.overwrite: release = create_or_get_release_draft(github, releases, args.version, tag_exists) + elif release is None: + release = dict(tag_name=args.version) + # Upload Electron with GitHub Releases API. upload_electron(github, release, os.path.join(DIST_DIR, DIST_NAME),