CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-standard-version

A Node.js command-line tool that automates versioning and changelog generation for projects following the Conventional Commits specification.

Pending
Overview
Eval results
Files

lifecycle.mddocs/

Lifecycle Hooks

Standard Version provides comprehensive lifecycle hook support, allowing custom scripts to run at specific stages of the release process. This enables integration with build systems, testing frameworks, deployment pipelines, and custom validation workflows.

Capabilities

Lifecycle Script Execution

Core function for executing lifecycle hook scripts.

/**
 * Execute lifecycle script for specified hook name
 * @param {Object} args - Configuration object containing scripts definition
 * @param {string} hookName - Name of the lifecycle hook to execute
 * @returns {Promise<string>} Promise resolving to script output (stdout)
 * @throws {Error} If script execution fails or returns non-zero exit code
 */
async function runLifecycleScript(args, hookName);

Usage Examples:

const runLifecycleScript = require('standard-version/lib/run-lifecycle-script');

const config = {
  scripts: {
    prebump: 'npm run test',
    postbump: 'npm run build',
    posttag: 'npm publish'
  },
  silent: false
};

try {
  // Execute prebump hook
  const output = await runLifecycleScript(config, 'prebump');
  console.log('Prebump output:', output);
  
  // Execute hook that doesn't exist (returns resolved promise)
  await runLifecycleScript(config, 'nonexistent'); // No-op
  
} catch (error) {
  console.error('Script failed:', error.message);
}

Available Lifecycle Hooks

Standard Version supports hooks at eight key stages of the release process.

/**
 * Lifecycle hook execution order and timing:
 * 
 * 1. prerelease  - Before any release processing begins
 * 2. prebump     - Before version calculation and file updates
 * 3. postbump    - After version bump, before changelog generation
 * 4. prechangelog- Before changelog generation
 * 5. postchangelog - After changelog generation, before commit
 * 6. precommit   - Before git commit creation
 * 7. postcommit  - After git commit, before tag creation
 * 8. pretag      - Before git tag creation  
 * 9. posttag     - After git tag creation (final step)
 */

Hook Execution Flow:

// Standard Version execution flow with hooks:

await runLifecycleScript(args, 'prerelease');
// → Validate environment, prepare workspace

await runLifecycleScript(args, 'prebump'); 
// → Run tests, lint code, validate dependencies

const newVersion = await bump(args, currentVersion);

await runLifecycleScript(args, 'postbump');
// → Build artifacts, update documentation

await runLifecycleScript(args, 'prechangelog');
// → Generate additional release notes, validate commits

await changelog(args, newVersion);

await runLifecycleScript(args, 'postchangelog');
// → Format changelog, add custom sections

await runLifecycleScript(args, 'precommit');
// → Final validation, security checks

await commit(args, newVersion);

await runLifecycleScript(args, 'postcommit');
// → Notify systems, prepare for tagging

await runLifecycleScript(args, 'pretag');
// → Final pre-tag validation

await tag(newVersion, isPrivate, args);

await runLifecycleScript(args, 'posttag');
// → Publish packages, deploy, notify stakeholders

Hook Configuration

Configure lifecycle hooks through the scripts configuration object.

interface LifecycleScripts {
  prerelease?: string;     // Before any release processing
  prebump?: string;        // Before version calculation  
  postbump?: string;       // After version bump
  prechangelog?: string;   // Before changelog generation
  postchangelog?: string;  // After changelog generation
  precommit?: string;      // Before git commit
  postcommit?: string;     // After git commit
  pretag?: string;         // Before git tag
  posttag?: string;        // After git tag (final)
}

Usage Examples:

{
  "scripts": {
    "prerelease": "echo 'Starting release process'",
    "prebump": "npm run lint && npm run test",
    "postbump": "npm run build:prod",
    "prechangelog": "node scripts/gather-contributors.js",
    "postchangelog": "node scripts/format-changelog.js", 
    "precommit": "npm run security-audit",
    "postcommit": "echo 'Changes committed successfully'",
    "pretag": "npm run validate-build",
    "posttag": "npm publish && npm run deploy"
  }
}

Common Hook Patterns

Typical use cases and patterns for each lifecycle hook.

Development and Testing Hooks:

{
  "scripts": {
    "prerelease": "git status --porcelain | grep -q . && exit 1 || echo 'Working directory clean'",
    "prebump": "npm run lint && npm run test && npm run test:integration",
    "postbump": "npm run build && npm run test:build"
  }
}

Build and Deployment Hooks:

{
  "scripts": {
    "postbump": "npm run build:prod && npm run compress-assets",
    "postchangelog": "node scripts/update-docs.js",
    "precommit": "npm run validate-bundle-size",
    "posttag": "npm publish && docker build -t myapp:$npm_package_version ."
  }
}

Notification and Integration Hooks:

{
  "scripts": {
    "prerelease": "slack-notify 'Starting release for $npm_package_name'",
    "postcommit": "git push origin main",
    "posttag": "github-release create --tag $npm_package_version && slack-notify 'Released $npm_package_version'"
  }
}

Environment Variables in Hooks

Hooks have access to npm environment variables and can use them for dynamic behavior.

/**
 * Available environment variables in hook scripts:
 * - npm_package_name - Package name from package.json
 * - npm_package_version - Current version from package.json  
 * - npm_config_* - npm configuration values
 * - Standard shell environment variables
 */

Usage Examples:

{
  "scripts": {
    "postbump": "echo 'Building $npm_package_name version $npm_package_version'",
    "posttag": "docker tag myapp:latest myapp:$npm_package_version",
    "precommit": "echo 'Committing version $npm_package_version' >> release.log"
  }
}

Complex Hook Examples

Advanced hook configurations for sophisticated workflows.

Multi-step Hook with Error Handling:

{
  "scripts": {
    "prebump": "npm run lint && npm run test && npm run security-check || (echo 'Pre-bump validation failed' && exit 1)",
    "postbump": "npm run build && npm run package && npm run verify-package",
    "posttag": "npm publish && npm run deploy-docs && npm run notify-slack"
  }
}

Conditional Hook Execution:

{
  "scripts": {
    "prebump": "[ \"$CI\" = \"true\" ] && npm run test:ci || npm run test",
    "posttag": "[ \"$NODE_ENV\" = \"production\" ] && npm publish || echo 'Skipping publish in non-production'",
    "postcommit": "[ -f \"deploy.sh\" ] && ./deploy.sh || echo 'No deploy script found'"
  }
}

Hook with Custom Script Files:

{
  "scripts": {
    "prebump": "./scripts/pre-release-check.sh",
    "postbump": "node scripts/build-and-test.js",
    "postchangelog": "python scripts/update-changelog.py",
    "posttag": "./scripts/deploy-release.sh $npm_package_version"
  }
}

Hook Script Output Handling

Hook scripts can influence standard-version behavior through their output.

/**
 * Special hook output behaviors:
 * 
 * prebump hook output:
 * - If prebump script outputs text, it overrides --release-as option
 * - Output should be: major, minor, patch, or specific version number
 * 
 * All hooks:
 * - stdout is captured and can be logged
 * - stderr is displayed immediately  
 * - Non-zero exit code stops release process
 */

Usage Examples:

#!/bin/bash
# prebump hook that determines release type based on commit analysis
if git log --oneline -1 | grep -q "BREAKING CHANGE"; then
  echo "major"
elif git log --oneline -1 | grep -q "feat:"; then
  echo "minor"  
else
  echo "patch"
fi
// Node.js prebump script
const { execSync } = require('child_process');

// Analyze commits to determine version bump
const commits = execSync('git log --oneline --since="1 week ago"', { encoding: 'utf8' });

if (commits.includes('BREAKING CHANGE') || commits.includes('!:')) {
  console.log('major');
} else if (commits.includes('feat:')) {
  console.log('minor');
} else {
  console.log('patch'); 
}

Error Handling in Hooks

Proper error handling patterns for lifecycle hooks.

/**
 * Hook error handling:
 * - Script exit code 0: Success, continue release process
 * - Script exit code non-zero: Failure, abort release process
 * - Uncaught exceptions in Node.js hooks: Abort release process
 * - Empty or missing script: No-op, continue release process
 */

Usage Examples:

#!/bin/bash
# Robust prebump hook with error handling
set -e  # Exit on any error

echo "Running pre-bump validations..."

# Run tests with proper error handling
if ! npm run test; then
  echo "Tests failed, aborting release"
  exit 1
fi

# Check for uncommitted changes
if [ -n "$(git status --porcelain)" ]; then
  echo "Working directory not clean, aborting release"
  exit 1
fi

echo "All validations passed"
exit 0
// Node.js hook with error handling
const { execSync } = require('child_process');

try {
  console.log('Running build and validation...');
  
  execSync('npm run build', { stdio: 'inherit' });
  execSync('npm run test:build', { stdio: 'inherit' });
  
  console.log('Build and validation successful');
  process.exit(0);
  
} catch (error) {
  console.error('Build or validation failed:', error.message);
  process.exit(1);
}

Hook Integration with CI/CD

Patterns for using hooks effectively in continuous integration environments.

{
  "scripts": {
    "prerelease": "[ \"$CI\" != \"true\" ] && echo 'Running in development mode' || echo 'Running in CI mode'",
    "prebump": "[ \"$CI\" = \"true\" ] && npm ci || npm install",
    "postbump": "[ \"$CI\" = \"true\" ] && npm run build:ci || npm run build:dev",
    "posttag": "[ \"$CI\" = \"true\" ] && [ \"$BRANCH\" = \"main\" ] && npm publish || echo 'Skipping publish'"
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-standard-version

docs

api.md

cli.md

configuration.md

index.md

lifecycle.md

updaters.md

tile.json