2025-07-31 13:40:28 -07:00
#!/usr/bin/env node
/ * *
* Release Workflow Script
*
* This script provides a comprehensive workflow for releasing a new version :
* 1. Updates the version ( major , minor , or patch )
* 2. Automatically updates the CHANGELOG . md with commit messages since the last release
* 3. Creates a GitHub release
* 4. Deploys to NPM
*
* Usage :
* node scripts / release - workflow . js [ patch | minor | major ]
*
* If no version type is specified , it defaults to "patch"
* /
/* global process, console */
import { execSync } from 'child_process'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import readline from 'readline'
// Get the directory of the current module
const _ _filename = fileURLToPath ( import . meta . url )
const _ _dirname = path . dirname ( _ _filename )
// Path to the root directory
const rootDir = path . join ( _ _dirname , '..' )
// Get the version type from command line arguments
const args = process . argv . slice ( 2 )
const versionType = args [ 0 ] || 'patch'
// Validate version type
if ( ! [ 'patch' , 'minor' , 'major' ] . includes ( versionType ) ) {
// eslint-disable-next-line no-console
console . error ( 'Error: Version type must be one of: patch, minor, major' )
// eslint-disable-next-line no-process-exit
process . exit ( 1 )
}
// Function to execute a command and log its output
function executeStep ( command , description ) {
// eslint-disable-next-line no-console
console . log ( ` \n 🚀 ${ description } ... \n ` )
try {
execSync ( command , { stdio : 'inherit' , cwd : rootDir } )
// eslint-disable-next-line no-console
console . log ( ` ✅ ${ description } completed successfully! \n ` )
return true
} catch ( error ) {
// eslint-disable-next-line no-console
2025-08-01 17:33:28 -07:00
console . error (
` ❌ Error during ${ description . toLowerCase ( ) } : ${ error . message } `
)
2025-07-31 13:40:28 -07:00
return false
}
}
// Main workflow
async function runReleaseWorkflow ( ) {
// eslint-disable-next-line no-console
console . log ( ` \n === Starting Release Workflow ( ${ versionType } ) === \n ` )
// Step 1: Build the project
if ( ! executeStep ( 'npm run build' , 'Building project' ) ) {
// eslint-disable-next-line no-process-exit
process . exit ( 1 )
}
// Step 2: Run tests to ensure everything is working
if ( ! executeStep ( 'npm test' , 'Running tests' ) ) {
// eslint-disable-next-line no-console
2025-08-01 17:33:28 -07:00
console . warn (
'⚠️ Tests failed. This might indicate issues with the release.'
)
2025-07-31 13:40:28 -07:00
// Ask the user if they want to continue despite test failures
// eslint-disable-next-line no-console
2025-08-01 17:33:28 -07:00
console . log (
'\n⚠️ Do you want to continue with the release process despite test failures? (y/N)'
)
2025-07-31 13:40:28 -07:00
const readline = require ( 'readline' ) . createInterface ( {
input : process . stdin ,
output : process . stdout
} )
2025-08-01 17:33:28 -07:00
const response = await new Promise ( ( resolve ) => {
readline . question ( '' , ( answer ) => {
2025-07-31 13:40:28 -07:00
readline . close ( )
resolve ( answer . toLowerCase ( ) )
} )
} )
2025-08-01 17:33:28 -07:00
2025-07-31 13:40:28 -07:00
if ( response !== 'y' && response !== 'yes' ) {
// eslint-disable-next-line no-console
console . error ( 'Release process aborted due to test failures.' )
// eslint-disable-next-line no-process-exit
process . exit ( 1 )
}
2025-08-01 17:33:28 -07:00
2025-07-31 13:40:28 -07:00
// eslint-disable-next-line no-console
console . log ( 'Continuing with release process despite test failures...' )
}
// Step 3: Update version and generate changelog
2025-08-01 17:33:28 -07:00
if (
! executeStep (
` npm run _release: ${ versionType } ` ,
` Updating version ( ${ versionType } ) and generating changelog `
)
) {
2025-07-31 13:40:28 -07:00
// eslint-disable-next-line no-process-exit
process . exit ( 1 )
}
// Step 4: Create GitHub release
2025-08-01 17:33:28 -07:00
if ( ! executeStep ( 'npm run _github-release' , 'Creating GitHub release' ) ) {
2025-07-31 13:40:28 -07:00
// eslint-disable-next-line no-console
2025-08-01 17:33:28 -07:00
console . log (
'Warning: GitHub release creation failed, but continuing with deployment...'
)
2025-07-31 13:40:28 -07:00
}
// Step 5: Publish to NPM
if ( ! executeStep ( 'npm publish' , 'Publishing to NPM' ) ) {
// eslint-disable-next-line no-process-exit
process . exit ( 1 )
}
// Get the new version from package.json
const packageJsonPath = path . join ( rootDir , 'package.json' )
const packageJson = JSON . parse ( fs . readFileSync ( packageJsonPath , 'utf8' ) )
const newVersion = packageJson . version
// eslint-disable-next-line no-console
console . log ( ` \n 🎉 Release v ${ newVersion } completed successfully! 🎉 \n ` )
// eslint-disable-next-line no-console
console . log ( 'Summary of actions:' )
// eslint-disable-next-line no-console
console . log ( ` - Project built and tested ` )
// eslint-disable-next-line no-console
console . log ( ` - Version bumped to v ${ newVersion } ( ${ versionType } ) ` )
// eslint-disable-next-line no-console
console . log ( ` - CHANGELOG.md updated with recent commits ` )
// eslint-disable-next-line no-console
console . log ( ` - GitHub release created with auto-generated notes ` )
// eslint-disable-next-line no-console
console . log ( ` - Package published to NPM ` )
// eslint-disable-next-line no-console
console . log ( '\nThank you for using the release workflow!\n' )
}
// Run the workflow
2025-08-01 17:33:28 -07:00
runReleaseWorkflow ( ) . catch ( ( error ) => {
2025-07-31 13:40:28 -07:00
// eslint-disable-next-line no-console
console . error ( 'Unexpected error during release workflow:' , error )
// eslint-disable-next-line no-process-exit
process . exit ( 1 )
} )