Debugging Strategies - lukeocodes/wikiinator GitHub Wiki
The Debugging Challenge
GitHub Actions run in ephemeral environments without direct access for debugging. We learned effective strategies for troubleshooting complex issues.
Progressive Debugging Approach
1. Start with Basic Logging
Level 1: Basic execution logging
echo "Starting action..."
echo "Processing files..."
echo "Action complete"
Level 2: State logging
echo "Current directory: $(pwd)"
echo "Files found: $(ls -la)"
echo "Environment variables: $(env | grep INPUT_)"
Level 3: Comprehensive debugging
log "Environment information:"
log " - Working directory: $(pwd)"
log " - Repository: $GITHUB_REPOSITORY"
log " - SHA: $GITHUB_SHA"
log " - Event: $GITHUB_EVENT_NAME"
log " - GitHub Output: $GITHUB_OUTPUT"
2. Strategic Information Gathering
Always log:
- Input parameters and their values
- File system state at key points
- Command outputs (but sanitize sensitive data)
- Environment variables
- Exit codes and error messages
Example implementation:
log "Configuration:"
log " - Docs path: $DOCS_PATH"
log " - Exclude files: $EXCLUDE_FILES"
log " - Wiki home file: $WIKI_HOME_FILE"
log " - Dry run: $DRY_RUN"
log " - Commit message: $COMMIT_MESSAGE"
Command Output Capture Strategies
1. Capture Both stdout and stderr
Basic capture:
output=$(command 2>&1)
exit_code=$?
Advanced capture with separation:
# Capture stderr separately
exec 3>&1
stderr=$(command 2>&1 1>&3)
exec 3>&-
2. Git Command Debugging
Git operations need special handling:
# Clone with error capture
if ! git clone "$wiki_url" wiki 2>/dev/null; then
log_error "Failed to clone wiki repository."
# Provide specific troubleshooting
fi
# Push with detailed error analysis
push_output=$(git push 2>&1)
push_exit_code=$?
if [ $push_exit_code -ne 0 ]; then
log_error "Git push failed with exit code: $push_exit_code"
log_error "Git push output: $push_output"
# Pattern match for specific errors
if echo "$push_output" | grep -q "403\|Permission.*denied"; then
handle_permission_error
fi
fi
3. File System State Debugging
Directory listings at key points:
log "Debug: Current directory structure"
ls -la . 2>/dev/null || log_warning "Could not list current directory"
log "Files in docs directory:"
ls -la "$DOCS_PATH/" 2>/dev/null || log_error "Cannot list docs directory"
log "Wiki directory contents:"
ls -la wiki/ 2>/dev/null || log_error "Cannot list wiki directory"
Error Context Preservation
1. Save State Before Errors
Capture environment before failure:
debug_info() {
log "=== DEBUG INFORMATION ==="
log "Working directory: $(pwd)"
log "Directory contents:"
ls -la . 2>/dev/null || log "Cannot list directory"
log "Git status (if applicable):"
git status 2>/dev/null || log "Not a git repository or git not available"
log "Environment variables:"
env | grep -E "(INPUT_|GITHUB_)" | sort
log "========================="
}
# Call before any operation that might fail
debug_info
2. Conditional Debug Output
Enable verbose debugging via input:
# action.yml
inputs:
debug:
description: "Enable debug output"
required: false
default: "false"
# In script
if [ "$INPUT_DEBUG" = "true" ]; then
set -x # Enable bash debug mode
debug_info
fi
Token and Security Debugging
1. Safe Token Information
Show token info without exposing it:
if [ -n "$INPUT_GITHUB_TOKEN" ]; then
log "Token info: ${INPUT_GITHUB_TOKEN:0:8}... (${#INPUT_GITHUB_TOKEN} chars)"
else
log_error "No GitHub token provided"
fi
2. Git Remote Debugging
Check remote configuration:
log "Git remote configuration:"
git remote -v 2>/dev/null || log_warning "Could not get git remote info"
log "Remote URL (sanitized):"
git remote get-url origin 2>/dev/null | sed 's/:[^@]*@/:***@/' || log_warning "No remote URL"
Performance and Timing Analysis
1. Operation Timing
Measure operation duration:
start_time=$(date +%s)
git clone "$wiki_url" wiki
end_time=$(date +%s)
duration=$((end_time - start_time))
log "Clone operation took $duration seconds"
2. File Processing Metrics
Track processing statistics:
log "Found $md_files markdown files"
log "Processing $files_synced files"
log "Copied $files_synced files to wiki"
GitHub Actions Specific Debugging
1. Action Outputs Debugging
Verify outputs are set correctly:
log "Setting GitHub Action outputs"
log " - files-synced: $files_synced"
log " - changes-made: $changes_made"
if [ -n "$GITHUB_OUTPUT" ]; then
echo "files-synced=$files_synced" >> "$GITHUB_OUTPUT"
echo "changes-made=$changes_made" >> "$GITHUB_OUTPUT"
log_success "Successfully wrote outputs to $GITHUB_OUTPUT"
else
log_warning "GITHUB_OUTPUT not set, skipping output generation"
fi
2. Step Dependencies
Verify action path and environment:
log "Action path: ${{ github.action_path }}"
log "Workspace: ${{ github.workspace }}"
log "Runner temp: ${{ runner.temp }}"
Common Debugging Patterns
1. The "Checkpoint" Pattern
Add checkpoints throughout the script:
checkpoint() {
local name="$1"
log "CHECKPOINT: $name"
log " - PWD: $(pwd)"
log " - Files synced so far: $files_synced"
log " - Changes made: $changes_made"
}
checkpoint "After clone"
checkpoint "After file processing"
checkpoint "Before commit"
2. The "State Dump" Pattern
Dump all relevant state on error:
error_state_dump() {
log_error "=== ERROR STATE DUMP ==="
log_error "Working directory: $(pwd)"
log_error "Files synced: $files_synced"
log_error "Changes made: $changes_made"
log_error "Last command exit code: $?"
log_error "Directory contents:"
ls -la . 2>/dev/null || log_error "Cannot list directory"
log_error "========================"
}
# Use in error handlers
trap error_state_dump ERR
3. The "Incremental Validation" Pattern
Validate state after each major operation:
validate_state() {
local operation="$1"
log "Validating state after: $operation"
case "$operation" in
"clone")
[ -d "wiki/.git" ] || { log_error "Wiki not cloned properly"; exit 1; }
;;
"copy")
[ "$files_synced" -gt 0 ] || { log_warning "No files were copied"; }
;;
"commit")
cd wiki && git log -1 --oneline && cd .. || { log_error "Commit failed"; exit 1; }
;;
esac
log "State validation passed for: $operation"
}
Testing and Development Debugging
1. Local Testing Setup
Create local test environment:
#!/bin/bash
# test-local.sh
export INPUT_GITHUB_TOKEN="your-token"
export INPUT_DOCS_PATH="docs"
export GITHUB_REPOSITORY="user/repo"
export GITHUB_SHA="test-sha"
export GITHUB_EVENT_NAME="push"
export GITHUB_OUTPUT="/tmp/github_output"
./sync-docs.sh
2. Dry Run Validation
Use dry run for safe testing:
if [ "$DRY_RUN" = "true" ]; then
log "DRY RUN: Would execute: git push"
log "DRY RUN: Would commit message: $full_commit_message"
log "DRY RUN: Files that would be synced: $files_synced"
else
# Actual operations
fi
Documentation of Debugging Sessions
1. Issue Templates
Create issue templates for common problems:
## Debugging Information
- Action version:
- Repository:
- Workflow file:
- Error message:
- Relevant logs:
## Steps to Reproduce
1.
2.
3.
## Expected vs Actual Behavior
Expected:
Actual:
2. Troubleshooting Runbook
Maintain a runbook of common issues:
- Permission errors and solutions
- Wiki setup problems
- Token configuration issues
- File processing edge cases
These debugging strategies evolved through multiple iterations and real-world usage, dramatically improving our ability to diagnose and resolve issues quickly.