/** * Release helper script. * Required to be run on the release branch. * * It merges the release branch to the `develop` and `master` branches and pushes them, along with the created tags. */ import inquirer from 'inquirer'; import execa from 'execa'; import { displayErrorMessage, displaySeparator, spawnProcess, } from './utils/index.mjs'; displaySeparator(); (async() => { // Initial verification prompt const answers = await inquirer.prompt([ { type: 'confirm', name: 'continueRelease', message: 'This script will take the current state of the release branch and merge, commit and push it to the' + ' `master` branch (as well as the tags).\nContinue?', default: true, } ]); if (!answers.continueRelease) { process.exit(0); } // Check if all the files are committed. { const processInfo = await spawnProcess('git status -s', { silent: true }); const output = processInfo.stdout.toString(); // If there are any uncommitted changes, kill the script. if (output.length > 0) { displayErrorMessage('There are uncommitted changes present. Exiting.'); process.exit(1); } } { // Check if we're on a release branch. const processInfo = await spawnProcess('git rev-parse --abbrev-ref HEAD', { silent: true }); const branchName = processInfo.stdout.toString(); const releaseVersion = branchName.replace('release/', ''); if (!branchName.startsWith('release/')) { displayErrorMessage('You are not on a release branch.'); process.exit(1); } // Pull the recent changes from the `develop` and `master` branches, to prevent conflicts after merging. await spawnProcess('git checkout master'); await spawnProcess('git pull origin master'); await spawnProcess('git checkout develop'); await spawnProcess('git pull origin develop'); await spawnProcess(`git checkout ${branchName}`); // Merge the changes to the `develop` and `master` branches. await spawnProcess(`git flow release finish -s ${releaseVersion}`); await spawnProcess('git checkout develop'); await spawnProcess('git push origin develop'); await spawnProcess('git checkout master'); await spawnProcess('git push origin master'); await spawnProcess('git push --tags'); const docsVersion = `prod-docs/${releaseVersion.substring(0, 4)}`; // e.g. "prod-docs/12.1" (without patch) const remoteDocsBranchExists = await spawnProcess( `git ls-remote --heads origin --list ${docsVersion}`, { silent: true }); await spawnProcess('git checkout develop'); if (remoteDocsBranchExists.stdout) { await spawnProcess(`git checkout ${docsVersion}`); await spawnProcess(`git pull origin ${docsVersion}`); } else { // Create a new branch based on the git tag (not `develop` branch). Thanks to that, the docs // branch will not contain the code changes made between freeze and release. await spawnProcess(`git checkout -b ${docsVersion} ${releaseVersion}`); const linesCount = parseInt((await execa.command('wc -l < .gitignore', { cwd: 'docs', shell: true })).stdout, 10); // Remove "/content/api/" entry from the ./docs/.gitignore file so generated API // docs can be committed to the branch. await execa.command('cat ./.gitignore | grep -v "^/content/api/$" | tee .gitignore', { cwd: 'docs', shell: true, }); const newLinesCount = parseInt((await execa.command('wc -l < .gitignore', { cwd: 'docs', shell: true })).stdout, 10); if (newLinesCount + 1 !== linesCount && !Number.isNaN(newLinesCount)) { displayErrorMessage( 'The docs/.gitignore file modified by the release script is incorrect. Continuing the script' + ' execution would result in a broken documentation build.' ); process.exit(1); } } // Regenerate docs API md files. await spawnProcess('npm run docs:api', { cwd: 'docs' }); // Update all available Docs versions for legacy docs. await spawnProcess('npm run docs:scripts:generate-legacy-docs-versions', { cwd: 'docs' }); // Commit the Docs changes to the Docs Production branch. await spawnProcess('git add .'); await spawnProcess(`git commit -m "${releaseVersion}"`); await spawnProcess(`git push origin ${docsVersion}`); // Back to `develop` branch. await spawnProcess('git checkout develop'); } })();