forked from electron/electron
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathprepare-release.js
executable file
·211 lines (194 loc) · 7.3 KB
/
prepare-release.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/usr/bin/env node
if (!process.env.CI) require('dotenv-safe').load()
const args = require('minimist')(process.argv.slice(2), {
boolean: ['automaticRelease', 'notesOnly', 'stable']
})
const ciReleaseBuild = require('./ci-release-build')
const GitHub = require('github')
const { execSync } = require('child_process')
const { GitProcess } = require('dugite')
const path = require('path')
const readline = require('readline')
const releaseNotesGenerator = require('./release-notes/index.js')
const { getCurrentBranch } = require('./lib/utils.js')
const bumpType = args._[0]
const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron'
require('colors')
const pass = '\u2713'.green
const fail = '\u2717'.red
// TODO (future) automatically determine version based on conventional commits
// via conventional-recommended-bump
if (!bumpType && !args.notesOnly) {
console.log(`Usage: prepare-release [stable | beta | nightly]` +
` (--stable) (--notesOnly) (--automaticRelease) (--branch)`)
process.exit(1)
}
const github = new GitHub()
const gitDir = path.resolve(__dirname, '..')
github.authenticate({ type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN })
async function getNewVersion (dryRun) {
if (!dryRun) {
console.log(`Bumping for new "${bumpType}" version.`)
}
const bumpScript = path.join(__dirname, 'bump-version.js')
const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`]
if (dryRun) scriptArgs.push('--dry-run')
try {
let bumpVersion = execSync(scriptArgs.join(' '), { encoding: 'UTF-8' })
bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim()
const newVersion = `v${bumpVersion}`
if (!dryRun) {
console.log(`${pass} Successfully bumped version to ${newVersion}`)
}
return newVersion
} catch (err) {
console.log(`${fail} Could not bump version, error was:`, err)
throw err
}
}
async function getReleaseNotes (currentBranch) {
if (bumpType === 'nightly') {
return 'Nightlies do not get release notes, please compare tags for info'
}
console.log(`Generating release notes for ${currentBranch}.`)
const releaseNotes = await releaseNotesGenerator(currentBranch)
if (releaseNotes.warning) {
console.warn(releaseNotes.warning)
}
return releaseNotes
}
async function createRelease (branchToTarget, isBeta) {
const releaseNotes = await getReleaseNotes(branchToTarget)
const newVersion = await getNewVersion()
await tagRelease(newVersion)
const githubOpts = {
owner: 'electron',
repo: targetRepo
}
console.log(`Checking for existing draft release.`)
const releases = await github.repos.getReleases(githubOpts)
.catch(err => {
console.log(`${fail} Could not get releases. Error was: `, err)
})
const drafts = releases.data.filter(release => release.draft &&
release.tag_name === newVersion)
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.draft = true
githubOpts.name = `electron ${newVersion}`
if (isBeta) {
if (newVersion.indexOf('nightly') > 0) {
githubOpts.body = `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, ` +
`or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`
} else {
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)}.\n \n ${releaseNotes.text}`
}
githubOpts.name = `${githubOpts.name}`
githubOpts.prerelease = true
} else {
githubOpts.body = releaseNotes
}
githubOpts.tag_name = newVersion
githubOpts.target_commitish = newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget
const release = await github.repos.createRelease(githubOpts)
.catch(err => {
console.log(`${fail} Error creating new release: `, err)
process.exit(1)
})
console.log(`Release has been created with id: ${release.id}.`)
console.log(`${pass} Draft release for ${newVersion} successful.`)
}
async function pushRelease (branch) {
const 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".`)
} else {
console.log(`${fail} Error pushing the release: ` +
`${pushDetails.stderr}`)
process.exit(1)
}
}
async function runReleaseBuilds (branch) {
await ciReleaseBuild(branch, {
ghRelease: true,
automaticRelease: args.automaticRelease
})
}
async function tagRelease (version) {
console.log(`Tagging release ${version}.`)
const checkoutDetails = await GitProcess.exec([ 'tag', '-a', '-m', version, version ], gitDir)
if (checkoutDetails.exitCode === 0) {
console.log(`${pass} Successfully tagged ${version}.`)
} else {
console.log(`${fail} Error tagging ${version}: ` +
`${checkoutDetails.stderr}`)
process.exit(1)
}
}
async function verifyNewVersion () {
const newVersion = await getNewVersion(true)
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 {
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)
})
})
}
// function to determine if there have been commits to master since the last release
async function changesToRelease () {
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'`], gitDir)
return !lastCommitWasRelease.test(lastCommit.stdout)
}
async function prepareRelease (isBeta, notesOnly) {
if (args.dryRun) {
const newVersion = await getNewVersion(true)
console.log(newVersion)
} else {
const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(gitDir)
if (notesOnly) {
const releaseNotes = await getReleaseNotes(currentBranch)
console.log(`Draft release notes are: \n${releaseNotes.text}`)
} else {
const changes = await changesToRelease(currentBranch)
if (changes) {
await verifyNewVersion()
await createRelease(currentBranch, isBeta)
await pushRelease(currentBranch)
await runReleaseBuilds(currentBranch)
} else {
console.log(`There are no new changes to this branch since the last release, aborting release.`)
process.exit(1)
}
}
}
}
prepareRelease(!args.stable, args.notesOnly)