Skip to content

Instantly share code, notes, and snippets.

@metruzanca
Last active August 14, 2021 00:43
Show Gist options
  • Save metruzanca/359e0adb5f9d087ec242f76aad98eb8e to your computer and use it in GitHub Desktop.
Save metruzanca/359e0adb5f9d087ec242f76aad98eb8e to your computer and use it in GitHub Desktop.
Nodejs create release script

Usage

  1. Install dependencies
npm i -D inquirer chalk exec-sh ts-node
  1. Add this structure to your package.json and include all file paths that have a version key that you want to modify.

This assumes version key in the first level of the jsons.

"semverPaths": [
  "./manifest.json",
  "./package.json"
]
  1. Add a Release script to package.json
 "scripts": {
    "release": "ts-node release.ts"
  },
  1. Create a new Release
npm run release [patch|minor|major] <optional release message>
  1. Bonus Add this workflow to .github/workflows/releases.yml
name: Releases

on: 
  push:
    tags:
    - '*'

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v2
      with:
        node-version: '14'
    - run: npm install
    # - run: npm test
    - run: npm run build
    - uses: ncipollo/release-action@v1
      with:
        # TODO Idk if this works, might need to list all files individually
        artifacts: "build/*"
        # bodyFile: "body.md"
        token: ${{ secrets.GITHUB_TOKEN }}
  1. PROFIT Imgur

Also, if you wanna create tags manually with this workflow, heres a TD;DR

# Create a tag (don't include v in semver or obsidian won't play nice)
git tag -a <semver> -m "<release message>"

# Push to trigger the release
git push --tags
const { resolve } = require("path")
const fs = require('fs').promises
const { promisify } = require('util')
const inquirer = require('inquirer')
const chalk = require('chalk')
const execSh = promisify(require('exec-sh'))
const {version: packageVersion, semverPaths} = require('./package.json')
const SEMVER = semverPaths as string[]
type Label = 'patch'|'minor'|'major'
type LabelNumber = 0|1|2
main()
async function main() {
const label = process.argv[2] as Label
const labelNumber = parseLabel(label)
const releaseMessage = process.argv[3]
const version = incrementSemver(packageVersion, labelNumber)
const { confirm } = await inquirer.prompt({
type: 'confirm',
name: 'confirm',
message: `Creating new Git tag for ${packageVersion} => ${colorizeVersion(version, labelNumber)}\nShall I proceed?`
})
if (confirm) {
try {
console.log(chalk.green('Updating Files...'))
await updateFiles(SEMVER, version)
console.log(chalk.green('Commiting Files...'))
await commit(version)
console.log(chalk.green('Creating Release Tag...'))
await createTag(version, label, releaseMessage)
console.log(chalk.green('Pushing Files & Tag...'))
await execSh(`git push --tags`)
console.log('Release Created and Deploying! 🚀')
} catch (error) {
console.error(chalk.red("Something went wrong while attempting to create git tags."))
console.error("I recommend running\n\ngit status\n\nto find out what happened.")
console.error("Make sure git is installed in this shell.")
}
}
}
function parseLabel(label: Label): LabelNumber {
switch (label) {
case 'major': return 0
case 'minor': return 1
case 'patch': return 2
default:
console.error(`Received "${process.argv[2]}" and was expecting one of ["patch", "minor", "major"]`)
process.exit(1)
}
}
function incrementSemver(version: string, label: LabelNumber) {
const currentVersion = version
.split('.')
.map((n:string) => parseInt(n))
currentVersion[label] = currentVersion[label] + 1
return currentVersion.join('.')
}
function colorizeVersion(version: string, label: LabelNumber) {
return version
.split('.')
.map((n, i) => i === label ? chalk.green(n) : n)
.join('.')
}
async function updateFiles(filePaths: string[], version: string) {
try {
for (const path of filePaths) {
const resolvedPath = resolve(path)
const data = await fs.readFile(resolvedPath, 'utf-8').then(JSON.parse)
data.version = version
await fs.writeFile(resolvedPath, JSON.stringify(data, null, 2))
}
} catch (error) {
process.exit(1)
}
}
function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
async function commit(version: string) {
const commitMessage = `Updated version ${packageVersion} => ${version}`
await execSh(`git add . && git commit -m "${commitMessage}"`)
}
async function createTag(version: string, label: string, message: string) {
message = message ? ` - ${message}` : ''
const tagMessage = `${capitalize(label)} update: ${packageVersion} => ${version}${message}`
await execSh(`git tag -a ${version} -m "${tagMessage}"`)
console.log(tagMessage)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment