Hook System User Guide¶
Learn how to use Gira's hook system to automate workflows, integrate external tools, and extend functionality.
๐ Table of Contents¶
- What are Hooks?
- Getting Started
- Hook Types & Events
- Environment Variables
- Writing Hooks
- Configuration
- Testing & Debugging
- Best Practices
- Troubleshooting
๐ฏ What are Hooks?¶
Hooks are custom scripts that automatically run in response to Gira events. They provide a powerful way to:
- Automate repetitive tasks - Auto-assign tickets, create git branches, update external systems
- Integrate external tools - Send notifications to Slack, sync with Jira, trigger CI/CD pipelines
- Enforce policies - Validate ticket formats, enforce naming conventions, require approvals
- Generate insights - Create reports, collect metrics, analyze team performance
How Hooks Work¶
- Event Occurs - A Gira command triggers an event (e.g., creating a ticket)
- Hook Discovery - Gira looks for corresponding hook scripts in
.gira/hooks/
- Environment Setup - Event data is passed to the hook via environment variables
- Script Execution - Your hook script runs with access to all event data
- Safe Completion - Hooks run with timeout protection and error isolation
๐ Getting Started¶
Step 1: Initialize Hooks¶
# Create the hooks directory with example templates
gira ext init
# Or initialize without overwriting existing hooks
gira ext init --force
This creates .gira/hooks/
directory with example hook templates you can customize.
Step 2: List Available Hooks¶
# View all hooks and their status
gira ext list
# Get detailed information about hooks
gira ext list --verbose
Step 3: Customize a Hook¶
Edit an existing hook template or create a new one:
# View a hook's contents
gira ext show ticket-created
# Edit with your preferred editor
nano .gira/hooks/ticket-created.sh
Step 4: Test Your Hook¶
# Test with sample data
gira ext test ticket-created
# Test with custom data
gira ext test ticket-created --data '{"ticket_id": "TEST-123", "ticket_title": "My Test"}'
Step 5: Enable Hooks¶
# Enable the hook system (enabled by default)
gira ext enable
# Verify hooks are enabled
gira config get hooks.enabled
๐จ Hook Types & Events¶
Available Hook Events¶
Hook Script | Event | When It Runs |
---|---|---|
ticket-created.sh |
Ticket Creation | After gira ticket create |
ticket-updated.sh |
Ticket Update | After gira ticket update |
ticket-moved.sh |
Status Change | After gira ticket move |
sprint-completed.py |
Sprint Completion | After gira sprint complete |
Hook Naming¶
Hooks are discovered by filename:
- ticket-created
(any extension or no extension)
- ticket-created.sh
- ticket-created.py
- ticket-created.rb
- ticket-created.js
๐ Environment Variables¶
All hooks receive comprehensive event data through environment variables.
Common Variables (All Events)¶
GIRA_ROOT # Path to gira project root
GIRA_HOOKS_DIR # Path to hooks directory
GIRA_EVENT_TYPE # Event type (ticket_created, ticket_updated, etc.)
GIRA_TIMESTAMP # Event timestamp (ISO format)
Ticket Events¶
# Basic ticket information
GIRA_TICKET_ID # Ticket ID (e.g., GCM-123)
GIRA_TICKET_TITLE # Ticket title
GIRA_TICKET_DESCRIPTION # Ticket description (multiline)
GIRA_TICKET_STATUS # Current status (todo, in_progress, review, done)
GIRA_TICKET_TYPE # Type (feature, bug, task, epic)
GIRA_TICKET_PRIORITY # Priority (low, medium, high, critical)
# Assignment and ownership
GIRA_TICKET_ASSIGNEE # Assignee email
GIRA_TICKET_REPORTER # Reporter email
# Organization
GIRA_TICKET_EPIC_ID # Epic ID if assigned
GIRA_TICKET_LABELS # Comma-separated labels
GIRA_TICKET_STORY_POINTS # Story points estimate
# Timestamps
GIRA_TICKET_CREATED_AT # Creation timestamp (ISO format)
GIRA_TICKET_UPDATED_AT # Last update timestamp (ISO format)
# Custom fields (if any)
GIRA_TICKET_CUSTOM_* # Custom field values
Move/Status Change Events¶
# Status change information
GIRA_OLD_STATUS # Previous status
GIRA_NEW_STATUS # New status
GIRA_STATUS_CHANGED_AT # When the status changed
Sprint Events¶
# Sprint information
GIRA_SPRINT_ID # Sprint ID
GIRA_SPRINT_NAME # Sprint name
GIRA_SPRINT_STATUS # Sprint status
GIRA_SPRINT_START_DATE # Start date (ISO format)
GIRA_SPRINT_END_DATE # End date (ISO format)
GIRA_SPRINT_TICKETS # Comma-separated list of ticket IDs
โ๏ธ Writing Hooks¶
Multi-Language Support¶
Hooks can be written in any language with a proper shebang:
Bash Hook¶
#!/bin/bash
# .gira/hooks/ticket-created.sh
echo "New ticket created: $GIRA_TICKET_ID - $GIRA_TICKET_TITLE"
# Send Slack notification
if [ "$GIRA_TICKET_PRIORITY" = "critical" ]; then
curl -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"text\": \"๐จ Critical ticket: $GIRA_TICKET_ID\"}"
fi
Python Hook¶
#!/usr/bin/env python3
# .gira/hooks/ticket-created.py
import os
import requests
from datetime import datetime
ticket_id = os.environ['GIRA_TICKET_ID']
ticket_title = os.environ['GIRA_TICKET_TITLE']
priority = os.environ['GIRA_TICKET_PRIORITY']
print(f"Processing ticket: {ticket_id}")
# Auto-assign high priority bugs to team lead
if priority == 'high' and os.environ['GIRA_TICKET_TYPE'] == 'bug':
# Update ticket assignee
print(f"Auto-assigning high priority bug to team lead")
# You could call gira commands or update files directly
Ruby Hook¶
#!/usr/bin/env ruby
# .gira/hooks/ticket-created.rb
ticket_id = ENV['GIRA_TICKET_ID']
ticket_title = ENV['GIRA_TICKET_TITLE']
labels = ENV['GIRA_TICKET_LABELS']&.split(',') || []
puts "Processing ticket: #{ticket_id}"
# Check for documentation requirement
if labels.include?('needs-docs')
puts "โ ๏ธ This ticket requires documentation updates"
# Could create documentation ticket automatically
end
Hook Script Requirements¶
- Executable permission:
chmod +x .gira/hooks/my-hook.sh
- Proper shebang:
#!/bin/bash
,#!/usr/bin/env python3
, etc. - Exit codes: Use appropriate exit codes (0 = success, non-zero = failure)
- Timeout aware: Keep hooks efficient (default 30-second timeout)
Best Practices¶
Error Handling¶
#!/bin/bash
set -e # Exit on any error
# Check required environment variables
if [ -z "$GIRA_TICKET_ID" ]; then
echo "Error: GIRA_TICKET_ID not provided" >&2
exit 1
fi
# Use timeout for external calls
timeout 10 curl -f "$WEBHOOK_URL" || {
echo "Warning: Webhook call failed" >&2
exit 0 # Don't fail the entire operation
}
Logging¶
#!/usr/bin/env python3
import os
import logging
from pathlib import Path
# Setup logging
log_dir = Path(os.environ['GIRA_ROOT']) / '.gira' / 'logs'
log_dir.mkdir(exist_ok=True)
logging.basicConfig(
filename=log_dir / 'hooks.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('ticket-created')
logger.info(f"Processing ticket {os.environ['GIRA_TICKET_ID']}")
Security¶
#!/bin/bash
# Validate inputs to prevent injection
ticket_id="$GIRA_TICKET_ID"
# Only allow expected ticket ID format
if [[ ! "$ticket_id" =~ ^[A-Z]+-[0-9]+$ ]]; then
echo "Error: Invalid ticket ID format" >&2
exit 1
fi
# Use environment variables for sensitive data
API_KEY="${EXTERNAL_API_KEY:-}"
if [ -z "$API_KEY" ]; then
echo "Warning: API key not configured" >&2
exit 0
fi
โ๏ธ Configuration¶
Configure hook behavior through Gira's configuration system.
Hook Configuration Options¶
# Enable/disable hook system
gira config set hooks.enabled true
# Set hook timeout (1-300 seconds)
gira config set hooks.timeout 30
# Configure failure behavior
gira config set hooks.on_failure warn # ignore, warn, fail
# Silent mode (suppress hook output)
gira config set hooks.silent false
View Current Configuration¶
# View all hook configuration
gira config list | grep hooks
# View specific setting
gira config get hooks.enabled
Configuration File¶
Hook settings are stored in .gira/config.json
:
๐งช Testing & Debugging¶
Testing Hooks¶
# Test with default sample data
gira ext test ticket-created
# Test with custom JSON data
gira ext test ticket-created --data '{
"ticket_id": "TEST-123",
"ticket_title": "Test Ticket",
"ticket_priority": "high"
}'
# Test all hooks
for hook in .gira/hooks/*; do
if [ -x "$hook" ]; then
echo "Testing $(basename "$hook")..."
gira ext test "$(basename "$hook" | cut -d. -f1)"
fi
done
Debugging Hook Issues¶
Check Hook Status¶
# List hooks and their executable status
gira ext list --verbose
# Check if hooks are enabled
gira config get hooks.enabled
Manual Hook Execution¶
# Run hook manually with environment variables
cd /path/to/gira/project
GIRA_TICKET_ID=TEST-123 \
GIRA_TICKET_TITLE="Test Ticket" \
GIRA_ROOT=$(pwd) \
./.gira/hooks/ticket-created.sh
Common Issues¶
Hook not executing:
- Check if hook is executable: ls -la .gira/hooks/
- Make executable: chmod +x .gira/hooks/my-hook.sh
- Verify hooks are enabled: gira config get hooks.enabled
Hook timing out:
- Increase timeout: gira config set hooks.timeout 60
- Optimize hook performance
- Use background processes for long operations
Hook failing:
- Check exit codes in your script
- Configure failure behavior: gira config set hooks.on_failure ignore
- Add error handling and logging
Logging and Monitoring¶
Create a logging hook to monitor all events:
#!/bin/bash
# .gira/hooks/logger.sh
LOG_FILE="$GIRA_ROOT/.gira/logs/events.log"
mkdir -p "$(dirname "$LOG_FILE")"
echo "$(date -Iseconds) - $GIRA_EVENT_TYPE - $GIRA_TICKET_ID - $GIRA_TICKET_TITLE" >> "$LOG_FILE"
๐ ๏ธ Best Practices¶
Performance¶
- Keep hooks fast: Aim for sub-second execution
- Use background processes for long operations:
- Cache external API calls when possible
- Batch operations instead of individual calls
Reliability¶
- Handle failures gracefully: Don't fail the main operation
- Use timeouts for external calls
- Log important events for debugging
- Test hooks thoroughly before deployment
Security¶
- Validate all inputs to prevent injection attacks
- Use environment variables for secrets, not hardcoded values
- Limit file system access to necessary directories
- Review hook permissions regularly
Maintainability¶
- Document your hooks with comments
- Use version control for hook scripts
- Follow consistent naming conventions
- Keep hooks simple - one responsibility per hook
๐ง Troubleshooting¶
Hook System Not Working¶
Problem: Hooks aren't running at all
- Check: gira config get hooks.enabled
- should be true
- Fix: gira ext enable
Problem: Hook directory doesn't exist
- Check: ls .gira/hooks/
- Fix: gira ext init
Individual Hook Issues¶
Problem: Hook not found
- Check: Hook filename matches event name exactly
- Fix: Rename hook file (e.g., ticket_created.sh
โ ticket-created.sh
)
Problem: Permission denied
- Check: ls -la .gira/hooks/
- should show executable permission
- Fix: chmod +x .gira/hooks/my-hook.sh
Problem: Hook times out
- Check: Current timeout with gira config get hooks.timeout
- Fix: Increase timeout gira config set hooks.timeout 60
- Better: Optimize hook performance
Problem: Environment variables missing
- Check: Print all env vars at start of hook: env | grep GIRA
- Fix: Ensure you're testing with proper event context
Debugging Tips¶
- Test in isolation: Use
gira ext test
to debug without side effects - Add logging: Log to
.gira/logs/
for persistent debugging - Use set -x: Add to bash scripts for detailed execution traces
- Check exit codes: Ensure hooks exit with 0 for success
- Validate environment: Check that all required variables are present
Getting Help¶
- CLI Help:
gira ext --help
orgira ext <command> --help
- Configuration:
gira config list | grep hooks
- Community: Share hook scripts and get help from other users
- Issues: Report bugs on GitHub with hook logs and configuration
Ready to build your first hook? Check out the Hook Examples for practical, copy-paste solutions!