123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- #!/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 { Octokit } = require('@octokit/rest');
- const { execSync } = require('child_process');
- const { GitProcess } = require('dugite');
- const path = require('path');
- const readline = require('readline');
- const releaseNotesGenerator = require('./notes/index.js');
- const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js');
- const bumpType = args._[0];
- const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron';
- const octokit = new Octokit({
- auth: process.env.ELECTRON_GITHUB_TOKEN
- });
- require('colors');
- const pass = '✓'.green;
- const fail = '✗'.red;
- if (!bumpType && !args.notesOnly) {
- console.log('Usage: prepare-release [stable | minor | beta | alpha | nightly]' +
- ' (--stable) (--notesOnly) (--automaticRelease) (--branch)');
- process.exit(1);
- }
- async function getNewVersion (dryRun) {
- if (!dryRun) {
- console.log(`Bumping for new "${bumpType}" version.`);
- }
- const bumpScript = path.join(__dirname, 'version-bumper.js');
- const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`];
- if (dryRun) scriptArgs.push('--dryRun');
- 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, newVersion) {
- if (bumpType === 'nightly') {
- return { text: 'Nightlies do not get release notes, please compare tags for info.' };
- }
- console.log(`Generating release notes for ${currentBranch}.`);
- const releaseNotes = await releaseNotesGenerator(currentBranch, newVersion);
- if (releaseNotes.warning) {
- console.warn(releaseNotes.warning);
- }
- return releaseNotes;
- }
- async function createRelease (branchToTarget, isBeta) {
- const newVersion = await getNewVersion();
- const releaseNotes = await getReleaseNotes(branchToTarget, newVersion);
- await tagRelease(newVersion);
- console.log('Checking for existing draft release.');
- const releases = await octokit.repos.listReleases({
- owner: 'electron',
- repo: targetRepo
- }).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.`);
- let releaseBody;
- let releaseIsPrelease = false;
- if (isBeta) {
- if (newVersion.indexOf('nightly') > 0) {
- releaseBody = '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 electron-nightly package and can be installed via `npm install electron-nightly`, ' +
- `or \`npm install electron-nightly@${newVersion.substr(1)}\`.\n \n ${releaseNotes.text}`;
- } else if (newVersion.indexOf('alpha') > 0) {
- releaseBody = 'Note: This is an alpha release. Please file new issues ' +
- 'for any bugs you find in it.\n \n This release is published to npm ' +
- 'under the alpha tag and can be installed via `npm install electron@alpha`, ' +
- `or \`npm install electron@${newVersion.substr(1)}\`.\n \n ${releaseNotes.text}`;
- } else {
- releaseBody = '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 install electron@${newVersion.substr(1)}\`.\n \n ${releaseNotes.text}`;
- }
- releaseIsPrelease = true;
- } else {
- releaseBody = releaseNotes.text;
- }
- const release = await octokit.repos.createRelease({
- owner: 'electron',
- repo: targetRepo,
- tag_name: newVersion,
- draft: true,
- name: `electron ${newVersion}`,
- body: releaseBody,
- prerelease: releaseIsPrelease,
- target_commitish: newVersion.indexOf('nightly') !== -1 ? 'main' : branchToTarget
- }).catch(err => {
- console.log(`${fail} Error creating new release: `, err);
- process.exit(1);
- });
- console.log(`Release has been created with id: ${release.data.id}.`);
- console.log(`${pass} Draft release for ${newVersion} successful.`);
- }
- async function pushRelease (branch) {
- const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR);
- 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
- });
- }
- async function tagRelease (version) {
- console.log(`Tagging release ${version}.`);
- const checkoutDetails = await GitProcess.exec(['tag', '-a', '-m', version, version], ELECTRON_DIR);
- 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 main since the last release
- async function changesToRelease () {
- // eslint-disable-next-line no-useless-escape
- const lastCommitWasRelease = new RegExp('^Bump v[0-9]+\.[0-9]+\.[0-9]+(-beta\.[0-9]+)?(-alpha\.[0-9]+)?(-nightly\.[0-9]+)?$', 'g');
- const lastCommit = await GitProcess.exec(['log', '-n', '1', '--pretty=format:\'%s\''], ELECTRON_DIR);
- 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(ELECTRON_DIR);
- if (notesOnly) {
- const newVersion = await getNewVersion(true);
- const releaseNotes = await getReleaseNotes(currentBranch, newVersion);
- console.log(`Draft release notes are: \n${releaseNotes.text}`);
- } else {
- const changes = await changesToRelease();
- 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)
- .catch((err) => {
- console.error(err);
- process.exit(1);
- });
|