Go to file
Daniel Samson f693d56762
Publish Image / publish (push) Successful in 23s Details
Handle Gitea review webhook type field alongside state
Gitea webhook payloads use "type" (e.g. "pull_request_review_rejected")
instead of "state" (e.g. "REQUEST_CHANGES"). Map both formats so
the pr-rework handler works with all Gitea webhook variations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 18:13:51 +01:00
.gitea/workflows Custom workspace image + fix slash command invocation 2026-04-12 14:15:17 +01:00
coder Route spec PR review feedback to rework-spec instead of rework-pr 2026-04-12 16:18:00 +01:00
k8s Migrate task queue from in-memory to BullMQ + Redis 2026-04-12 15:40:12 +01:00
src Handle Gitea review webhook type field alongside state 2026-04-12 18:13:51 +01:00
.gitignore Initial commit: SDLC Orchestrator replacing n8n 2026-04-12 12:44:28 +01:00
Dockerfile Initial commit: SDLC Orchestrator replacing n8n 2026-04-12 12:44:28 +01:00
README.md Comprehensive setup guide for adding new projects 2026-04-12 15:03:40 +01:00
package-lock.json Migrate task queue from in-memory to BullMQ + Redis 2026-04-12 15:40:12 +01:00
package.json Migrate task queue from in-memory to BullMQ + Redis 2026-04-12 15:40:12 +01:00
tsconfig.json Initial commit: SDLC Orchestrator replacing n8n 2026-04-12 12:44:28 +01:00

README.md

SDLC Orchestrator

Lightweight webhook-driven orchestrator for the Gitea + Coder + Claude Code SDLC pipeline. Receives Gitea webhooks, creates ephemeral Coder workspaces running Claude Code, and cleans up when done.

Architecture

Gitea Webhook → Orchestrator → Coder API (create workspace)
                             → Gitea API (post status comment)

Workspace runs Claude Code → reads issue → does work → posts results to Gitea
                           → POST /webhook/task-complete/:name (callback)

Orchestrator receives callback → deletes workspace via Coder API

Adding a New Project

To add a new Gitea repo to the SDLC pipeline:

1. Add Claude Code slash commands to the repo

Create .claude/commands/ in the repo with command files for each SDLC stage:

.claude/commands/
├── analyse.md        # Business analyst — requirements & acceptance criteria
├── architect.md      # Systems architect — design doc & spec
├── develop.md        # Developer — implement & create PR
├── review-code.md    # Reviewer — approve or request changes
├── test.md           # QA — test report & additional tests
├── fix-bug.md        # Bug fix — skip analyse/architect
├── rework-pr.md      # Address review feedback
├── release.md        # Tag RC or production release
├── devops.md         # Migration & deployment
└── maintenance.md    # Weekly maintenance tasks

Each file uses this format:

---
description: What this command does
allowed-tools: Read, Bash, Glob, Grep
---

You are a **Role Name**. Your job is to...

## Process

1. **Read the issue**:
   ```bash
   curl -s "https://gitea.samson.media/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}/issues/$ARGUMENTS" \
     -H "Authorization: token ${GITEA_TOKEN}" | jq '{title, body, labels: [.labels[].name]}'
  1. Do your work...

  2. Post a comment:

    curl -X POST "https://gitea.samson.media/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}/issues/$ARGUMENTS/comments" \
      -H "Authorization: token ${GITEA_TOKEN}" \
      -H "Content-Type: application/json" \
      -d '{"body": "..."}'
    

`$ARGUMENTS` is replaced with the issue/PR number at runtime.

### 2. Add bot users as collaborators

Add both `claude-dev` and `claude-review` as **direct collaborators** with **Write** access on the repo. Org membership alone is not sufficient.

### 3. Configure Gitea webhooks

In the repo's **Settings → Webhooks**, create these webhooks:

| # | URL | Events |
|---|-----|--------|
| 1 | `https://sdlc.samson.media/webhook/gitea-issue-triage` | Issues: `opened` |
| 2 | `https://sdlc.samson.media/webhook/gitea-issue-label` | Issues: `labeled` |
| 3 | `https://sdlc.samson.media/webhook/gitea-issue-comment` | Issue Comment: `created`, `edited` |
| 4 | `https://sdlc.samson.media/webhook/gitea-pr-review` | Pull Request: `opened`, `synchronized` |
| 5 | `https://sdlc.samson.media/webhook/gitea-pr-review-rework` | Pull Request Review: `submitted` |
| 6 | `https://sdlc.samson.media/webhook/gitea-release` | Issues: `opened`, `labeled` |

All webhooks use:
- Content type: `application/json`
- Method: `POST`

### 4. (Optional) Add to maintenance cron

To include the repo in weekly maintenance, add it to the `MAINTENANCE_REPOS` env var:

MAINTENANCE_REPOS=org1/repo1=https://gitea.samson.media/org1/repo1.git,org2/repo2=https://gitea.samson.media/org2/repo2.git


### 5. Test it

Create an issue in the repo. The orchestrator will:
1. Receive the `issues/opened` webhook
2. Create a Coder workspace running `/analyse`
3. Claude reads the issue, posts clarifying questions as a comment
4. Workspace is deleted automatically when done

## SDLC Flow

| Stage | Trigger | Task Type | Bot Account |
|-------|---------|-----------|-------------|
| Analyse | Issue opened / comment | `analyse` | claude-dev |
| Architect | Label: `ready-for-architecture` | `architect` | claude-dev |
| Develop | Label: `ready-for-development` | `develop` | claude-dev |
| Review | PR opened/updated | `review-code` | claude-review |
| Rework | Review: changes requested | `rework-pr` | claude-dev |
| Test | Review: approved | `test` | claude-review |
| Deploy | Label: `ready-for-deployment` | `devops` | claude-dev |
| Fix Bug | Issue opened with `bug` label | `fix-bug` | claude-dev |

## Infrastructure Setup

### Prerequisites

- k3s cluster with Traefik ingress and cert-manager
- Coder instance (e.g. `coder.samson.media`)
- Gitea instance (e.g. `gitea.samson.media`)
- Docker registry (e.g. `registry.samson.media`)
- Claude Code Max subscription (for OAuth token)

### Deploy the Orchestrator

1. **Build the workspace image** (pre-installed Node.js, Claude Code, Playwright deps):
   ```bash
   docker build -f coder/workspace.Dockerfile -t registry.samson.media/coder-workspace:latest .
   docker push registry.samson.media/coder-workspace:latest
  1. Push the Coder template:

    coder templates push cloudflare-worker --directory coder/cloudflare-worker --yes
    

    Note the template ID from the Coder dashboard.

  2. Create Gitea bot accounts:

    • claude-dev — used for most stages (analyse, develop, etc.)
    • claude-review — used for review and test stages
    • Generate API tokens for each
  3. Generate Claude Code OAuth token (uses Max subscription instead of API credits):

    claude setup-token
    

    This opens a browser for auth and outputs a long-lived token (sk-ant-oat01-...).

  4. Create k8s secrets:

    kubectl create namespace sdlc-orchestrator
    kubectl create secret generic orchestrator-secrets -n sdlc-orchestrator \
      --from-literal=CODER_URL=https://coder.samson.media \
      --from-literal=CODER_TOKEN=<coder-session-token> \
      --from-literal=CODER_TEMPLATE_ID=<template-id> \
      --from-literal=GITEA_DEV_TOKEN=<claude-dev-token> \
      --from-literal=GITEA_REVIEW_TOKEN=<claude-review-token> \
      --from-literal=CLAUDE_OAUTH_TOKEN=<oauth-token-from-step-4> \
      --from-literal=CALLBACK_URL=https://sdlc.samson.media \
      --from-literal=GITEA_URL=https://gitea.samson.media \
      --from-literal=BOT_DEV_USERNAME=claude-dev \
      --from-literal=BOT_REVIEW_USERNAME=claude-review
    
  5. Deploy:

    # Tag to trigger CI build
    git tag -a v1.0.0 -m "Initial release"
    git push origin main --tags
    
    # Or deploy manually
    docker build -t registry.samson.media/sdlc-orchestrator:latest .
    docker push registry.samson.media/sdlc-orchestrator:latest
    kubectl apply -f k8s/deployment.yaml
    

Environment Variables

Variable Required Description
PORT No HTTP port (default: 3000)
CODER_URL Yes Coder API base URL
CODER_TOKEN Yes Coder session token
CODER_TEMPLATE_ID Yes Coder workspace template ID
GITEA_DEV_TOKEN Yes Gitea API token for claude-dev
GITEA_REVIEW_TOKEN Yes Gitea API token for claude-review
CLAUDE_OAUTH_TOKEN Yes Claude Code Max OAuth token
CALLBACK_URL Yes Public URL of this service (e.g. https://sdlc.samson.media)
GITEA_URL No Gitea base URL (default: https://gitea.samson.media)
BOT_DEV_USERNAME No Dev bot username (default: claude-dev)
BOT_REVIEW_USERNAME No Review bot username (default: claude-review)
QUEUE_CONCURRENCY No Max concurrent workspaces (default: 2)
MAINTENANCE_REPOS No Comma-separated org/repo=clone_url for weekly maintenance

Important: Do NOT set ANTHROPIC_API_KEY in the workspace — it takes priority over the OAuth token and will use pay-per-use credits instead of your Max subscription.

Webhook Endpoints

Endpoint Purpose
GET /health Health check
GET /queue Queue status (pending, running, active workspaces)
POST /webhook/gitea-issue-triage Route new issues to analyse or fix-bug
POST /webhook/gitea-issue-label Stage transitions via labels
POST /webhook/gitea-issue-comment Re-trigger analyst on new/edited comments
POST /webhook/gitea-pr-review Trigger code review on PR open/update
POST /webhook/gitea-pr-review-rework Route review outcomes to rework or test
POST /webhook/gitea-release Handle release workflow
POST /webhook/task-complete/:name Callback from workspaces when done

How Workspaces Work

Workspaces are ephemeral (emptyDir, no persistent storage):

  1. Orchestrator creates workspace via Coder API with parameters (issue number, task type, tokens, callback URL)
  2. Startup script clones the repo and checks out develop
  3. For heavy stages (develop, test, review, rework, fix-bug, devops): installs deps, builds frontend, installs Playwright
  4. For lightweight stages (analyse, architect, release, maintenance): skips build
  5. Reads .claude/commands/<task_type>.md, strips YAML frontmatter, substitutes $ARGUMENTS with issue number
  6. Runs claude -p --dangerously-skip-permissions --verbose "<prompt>"
  7. On completion (success or failure via ERR/EXIT trap): POSTs to callback URL
  8. Orchestrator receives callback, deletes workspace via Coder API

Resilience

  • Deduplication: Tasks keyed by {taskType}-{repo}-{issue}. Duplicate webhooks are dropped.
  • Startup reconciliation: On boot, queries Coder for running workspaces and re-adopts them into the queue.
  • 409 handling: If workspace already exists — adopts if running, deletes and retries if stopped/failed.
  • Callback trap: Startup script uses trap to always fire the callback, even on clone failure or other errors.
  • TTL safety net: Coder template has 1-hour auto-stop as a backstop for missed callbacks.

CI/CD

Workflow Trigger Output
publish.yml v* tags registry.samson.media/sdlc-orchestrator:<version> + :latest
publish-workspace.yml Changes to coder/workspace.Dockerfile on main registry.samson.media/coder-workspace:latest

Fleet GitOps (in samson-media/devops repo) watches registry.samson.media/sdlc-orchestrator:latest for deployment.