Skip to content

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?

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

  1. Event Occurs - A Gira command triggers an event (e.g., creating a ticket)
  2. Hook Discovery - Gira looks for corresponding hook scripts in .gira/hooks/
  3. Environment Setup - Event data is passed to the hook via environment variables
  4. Script Execution - Your hook script runs with access to all event data
  5. 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

  1. Executable permission: chmod +x .gira/hooks/my-hook.sh
  2. Proper shebang: #!/bin/bash, #!/usr/bin/env python3, etc.
  3. Exit codes: Use appropriate exit codes (0 = success, non-zero = failure)
  4. 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:

{
  "hooks": {
    "enabled": true,
    "timeout": 30,
    "on_failure": "warn",
    "silent": false
  }
}

๐Ÿงช 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:
    # Run in background, don't block Gira
    (long_running_task &)
    
  • 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

  1. Test in isolation: Use gira ext test to debug without side effects
  2. Add logging: Log to .gira/logs/ for persistent debugging
  3. Use set -x: Add to bash scripts for detailed execution traces
  4. Check exit codes: Ensure hooks exit with 0 for success
  5. Validate environment: Check that all required variables are present

Getting Help

  • CLI Help: gira ext --help or gira 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!