feat: kompose hooks

This commit is contained in:
2025-10-11 10:24:25 +02:00
parent bb9e8ac603
commit 2efc30d8f9
5 changed files with 1095 additions and 50 deletions

View File

@@ -0,0 +1,89 @@
# Changelog
All notable changes to Kompose will be documented in this file.
## [Unreleased]
### Added
- **Docker Compose Command Hooks**: Added comprehensive hook system for all Docker Compose commands
- Pre and post hooks for: `up`, `down`, `start`, `stop`, `restart`, `build`, `pull`, `logs`, and more
- Hooks receive full command arguments as parameters
- Post-command hooks only execute on successful command execution
- Consistent with existing database hooks pattern
- **Hook Template**: Added `hooks.sh.template` as a comprehensive reference for creating custom hooks
- Includes all available hook types with examples
- Provides utility functions and best practices
- Serves as a starting point for new stacks
### Changed
- **Documentation**: Completely rewrote hooks documentation (`docs/content/3.guide/hooks.md`)
- Added comprehensive examples for all hook types
- Included real-world use cases (monitoring, backups, cache warming)
- Added troubleshooting section
- Added security considerations
- Added best practices and advanced patterns
- **Core Script**: Enhanced `execute_stack_command()` function in `kompose.sh`
- Added hook execution before and after Docker Compose commands
- Improved error handling and exit code management
- Hooks properly execute in dry-run mode
### Documentation Updates
- Updated CLI help text with complete list of Docker Compose command hooks
- Added hook execution flow diagrams
- Expanded examples section with practical use cases
- Added advanced patterns for conditional execution and hook chaining
## Hook System
### Available Hooks
**Database Hooks:**
- `hook_pre_db_export` - Before database export
- `hook_post_db_export` - After database export (receives dump file path)
- `hook_pre_db_import` - Before database import (receives dump file path)
- `hook_post_db_import` - After database import (receives dump file path)
**Docker Compose Command Hooks:**
- `hook_pre_<command>` - Before any Docker Compose command
- `hook_post_<command>` - After any Docker Compose command (only on success)
Supported commands: `up`, `down`, `start`, `stop`, `restart`, `build`, `pull`, `logs`, `ps`, `exec`, `run`, `create`, `kill`, `pause`, `unpause`, `port`, `top`
### Usage Examples
```bash
# Hooks execute automatically for supported commands
./kompose.sh news up -d # Triggers pre_up and post_up hooks
./kompose.sh blog restart # Triggers pre_restart and post_restart hooks
./kompose.sh "*" down # Triggers pre_down and post_down hooks
# Test hooks in dry-run mode
./kompose.sh news up -d --dry-run
```
### Migration Guide
For existing stacks with `hooks.sh`:
1. No changes required - existing database hooks continue to work
2. Optionally add new Docker Compose command hooks as needed
3. See `hooks.sh.template` for all available hooks
4. Refer to updated documentation for examples and best practices
---
## Previous Versions
### [1.0.0] - Initial Release
- Basic Docker Compose stack management
- Database import/export functionality
- Database hooks (pre/post export/import)
- Pattern-based stack selection
- Environment variable management
- Dry-run mode
- Stack listing and filtering

View File

@@ -0,0 +1,203 @@
# Docker Compose Command Hooks - Quick Reference
## Overview
Kompose now supports hooks for all Docker Compose commands, similar to the existing database import/export hooks. This allows you to run custom logic before and after any `docker compose` command.
## What Changed
### Core Script (`kompose.sh`)
- Enhanced `execute_stack_command()` to support pre/post hooks
- Hooks automatically execute for supported Docker Compose commands
- Post-hooks only run if the command succeeds
### Documentation
- Completely rewritten hooks guide with comprehensive examples
- Added template file `hooks.sh.template` for easy setup
- Updated help text with complete hook reference
## Available Hooks
### Format
```bash
hook_pre_<command> # Before docker compose <command>
hook_post_<command> # After docker compose <command> (only on success)
```
### Supported Commands
- **Lifecycle**: `up`, `down`, `start`, `stop`, `restart`
- **Build**: `build`, `pull`, `create`
- **Monitoring**: `logs`, `ps`, `top`, `port`
- **Execution**: `exec`, `run`
- **Control**: `kill`, `pause`, `unpause`
## Quick Examples
### Example 1: Health Check After Startup
```bash
# In your stack's hooks.sh
hook_post_up() {
echo " Verifying services are healthy..."
sleep 5
docker compose ps --format json | jq '.[] | select(.Health == "healthy")'
return 0
}
```
### Example 2: Backup Before Shutdown
```bash
hook_pre_down() {
echo " Creating backup before shutdown..."
./backup.sh
return 0
}
```
### Example 3: Clear Cache Before Restart
```bash
hook_pre_restart() {
echo " Clearing application caches..."
docker exec ${COMPOSE_PROJECT_NAME}_redis redis-cli FLUSHALL
return 0
}
```
### Example 4: Tag Images After Build
```bash
hook_post_build() {
local tag="$(date +%Y%m%d-%H%M%S)"
echo " Tagging image with: $tag"
docker tag ${COMPOSE_PROJECT_NAME}_app:latest ${COMPOSE_PROJECT_NAME}_app:$tag
return 0
}
```
## Getting Started
### 1. Copy Template
```bash
cp hooks.sh.template your-stack/hooks.sh
chmod +x your-stack/hooks.sh
```
### 2. Uncomment Hooks You Need
Edit `your-stack/hooks.sh` and uncomment the hooks you want to use.
### 3. Test in Dry-Run Mode
```bash
./kompose.sh your-stack up -d --dry-run
```
### 4. Execute
```bash
./kompose.sh your-stack up -d
```
## Accessing Command Arguments
Hooks receive the full command and arguments:
```bash
hook_pre_up() {
local -a args=("$@")
echo " Command: ${args[0]}" # "up"
echo " Arguments: ${args[*]}" # "up -d --build"
# Check for specific flags
if [[ " ${args[*]} " =~ " --build " ]]; then
echo " Build flag detected!"
fi
return 0
}
```
## Environment Variables
Hooks have access to:
- `$SCRIPT_DIR` - Root kompose directory
- `$stack` - Current stack name
- All variables from root and stack `.env` files
- CLI overrides from `-e` flags
## Best Practices
**DO:**
- Return `0` for success, `1` for failure
- Use indented output: `echo " Message"`
- Check container status before `docker exec`
- Test with `--dry-run` first
- Make non-critical operations return `0`
**DON'T:**
- Assume containers are running
- Use blocking operations without timeouts
- Hardcode paths or credentials
- Make hooks too complex
## Testing
```bash
# Preview what would happen
./kompose.sh news up -d --dry-run
# Check syntax
bash -n your-stack/hooks.sh
# Debug mode (add to top of hook function)
hook_pre_up() {
set -x # Enable debug output
# your code here
set +x # Disable debug output
return 0
}
```
## Real-World Use Cases
1. **Automated Backups** - Before stopping/rebuilding
2. **Health Monitoring** - After starting services
3. **Cache Management** - Before/after restarts
4. **Image Management** - Tag and push after builds
5. **Notifications** - Alert on lifecycle events
6. **Data Migration** - Run migrations after deployment
7. **Log Rotation** - Before starting new logs
8. **Secret Management** - Pull secrets before startup
## Migration from Old Version
If you already have `hooks.sh` with database hooks:
1. ✅ No changes required - database hooks still work
2. ✅ Just add new command hooks as needed
3. ✅ All hooks can coexist in the same file
## Resources
- **Full Documentation**: `docs/content/3.guide/hooks.md`
- **Template**: `hooks.sh.template`
- **Example**: `sexy/hooks.sh` (Directus schema management)
- **Help**: `./kompose.sh --help`
## Troubleshooting
### Hook not executing?
1. Check file permissions: `chmod +x hooks.sh`
2. Verify function names: `hook_pre_<command>` or `hook_post_<command>`
3. Test with dry-run: `./kompose.sh stack command --dry-run`
### Hook failing?
1. Check return codes: `return 0` for success
2. Add debug output: `set -x` at start of function
3. Verify container names and status
4. Check environment variables are loaded
## Support
For issues or questions:
1. Check the documentation: `docs/content/3.guide/hooks.md`
2. Review the template: `hooks.sh.template`
3. Look at examples: `sexy/hooks.sh`

View File

@@ -3,9 +3,11 @@ title: Hooks System
description: Extend Kompose with custom hooks
---
Extend Kompose functionality with custom hooks for each stack.
Extend Kompose functionality with custom hooks for each stack. Hooks allow you to run custom logic before and after Docker Compose commands and database operations.
### Available Hooks
## Hook Types
### Database Hooks
| Hook | Timing | Arguments | Use Case |
|------|--------|-----------|----------|
@@ -14,76 +16,431 @@ Extend Kompose functionality with custom hooks for each stack.
| `hook_pre_db_import` | Before DB import | `$1` = dump file path | Prepare environment, schema setup |
| `hook_post_db_import` | After DB import | `$1` = dump file path | Restart services, clear caches |
### Creating Hooks
### Docker Compose Command Hooks
Create `<stack>/hooks.sh`:
All Docker Compose commands support pre and post hooks. The hooks receive the full command arguments.
| Hook | Timing | Arguments | Use Case |
|------|--------|-----------|----------|
| `hook_pre_up` | Before `docker compose up` | `$@` = command args | Pre-flight checks, setup |
| `hook_post_up` | After `docker compose up` | `$@` = command args | Health checks, initialization |
| `hook_pre_down` | Before `docker compose down` | `$@` = command args | Graceful shutdown, backups |
| `hook_post_down` | After `docker compose down` | `$@` = command args | Cleanup, notifications |
| `hook_pre_start` | Before `docker compose start` | `$@` = command args | Validate state |
| `hook_post_start` | After `docker compose start` | `$@` = command args | Verify services |
| `hook_pre_stop` | Before `docker compose stop` | `$@` = command args | Save state |
| `hook_post_stop` | After `docker compose stop` | `$@` = command args | Cleanup |
| `hook_pre_restart` | Before `docker compose restart` | `$@` = command args | Prepare for restart |
| `hook_post_restart` | After `docker compose restart` | `$@` = command args | Verify restart |
| `hook_pre_build` | Before `docker compose build` | `$@` = command args | Code generation |
| `hook_post_build` | After `docker compose build` | `$@` = command args | Tag images, push to registry |
| `hook_pre_pull` | Before `docker compose pull` | `$@` = command args | Check registry availability |
| `hook_post_pull` | After `docker compose pull` | `$@` = command args | Verify images |
| `hook_pre_logs` | Before `docker compose logs` | `$@` = command args | Setup log filters |
| `hook_post_logs` | After `docker compose logs` | `$@` = command args | Process logs |
**Also supported:** `ps`, `exec`, `run`, `create`, `kill`, `pause`, `unpause`, `port`, `top`
::alert{type="info"}
**Note:** Post-command hooks only execute if the Docker Compose command succeeds (exit code 0).
::
## Creating Hooks
Create `<stack>/hooks.sh` in your stack directory:
```bash
#!/usr/bin/env bash
# Export schema before database export
hook_pre_db_export() {
echo " Exporting application schema..."
docker exec sexy_api npx directus schema snapshot --yes ./schema.yaml
# Hook naming convention:
# - hook_pre_<command> - runs before the command
# - hook_post_<command> - runs after the command (only on success)
# Example: Setup before starting containers
hook_pre_up() {
local -a args=("$@")
echo " Running pre-flight checks..."
# Check if required directories exist
if [[ ! -d "./uploads" ]]; then
echo " Creating uploads directory..."
mkdir -p ./uploads
fi
return 0 # 0 = success, 1 = failure
}
# Apply schema before database import
hook_pre_db_import() {
local dump_file="$1"
echo " Applying schema snapshot..."
docker exec sexy_api npx directus schema apply --yes ./schema.yaml
# Example: Verify services after startup
hook_post_up() {
local -a args=("$@")
echo " Waiting for services to be healthy..."
# Wait for health check
sleep 5
if docker compose ps | grep -q "healthy"; then
echo " ✓ Services are healthy"
return 0
else
echo " ⚠ Warning: Some services may not be healthy"
return 0 # Non-critical, don't fail
fi
}
# Example: Backup before shutdown
hook_pre_down() {
echo " Creating backup before shutdown..."
./backup.sh
return 0
}
# Restart service after import
hook_post_db_import() {
local dump_file="$1"
echo " Restarting application..."
docker restart sexy_api
# Example: Restart dependent services
hook_post_restart() {
echo " Restarting dependent services..."
# Restart a related service that depends on this stack
return 0
}
```
### Real-World Example: Directus (sexy stack)
## Real-World Examples
The `sexy` stack uses hooks for Directus schema management:
### Example 1: Directus Schema Management (sexy stack)
**Export Flow:**
1. `pre_db_export`: Export Directus schema snapshot
2. Database export creates SQL dump
3. Result: Both database dump + schema snapshot
**Import Flow:**
1. `pre_db_import`: Apply Directus schema from snapshot
2. Database import loads SQL dump
3. `post_db_import`: Restart Directus container
4. Result: Fully synchronized schema + data
### Testing Hooks
The `sexy` stack uses hooks for Directus schema synchronization:
```bash
# Preview hook execution
#!/usr/bin/env bash
# Export Directus schema before database export
hook_pre_db_export() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local snapshot_file="directus_schema_${timestamp}.yaml"
echo " Exporting Directus schema snapshot..."
if docker exec sexy_api npx directus schema snapshot "$snapshot_file" > /dev/null 2>&1; then
echo " Schema snapshot saved: $snapshot_file"
return 0
else
echo " Warning: Could not export schema snapshot"
return 0 # Don't fail the entire export
fi
}
# Import Directus schema before database import
hook_pre_db_import() {
local dump_file="$1"
# Find most recent schema snapshot
local snapshot_file=$(ls -t directus_schema_*.yaml 2>/dev/null | head -1)
if [[ -z "$snapshot_file" ]]; then
echo " No schema snapshot found, skipping"
return 0
fi
echo " Applying Directus schema from: $snapshot_file"
if docker exec sexy_api npx directus schema apply "$snapshot_file" > /dev/null 2>&1; then
echo " Schema applied successfully"
return 0
else
echo " Warning: Could not apply schema snapshot"
return 0
fi
}
# Reload Directus after import
hook_post_db_import() {
echo " Restarting Directus to apply changes..."
docker restart sexy_api > /dev/null 2>&1
return 0
}
```
### Example 2: Health Monitoring
```bash
#!/usr/bin/env bash
# Send notification when services start
hook_post_up() {
local -a args=("$@")
echo " Notifying monitoring system..."
# Example: Send webhook
curl -X POST https://monitoring.example.com/webhook \
-H "Content-Type: application/json" \
-d "{\"stack\": \"${stack}\", \"status\": \"up\"}" \
2>/dev/null
return 0
}
# Alert on shutdown
hook_post_down() {
echo " Sending shutdown notification..."
curl -X POST https://monitoring.example.com/webhook \
-H "Content-Type: application/json" \
-d "{\"stack\": \"${stack}\", \"status\": \"down\"}" \
2>/dev/null
return 0
}
```
### Example 3: Automated Backups
```bash
#!/usr/bin/env bash
# Backup volumes before rebuild
hook_pre_build() {
echo " Backing up volumes before rebuild..."
local backup_dir="./backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup_dir"
# Backup volumes
docker run --rm \
-v app_data:/data \
-v "$backup_dir:/backup" \
alpine tar czf /backup/data.tar.gz -C /data .
echo " Backup saved to: $backup_dir"
return 0
}
# Tag and push after successful build
hook_post_build() {
echo " Tagging and pushing image..."
local image="${COMPOSE_PROJECT_NAME}_app"
local tag="$(date +%Y%m%d-%H%M%S)"
docker tag "$image:latest" "$image:$tag"
docker push "$image:$tag"
return 0
}
```
### Example 4: Cache Warming
```bash
#!/usr/bin/env bash
# Warm caches after starting
hook_post_up() {
echo " Warming application caches..."
# Wait for app to be ready
sleep 10
# Hit cache endpoints
curl -s http://localhost:3000/api/cache/warm > /dev/null
return 0
}
# Clear caches before restart
hook_pre_restart() {
echo " Clearing application caches..."
docker exec app_container redis-cli FLUSHALL
return 0
}
```
## Hook Execution Flow
### Database Export Flow
```
1. hook_pre_db_export
2. pg_dump (database export)
3. hook_post_db_export (receives dump file path)
```
### Database Import Flow
```
1. hook_pre_db_import (receives dump file path)
2. Database drop & recreate
3. psql (database import)
4. hook_post_db_import (receives dump file path)
```
### Docker Compose Command Flow
```
1. hook_pre_<command> (receives command args)
2. docker compose <command>
3. hook_post_<command> (only if command succeeds, receives command args)
```
## Testing Hooks
```bash
# Preview hook execution with dry-run
./kompose.sh sexy db:export --dry-run
./kompose.sh sexy up -d --dry-run
# Execute with hooks
./kompose.sh sexy db:export
./kompose.sh sexy up -d
# Import with hooks
./kompose.sh sexy db:import
# Test specific commands with hooks
./kompose.sh news restart
./kompose.sh blog build
./kompose.sh "*" down
```
### Hook Best Practices
## Accessing Command Arguments
**DO:**
- Return 0 for success, 1 for failure
- Use indented output: `echo " Message"`
- Make non-critical operations return 0
- Check container status before `docker exec`
- Test in dry-run mode first
Hooks receive the full command and its arguments:
**DON'T:**
- Assume containers are running
- Use blocking operations without timeouts
- Forget error handling
- Hardcode paths or credentials
```bash
hook_pre_up() {
local -a args=("$@")
# Access specific arguments
echo " Command: ${args[0]}" # "up"
echo " All args: ${args[*]}" # "up -d --build"
# Check for specific flags
if [[ " ${args[*]} " =~ " --build " ]]; then
echo " Build flag detected, running pre-build tasks..."
fi
return 0
}
```
## Hook Best Practices
### ✅ DO:
- **Return proper exit codes**: `0` for success, `1` for failure
- **Use indented output**: `echo " Message"` for better readability
- **Make non-critical operations non-blocking**: Return `0` even on minor failures
- **Check container status** before using `docker exec`
- **Test in dry-run mode** first: `./kompose.sh stack command --dry-run`
- **Use environment variables** available from `.env` files
- **Add timeouts** for long-running operations
- **Document your hooks** with comments
### ❌ DON'T:
- **Assume containers are running** before executing commands
- **Use blocking operations** without timeouts
- **Forget error handling** for external commands
- **Hardcode paths, credentials, or hostnames**
- **Perform destructive operations** in `pre_*` hooks without confirmation
- **Make hooks too complex** - consider separate scripts if needed
- **Ignore exit codes** from important operations
## Available Environment Variables
Hooks have access to all environment variables from:
- Root `.env` file
- Stack-specific `.env` file
- CLI overrides (`-e KEY=VALUE`)
Common variables:
```bash
$SCRIPT_DIR # Root kompose directory
$stack # Current stack name
$COMPOSE_PROJECT_NAME
$DB_NAME
$DB_HOST
$DB_PORT
$DB_USER
$DB_PASSWORD
# ... all variables from .env files
```
## Troubleshooting
### Hook not executing
1. Verify `hooks.sh` has execute permissions: `chmod +x <stack>/hooks.sh`
2. Check function naming: `hook_pre_<command>` or `hook_post_<command>`
3. Test with dry-run mode to see if hooks are detected
4. Check for bash syntax errors: `bash -n <stack>/hooks.sh`
### Hook failing
1. Add `set -x` at the top of your hook for debugging
2. Check return codes: `return 0` for success
3. Verify container names and states
4. Check environment variables are loaded
5. Look at Kompose output for error messages
### Best debugging approach
```bash
# Enable dry-run to see what would execute
./kompose.sh stack command --dry-run
# Add debug output in your hook
hook_pre_up() {
echo " [DEBUG] Stack: $stack"
echo " [DEBUG] Args: $*"
echo " [DEBUG] Container: $(docker ps --filter name=$stack)"
# ... your hook logic
return 0
}
```
## Advanced Patterns
### Conditional Execution
```bash
hook_pre_up() {
# Only run in production
if [[ "${ENVIRONMENT}" == "production" ]]; then
echo " Running production pre-flight checks..."
./production-checks.sh
fi
return 0
}
```
### Parallel Hook Execution
```bash
hook_post_up() {
echo " Running parallel initialization tasks..."
# Background jobs
(cache_warm) &
(index_rebuild) &
# Wait for all background jobs
wait
return 0
}
```
### Hook Chaining
```bash
hook_post_restart() {
# Call another stack's command
echo " Restarting dependent services..."
"${SCRIPT_DIR}/kompose.sh" dependent-stack restart
return 0
}
```
## Security Considerations
- Never log sensitive information (passwords, tokens)
- Validate user input in hooks that accept parameters
- Use quotes around variables to prevent injection
- Restrict file permissions on `hooks.sh`: `chmod 750 hooks.sh`
- Avoid executing user-supplied data without validation
- Use absolute paths or `$SCRIPT_DIR` for file references
## Related
- [Database Operations](/guide/database) - Learn about database import/export
- [Stack Management](/guide/stack-management) - Managing multiple stacks
- [Configuration](/guide/configuration) - Environment variables and settings

View File

@@ -0,0 +1,340 @@
#!/usr/bin/env bash
# Template hooks file for Kompose stacks
# Copy this file to your stack directory and uncomment/modify the hooks you need
#
# Available hooks:
# - Database: pre_db_export, post_db_export, pre_db_import, post_db_import
# - Docker Compose: pre_<command>, post_<command> for any docker compose command
#
# Hooks receive command arguments as parameters: $1, $2, etc., or $@ for all args
# Return 0 for success, 1 for failure
# Post-command hooks only execute if the command succeeds
# ================================================================================
# DATABASE HOOKS
# ================================================================================
# Execute before database export
# Use case: Export schemas, prepare data, stop write operations
#hook_pre_db_export() {
# echo " Running pre-export tasks..."
# # Example: Export application schema
# # docker exec ${COMPOSE_PROJECT_NAME}_app npm run schema:export
# return 0
#}
# Execute after database export
# Arguments: $1 = path to the dump file
# Use case: Compress dumps, upload to S3, send notifications
#hook_post_db_export() {
# local dump_file="$1"
# echo " Running post-export tasks..."
# echo " Dump file: $dump_file"
# # Example: Compress dump
# # gzip "$dump_file"
# return 0
#}
# Execute before database import
# Arguments: $1 = path to the dump file
# Use case: Prepare environment, load schemas, clear caches
#hook_pre_db_import() {
# local dump_file="$1"
# echo " Running pre-import tasks..."
# echo " Importing from: $dump_file"
# # Example: Apply schema migrations
# # docker exec ${COMPOSE_PROJECT_NAME}_app npm run schema:apply
# return 0
#}
# Execute after database import
# Arguments: $1 = path to the dump file
# Use case: Restart services, rebuild indexes, warm caches
#hook_post_db_import() {
# local dump_file="$1"
# echo " Running post-import tasks..."
# # Example: Restart application
# # docker restart ${COMPOSE_PROJECT_NAME}_app
# return 0
#}
# ================================================================================
# DOCKER COMPOSE COMMAND HOOKS
# ================================================================================
# Execute before 'docker compose up'
# Arguments: $@ = command arguments (e.g., "up", "-d", "--build")
# Use case: Pre-flight checks, create directories, pull secrets
#hook_pre_up() {
# local -a args=("$@")
# echo " Running pre-up checks..."
#
# # Example: Create required directories
# if [[ ! -d "./uploads" ]]; then
# echo " Creating uploads directory..."
# mkdir -p ./uploads
# fi
#
# # Example: Check for required environment variables
# if [[ -z "${API_KEY}" ]]; then
# echo " ERROR: API_KEY not set"
# return 1
# fi
#
# return 0
#}
# Execute after 'docker compose up'
# Arguments: $@ = command arguments
# Use case: Health checks, initialization, warm caches
#hook_post_up() {
# local -a args=("$@")
# echo " Running post-up tasks..."
#
# # Example: Wait for services to be healthy
# echo " Waiting for services to be healthy..."
# sleep 5
#
# # Example: Initialize application
# # docker exec ${COMPOSE_PROJECT_NAME}_app npm run init
#
# return 0
#}
# Execute before 'docker compose down'
# Arguments: $@ = command arguments
# Use case: Graceful shutdown, backups, save state
#hook_pre_down() {
# local -a args=("$@")
# echo " Running pre-down tasks..."
#
# # Example: Create backup before shutdown
# # ./backup.sh
#
# # Example: Check for --volumes flag
# if [[ " ${args[*]} " =~ " --volumes " ]] || [[ " ${args[*]} " =~ " -v " ]]; then
# echo " WARNING: Volumes will be removed!"
# # You could add a confirmation prompt here
# fi
#
# return 0
#}
# Execute after 'docker compose down'
# Arguments: $@ = command arguments
# Use case: Cleanup, notifications
#hook_post_down() {
# local -a args=("$@")
# echo " Running post-down tasks..."
#
# # Example: Send notification
# # curl -X POST https://hooks.slack.com/... -d '{"text":"Stack stopped"}'
#
# return 0
#}
# Execute before 'docker compose start'
# Arguments: $@ = command arguments
#hook_pre_start() {
# local -a args=("$@")
# echo " Running pre-start tasks..."
# return 0
#}
# Execute after 'docker compose start'
# Arguments: $@ = command arguments
#hook_post_start() {
# local -a args=("$@")
# echo " Running post-start tasks..."
#
# # Example: Verify services are running
# # docker compose ps
#
# return 0
#}
# Execute before 'docker compose stop'
# Arguments: $@ = command arguments
#hook_pre_stop() {
# local -a args=("$@")
# echo " Running pre-stop tasks..."
#
# # Example: Flush pending writes
# # docker exec ${COMPOSE_PROJECT_NAME}_app npm run flush
#
# return 0
#}
# Execute after 'docker compose stop'
# Arguments: $@ = command arguments
#hook_post_stop() {
# local -a args=("$@")
# echo " Running post-stop tasks..."
# return 0
#}
# Execute before 'docker compose restart'
# Arguments: $@ = command arguments
#hook_pre_restart() {
# local -a args=("$@")
# echo " Running pre-restart tasks..."
#
# # Example: Clear caches before restart
# # docker exec ${COMPOSE_PROJECT_NAME}_redis redis-cli FLUSHALL
#
# return 0
#}
# Execute after 'docker compose restart'
# Arguments: $@ = command arguments
#hook_post_restart() {
# local -a args=("$@")
# echo " Running post-restart tasks..."
#
# # Example: Verify services are healthy
# # ./health-check.sh
#
# return 0
#}
# Execute before 'docker compose build'
# Arguments: $@ = command arguments
#hook_pre_build() {
# local -a args=("$@")
# echo " Running pre-build tasks..."
#
# # Example: Run code generation
# # npm run generate
#
# # Example: Backup current image
# # docker tag ${COMPOSE_PROJECT_NAME}_app:latest ${COMPOSE_PROJECT_NAME}_app:backup
#
# return 0
#}
# Execute after 'docker compose build'
# Arguments: $@ = command arguments
#hook_post_build() {
# local -a args=("$@")
# echo " Running post-build tasks..."
#
# # Example: Tag and push image
# # local tag="$(date +%Y%m%d-%H%M%S)"
# # docker tag ${COMPOSE_PROJECT_NAME}_app:latest registry.example.com/app:$tag
# # docker push registry.example.com/app:$tag
#
# return 0
#}
# Execute before 'docker compose pull'
# Arguments: $@ = command arguments
#hook_pre_pull() {
# local -a args=("$@")
# echo " Running pre-pull tasks..."
#
# # Example: Check registry availability
# # curl -f https://registry.example.com/v2/ > /dev/null
#
# return 0
#}
# Execute after 'docker compose pull'
# Arguments: $@ = command arguments
#hook_post_pull() {
# local -a args=("$@")
# echo " Running post-pull tasks..."
#
# # Example: Verify pulled images
# # docker images | grep ${COMPOSE_PROJECT_NAME}
#
# return 0
#}
# Execute before 'docker compose logs'
# Arguments: $@ = command arguments
#hook_pre_logs() {
# local -a args=("$@")
# # Usually not needed, but available if you need it
# return 0
#}
# Execute after 'docker compose logs'
# Arguments: $@ = command arguments
#hook_post_logs() {
# local -a args=("$@")
# # Usually not needed, but available if you need it
# return 0
#}
# ================================================================================
# OTHER DOCKER COMPOSE COMMANDS
# ================================================================================
# The following commands also support pre/post hooks:
# - ps, exec, run, create, kill, pause, unpause, port, top
#
# Example:
#hook_pre_ps() {
# echo " Listing containers..."
# return 0
#}
# ================================================================================
# UTILITY FUNCTIONS (Optional)
# ================================================================================
# Helper function to check if a container is running
#is_container_running() {
# local container_name="$1"
# docker ps --format '{{.Names}}' | grep -q "^${container_name}$"
#}
# Helper function to wait for a container to be healthy
#wait_for_healthy() {
# local container_name="$1"
# local max_attempts="${2:-30}"
# local attempt=0
#
# while [[ $attempt -lt $max_attempts ]]; do
# if docker inspect --format='{{.State.Health.Status}}' "$container_name" 2>/dev/null | grep -q "healthy"; then
# return 0
# fi
# sleep 1
# ((attempt++))
# done
#
# return 1
#}
# Helper function to send notifications
#send_notification() {
# local message="$1"
# # Implement your notification logic here (Slack, Discord, email, etc.)
# # curl -X POST https://hooks.slack.com/... -d "{\"text\":\"$message\"}"
# return 0
#}
# ================================================================================
# NOTES
# ================================================================================
#
# Environment Variables Available:
# - $SCRIPT_DIR: Root kompose directory
# - $stack: Current stack name
# - All variables from root .env and stack .env files
# - CLI overrides from -e flags
#
# Best Practices:
# - Return 0 for success, 1 for failure
# - Use indented output: echo " Message"
# - Make non-critical operations return 0
# - Check container status before docker exec
# - Test with --dry-run first
# - Add timeouts for long operations
# - Document your hooks with comments
#
# Testing:
# - Dry run: ./kompose.sh stack command --dry-run
# - Check syntax: bash -n hooks.sh
# - Debug: Add 'set -x' at the top of your hook function
#

View File

@@ -452,6 +452,22 @@ execute_stack_command() {
return 1
fi
# Extract the main command (e.g., "up" from "up -d")
local main_command="${cmd[0]}"
local hook_name=""
# Map docker compose commands to hook names
case "${main_command}" in
up|down|start|stop|restart|pull|build|ps|logs|exec|run|create|kill|pause|unpause|port|top)
hook_name="${main_command}"
;;
esac
# Execute pre-command hook if hook name is determined
if [[ -n "${hook_name}" ]]; then
execute_hook "${stack}" "pre_${hook_name}" "${cmd[@]}" || return 1
fi
log_stack "${stack}" "Executing: docker compose ${cmd[*]}"
# Build environment args
@@ -464,10 +480,17 @@ execute_stack_command() {
fi
log_dry_run "cd ${stack_dir} && docker compose${env_str} -f ${compose_file} ${cmd[*]}"
log_stack "${stack}" "${CYAN}[DRY-RUN]${RESET} Command would be executed"
# Execute post-command hook in dry-run mode
if [[ -n "${hook_name}" ]]; then
execute_hook "${stack}" "post_${hook_name}" "${cmd[@]}" || return 1
fi
return 0
fi
# Change to stack directory and execute command
local exit_code=0
(
cd "${stack_dir}"
if [[ ${#env_args[@]} -gt 0 ]]; then
@@ -475,17 +498,25 @@ execute_stack_command() {
log_stack "${stack}" "${GREEN}${RESET} Command completed successfully"
else
log_stack "${stack}" "${RED}${RESET} Command failed"
return 1
exit 1
fi
else
if docker compose -f "${compose_file}" "${cmd[@]}"; then
log_stack "${stack}" "${GREEN}${RESET} Command completed successfully"
else
log_stack "${stack}" "${RED}${RESET} Command failed"
return 1
exit 1
fi
fi
)
exit_code=$?
# Execute post-command hook if command succeeded and hook name is determined
if [[ ${exit_code} -eq 0 ]] && [[ -n "${hook_name}" ]]; then
execute_hook "${stack}" "post_${hook_name}" "${cmd[@]}" || return 1
fi
return ${exit_code}
}
# Display help message
@@ -542,12 +573,37 @@ DATABASE OPERATIONS:
Exports are saved as: <stack-dir>/<db-name>_<timestamp>.sql
HOOKS:
Stacks can define custom hooks in hooks.sh:
Stacks can define custom hooks in hooks.sh for lifecycle management.
Database Hooks:
- hook_pre_db_export - Before database export
- hook_post_db_export - After database export (receives dump file path)
- hook_pre_db_import - Before database import (receives dump file path)
- hook_post_db_import - After database import (receives dump file path)
Docker Compose Command Hooks:
- hook_pre_up - Before 'docker compose up' (receives command args)
- hook_post_up - After 'docker compose up' (receives command args)
- hook_pre_down - Before 'docker compose down'
- hook_post_down - After 'docker compose down'
- hook_pre_start - Before 'docker compose start'
- hook_post_start - After 'docker compose start'
- hook_pre_stop - Before 'docker compose stop'
- hook_post_stop - After 'docker compose stop'
- hook_pre_restart - Before 'docker compose restart'
- hook_post_restart - After 'docker compose restart'
- hook_pre_logs - Before 'docker compose logs'
- hook_post_logs - After 'docker compose logs'
- hook_pre_build - Before 'docker compose build'
- hook_post_build - After 'docker compose build'
- hook_pre_pull - Before 'docker compose pull'
- hook_post_pull - After 'docker compose pull'
And also: ps, exec, run, create, kill, pause, unpause, port, top
Hook functions receive the full command arguments as parameters.
Post-command hooks only execute if the command succeeds.
Example: sexy stack uses hooks for Directus schema snapshots
EXAMPLES: