Merge pull request #11427 from electron/prepare-for-release

Preparation for 1.7.10 release
This commit is contained in:
John Kleinschmidt
2017-12-18 13:20:31 -05:00
committed by GitHub
9 changed files with 1189 additions and 116 deletions

View File

@@ -3,20 +3,19 @@ version: 2
jobs:
electron-linux-arm:
docker:
- image: electronbuilds/electron:0.0.3
- image: electronbuilds/electron:0.0.4
environment:
TARGET_ARCH: arm
resource_class: xlarge
resource_class: 2xlarge
steps:
- checkout
- run: sh -e /etc/init.d/xvfb start
- run:
name: Check for release
command: |
MESSAGE="$(git log --format=%B -n 1 HEAD)"
case ${MESSAGE} in
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
esac
if [ -n "${RUN_RELEASE_BUILD}" ]; then
echo 'release build triggered from api'
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
fi
- run:
name: Bootstrap
command: |
@@ -50,29 +49,30 @@ jobs:
- run:
name: Upload distribution
command: |
if [ "$ELECTRON_RELEASE" == "1" ]; then
echo 'Uploading Electron release distribution'
if [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" != "1" ]; then
echo 'Uploading Electron release distribution to github releases'
script/upload.py
elif [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" == "1" ]; then
echo 'Uploading Electron release distribution to s3'
script/upload.py --upload_to_s3
else
echo 'Skipping upload distribution because build is not for release'
fi
electron-linux-ia32:
docker:
- image: electronbuilds/electron:0.0.3
- image: electronbuilds/electron:0.0.4
environment:
TARGET_ARCH: ia32
resource_class: xlarge
steps:
- checkout
- run: sh -e /etc/init.d/xvfb start
- run:
name: Check for release
command: |
MESSAGE="$(git log --format=%B -n 1 HEAD)"
case ${MESSAGE} in
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
esac
if [ -n "${RUN_RELEASE_BUILD}" ]; then
echo 'release build triggered from api'
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
fi
- run:
name: Bootstrap
command: |
@@ -106,29 +106,34 @@ jobs:
- run:
name: Upload distribution
command: |
if [ "$ELECTRON_RELEASE" == "1" ]; then
echo 'Uploading Electron release distribution'
if [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" != "1" ]; then
echo 'Uploading Electron release distribution to github releases'
script/upload.py
elif [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" == "1" ]; then
echo 'Uploading Electron release distribution to s3'
script/upload.py --upload_to_s3
else
echo 'Skipping upload distribution because build is not for release'
fi
electron-linux-x64:
docker:
- image: electronbuilds/electron:0.0.3
- image: electronbuilds/electron:0.0.4
environment:
TARGET_ARCH: x64
DISPLAY: ':99.0'
resource_class: xlarge
steps:
- checkout
- run: sh -e /etc/init.d/xvfb start
- run:
name: Setup for headless testing
command: sh -e /etc/init.d/xvfb start
- run:
name: Check for release
command: |
MESSAGE="$(git log --format=%B -n 1 HEAD)"
case ${MESSAGE} in
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
esac
if [ -n "${RUN_RELEASE_BUILD}" ]; then
echo 'release build triggered from api'
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
fi
- run:
name: Bootstrap
command: |
@@ -162,9 +167,12 @@ jobs:
- run:
name: Upload distribution
command: |
if [ "$ELECTRON_RELEASE" == "1" ]; then
echo 'Uploading Electron release distribution'
if [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" != "1" ]; then
echo 'Uploading Electron release distribution to github releases'
script/upload.py
elif [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" == "1" ]; then
echo 'Uploading Electron release distribution to s3'
script/upload.py --upload_to_s3
else
echo 'Skipping upload distribution because build is not for release'
fi
@@ -190,10 +198,17 @@ jobs:
else
echo 'Skipping verify ffmpeg on release build'
fi
- run:
name: Generate Typescript Definitions
command: npm run create-typescript-definitions
- store_test_results:
path: junit
- store_artifacts:
path: junit
- store_artifacts:
path: out/electron.d.ts
- store_artifacts:
path: out/electron-api.json
workflows:
version: 2

View File

@@ -3,6 +3,7 @@
import os
import re
import sys
import argparse
from lib.util import execute, get_electron_version, parse_version, scoped_cwd
@@ -11,29 +12,85 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
def main():
if len(sys.argv) != 2 or sys.argv[1] == '-h':
print 'Usage: bump-version.py [<version> | major | minor | patch]'
parser = argparse.ArgumentParser(
description='Bump version numbers. Must specify at least one of the three'
+' options:\n'
+' --bump=patch to increment patch version, or\n'
+' --stable to promote current beta to stable, or\n'
+' --version={version} to set version number directly\n'
+'Note that you can use both --bump and --stable '
+'simultaneously.',
formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument(
'--version',
default=None,
dest='new_version',
help='new version number'
)
parser.add_argument(
'--bump',
action='store',
default=None,
dest='bump',
help='increment [major | minor | patch | beta]'
)
parser.add_argument(
'--stable',
action='store_true',
default= False,
dest='stable',
help='promote to stable (i.e. remove `-beta.x` suffix)'
)
parser.add_argument(
'--dry-run',
action='store_true',
default= False,
dest='dry_run',
help='just to check that version number is correct'
)
args = parser.parse_args()
if args.new_version == None and args.bump == None and args.stable == False:
parser.print_help()
return 1
option = sys.argv[1]
increments = ['major', 'minor', 'patch', 'build']
if option in increments:
version = get_electron_version()
versions = parse_version(version.split('-')[0])
versions = increase_version(versions, increments.index(option))
else:
versions = parse_version(option)
increments = ['major', 'minor', 'patch', 'beta']
curr_version = get_electron_version()
versions = parse_version(re.sub('-beta', '', curr_version))
if args.bump in increments:
versions = increase_version(versions, increments.index(args.bump))
if versions[3] == '0':
# beta starts at 1
versions = increase_version(versions, increments.index('beta'))
if args.stable == True:
versions[3] = '0'
if args.new_version != None:
versions = parse_version(re.sub('-beta', '', args.new_version))
version = '.'.join(versions[:3])
suffix = '' if versions[3] == '0' else '-beta.' + versions[3]
if args.dry_run:
print 'new version number would be: {0}\n'.format(version + suffix)
return 0
with scoped_cwd(SOURCE_ROOT):
update_electron_gyp(version)
update_electron_gyp(version, suffix)
update_win_rc(version, versions)
update_version_h(versions)
update_version_h(versions, suffix)
update_info_plist(version)
update_package_json(version)
tag_version(version)
update_package_json(version, suffix)
tag_version(version, suffix)
print 'Bumped to version: {0}'.format(version + suffix)
def increase_version(versions, index):
for i in range(index + 1, 4):
@@ -42,14 +99,14 @@ def increase_version(versions, index):
return versions
def update_electron_gyp(version):
pattern = re.compile(" *'version%' *: *'[0-9.]+'")
def update_electron_gyp(version, suffix):
pattern = re.compile(" *'version%' *: *'[0-9.]+(-beta[0-9.]*)?'")
with open('electron.gyp', 'r') as f:
lines = f.readlines()
for i in range(0, len(lines)):
if pattern.match(lines[i]):
lines[i] = " 'version%': '{0}',\n".format(version)
lines[i] = " 'version%': '{0}',\n".format(version + suffix)
with open('electron.gyp', 'w') as f:
f.write(''.join(lines))
return
@@ -81,7 +138,7 @@ def update_win_rc(version, versions):
f.write(''.join(lines))
def update_version_h(versions):
def update_version_h(versions, suffix):
version_h = os.path.join('atom', 'common', 'atom_version.h')
with open(version_h, 'r') as f:
lines = f.readlines()
@@ -93,6 +150,11 @@ def update_version_h(versions):
lines[i + 1] = '#define ATOM_MINOR_VERSION {0}\n'.format(versions[1])
lines[i + 2] = '#define ATOM_PATCH_VERSION {0}\n'.format(versions[2])
if (suffix):
lines[i + 3] = '#define ATOM_PRE_RELEASE_VERSION {0}\n'.format(suffix)
else:
lines[i + 3] = '// #define ATOM_PRE_RELEASE_VERSION\n'
with open(version_h, 'w') as f:
f.write(''.join(lines))
return
@@ -114,7 +176,7 @@ def update_info_plist(version):
f.write(''.join(lines))
def update_package_json(version):
def update_package_json(version, suffix):
package_json = 'package.json'
with open(package_json, 'r') as f:
lines = f.readlines()
@@ -122,15 +184,15 @@ def update_package_json(version):
for i in range(0, len(lines)):
line = lines[i];
if 'version' in line:
lines[i] = ' "version": "{0}",\n'.format(version)
lines[i] = ' "version": "{0}",\n'.format(version + suffix)
break
with open(package_json, 'w') as f:
f.write(''.join(lines))
def tag_version(version):
execute(['git', 'commit', '-a', '-m', 'Bump v{0}'.format(version)])
def tag_version(version, suffix):
execute(['git', 'commit', '-a', '-m', 'Bump v{0}'.format(version + suffix)])
if __name__ == '__main__':

209
script/ci-release-build.js Normal file
View File

@@ -0,0 +1,209 @@
const assert = require('assert')
const request = require('request')
const buildAppVeyorURL = 'https://windows-ci.electronjs.org/api/builds'
const jenkinsServer = 'https://mac-ci.electronjs.org'
const circleCIJobs = [
'electron-linux-arm',
'electron-linux-ia32',
'electron-linux-x64'
]
const jenkinsJobs = [
'electron-mas-x64-release',
'electron-osx-x64-release'
]
async function makeRequest (requestOptions, parseResponse) {
return new Promise((resolve, reject) => {
request(requestOptions, (err, res, body) => {
if (!err && res.statusCode >= 200 && res.statusCode < 300) {
if (parseResponse) {
const build = JSON.parse(body)
resolve(build)
} else {
resolve(body)
}
} else {
if (parseResponse) {
console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions)
} else {
console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions)
}
reject(err)
}
})
})
}
async function circleCIcall (buildUrl, targetBranch, job, ghRelease) {
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 = {
'build_parameters': {
'CIRCLE_JOB': job
}
}
if (ghRelease) {
buildRequest.build_parameters.ELECTRON_RELEASE = 1
} else {
buildRequest.build_parameters.RUN_RELEASE_BUILD = 'true'
}
let circleResponse = await makeRequest({
method: 'POST',
url: buildUrl,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(buildRequest)
}, true).catch(err => {
console.log('Error calling CircleCI:', err)
})
console.log(`Check ${circleResponse.build_url} for status. (${job})`)
}
async function buildAppVeyor (targetBranch, ghRelease) {
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) {
environmentVariables.ELECTRON_RELEASE = 1
} else {
environmentVariables.RUN_RELEASE_BUILD = 'true'
}
const requestOpts = {
url: buildAppVeyorURL,
auth: {
bearer: process.env.APPVEYOR_TOKEN
},
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
accountName: 'AppVeyor',
projectSlug: 'electron',
branch: targetBranch,
environmentVariables
}),
method: 'POST'
}
let appVeyorResponse = await makeRequest(requestOpts, true).catch(err => {
console.log('Error calling AppVeyor:', err)
})
const buildUrl = `https://windows-ci.electronjs.org/project/AppVeyor/electron/build/${appVeyorResponse.version}`
console.log(`AppVeyor release build request successful. Check build status at ${buildUrl}`)
}
function buildCircleCI (targetBranch, ghRelease, job) {
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)
} else {
circleCIJobs.forEach((job) => circleCIcall(circleBuildUrl, targetBranch, job, ghRelease))
}
}
async function buildJenkins (targetBranch, ghRelease, job) {
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)
} else {
jenkinsJobs.forEach((job) => {
callJenkinsBuild(job, jenkinsCrumb, targetBranch, ghRelease)
})
}
}
async function callJenkins (path, requestParameters, requestHeaders) {
let requestOptions = {
url: `${jenkinsServer}/${path}`,
auth: {
user: 'build',
pass: process.env.JENKINS_AUTH_TOKEN
},
qs: requestParameters
}
if (requestHeaders) {
requestOptions.headers = requestHeaders
}
let jenkinsResponse = await makeRequest(requestOptions).catch(err => {
console.log(`Error calling Jenkins:`, err)
})
return jenkinsResponse
}
async function callJenkinsBuild (job, jenkinsCrumb, targetBranch, ghRelease) {
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) {
jenkinsParams.RUN_RELEASE_BUILD = 1
}
await callJenkins(`job/${job}/buildWithParameters`, jenkinsParams, jenkinsCrumb)
.catch(err => {
console.log(`Error calling Jenkins build`, err)
})
let buildUrl = `${jenkinsServer}/job/${job}/lastBuild/`
console.log(`Jenkins build request successful. Check build status at ${buildUrl}.`)
}
async function getJenkinsCrumb () {
let crumbResponse = await callJenkins('crumbIssuer/api/xml', {
xpath: 'concat(//crumbRequestField,":",//crumb)'
}).catch(err => {
console.log(`Error getting jenkins crumb:`, err)
})
let crumbDetails = crumbResponse.split(':')
let crumbHeader = {}
crumbHeader[crumbDetails[0]] = crumbDetails[1]
return crumbHeader
}
function runRelease (targetBranch, options) {
if (options.ci) {
switch (options.ci) {
case 'CircleCI': {
buildCircleCI(targetBranch, options.ghRelease, options.job)
break
}
case 'AppVeyor': {
buildAppVeyor(targetBranch, options.ghRelease)
break
}
case 'Jenkins': {
buildJenkins(targetBranch, options.ghRelease, options.job)
break
}
}
} else {
buildCircleCI(targetBranch, options.ghRelease, options.job)
buildAppVeyor(targetBranch, options.ghRelease)
buildJenkins(targetBranch, options.ghRelease, options.job)
}
}
module.exports = runRelease
if (require.main === module) {
const args = require('minimist')(process.argv.slice(2))
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
`)
process.exit(0)
}
runRelease(targetBranch, args)
}

116
script/merge-release.js Executable file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/env node
require('colors')
const assert = require('assert')
const branchToRelease = process.argv[2]
const fail = '\u2717'.red
const { GitProcess, GitError } = require('dugite')
const pass = '\u2713'.green
const path = require('path')
const pkg = require('../package.json')
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
if (!branchToRelease) {
console.log(`Usage: merge-release branch`)
process.exit(1)
}
const gitDir = path.resolve(__dirname, '..')
async function callGit (args, errorMessage, successMessage) {
let gitResult = await GitProcess.exec(args, gitDir)
if (gitResult.exitCode === 0) {
console.log(`${pass} ${successMessage}`)
return true
} else {
console.log(`${fail} ${errorMessage} ${gitResult.stderr}`)
process.exit(1)
}
}
async function checkoutBranch (branchName) {
console.log(`Checking out ${branchName}.`)
let errorMessage = `Error checking out branch ${branchName}:`
let successMessage = `Successfully checked out branch ${branchName}.`
return callGit(['checkout', branchName], errorMessage, successMessage)
}
async function commitMerge () {
console.log(`Committing the merge for v${pkg.version}`)
let errorMessage = `Error committing merge:`
let successMessage = `Successfully committed the merge for v${pkg.version}`
let gitArgs = ['commit', '-m', `v${pkg.version}`]
return callGit(gitArgs, errorMessage, successMessage)
}
async function mergeReleaseIntoBranch (branchName) {
console.log(`Merging release branch into ${branchName}.`)
let mergeArgs = ['merge', 'release', '--squash']
let mergeDetails = await GitProcess.exec(mergeArgs, gitDir)
if (mergeDetails.exitCode === 0) {
return true
} else {
const error = GitProcess.parseError(mergeDetails.stderr)
if (error === GitError.MergeConflicts) {
console.log(`${fail} Could not merge release branch into ${branchName} ` +
`due to merge conflicts.`)
return false
} else {
console.log(`${fail} Could not merge release branch into ${branchName} ` +
`due to an error: ${mergeDetails.stderr}.`)
process.exit(1)
}
}
}
async function pushBranch (branchName) {
console.log(`Pushing branch ${branchName}.`)
let pushArgs = ['push', 'origin', branchName]
let errorMessage = `Could not push branch ${branchName} due to an error:`
let successMessage = `Successfully pushed branch ${branchName}.`
return callGit(pushArgs, errorMessage, successMessage)
}
async function pull () {
console.log(`Performing a git pull`)
let errorMessage = `Could not pull due to an error:`
let successMessage = `Successfully performed a git pull`
return callGit(['pull'], errorMessage, successMessage)
}
async function rebase (targetBranch) {
console.log(`Rebasing release branch from ${targetBranch}`)
let errorMessage = `Could not rebase due to an error:`
let successMessage = `Successfully rebased release branch from ` +
`${targetBranch}`
return callGit(['rebase', targetBranch], errorMessage, successMessage)
}
async function mergeRelease () {
await checkoutBranch(branchToRelease)
let mergeSuccess = await mergeReleaseIntoBranch(branchToRelease)
if (mergeSuccess) {
console.log(`${pass} Successfully merged release branch into ` +
`${branchToRelease}.`)
await commitMerge()
let pushSuccess = await pushBranch(branchToRelease)
if (pushSuccess) {
console.log(`${pass} Success!!! ${branchToRelease} now has the latest release!`)
}
} else {
console.log(`Trying rebase of ${branchToRelease} into release branch.`)
await pull()
await checkoutBranch('release')
let rebaseResult = await rebase(branchToRelease)
if (rebaseResult) {
let pushResult = pushBranch('HEAD')
if (pushResult) {
console.log(`Rebase of ${branchToRelease} into release branch was ` +
`successful. Let release builds run and then try this step again.`)
}
// Exit as failure so release doesn't continue
process.exit(1)
}
}
}
mergeRelease()

181
script/prepare-release.js Executable file
View File

@@ -0,0 +1,181 @@
#!/usr/bin/env node
require('colors')
const args = require('minimist')(process.argv.slice(2))
const assert = require('assert')
const ciReleaseBuild = require('./ci-release-build')
const { execSync } = require('child_process')
const fail = '\u2717'.red
const { GitProcess, GitError } = require('dugite')
const GitHub = require('github')
const pass = '\u2713'.green
const path = require('path')
const pkg = require('../package.json')
const versionType = args._[0]
// TODO (future) automatically determine version based on conventional commits
// via conventional-recommended-bump
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)`)
process.exit(1)
}
const github = new GitHub()
const gitDir = path.resolve(__dirname, '..')
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
async function createReleaseBranch () {
console.log(`Creating release branch.`)
let checkoutDetails = await GitProcess.exec([ 'checkout', '-b', 'release' ], gitDir)
if (checkoutDetails.exitCode === 0) {
console.log(`${pass} Successfully created the release branch.`)
} else {
const error = GitProcess.parseError(checkoutDetails.stderr)
if (error === GitError.BranchAlreadyExists) {
console.log(`${fail} Release branch already exists, aborting prepare ` +
`release process.`)
} else {
console.log(`${fail} Error creating release branch: ` +
`${checkoutDetails.stderr}`)
}
process.exit(1)
}
}
function getNewVersion () {
console.log(`Bumping for new "${versionType}" version.`)
let bumpScript = path.join(__dirname, 'bump-version.py')
let scriptArgs = [bumpScript, `--bump ${versionType}`]
if (args.stable) {
scriptArgs.push('--stable')
}
try {
let bumpVersion = execSync(scriptArgs.join(' '), {encoding: 'UTF-8'})
bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim()
let newVersion = `v${bumpVersion}`
console.log(`${pass} Successfully bumped version to ${newVersion}`)
return newVersion
} catch (err) {
console.log(`${fail} Could not bump version, error was:`, err)
}
}
async function getCurrentBranch (gitDir) {
console.log(`Determining current git branch`)
let gitArgs = ['rev-parse', '--abbrev-ref', 'HEAD']
let branchDetails = await GitProcess.exec(gitArgs, gitDir)
if (branchDetails.exitCode === 0) {
let currentBranch = branchDetails.stdout.trim()
console.log(`${pass} Successfully determined current git branch is ` +
`${currentBranch}`)
return currentBranch
} else {
let error = GitProcess.parseError(branchDetails.stderr)
console.log(`${fail} Could not get details for the current branch,
error was ${branchDetails.stderr}`, error)
process.exit(1)
}
}
async function getReleaseNotes (currentBranch) {
console.log(`Generating release notes for ${currentBranch}.`)
let githubOpts = {
owner: 'electron',
repo: 'electron',
base: `v${pkg.version}`,
head: currentBranch
}
let releaseNotes = '(placeholder)\n'
console.log(`Checking for commits from ${pkg.version} to ${currentBranch}`)
let commitComparison = await github.repos.compareCommits(githubOpts)
.catch(err => {
console.log(`{$fail} Error checking for commits from ${pkg.version} to ` +
`${currentBranch}`, err)
process.exit(1)
})
commitComparison.data.commits.forEach(commitEntry => {
let commitMessage = commitEntry.commit.message
if (commitMessage.toLowerCase().indexOf('merge') > -1) {
releaseNotes += `${commitMessage} \n`
}
})
console.log(`${pass} Done generating release notes for ${currentBranch}.`)
return releaseNotes
}
async function createRelease (branchToTarget, isBeta) {
let releaseNotes = await getReleaseNotes(branchToTarget)
let newVersion = getNewVersion()
const githubOpts = {
owner: 'electron',
repo: 'electron'
}
console.log(`Checking for existing draft release.`)
let releases = await github.repos.getReleases(githubOpts)
.catch(err => {
console.log('$fail} Could not get releases. Error was', err)
})
let drafts = releases.data.filter(release => release.draft)
if (drafts.length > 0) {
console.log(`${fail} Aborting because draft release for
${drafts[0].tag_name} already exists.`)
process.exit(1)
}
console.log(`${pass} A draft release does not exist; creating one.`)
githubOpts.body = releaseNotes
githubOpts.draft = true
githubOpts.name = `electron ${newVersion}`
if (isBeta) {
githubOpts.body = `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, ` +
`or npm i electron@${newVersion.substr(1)}.`
githubOpts.name = `${githubOpts.name}`
githubOpts.prerelease = true
}
githubOpts.tag_name = newVersion
githubOpts.target_commitish = branchToTarget
await github.repos.createRelease(githubOpts)
.catch(err => {
console.log(`${fail} Error creating new release: `, err)
process.exit(1)
})
console.log(`${pass} Draft release for ${newVersion} has been created.`)
}
async function pushRelease () {
let pushDetails = await GitProcess.exec(['push', 'origin', 'HEAD'], gitDir)
if (pushDetails.exitCode === 0) {
console.log(`${pass} Successfully pushed the release branch. Wait for ` +
`release builds to finish before running "npm run release".`)
} else {
console.log(`${fail} Error pushing the release branch: ` +
`${pushDetails.stderr}`)
process.exit(1)
}
}
async function runReleaseBuilds () {
await ciReleaseBuild('release', {
ghRelease: true
})
}
async function prepareRelease (isBeta, notesOnly) {
let currentBranch = await getCurrentBranch(gitDir)
if (notesOnly) {
let releaseNotes = await getReleaseNotes(currentBranch)
console.log(`Draft release notes are: ${releaseNotes}`)
} else {
await createReleaseBranch()
await createRelease(currentBranch, isBeta)
await pushRelease()
await runReleaseBuilds()
}
}
prepareRelease(!args.stable, args.notesOnly)

459
script/release.js Executable file
View File

@@ -0,0 +1,459 @@
#!/usr/bin/env node
require('colors')
const args = require('minimist')(process.argv.slice(2))
const assert = require('assert')
const fs = require('fs')
const { execSync } = require('child_process')
const GitHub = require('github')
const { GitProcess } = require('dugite')
const nugget = require('nugget')
const pkg = require('../package.json')
const pkgVersion = `v${pkg.version}`
const pass = '\u2713'.green
const path = require('path')
const fail = '\u2717'.red
const sumchecker = require('sumchecker')
const temp = require('temp').track()
const { URL } = require('url')
let failureCount = 0
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
const github = new GitHub({
followRedirects: false
})
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
const gitDir = path.resolve(__dirname, '..')
async function getDraftRelease (version, skipValidation) {
let releaseInfo = await github.repos.getReleases({owner: 'electron', repo: 'electron'})
let drafts
let versionToCheck
if (version) {
drafts = releaseInfo.data
.filter(release => release.tag_name === version)
versionToCheck = version
} else {
drafts = releaseInfo.data
.filter(release => release.draft)
versionToCheck = pkgVersion
}
const draft = drafts[0]
if (!skipValidation) {
failureCount = 0
check(drafts.length === 1, 'one draft exists', true)
check(draft.tag_name === versionToCheck, `draft release version matches local package.json (${versionToCheck})`)
if (versionToCheck.indexOf('beta')) {
check(draft.prerelease, 'draft is a prerelease')
}
check(draft.body.length > 50 && !draft.body.includes('(placeholder)'), 'draft has release notes')
check((failureCount === 0), `Draft release looks good to go.`, true)
}
return draft
}
async function validateReleaseAssets (release) {
const requiredAssets = assetsForVersion(release.tag_name).sort()
const extantAssets = release.assets.map(asset => asset.name).sort()
const downloadUrls = release.assets.map(asset => asset.browser_download_url).sort()
failureCount = 0
requiredAssets.forEach(asset => {
check(extantAssets.includes(asset), asset)
})
check((failureCount === 0), `All required GitHub assets exist for release`, true)
if (release.draft) {
await verifyAssets(release)
} else {
await verifyShasums(downloadUrls)
.catch(err => {
console.log(`${fail} error verifyingShasums`, err)
})
}
const s3Urls = s3UrlsForVersion(release.tag_name)
await verifyShasums(s3Urls, true)
}
function check (condition, statement, exitIfFail = false) {
if (condition) {
console.log(`${pass} ${statement}`)
} else {
failureCount++
console.log(`${fail} ${statement}`)
if (exitIfFail) process.exit(1)
}
}
function assetsForVersion (version) {
const patterns = [
`electron-${version}-darwin-x64-dsym.zip`,
`electron-${version}-darwin-x64-symbols.zip`,
`electron-${version}-darwin-x64.zip`,
`electron-${version}-linux-arm-symbols.zip`,
`electron-${version}-linux-arm.zip`,
`electron-${version}-linux-armv7l-symbols.zip`,
`electron-${version}-linux-armv7l.zip`,
`electron-${version}-linux-ia32-symbols.zip`,
`electron-${version}-linux-ia32.zip`,
`electron-${version}-linux-x64-symbols.zip`,
`electron-${version}-linux-x64.zip`,
`electron-${version}-mas-x64-dsym.zip`,
`electron-${version}-mas-x64-symbols.zip`,
`electron-${version}-mas-x64.zip`,
`electron-${version}-win32-ia32-pdb.zip`,
`electron-${version}-win32-ia32-symbols.zip`,
`electron-${version}-win32-ia32.zip`,
`electron-${version}-win32-x64-pdb.zip`,
`electron-${version}-win32-x64-symbols.zip`,
`electron-${version}-win32-x64.zip`,
`electron-api.json`,
`electron.d.ts`,
`ffmpeg-${version}-darwin-x64.zip`,
`ffmpeg-${version}-linux-arm.zip`,
`ffmpeg-${version}-linux-armv7l.zip`,
`ffmpeg-${version}-linux-ia32.zip`,
`ffmpeg-${version}-linux-x64.zip`,
`ffmpeg-${version}-mas-x64.zip`,
`ffmpeg-${version}-win32-ia32.zip`,
`ffmpeg-${version}-win32-x64.zip`,
`SHASUMS256.txt`
]
return patterns
}
function s3UrlsForVersion (version) {
const bucket = `https://gh-contractor-zcbenz.s3.amazonaws.com/`
const patterns = [
`${bucket}atom-shell/dist/${version}/iojs-${version}-headers.tar.gz`,
`${bucket}atom-shell/dist/${version}/iojs-${version}.tar.gz`,
`${bucket}atom-shell/dist/${version}/node-${version}.tar.gz`,
`${bucket}atom-shell/dist/${version}/node.lib`,
`${bucket}atom-shell/dist/${version}/win-x64/iojs.lib`,
`${bucket}atom-shell/dist/${version}/win-x86/iojs.lib`,
`${bucket}atom-shell/dist/${version}/x64/node.lib`,
`${bucket}atom-shell/dist/${version}/SHASUMS.txt`,
`${bucket}atom-shell/dist/${version}/SHASUMS256.txt`,
`${bucket}atom-shell/dist/index.json`
]
return patterns
}
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()
check((pkgVersion.indexOf(appVersion) === 0), `App version ${appVersion} matches ` +
`package version ${pkgVersion}.`, true)
}
function runScript (scriptName, scriptArgs, cwd) {
let scriptCommand = `${scriptName} ${scriptArgs.join(' ')}`
let scriptOptions = {
encoding: 'UTF-8'
}
if (cwd) {
scriptOptions.cwd = cwd
}
try {
return execSync(scriptCommand, scriptOptions)
} catch (err) {
console.log(`${fail} Error running ${scriptName}`, err)
process.exit(1)
}
}
function uploadNodeShasums () {
console.log('Uploading Node SHASUMS file to S3.')
let scriptPath = path.join(__dirname, 'upload-node-checksums.py')
runScript(scriptPath, ['-v', pkgVersion])
console.log(`${pass} Done uploading Node SHASUMS file to S3.`)
}
function uploadIndexJson () {
console.log('Uploading index.json to S3.')
let scriptPath = path.join(__dirname, 'upload-index-json.py')
runScript(scriptPath, [])
console.log(`${pass} Done uploading index.json to S3.`)
}
async function createReleaseShasums (release) {
let fileName = 'SHASUMS256.txt'
let existingAssets = release.assets.filter(asset => asset.name === fileName)
if (existingAssets.length > 0) {
console.log(`${fileName} already exists on GitHub; deleting before creating new file.`)
await github.repos.deleteAsset({
owner: 'electron',
repo: 'electron',
id: existingAssets[0].id
}).catch(err => {
console.log(`${fail} Error deleting ${fileName} on GitHub:`, err)
})
}
console.log(`Creating and uploading the release ${fileName}.`)
let scriptPath = path.join(__dirname, 'merge-electron-checksums.py')
let checksums = runScript(scriptPath, ['-v', pkgVersion])
console.log(`${pass} Generated release SHASUMS.`)
let filePath = await saveShaSumFile(checksums, fileName)
console.log(`${pass} Created ${fileName} file.`)
await uploadShasumFile(filePath, fileName, release)
console.log(`${pass} Successfully uploaded ${fileName} to GitHub.`)
}
async function uploadShasumFile (filePath, fileName, release) {
let githubOpts = {
owner: 'electron',
repo: 'electron',
id: release.id,
filePath,
name: fileName
}
return github.repos.uploadAsset(githubOpts)
.catch(err => {
console.log(`${fail} Error uploading ${filePath} to GitHub:`, err)
process.exit(1)
})
}
function saveShaSumFile (checksums, fileName) {
return new Promise((resolve, reject) => {
temp.open(fileName, (err, info) => {
if (err) {
console.log(`${fail} Could not create ${fileName} file`)
process.exit(1)
} else {
fs.writeFileSync(info.fd, checksums)
fs.close(info.fd, (err) => {
if (err) {
console.log(`${fail} Could close ${fileName} file`)
process.exit(1)
}
resolve(info.path)
})
}
})
})
}
async function publishRelease (release) {
let githubOpts = {
owner: 'electron',
repo: 'electron',
id: release.id,
tag_name: release.tag_name,
draft: false
}
return github.repos.editRelease(githubOpts)
.catch(err => {
console.log(`${fail} Error publishing release:`, err)
process.exit(1)
})
}
async function makeRelease (releaseToValidate) {
if (releaseToValidate) {
console.log(`Validating release ${args.validateRelease}`)
let release = await getDraftRelease(args.validateRelease)
await validateReleaseAssets(release)
} else {
checkVersion()
let draftRelease = await getDraftRelease()
uploadNodeShasums()
uploadIndexJson()
await createReleaseShasums(draftRelease)
// Fetch latest version of release before verifying
draftRelease = await getDraftRelease(pkgVersion, true)
await validateReleaseAssets(draftRelease)
await publishRelease(draftRelease)
await cleanupReleaseBranch()
console.log(`${pass} SUCCESS!!! Release has been published. Please run ` +
`"npm run publish-to-npm" to publish release to npm.`)
}
}
async function makeTempDir () {
return new Promise((resolve, reject) => {
temp.mkdir('electron-publish', (err, dirPath) => {
if (err) {
reject(err)
} else {
resolve(dirPath)
}
})
})
}
async function verifyAssets (release) {
let downloadDir = await makeTempDir()
let githubOpts = {
owner: 'electron',
repo: 'electron',
headers: {
Accept: 'application/octet-stream'
}
}
console.log(`Downloading files from GitHub to verify shasums`)
let shaSumFile = 'SHASUMS256.txt'
let filesToCheck = await Promise.all(release.assets.map(async (asset) => {
githubOpts.id = asset.id
let assetDetails = await github.repos.getAsset(githubOpts)
await downloadFiles(assetDetails.meta.location, downloadDir, false, asset.name)
return asset.name
})).catch(err => {
console.log(`${fail} Error downloading files from GitHub`, err)
process.exit(1)
})
filesToCheck = filesToCheck.filter(fileName => fileName !== shaSumFile)
let checkerOpts
await validateChecksums({
algorithm: 'sha256',
filesToCheck,
fileDirectory: downloadDir,
shaSumFile,
checkerOpts,
fileSource: 'GitHub'
})
}
function downloadFiles (urls, directory, quiet, targetName) {
return new Promise((resolve, reject) => {
let nuggetOpts = {
dir: directory
}
if (quiet) {
nuggetOpts.quiet = quiet
}
if (targetName) {
nuggetOpts.target = targetName
}
nugget(urls, nuggetOpts, (err) => {
if (err) {
reject(err)
} else {
resolve()
}
})
})
}
async function verifyShasums (urls, isS3) {
let fileSource = isS3 ? 'S3' : 'GitHub'
console.log(`Downloading files from ${fileSource} to verify shasums`)
let downloadDir = await makeTempDir()
let filesToCheck = []
try {
if (!isS3) {
await downloadFiles(urls, downloadDir)
filesToCheck = urls.map(url => {
let currentUrl = new URL(url)
return path.basename(currentUrl.pathname)
}).filter(file => file.indexOf('SHASUMS') === -1)
} else {
const s3VersionPath = `/atom-shell/dist/${pkgVersion}/`
await Promise.all(urls.map(async (url) => {
let currentUrl = new URL(url)
let dirname = path.dirname(currentUrl.pathname)
let filename = path.basename(currentUrl.pathname)
let s3VersionPathIdx = dirname.indexOf(s3VersionPath)
if (s3VersionPathIdx === -1 || dirname === s3VersionPath) {
if (s3VersionPathIdx !== -1 && filename.indexof('SHASUMS') === -1) {
filesToCheck.push(filename)
}
await downloadFiles(url, downloadDir, true)
} else {
let subDirectory = dirname.substr(s3VersionPathIdx + s3VersionPath.length)
let fileDirectory = path.join(downloadDir, subDirectory)
try {
fs.statSync(fileDirectory)
} catch (err) {
fs.mkdirSync(fileDirectory)
}
filesToCheck.push(path.join(subDirectory, filename))
await downloadFiles(url, fileDirectory, true)
}
}))
}
} catch (err) {
console.log(`${fail} Error downloading files from ${fileSource}`, err)
process.exit(1)
}
console.log(`${pass} Successfully downloaded the files from ${fileSource}.`)
let checkerOpts
if (isS3) {
checkerOpts = { defaultTextEncoding: 'binary' }
}
await validateChecksums({
algorithm: 'sha256',
filesToCheck,
fileDirectory: downloadDir,
shaSumFile: 'SHASUMS256.txt',
checkerOpts,
fileSource
})
if (isS3) {
await validateChecksums({
algorithm: 'sha1',
filesToCheck,
fileDirectory: downloadDir,
shaSumFile: 'SHASUMS.txt',
checkerOpts,
fileSource
})
}
}
async function validateChecksums (validationArgs) {
console.log(`Validating checksums for files from ${validationArgs.fileSource} ` +
`against ${validationArgs.shaSumFile}.`)
let shaSumFilePath = path.join(validationArgs.fileDirectory, validationArgs.shaSumFile)
let checker = new sumchecker.ChecksumValidator(validationArgs.algorithm,
shaSumFilePath, validationArgs.checkerOpts)
await checker.validate(validationArgs.fileDirectory, validationArgs.filesToCheck)
.catch(err => {
if (err instanceof sumchecker.ChecksumMismatchError) {
console.error(`${fail} The checksum of ${err.filename} from ` +
`${validationArgs.fileSource} did not match the shasum in ` +
`${validationArgs.shaSumFile}`)
} else if (err instanceof sumchecker.ChecksumParseError) {
console.error(`${fail} The checksum file ${validationArgs.shaSumFile} ` +
`from ${validationArgs.fileSource} could not be parsed.`, err)
} else if (err instanceof sumchecker.NoChecksumFoundError) {
console.error(`${fail} The file ${err.filename} from ` +
`${validationArgs.fileSource} was not in the shasum file ` +
`${validationArgs.shaSumFile}.`)
} else {
console.error(`${fail} Error matching files from ` +
`${validationArgs.fileSource} shasums in ${validationArgs.shaSumFile}.`, err)
}
process.exit(1)
})
console.log(`${pass} All files from ${validationArgs.fileSource} match ` +
`shasums defined in ${validationArgs.shaSumFile}.`)
}
async function cleanupReleaseBranch () {
console.log(`Cleaning up release branch.`)
let errorMessage = `Could not delete local release branch.`
let successMessage = `Successfully deleted local release branch.`
await callGit(['branch', '-D', 'release'], errorMessage, successMessage)
errorMessage = `Could not delete remote release branch.`
successMessage = `Successfully deleted remote release branch.`
return callGit(['push', 'origin', ':release'], errorMessage, successMessage)
}
async function callGit (args, errorMessage, successMessage) {
let gitResult = await GitProcess.exec(args, gitDir)
if (gitResult.exitCode === 0) {
console.log(`${pass} ${successMessage}`)
return true
} else {
console.log(`${fail} ${errorMessage} ${gitResult.stderr}`)
process.exit(1)
}
}
makeRelease(args.validateRelease)

View File

@@ -2,6 +2,10 @@ const GitHub = require('github')
const github = new GitHub()
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
if (process.argv.length < 5) {
console.log('Usage: upload-to-github filePath fileName releaseId')
process.exit(1)
}
let filePath = process.argv[2]
let fileName = process.argv[3]
let releaseId = process.argv[4]
@@ -13,9 +17,35 @@ let githubOpts = {
filePath: filePath,
name: fileName
}
github.repos.uploadAsset(githubOpts).then(() => {
process.exit()
}).catch((err) => {
console.log(`Error uploading ${fileName} to GitHub:`, err)
process.exitCode = 1
})
let retry = 0
function uploadToGitHub () {
github.repos.uploadAsset(githubOpts).then(() => {
console.log(`Successfully uploaded ${fileName} to GitHub.`)
process.exit()
}).catch((err) => {
if (retry < 4) {
console.log(`Error uploading ${fileName} to GitHub, will retry. Error was:`, err)
retry++
github.repos.getRelease(githubOpts).then(release => {
let existingAssets = release.data.assets.filter(asset => asset.name === fileName)
if (existingAssets.length > 0) {
console.log(`${fileName} already exists; will delete before retrying upload.`)
github.repos.deleteAsset({
owner: 'electron',
repo: 'electron',
id: existingAssets[0].id
}).then(uploadToGitHub).catch(uploadToGitHub)
} else {
uploadToGitHub()
}
})
} else {
console.log(`Error retrying uploading ${fileName} to GitHub:`, err)
process.exitCode = 1
}
})
}
uploadToGitHub()

View File

@@ -36,70 +36,65 @@ PDB_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'pdb')
def main():
args = parse_args()
if not args.publish_release:
if not dist_newer_than_head():
run_python_script('create-dist.py')
if not dist_newer_than_head():
run_python_script('create-dist.py')
build_version = get_electron_build_version()
if not ELECTRON_VERSION.startswith(build_version):
error = 'Tag name ({0}) should match build version ({1})\n'.format(
ELECTRON_VERSION, build_version)
sys.stderr.write(error)
sys.stderr.flush()
return 1
build_version = get_electron_build_version()
if not ELECTRON_VERSION.startswith(build_version):
error = 'Tag name ({0}) should match build version ({1})\n'.format(
ELECTRON_VERSION, build_version)
sys.stderr.write(error)
sys.stderr.flush()
return 1
github = GitHub(auth_token())
releases = github.repos(ELECTRON_REPO).releases.get()
tag_exists = False
for release in releases:
if not release['draft'] and release['tag_name'] == args.version:
for r in releases:
if not r['draft'] and r['tag_name'] == args.version:
release = r
tag_exists = True
break
release = create_or_get_release_draft(github, releases, args.version,
tag_exists)
if args.publish_release:
# Upload the Node SHASUMS*.txt.
run_python_script('upload-node-checksums.py', '-v', ELECTRON_VERSION)
# Upload the index.json.
run_python_script('upload-index-json.py')
# Create and upload the Electron SHASUMS*.txt
release_electron_checksums(release)
# Press the publish button.
publish_release(github, release['id'])
# TODO: run publish-to-npm script here
# Do not upload other files when passed "-p".
return
if not args.upload_to_s3:
assert tag_exists == args.overwrite, \
'You have to pass --overwrite to overwrite a published release'
if not args.overwrite:
release = create_or_get_release_draft(github, releases, args.version,
tag_exists)
# Upload Electron with GitHub Releases API.
upload_electron(github, release, os.path.join(DIST_DIR, DIST_NAME))
upload_electron(github, release, os.path.join(DIST_DIR, SYMBOLS_NAME))
upload_electron(github, release, os.path.join(DIST_DIR, DIST_NAME),
args.upload_to_s3)
if get_target_arch() != 'mips64el':
upload_electron(github, release, os.path.join(DIST_DIR, SYMBOLS_NAME),
args.upload_to_s3)
if PLATFORM == 'darwin':
upload_electron(github, release, os.path.join(DIST_DIR,
'electron-api.json'))
upload_electron(github, release, os.path.join(DIST_DIR, 'electron.d.ts'))
upload_electron(github, release, os.path.join(DIST_DIR, DSYM_NAME))
'electron-api.json'), args.upload_to_s3)
upload_electron(github, release, os.path.join(DIST_DIR, 'electron.d.ts'),
args.upload_to_s3)
upload_electron(github, release, os.path.join(DIST_DIR, DSYM_NAME),
args.upload_to_s3)
elif PLATFORM == 'win32':
upload_electron(github, release, os.path.join(DIST_DIR, PDB_NAME))
upload_electron(github, release, os.path.join(DIST_DIR, PDB_NAME),
args.upload_to_s3)
# Upload free version of ffmpeg.
ffmpeg = get_zip_name('ffmpeg', ELECTRON_VERSION)
upload_electron(github, release, os.path.join(DIST_DIR, ffmpeg))
upload_electron(github, release, os.path.join(DIST_DIR, ffmpeg),
args.upload_to_s3)
# Upload chromedriver and mksnapshot for minor version update.
if parse_version(args.version)[2] == '0':
chromedriver = get_zip_name('chromedriver', ELECTRON_VERSION)
upload_electron(github, release, os.path.join(DIST_DIR, chromedriver))
upload_electron(github, release, os.path.join(DIST_DIR, chromedriver),
args.upload_to_s3)
mksnapshot = get_zip_name('mksnapshot', ELECTRON_VERSION)
upload_electron(github, release, os.path.join(DIST_DIR, mksnapshot))
upload_electron(github, release, os.path.join(DIST_DIR, mksnapshot),
args.upload_to_s3)
if PLATFORM == 'win32' and not tag_exists:
if PLATFORM == 'win32' and not tag_exists and not args.upload_to_s3:
# Upload PDBs to Windows symbol server.
run_python_script('upload-windows-pdb.py')
@@ -112,9 +107,18 @@ def parse_args():
parser = argparse.ArgumentParser(description='upload distribution file')
parser.add_argument('-v', '--version', help='Specify the version',
default=ELECTRON_VERSION)
parser.add_argument('-o', '--overwrite',
help='Overwrite a published release',
action='store_true')
parser.add_argument('-p', '--publish-release',
help='Publish the release',
action='store_true')
parser.add_argument('-s', '--upload_to_s3',
help='Upload assets to s3 bucket',
dest='upload_to_s3',
action='store_true',
default=False,
required=False)
return parser.parse_args()
@@ -124,7 +128,7 @@ def run_python_script(script, *args):
def get_electron_build_version():
if get_target_arch() == 'arm' or os.environ.has_key('CI'):
if get_target_arch().startswith('arm') or os.environ.has_key('CI'):
# In CI we just build as told.
return ELECTRON_VERSION
if PLATFORM == 'darwin':
@@ -198,17 +202,17 @@ def create_release_draft(github, tag):
return r
def release_electron_checksums(release):
checksums = run_python_script('merge-electron-checksums.py',
'-v', ELECTRON_VERSION)
filename = 'SHASUMS256.txt'
filepath = os.path.join(SOURCE_ROOT, filename)
with open(filepath, 'w') as sha_file:
sha_file.write(checksums.decode('utf-8'))
upload_io_to_github(release, filename, filepath)
def upload_electron(github, release, file_path, upload_to_s3):
# if upload_to_s3 is set, skip github upload.
if upload_to_s3:
bucket, access_key, secret_key = s3_config()
key_prefix = 'electron-artifacts/{0}'.format(release['tag_name'])
s3put(bucket, access_key, secret_key, os.path.dirname(file_path),
key_prefix, [file_path])
upload_sha256_checksum(release['tag_name'], file_path, key_prefix)
return
def upload_electron(github, release, file_path):
# Delete the original file before uploading in CI.
filename = os.path.basename(file_path)
if os.environ.has_key('CI'):
@@ -231,7 +235,7 @@ def upload_electron(github, release, file_path):
arm_filename = filename.replace('armv7l', 'arm')
arm_file_path = os.path.join(os.path.dirname(file_path), arm_filename)
shutil.copy2(file_path, arm_file_path)
upload_electron(github, release, arm_file_path)
upload_electron(github, release, arm_file_path, upload_to_s3)
def upload_io_to_github(release, filename, filepath):
@@ -241,9 +245,11 @@ def upload_io_to_github(release, filename, filepath):
execute(['node', script_path, filepath, filename, str(release['id'])])
def upload_sha256_checksum(version, file_path):
def upload_sha256_checksum(version, file_path, key_prefix=None):
bucket, access_key, secret_key = s3_config()
checksum_path = '{}.sha256sum'.format(file_path)
if key_prefix is None:
key_prefix = 'atom-shell/tmp/{0}'.format(version)
sha256 = hashlib.sha256()
with open(file_path, 'rb') as f:
sha256.update(f.read())
@@ -252,12 +258,7 @@ def upload_sha256_checksum(version, file_path):
with open(checksum_path, 'w') as checksum:
checksum.write('{} *{}'.format(sha256.hexdigest(), filename))
s3put(bucket, access_key, secret_key, os.path.dirname(checksum_path),
'atom-shell/tmp/{0}'.format(version), [checksum_path])
def publish_release(github, release_id):
data = dict(draft=False)
github.repos(ELECTRON_REPO).releases(release_id).patch(data=data)
key_prefix, [checksum_path])
def auth_token():