Created
March 6, 2019 18:15
-
-
Save dsoike/fab528b669619cf8eda748f9e22852cf to your computer and use it in GitHub Desktop.
Release Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const chalk = require('chalk') | |
const exec = require('child_process').exec | |
const fs = require('fs') | |
const readline = require('readline') | |
const spawn = require('child_process').spawn | |
const repoName = 'MyRepo' | |
const repoUrl = `https://github.com/MyOrg/${repoName}` | |
const checkMark = chalk.green('\u2713') | |
async function createRelease() { | |
printTitle() | |
await confirmContinue() | |
const releaseVersion = await askForReleaseVersion() | |
const releaseBranchName = `release-v${releaseVersion}` | |
const builtReleaseBranchName = `release-v${releaseVersion}-built` | |
const releaseTagName = `v${releaseVersion}` | |
const nodeVersion = await getNodeVersion() | |
await checkNodeVersion(nodeVersion) | |
await createGitBranch(releaseBranchName) | |
await updatePackageJson(releaseVersion) | |
await commitVersionChanges(releaseVersion) | |
await pushGitBranch(releaseBranchName) | |
await createGitBranch(builtReleaseBranchName) | |
await deleteBuildFiles() | |
await generateBuildFiles() | |
await commitBuildFiles() | |
await pushGitBranch(builtReleaseBranchName) | |
await createReleaseTag(releaseTagName) | |
await pushReleaseTag(releaseTagName) | |
printDone(releaseTagName, releaseBranchName) | |
} | |
function printTitle() { | |
console.info(chalk.bold(chalk.underline(`Create ${repoName} Release`))) | |
console.info('You are about to create a release. This process will:') | |
console.info(' - Create a release branch (release-v1.2.3)') | |
console.info(' - Update package.json with your specified release version') | |
console.info(' - Push your release branch to the remote') | |
console.info(' - Create a built release branch (release-v1.2.3-built)') | |
console.info(' - Compile build assets, git force add, and commit to your built branch') | |
console.info(' - Tag this new commit with the release version (v1.2.3)') | |
console.info(' - Push the built release branch and release tag to the remote') | |
console.info('') | |
} | |
function confirmContinue() { | |
return new Promise((resolve, reject) => { | |
const rl = readline.createInterface({input: process.stdin, output: process.stdout}) | |
rl.question('Do you want to continue? [y/n] ', (answer) => { | |
rl.close() | |
console.info('') | |
if (answer === 'y') { | |
resolve() | |
} else { | |
reject() | |
} | |
}); | |
}) | |
} | |
function askForReleaseVersion() { | |
return new Promise((resolve, reject) => { | |
const rl = readline.createInterface({input: process.stdin, output: process.stdout}) | |
rl.question('What release version would you like to create? (ex: 1.2.3) ', (releaseVersion) => { | |
rl.close() | |
if (releaseVersion.match(/^\d+\.\d+\.\d+$/)) { | |
console.info(` -> Proceeding with release version: ${chalk.bold(releaseVersion)}\n`) | |
resolve(releaseVersion) | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - ${releaseVersion} is not a valid release version\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function getNodeVersion() { | |
return new Promise((resolve, reject) => { | |
let packageJsonPath = `${process.env.PWD}/package.json` | |
let packageJson = require(packageJsonPath) | |
if (!packageJson.hasOwnProperty('engines') || !packageJson.engines.hasOwnProperty('node')) { | |
const errMsg = `unable to retrieve node version from package.json b/c 'engines.node' field not found` | |
console.error(chalk.red(`${chalk.bold('Error!')} - ${errMsg}\n`)) | |
reject() | |
} else { | |
resolve(packageJson.engines.node) | |
} | |
}) | |
} | |
function checkNodeVersion(nodeVersion) { | |
return new Promise((resolve, reject) => { | |
if (process.version.indexOf(nodeVersion) > 0) { | |
resolve() | |
} else { | |
const errMsg = `your current node version is ${process.version} - not v${nodeVersion}` | |
console.error(chalk.red(`${chalk.bold('Error!')} - ${errMsg}`)) | |
console.info(`You can check your current node version with: ${chalk.blue('node --version')}`) | |
console.info(`If you have Node Version Manager, you can: ${chalk.blue(`nvm install ${nodeVersion}`)}\n`) | |
reject() | |
} | |
}) | |
} | |
function createGitBranch(branchName) { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Creating Git Branch`)) | |
const childProcess = exec(`git checkout -b ${branchName}`, function(err) { | |
if (err) { console.error(err) } | |
}) | |
childProcess.on('exit', function(exitCode) { | |
if (exitCode === 0) { | |
console.info(`${checkMark} git branch '${branchName}' created\n`) | |
resolve() | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to create git branch ${branchName}\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function updatePackageJson(releaseVersion) { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Updating package.json`)) | |
let packageJsonPath = `${process.env.PWD}/package.json` | |
let packageJson = require(packageJsonPath) | |
if (!packageJson.hasOwnProperty('version')) { | |
const errMsg = `unable to update package.json b/c 'version' field not found` | |
console.error(chalk.red(`${chalk.bold('Error!')} - ${errMsg}\n`)) | |
reject() | |
} else { | |
packageJson.version = releaseVersion | |
fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, function(err) { | |
if (err) { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to update package.json\n`)) | |
console.error(err) | |
reject() | |
} else { | |
console.info(`${checkMark} package.json updated with version=${releaseVersion}\n`) | |
resolve() | |
} | |
}) | |
} | |
}) | |
} | |
function commitVersionChanges(releaseVersion) { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Committing Version Changes`)) | |
const commitMessage = `Release v${releaseVersion}` | |
const childProcess = exec(`git add -A && git commit -m "${commitMessage}"`, function(err) { | |
if (err) { console.error(err) } | |
}) | |
childProcess.on('exit', function(exitCode) { | |
if (exitCode === 0) { | |
console.info(`${checkMark} version changes committed\n`) | |
resolve() | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to commit version changes\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function pushGitBranch(branchName) { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Pushing Git Branch`)) | |
const childProcess = exec(`git push --set-upstream origin ${branchName}`, function(err) { | |
if (err) { console.error(err) } | |
}) | |
childProcess.on('exit', function(exitCode) { | |
if (exitCode === 0) { | |
console.info(`${checkMark} git branch ${branchName} pushed\n`) | |
resolve() | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to push git branch ${branchName}\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function deleteBuildFiles() { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Deleting Build Files`)) | |
const childProcess = spawn('rm -rf $PWD/build/*', [], {shell: true}) | |
childProcess.stdout.on('data', (data) => { console.info(data.toString()) }) | |
childProcess.stderr.on('data', (data) => { console.error(data.toString()) }) | |
childProcess.on('close', (exitCode) => { | |
if (exitCode === 0) { | |
console.info(`${checkMark} build files deleted\n`) | |
resolve() | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to delete build files\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function generateBuildFiles() { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Generating Build Files`)) | |
const childProcess = spawn('npm run build --silent', [], {shell: true}) | |
childProcess.stdout.on('data', (data) => { console.info(data.toString().trim()) }) | |
childProcess.stderr.on('data', (data) => { console.error(data.toString()) }) | |
childProcess.on('close', (exitCode) => { | |
if (exitCode === 0) { | |
console.info(`${checkMark} build files generated\n`) | |
resolve() | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to generate build files\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function commitBuildFiles() { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Committing Build Files`)) | |
const commitMessage = `Force commit build files` | |
const childProcess = exec(`git add -f build && git commit -m "${commitMessage}"`, function(err) { | |
if (err) { console.error(err) } | |
}) | |
childProcess.on('exit', function(exitCode) { | |
if (exitCode === 0) { | |
console.info(`${checkMark} build files committed\n`) | |
resolve() | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to commit build files\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function createReleaseTag(tagName) { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Creating Release Tag`)) | |
const childProcess = exec(`git tag ${tagName}`, function(err) { | |
if (err) { console.error(err) } | |
}) | |
childProcess.on('exit', function(exitCode) { | |
if (exitCode === 0) { | |
console.info(`${checkMark} release tag ${tagName} created\n`) | |
resolve() | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to create release tag ${tagName}\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function pushReleaseTag(tagName) { | |
return new Promise((resolve, reject) => { | |
console.info(chalk.underline(`Pushing Release Tag`)) | |
const childProcess = exec(`git push origin ${tagName}`, function(err) { | |
if (err) { console.error(err) } | |
}) | |
childProcess.on('exit', function(exitCode) { | |
if (exitCode === 0) { | |
console.info(`${checkMark} release tag ${tagName} pushed\n`) | |
resolve() | |
} else { | |
console.error(chalk.red(`${chalk.bold('Error!')} - unable to push release tag ${tagName}\n`)) | |
reject() | |
} | |
}) | |
}) | |
} | |
function printDone(releaseTag, releaseBranch) { | |
console.info(chalk.bold(chalk.underline(`${repoName} Release Created!`))) | |
console.info('') | |
console.info('Please give your release a title and provide release notes.') | |
console.info('You can edit and publish your release here:') | |
console.info(`${repoUrl}/releases/tag/${releaseTag}`) | |
console.info('') | |
console.info('You should also create a pull request for your release branch.') | |
console.info('You can open the pull request here:') | |
console.info(`${repoUrl}/compare/${releaseBranch}?expand=1`) | |
console.info('') | |
} | |
if (require.main === module) { | |
createRelease() | |
.then(() => { process.exit(0) }) | |
.catch((err) => { if (err) { console.error(err) } process.exit(1) }) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment