Hooks
Hooks are lifecycle events that allow you to run custom scripts at specific stages of the release workflow. They can be either shell commands (strings) or JavaScript/TypeScript functions.
Hook Types
Relizy provides three types of hooks for each step:
before:<step>- Executed before the step startssuccess:<step>- Executed after the step completes successfullyerror:<step>- Executed when an error occurs during the step
Available Steps
Hooks are available for the following steps:
bump- Version bumpingchangelog- Changelog generationcommit-and-tag- Git commit and tag creationpush- Push changes to remote repositorypublish- Package publication to npm registryprovider-release- Release publication to Git provider (GitHub/GitLab)release- Full release workflow (only available forrelizy release)
Special Hook: generate:changelog
The generate:changelog hook is unique and allows you to customize the generated changelog content. This hook is executed for each package's changelog generation and root changelog generation.
Parameters
type GenerateChangelogHook = (
config: ResolvedRelizyConfig,
dryRun: boolean,
params: {
commits: GitCommit[]
changelog: string
}
) => string | void | null | undefined | Promise<string | void | null | undefined>config- Resolved Relizy configurationdryRun- Whether the command is running in dry-run modeparams.commits- Array of Git commits for the current packageparams.changelog- The generated changelog content by Relizy
Return Value
- Return a string to replace the generated changelog with your custom content
- Return
undefined,void, ornullto keep the original generated changelog
Use Cases
- Enhance the changelog - Add additional information to Relizy's generated changelog
- Complete replacement - Ignore Relizy's output and generate your own changelog from scratch using the commits
- AI-powered generation - Use AI services to generate human-readable changelogs
- Custom formatting - Apply specific formatting rules or templates
Example
import { defineConfig } from 'relizy'
export default defineConfig({
hooks: {
'generate:changelog': async (config, dryRun, { commits, changelog }) => {
// Use AI to enhance the changelog
const enhancedChangelog = await generateAIChangelog(commits)
return enhancedChangelog
// Or keep the original by returning nothing
// return undefined
}
}
})Configuration
Hooks are configured in the hooks object in your relizy.config.ts:
String Commands
Execute shell commands:
import { defineConfig } from 'relizy'
export default defineConfig({
hooks: {
'before:bump': 'echo "Starting version bump"',
'success:bump': 'npm run build',
'error:bump': 'echo "Version bump failed" && exit 1'
}
})Function Hooks
Execute JavaScript/TypeScript functions:
import { defineConfig } from 'relizy'
export default defineConfig({
hooks: {
'before:bump': (config, dryRun) => {
console.log('Starting bump with config:', config.monorepo?.versionMode)
console.log('Dry run mode:', dryRun)
},
'success:bump': async (config, dryRun) => {
// Async operations are supported
await sendNotification('Version bumped successfully')
},
'error:bump': (config, dryRun) => {
console.error('Bump failed!')
// You can throw to stop execution
throw new Error('Critical error during bump')
}
}
})Hook Execution Context
Parameters
All hooks (except generate:changelog) receive:
config(ResolvedRelizyConfig) - The resolved configuration objectdryRun(boolean) - Whether the command is running in dry-run mode
Dry Run Mode
When a command is executed with --dry-run, hooks are still executed but you should check the dryRun parameter to avoid side effects:
export default defineConfig({
hooks: {
'success:publish': (config, dryRun) => {
if (dryRun) {
console.log('[dry-run] Would send notification')
return
}
sendNotification('Packages published!')
}
}
})Hooks by Command
Different commands execute different hooks based on their workflow:
relizy bump
before:bumpsuccess:bump|error:bump
relizy changelog
before:changeloggenerate:changelog(for each package and root changelog)success:changelog|error:changelog
relizy publish
before:publishsuccess:publish|error:publish
relizy provider-release
before:provider-releasesuccess:provider-release|error:provider-release
relizy release
The release command orchestrates multiple steps and executes hooks for each:
before:release- Bump →
before:bump, (success:bump|error:bump) - Changelog →
before:changelog,generate:changelog, (success:changelog|error:changelog) - Commit and Tag →
before:commit-and-tag, (success:commit-and-tag|error:commit-and-tag) - Push →
before:push, (success:push|error:push) - Publish →
before:publish, (success:publish|error:publish) - Provider Release →
before:provider-release, (success:provider-release|error:provider-release) success:release|error:release
TIP
You can disable specific steps with flags like --no-changelog, --no-push, or --no-publish. Hooks for disabled steps won't be executed.
Practical Examples
Send Slack Notifications
import { defineConfig } from 'relizy'
export default defineConfig({
hooks: {
'success:release': async (config, dryRun) => {
if (dryRun)
return
await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `🚀 New release published!`
})
})
}
}
})Run Tests Before Publishing
import { defineConfig } from 'relizy'
export default defineConfig({
hooks: {
'before:publish': 'npm run test',
'error:publish': (config, dryRun) => {
console.error('❌ Publishing failed, rolling back...')
// Implement rollback logic
}
}
})Update Documentation
import { defineConfig } from 'relizy'
export default defineConfig({
hooks: {
'success:bump': 'npm run docs:generate',
'success:changelog': 'npm run docs:build'
}
})AI-Enhanced Changelog Generation
import OpenAI from 'openai'
import { defineConfig } from 'relizy'
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
export default defineConfig({
hooks: {
'generate:changelog': async (config, dryRun, { commits, changelog }) => {
if (dryRun || commits.length === 0) {
return undefined // Keep original
}
const commitMessages = commits
.map(c => `${c.type}: ${c.message}`)
.join('\n')
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{
role: 'user',
content: `Generate a user-friendly changelog entry from these commits:\n${commitMessages}`
}]
})
return response.choices[0].message.content || changelog
}
}
})Deploy After Release
import { defineConfig } from 'relizy'
export default defineConfig({
hooks: {
'success:provider-release': async (config, dryRun) => {
if (dryRun) {
console.log('[dry-run] Would trigger deployment')
return
}
// Trigger deployment pipeline
await fetch('https://api.yourci.com/deploy', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.CI_TOKEN}` }
})
}
}
})Error Handling
When an error hook throws an error or a string command exits with a non-zero code, the entire workflow stops:
export default defineConfig({
'error:bump': (config, dryRun) => {
// Log error details
console.error('Bump failed, check logs')
// Stop the workflow
throw new Error('Critical failure')
}
})Best Practices
- Check
dryRun- Always respect the dry-run flag to avoid side effects during testing - Handle errors gracefully - Use try-catch blocks in async hooks
- Keep hooks fast - Long-running hooks slow down the release process
- Use async/await - For asynchronous operations, always return promises
- Log appropriately - Use console.log/error for visibility during execution
- Avoid blocking operations - Don't use hooks for interactive prompts
TypeScript Types
type HookType = 'before' | 'success' | 'error'
type HookStep
= | 'bump'
| 'changelog'
| 'commit-and-tag'
| 'provider-release'
| 'publish'
| 'push'
type HookConfig = {
[K in `${HookType}:${HookStep}`]?:
| string
| ((config: ResolvedRelizyConfig, dryRun: boolean) => any)
} & {
'generate:changelog'?: (
config: ResolvedRelizyConfig,
dryRun: boolean,
params: {
commits: GitCommit[]
changelog: string
}
) => string | void | null | undefined | Promise<string | void | null | undefined>
}