Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 21 additions & 128 deletions .github/workflows/claude-orchestrator.yml
Original file line number Diff line number Diff line change
@@ -1,44 +1,35 @@
---
# Claude Orchestrator Workflow (Reusable)
# Claude Orchestrator Workflow (Reusable) - FIXED
#
# PURPOSE: This workflow orchestrates all Claude AI interactions by handling
# different trigger types and routing them to the appropriate execution mode.
# This is the reusable version that will be hosted in dotCMS/claude-workflows
# PURPOSE: This workflow orchestrates Claude AI interactions by providing
# a simple, reliable interface to the claude-executor. Consumer repositories
# handle their own webhook triggers and call this workflow.
#
# WHY: Consolidates all Claude triggers into a single workflow to prevent
# duplicate runs while maintaining clear separation between interactive
# and automatic modes. Designed to be reusable across different repositories.
# WHY: The original orchestrator design was flawed because workflow_call
# loses the original event context. This version is a lightweight wrapper
# that works reliably as a reusable workflow.
#
# ROLE:
# - Handles all trigger events (issues, comments, PR reviews, PR changes)
# - Routes to appropriate execution mode based on trigger type
# - Manages conditional logic to prevent duplicate workflow runs
# - Applies configurable path exclusions
# - Orchestrates between interactive (@claude mentions) and automatic modes
# ARCHITECTURE: Consumer repositories handle webhook events and conditional
# logic, then call this workflow with the appropriate trigger mode and configuration.
#
# USAGE: This is a reusable orchestrator that can be called from any repository
# USAGE: This is a reusable workflow that can be called from any repository
# with repository-specific configurations.

name: Claude Orchestrator (Reusable)

on:
workflow_call:
inputs:
automatic_review_prompt:
description: 'Custom prompt for automatic PR reviews'
trigger_mode:
description: 'Trigger mode: interactive or automatic'
required: true
type: string
direct_prompt:
description: 'Direct prompt for automatic mode'
required: false
type: string
default: |
Please review this pull request and provide feedback on:
- Code quality and best practices
- Potential bugs or issues
- Performance considerations
- Security concerns
- Test coverage

Be constructive and helpful in your feedback.
allowed_tools:
description: 'Custom allowed tools configuration'
description: 'Allowed tools configuration'
required: false
type: string
default: |
Expand All @@ -59,111 +50,13 @@ on:
required: true

jobs:
# Interactive Claude mentions in comments
claude-comment-mention:
if: |
(github.event_name == 'issue_comment' && (
contains(github.event.comment.body, '@claude') ||
contains(github.event.comment.body, '@Claude') ||
contains(github.event.comment.body, '@CLAUDE')
)) ||
(github.event_name == 'pull_request_review_comment' && (
contains(github.event.comment.body, '@claude') ||
contains(github.event.comment.body, '@Claude') ||
contains(github.event.comment.body, '@CLAUDE')
))
uses: dotCMS/claude-workflows/.github/workflows/claude-executor.yml@main
with:
trigger_mode: interactive
allowed_tools: ${{ inputs.allowed_tools }}
timeout_minutes: ${{ inputs.timeout_minutes }}
runner: ${{ inputs.runner }}
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

# Interactive Claude mentions in PR reviews
claude-review-mention:
if: |
github.event_name == 'pull_request_review' && (
contains(github.event.review.body, '@claude') ||
contains(github.event.review.body, '@Claude') ||
contains(github.event.review.body, '@CLAUDE')
)
uses: dotCMS/claude-workflows/.github/workflows/claude-executor.yml@main
with:
trigger_mode: interactive
allowed_tools: ${{ inputs.allowed_tools }}
timeout_minutes: ${{ inputs.timeout_minutes }}
runner: ${{ inputs.runner }}
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

# Interactive Claude mentions in issues
claude-issue-mention:
if: |
github.event_name == 'issues' &&
(
(
contains(github.event.issue.body, '@claude') ||
contains(github.event.issue.body, '@Claude') ||
contains(github.event.issue.body, '@CLAUDE')
) ||
(
contains(github.event.issue.title, '@claude') ||
contains(github.event.issue.title, '@Claude') ||
contains(github.event.issue.title, '@CLAUDE')
)
)
uses: dotCMS/claude-workflows/.github/workflows/claude-executor.yml@main
with:
trigger_mode: interactive
allowed_tools: ${{ inputs.allowed_tools }}
timeout_minutes: ${{ inputs.timeout_minutes }}
runner: ${{ inputs.runner }}
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

# Interactive Claude mentions in PR titles/bodies
claude-pr-mention:
if: |
github.event_name == 'pull_request' &&
(
(
contains(github.event.pull_request.title, '@claude') ||
contains(github.event.pull_request.title, '@Claude') ||
contains(github.event.pull_request.title, '@CLAUDE')
) ||
(
contains(github.event.pull_request.body, '@claude') ||
contains(github.event.pull_request.body, '@Claude') ||
contains(github.event.pull_request.body, '@CLAUDE')
)
)
uses: dotCMS/claude-workflows/.github/workflows/claude-executor.yml@main
with:
trigger_mode: interactive
allowed_tools: ${{ inputs.allowed_tools }}
timeout_minutes: ${{ inputs.timeout_minutes }}
runner: ${{ inputs.runner }}
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

# Automatic PR reviews (no @claude mention)
claude-automatic-review:
if: |
github.event_name == 'pull_request' &&
!contains(github.event.pull_request.title, '@claude') &&
!contains(github.event.pull_request.title, '@Claude') &&
!contains(github.event.pull_request.title, '@CLAUDE') &&
!contains(github.event.pull_request.body, '@claude') &&
!contains(github.event.pull_request.body, '@Claude') &&
!contains(github.event.pull_request.body, '@CLAUDE')
claude-execution:
uses: dotCMS/claude-workflows/.github/workflows/claude-executor.yml@main
with:
trigger_mode: automatic
direct_prompt: ${{ inputs.automatic_review_prompt }}
trigger_mode: ${{ inputs.trigger_mode }}
direct_prompt: ${{ inputs.direct_prompt }}
allowed_tools: ${{ inputs.allowed_tools }}
timeout_minutes: ${{ inputs.timeout_minutes }}
runner: ${{ inputs.runner }}
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
163 changes: 163 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Claude Workflows Architecture

## Overview

This repository provides centralized, reusable GitHub Actions workflows for Claude AI integration. The architecture follows a simple, reliable pattern where consumer repositories handle their own triggers and call centralized execution workflows.

## Architecture Diagram

```
┌─────────────────────────────────────────┐
│ Consumer Repository │
│ ┌───────────────────────────────────┐ │
│ │ Consumer Workflow File │ │
│ │ (.github/workflows/claude.yml) │ │
│ │ │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ Webhook Triggers │ │ │
│ │ │ • issue_comment │ │ │
│ │ │ • pull_request │ │ │
│ │ │ • pull_request_review │ │ │
│ │ │ • issues │ │ │
│ │ └─────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ Conditional Logic │ │ │
│ │ │ • Check for @claude │ │ │
│ │ │ • Route to appropriate │ │ │
│ │ │ trigger mode │ │ │
│ │ └─────────────────────────────┘ │ │
│ └───────────────┬───────────────────┘ │
└──────────────────┼──────────────────────┘
▼ (workflow_call)
┌─────────────────────────────────────────┐
│ Claude Workflows Repo │
│ │
│ ┌───────────────────────────────────┐ │
│ │ claude-orchestrator.yml │ │
│ │ (Lightweight Wrapper) │ │
│ │ │ │
│ │ • Receives trigger_mode │ │
│ │ • Receives configuration │ │
│ │ • Passes through to executor │ │
│ └───────────────┬───────────────────┘ │
│ │ │
│ ▼ (workflow_call) │
│ ┌───────────────────────────────────┐ │
│ │ claude-executor.yml │ │
│ │ (Execution Engine) │ │
│ │ │ │
│ │ • Runs Claude AI with tools │ │
│ │ • Handles interactive/automatic │ │
│ │ • Posts results as comments │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
```

## Workflow Types

### Consumer Workflows

Located in consuming repositories (e.g., `infrastructure-as-code`, `dotcat`):

- Handle webhook events directly
- Implement trigger conditions and routing logic
- Call centralized workflows with appropriate parameters
- Provide repository-specific configuration (tools, prompts, timeouts)

### Centralized Workflows

Located in this repository (`claude-workflows`):

#### 1. `claude-orchestrator.yml` (Recommended)

- Lightweight wrapper around the executor
- Simple interface for consumer repositories
- Reliable and predictable behavior

#### 2. `claude-executor.yml`

- Core execution engine
- Runs Claude AI with configured tools
- Handles both interactive and automatic modes
- Posts results back to GitHub

## Key Benefits

### ✅ Reliable Event Handling

- Consumer repositories maintain full control over webhook events
- No loss of event context
- No double triggering issues

### ✅ DRY Principle

- Centralized execution logic in `claude-executor.yml`
- Reusable across multiple repositories
- Consistent behavior and updates

### ✅ Security Isolation

- Each repository provides its own `ANTHROPIC_API_KEY`
- Cost tracking per repository
- No shared credentials

### ✅ Flexibility

- Repository-specific tool configurations
- Custom prompts and timeouts
- Configurable path exclusions

## Migration Path

If you're using the old orchestrator pattern:

### Before (Problematic)

```yaml
jobs:
claude:
uses: dotCMS/claude-workflows/.github/workflows/claude-orchestrator.yml@main
with:
allowed_tools: "Bash(git status)"
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
```

### After (Fixed)

```yaml
jobs:
claude-comment-mention:
if: |
github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '@claude')
uses: dotCMS/claude-workflows/.github/workflows/claude-orchestrator.yml@main
with:
trigger_mode: interactive
allowed_tools: "Bash(git status)"
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
```

## Examples

See the `examples/` directory for complete working examples:

- `consumer-repo-workflow.yml` - General purpose template
- `infrastructure-consumer-workflow.yml` - Infrastructure-specific example

## Why This Architecture?

The original orchestrator design attempted to centralize trigger logic, but GitHub Actions `workflow_call` loses the original webhook event context. When a consumer workflow calls a reusable workflow:

1. Consumer receives `issue_comment` event
2. Consumer calls orchestrator via `workflow_call`
3. Orchestrator sees `github.event_name` as `"workflow_call"`, not `"issue_comment"`
4. All conditional logic fails
5. Multiple jobs may trigger unexpectedly

The new architecture solves this by keeping trigger logic where the event context is available (in the consumer workflow) and using centralized workflows only for execution.

Loading