diff --git a/Projects/kompose/CHANGELOG.md b/Projects/kompose/CHANGELOG.md new file mode 100644 index 00000000..e6d057c6 --- /dev/null +++ b/Projects/kompose/CHANGELOG.md @@ -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_` - Before any Docker Compose command +- `hook_post_` - 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 diff --git a/Projects/kompose/HOOKS_QUICKREF.md b/Projects/kompose/HOOKS_QUICKREF.md new file mode 100644 index 00000000..d292f322 --- /dev/null +++ b/Projects/kompose/HOOKS_QUICKREF.md @@ -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_ # Before docker compose +hook_post_ # After docker compose (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_` or `hook_post_` +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` diff --git a/Projects/kompose/docs/content/3.guide/hooks.md b/Projects/kompose/docs/content/3.guide/hooks.md index dfdf3726..cf2f4195 100755 --- a/Projects/kompose/docs/content/3.guide/hooks.md +++ b/Projects/kompose/docs/content/3.guide/hooks.md @@ -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 `/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 `/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_ - runs before the command +# - hook_post_ - 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_ (receives command args) +2. docker compose +3. hook_post_ (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 /hooks.sh` +2. Check function naming: `hook_pre_` or `hook_post_` +3. Test with dry-run mode to see if hooks are detected +4. Check for bash syntax errors: `bash -n /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 diff --git a/Projects/kompose/hooks.sh.template b/Projects/kompose/hooks.sh.template new file mode 100644 index 00000000..23d16383 --- /dev/null +++ b/Projects/kompose/hooks.sh.template @@ -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_, post_ 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 +# diff --git a/Projects/kompose/kompose.sh b/Projects/kompose/kompose.sh index a11079c5..3dcd2ad9 100755 --- a/Projects/kompose/kompose.sh +++ b/Projects/kompose/kompose.sh @@ -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: /_.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: