ci: Add claude task runner POC (#24179)

This commit is contained in:
Declan Carroll 2026-01-13 09:58:12 +00:00 committed by GitHub
parent 9795214836
commit 9bd52fab39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 341 additions and 0 deletions

179
.github/claude-templates/security-fix.md vendored Normal file
View file

@ -0,0 +1,179 @@
# Security Vulnerability Fix Guidelines
## Overview
This guide covers how to fix security vulnerabilities in the n8n codebase. Follow a systematic approach to identify, fix, and verify vulnerabilities in dependencies or base images.
## Decision Tree
```
Is it a direct dependency?
→ Yes: Update in catalog or package.json
→ No: Is it transitive?
→ Yes: Add pnpm override
→ No: Is it base image?
→ Yes: Update Dockerfile, trigger base image workflow
```
## Process Flow
```
Scan → Investigate → Fix → Verify
↓ ↓ ↓ ↓
pnpm pnpm why Update pnpm
build: (trace) deps build:
docker: or docker:
scan override scan
```
## Step-by-Step Process
### 1. Initial Setup
Start with a clean install:
```bash
pnpm install --frozen-lockfile
```
### 2. Scan for Vulnerabilities
Run the Docker scan to verify if the vulnerability exists:
```bash
pnpm build:docker:scan
```
### 3. Investigate the Source
Use `pnpm why` to trace where the vulnerable package is coming from:
```bash
pnpm why <package-name> -r
```
### 4. Determine Fix Strategy
#### Case A: Direct Dependency
If the vulnerable package is a **direct dependency**:
**Update via Catalog** (preferred for shared dependencies):
```yaml
# pnpm-workspace.yaml
catalog:
'@azure/identity': 4.13.0 # Updated version
```
```json
// packages/cli/package.json
{
"dependencies": {
"@azure/identity": "catalog:"
}
}
```
**Or update directly in package.json:**
```json
{
"dependencies": {
"vulnerable-package": "^1.2.3"
}
}
```
Then: `pnpm install`
#### Case B: Transitive Dependency
If the vulnerable package is a **transitive dependency**:
**Add an override** in the root `package.json`:
```json
{
"pnpm": {
"overrides": {
"vulnerable-package": "^1.2.3"
}
}
}
```
**For multiple versions:**
```json
{
"pnpm": {
"overrides": {
"vulnerable-package@3": "^3.2.1",
"vulnerable-package@4": "^4.0.1"
}
}
}
```
Then: `pnpm install`
#### Case C: Base Image / NPM Issue
If the vulnerability comes from the **base Docker image**:
1. Check `docker/images/n8n-base/Dockerfile`
2. Update Node version or Alpine packages if needed
3. Note: Base image rebuild requires manual workflow trigger
### 5. Verify the Fix
```bash
pnpm install
pnpm why <package-name> # Check version updated
pnpm build:docker:scan # Confirm vulnerability resolved
```
## Commit & PR Standards
### Commit Format
```
{type}({scope}): {neutral description}
{Brief neutral context}
Addresses: CVE-XXXX-XXXXX
Refs: {LINEAR-ID}
```
### Type Selection
| Scenario | Type |
|----------|------|
| Dependency update | `fix(deps)` |
| Code vulnerability fix | `fix` |
| License/compliance | `chore` |
| Docker/build hardening | `build` |
### Title Language - USE NEUTRAL LANGUAGE
Commit/PR titles appear in changelogs. Use neutral language:
| ❌ Avoid | ✅ Use Instead |
|----------|----------------|
| CVE-XXXX-XXXXX | (footer only) |
| vulnerability, exploit | issue, concern |
| critical, security fix | improvement, update |
| patch vulnerability | validate, harden, ensure |
### Example Commit
**Good:**
```
fix(deps): update jws to 4.0.1
Updates jws package to latest stable version.
Addresses: CVE-2025-65945
Refs: SEC-412
```
**Bad:**
```
fix(security): patch critical CVE-2025-65945 in jws
```
## Done Checklist
- [ ] `pnpm build:docker:scan` shows no vulnerability for the CVE
- [ ] `pnpm why <package>` shows updated version
- [ ] Commit follows neutral language format (no CVE in title)
- [ ] PR references Linear ticket if provided
## Common Commands
```bash
pnpm install --frozen-lockfile # Initial setup
pnpm build:docker:scan # Scan for vulnerabilities
pnpm why <package-name> -r # Investigate dependency
pnpm install # Update lockfile after changes
pnpm list <package-name> # Check specific package versions
```

162
.github/workflows/util-claude-task.yml vendored Normal file
View file

@ -0,0 +1,162 @@
name: 'Util: Claude Task Runner'
on:
workflow_dispatch:
inputs:
task:
description: 'Task description - what should Claude do?'
required: true
type: string
branch_name:
description: 'Branch name to create (leave empty for auto-generated)'
required: false
type: string
create_pr:
description: 'Create PR after completing task'
required: false
type: boolean
default: true
base_branch:
description: 'Base branch for PR'
required: false
type: string
default: 'master'
jobs:
run-claude-task:
runs-on: blacksmith-4vcpu-ubuntu-2204
timeout-minutes: 60
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.base_branch }}
- name: Setup Node.js
uses: ./.github/actions/setup-nodejs
with:
build-command: 'echo "Skipping build - Claude will build if needed"'
- name: Generate branch name
id: branch
shell: bash
env:
INPUT_BRANCH_NAME: ${{ inputs.branch_name }}
run: |
if [ -n "$INPUT_BRANCH_NAME" ]; then
BRANCH="$INPUT_BRANCH_NAME"
else
# Auto-generate branch name from timestamp
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BRANCH="claude/task-${TIMESTAMP}"
fi
echo "name=$BRANCH" >> "$GITHUB_OUTPUT"
- name: Create working branch
shell: bash
run: |
git checkout -b "${{ steps.branch.outputs.name }}"
- name: Prepare Claude prompt
id: prompt
shell: bash
env:
INPUT_TASK: ${{ inputs.task }}
run: |
# Build the prompt with task and pointer to templates
{
echo 'CLAUDE_PROMPT<<EOF'
echo "# Task"
echo "$INPUT_TASK"
echo ""
echo "# Guidelines"
echo "Check .github/claude-templates/ for relevant guides before starting."
echo "Read any templates that match your task type (e.g., security-fix.md for CVE fixes)."
echo ""
echo "# Instructions"
echo "1. Read relevant templates from .github/claude-templates/ first"
echo "2. Complete the task described above"
echo "3. Follow the guidelines from the templates"
echo "4. Make commits as you work with descriptive messages"
echo "5. Ensure code passes linting and type checks before finishing"
echo 'EOF'
} >> "$GITHUB_ENV"
- name: Run Claude
uses: anthropics/claude-code-action@beta
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
timeout_minutes: '45'
direct_prompt: ${{ env.CLAUDE_PROMPT }}
allowed_tools: |
Bash
Read
Write
Edit
Glob
Grep
WebFetch
WebSearch
TodoWrite
- name: Push branch
shell: bash
env:
INPUT_BASE_BRANCH: ${{ inputs.base_branch }}
BRANCH_NAME: ${{ steps.branch.outputs.name }}
run: |
# Check if there are any commits to push
if git log "origin/$INPUT_BASE_BRANCH..HEAD" --oneline | grep -q .; then
git push -u origin "$BRANCH_NAME"
echo "CHANGES_MADE=true" >> "$GITHUB_ENV"
else
echo "No commits were made by Claude"
echo "CHANGES_MADE=false" >> "$GITHUB_ENV"
fi
- name: Create Pull Request
if: inputs.create_pr && env.CHANGES_MADE == 'true'
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INPUT_TASK: ${{ inputs.task }}
INPUT_BASE_BRANCH: ${{ inputs.base_branch }}
run: |
# Create PR with task description in body
gh pr create \
--title "chore: Claude automated task" \
--body "## Summary
Automated task completed by Claude.
### Task Description
$INPUT_TASK
---
*This PR was created automatically by the Claude Task Runner workflow.*" \
--base "$INPUT_BASE_BRANCH" \
--draft
- name: Summary
shell: bash
env:
INPUT_TASK: ${{ inputs.task }}
INPUT_CREATE_PR: ${{ inputs.create_pr }}
BRANCH_NAME: ${{ steps.branch.outputs.name }}
run: |
{
echo "## Claude Task Runner Summary"
echo ""
echo "**Task:** $INPUT_TASK"
echo "**Branch:** $BRANCH_NAME"
echo "**Changes Made:** $CHANGES_MADE"
if [ "$CHANGES_MADE" == "true" ] && [ "$INPUT_CREATE_PR" == "true" ]; then
echo "**PR Created:** Yes (Draft)"
fi
} >> "$GITHUB_STEP_SUMMARY"