prepare-release.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #!/usr/bin/env node
  2. require('colors')
  3. const args = require('minimist')(process.argv.slice(2))
  4. const assert = require('assert')
  5. const ciReleaseBuild = require('./ci-release-build')
  6. const { execSync } = require('child_process')
  7. const fail = '\u2717'.red
  8. const { GitProcess, GitError } = require('dugite')
  9. const GitHub = require('github')
  10. const pass = '\u2713'.green
  11. const path = require('path')
  12. const pkg = require('../package.json')
  13. const versionType = args._[0]
  14. // TODO (future) automatically determine version based on conventional commits
  15. // via conventional-recommended-bump
  16. assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
  17. if (!versionType && !args.notesOnly) {
  18. console.log(`Usage: prepare-release versionType [major | minor | patch ]` +
  19. ` (--notesOnly)`)
  20. process.exit(1)
  21. }
  22. const github = new GitHub()
  23. const gitDir = path.resolve(__dirname, '..')
  24. github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
  25. async function createReleaseBranch () {
  26. console.log(`Creating release branch.`)
  27. let checkoutDetails = await GitProcess.exec([ 'checkout', '-b', 'release-1-6-x' ], gitDir)
  28. if (checkoutDetails.exitCode === 0) {
  29. console.log(`${pass} Successfully created the release branch.`)
  30. } else {
  31. const error = GitProcess.parseError(checkoutDetails.stderr)
  32. if (error === GitError.BranchAlreadyExists) {
  33. console.log(`${fail} Release branch already exists, aborting prepare ` +
  34. `release process.`)
  35. } else {
  36. console.log(`${fail} Error creating release branch: ` +
  37. `${checkoutDetails.stderr}`)
  38. }
  39. process.exit(1)
  40. }
  41. }
  42. function getNewVersion () {
  43. console.log(`Bumping for new "${versionType}" version.`)
  44. let bumpScript = path.join(__dirname, 'bump-version.py')
  45. let scriptArgs = [bumpScript, `${versionType}`]
  46. if (args.stable) {
  47. scriptArgs.push('--stable')
  48. }
  49. try {
  50. let bumpVersion = execSync(scriptArgs.join(' '), {encoding: 'UTF-8'})
  51. bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim()
  52. let newVersion = `v${bumpVersion}`
  53. console.log(`${pass} Successfully bumped version to ${newVersion}`)
  54. return newVersion
  55. } catch (err) {
  56. console.log(`${fail} Could not bump version, error was:`, err)
  57. }
  58. }
  59. async function getCurrentBranch (gitDir) {
  60. console.log(`Determining current git branch`)
  61. let gitArgs = ['rev-parse', '--abbrev-ref', 'HEAD']
  62. let branchDetails = await GitProcess.exec(gitArgs, gitDir)
  63. if (branchDetails.exitCode === 0) {
  64. let currentBranch = branchDetails.stdout.trim()
  65. console.log(`${pass} Successfully determined current git branch is ` +
  66. `${currentBranch}`)
  67. return currentBranch
  68. } else {
  69. let error = GitProcess.parseError(branchDetails.stderr)
  70. console.log(`${fail} Could not get details for the current branch,
  71. error was ${branchDetails.stderr}`, error)
  72. process.exit(1)
  73. }
  74. }
  75. async function getReleaseNotes (currentBranch) {
  76. console.log(`Generating release notes for ${currentBranch}.`)
  77. let githubOpts = {
  78. owner: 'electron',
  79. repo: 'electron',
  80. base: `v${pkg.version}`,
  81. head: currentBranch
  82. }
  83. let releaseNotes = '(placeholder)\n'
  84. console.log(`Checking for commits from ${pkg.version} to ${currentBranch}`)
  85. let commitComparison = await github.repos.compareCommits(githubOpts)
  86. .catch(err => {
  87. console.log(`{$fail} Error checking for commits from ${pkg.version} to ` +
  88. `${currentBranch}`, err)
  89. process.exit(1)
  90. })
  91. commitComparison.data.commits.forEach(commitEntry => {
  92. let commitMessage = commitEntry.commit.message
  93. if (commitMessage.toLowerCase().indexOf('merge') > -1) {
  94. releaseNotes += `${commitMessage} \n`
  95. }
  96. })
  97. console.log(`${pass} Done generating release notes for ${currentBranch}.`)
  98. return releaseNotes
  99. }
  100. async function createRelease (branchToTarget, isBeta) {
  101. let releaseNotes = await getReleaseNotes(branchToTarget)
  102. let newVersion = getNewVersion()
  103. const githubOpts = {
  104. owner: 'electron',
  105. repo: 'electron'
  106. }
  107. console.log(`Checking for existing draft release.`)
  108. let releases = await github.repos.getReleases(githubOpts)
  109. .catch(err => {
  110. console.log('$fail} Could not get releases. Error was', err)
  111. })
  112. let drafts = releases.data.filter(release => release.draft &&
  113. release.tag_name === newVersion)
  114. if (drafts.length > 0) {
  115. console.log(`${fail} Aborting because draft release for
  116. ${drafts[0].tag_name} already exists.`)
  117. process.exit(1)
  118. }
  119. console.log(`${pass} A draft release does not exist; creating one.`)
  120. githubOpts.body = releaseNotes
  121. githubOpts.draft = true
  122. githubOpts.name = `electron ${newVersion}`
  123. if (isBeta) {
  124. githubOpts.body = `Note: This is a beta release. Please file new issues ` +
  125. `for any bugs you find in it.\n \n This release is published to npm ` +
  126. `under the beta tag and can be installed via npm install electron@beta, ` +
  127. `or npm i electron@${newVersion.substr(1)}.`
  128. githubOpts.name = `${githubOpts.name}`
  129. githubOpts.prerelease = true
  130. }
  131. githubOpts.tag_name = newVersion
  132. githubOpts.target_commitish = branchToTarget
  133. await github.repos.createRelease(githubOpts)
  134. .catch(err => {
  135. console.log(`${fail} Error creating new release: `, err)
  136. process.exit(1)
  137. })
  138. console.log(`${pass} Draft release for ${newVersion} has been created.`)
  139. }
  140. async function pushRelease () {
  141. let pushDetails = await GitProcess.exec(['push', 'origin', 'HEAD'], gitDir)
  142. if (pushDetails.exitCode === 0) {
  143. console.log(`${pass} Successfully pushed the release branch. Wait for ` +
  144. `release builds to finish before running "npm run release".`)
  145. } else {
  146. console.log(`${fail} Error pushing the release branch: ` +
  147. `${pushDetails.stderr}`)
  148. process.exit(1)
  149. }
  150. }
  151. async function runReleaseBuilds () {
  152. await ciReleaseBuild('release-1-6-x', {
  153. ghRelease: true
  154. })
  155. }
  156. async function prepareRelease (isBeta, notesOnly) {
  157. let currentBranch = await getCurrentBranch(gitDir)
  158. if (notesOnly) {
  159. let releaseNotes = await getReleaseNotes(currentBranch)
  160. console.log(`Draft release notes are: ${releaseNotes}`)
  161. } else {
  162. await createReleaseBranch()
  163. await createRelease(currentBranch, isBeta)
  164. await pushRelease()
  165. await runReleaseBuilds()
  166. }
  167. }
  168. prepareRelease(!args.stable, args.notesOnly)