Security compliance - SHA pinning for all actions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
672 lines
23 KiB
YAML
672 lines
23 KiB
YAML
# .github/workflows/autonomous-orchestrator.yml
|
|
# Master coordinator for all autonomous agents
|
|
# Drop this in any repo for full autonomous operation
|
|
|
|
name: "Autonomous Orchestrator"
|
|
|
|
on:
|
|
push:
|
|
branches: [main, master, develop]
|
|
pull_request:
|
|
types: [opened, synchronize, reopened, ready_for_review]
|
|
issues:
|
|
types: [opened, labeled]
|
|
issue_comment:
|
|
types: [created]
|
|
schedule:
|
|
- cron: '0 */4 * * *' # Every 4 hours
|
|
workflow_dispatch:
|
|
inputs:
|
|
mode:
|
|
description: 'Operation mode'
|
|
required: false
|
|
default: 'auto'
|
|
type: choice
|
|
options:
|
|
- auto
|
|
- aggressive
|
|
- conservative
|
|
- audit-only
|
|
force_deploy:
|
|
description: 'Force deployment'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
issues: write
|
|
actions: write
|
|
security-events: write
|
|
checks: write
|
|
|
|
concurrency:
|
|
group: autonomous-${{ github.repository }}-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
BLACKROAD_AGENT_API: https://blackroad-agents.amundsonalexa.workers.dev
|
|
MEMORY_ENABLED: true
|
|
AUTO_MERGE: true
|
|
AUTO_FIX: true
|
|
|
|
jobs:
|
|
# ============================================
|
|
# Stage 1: Intelligence Gathering
|
|
# ============================================
|
|
analyze:
|
|
name: "Analyze Repository"
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
project_type: ${{ steps.detect.outputs.type }}
|
|
has_tests: ${{ steps.detect.outputs.has_tests }}
|
|
has_build: ${{ steps.detect.outputs.has_build }}
|
|
health_score: ${{ steps.health.outputs.score }}
|
|
priority: ${{ steps.priority.outputs.level }}
|
|
action_plan: ${{ steps.plan.outputs.actions }}
|
|
memory_context: ${{ steps.memory.outputs.context }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Detect Project Type
|
|
id: detect
|
|
run: |
|
|
TYPE="unknown"
|
|
HAS_TESTS="false"
|
|
HAS_BUILD="false"
|
|
|
|
# Node.js
|
|
if [ -f "package.json" ]; then
|
|
TYPE="nodejs"
|
|
grep -q '"test"' package.json && HAS_TESTS="true"
|
|
grep -q '"build"' package.json && HAS_BUILD="true"
|
|
# Python
|
|
elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ] || [ -f "setup.py" ]; then
|
|
TYPE="python"
|
|
[ -d "tests" ] || [ -d "test" ] && HAS_TESTS="true"
|
|
# Go
|
|
elif [ -f "go.mod" ]; then
|
|
TYPE="go"
|
|
find . -name "*_test.go" | head -1 | grep -q . && HAS_TESTS="true"
|
|
# Rust
|
|
elif [ -f "Cargo.toml" ]; then
|
|
TYPE="rust"
|
|
HAS_TESTS="true"
|
|
HAS_BUILD="true"
|
|
# Salesforce
|
|
elif [ -f "sfdx-project.json" ]; then
|
|
TYPE="salesforce"
|
|
# Static site
|
|
elif [ -f "index.html" ]; then
|
|
TYPE="static"
|
|
# Cloudflare Worker
|
|
elif [ -f "wrangler.toml" ]; then
|
|
TYPE="cloudflare-worker"
|
|
HAS_BUILD="true"
|
|
fi
|
|
|
|
echo "type=$TYPE" >> $GITHUB_OUTPUT
|
|
echo "has_tests=$HAS_TESTS" >> $GITHUB_OUTPUT
|
|
echo "has_build=$HAS_BUILD" >> $GITHUB_OUTPUT
|
|
echo "Detected: $TYPE (tests=$HAS_TESTS, build=$HAS_BUILD)"
|
|
|
|
- name: Calculate Health Score
|
|
id: health
|
|
run: |
|
|
SCORE=100
|
|
|
|
# Check for common issues
|
|
[ ! -f "README.md" ] && SCORE=$((SCORE - 10))
|
|
[ ! -f ".gitignore" ] && SCORE=$((SCORE - 5))
|
|
[ ! -d ".github/workflows" ] && SCORE=$((SCORE - 15))
|
|
|
|
# Security checks
|
|
grep -rn "password\s*=" --include="*.js" --include="*.ts" --include="*.py" . 2>/dev/null | grep -v node_modules && SCORE=$((SCORE - 20))
|
|
grep -rn "api_key\s*=" --include="*.js" --include="*.ts" --include="*.py" . 2>/dev/null | grep -v node_modules && SCORE=$((SCORE - 20))
|
|
|
|
# Stale checks
|
|
LAST_COMMIT=$(git log -1 --format=%ct 2>/dev/null || echo 0)
|
|
NOW=$(date +%s)
|
|
DAYS_SINCE=$(( (NOW - LAST_COMMIT) / 86400 ))
|
|
[ $DAYS_SINCE -gt 90 ] && SCORE=$((SCORE - 10))
|
|
|
|
[ $SCORE -lt 0 ] && SCORE=0
|
|
echo "score=$SCORE" >> $GITHUB_OUTPUT
|
|
echo "Health Score: $SCORE/100"
|
|
|
|
- name: Determine Priority
|
|
id: priority
|
|
run: |
|
|
PRIORITY="normal"
|
|
|
|
# High priority triggers
|
|
if echo "${{ github.event.issue.labels.*.name }}" | grep -qE "critical|urgent|security"; then
|
|
PRIORITY="critical"
|
|
elif echo "${{ github.event.issue.labels.*.name }}" | grep -qE "high|important"; then
|
|
PRIORITY="high"
|
|
elif [ "${{ github.event_name }}" = "schedule" ]; then
|
|
PRIORITY="background"
|
|
fi
|
|
|
|
echo "level=$PRIORITY" >> $GITHUB_OUTPUT
|
|
echo "Priority: $PRIORITY"
|
|
|
|
- name: Fetch Memory Context
|
|
id: memory
|
|
run: |
|
|
# Try to get memory from BlackRoad API
|
|
CONTEXT=$(curl -s -f "${{ env.BLACKROAD_AGENT_API }}/memory/${{ github.repository }}" 2>/dev/null || echo '{}')
|
|
echo "context=$CONTEXT" >> $GITHUB_OUTPUT
|
|
|
|
- name: Create Action Plan
|
|
id: plan
|
|
run: |
|
|
ACTIONS="[]"
|
|
|
|
# Build action list based on context
|
|
case "${{ github.event_name }}" in
|
|
push)
|
|
ACTIONS='["test","build","security_scan","quality_check"]'
|
|
;;
|
|
pull_request)
|
|
ACTIONS='["test","build","code_review","security_scan","auto_merge"]'
|
|
;;
|
|
issues)
|
|
ACTIONS='["triage","assign","respond"]'
|
|
;;
|
|
schedule)
|
|
ACTIONS='["health_check","dependency_update","stale_cleanup","security_audit"]'
|
|
;;
|
|
*)
|
|
ACTIONS='["analyze"]'
|
|
;;
|
|
esac
|
|
|
|
echo "actions=$ACTIONS" >> $GITHUB_OUTPUT
|
|
|
|
# ============================================
|
|
# Stage 2: Test & Build
|
|
# ============================================
|
|
test-and-build:
|
|
name: "Test & Build"
|
|
needs: analyze
|
|
runs-on: ubuntu-latest
|
|
if: needs.analyze.outputs.project_type != 'unknown'
|
|
outputs:
|
|
test_result: ${{ steps.test.outputs.result }}
|
|
build_result: ${{ steps.build.outputs.result }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
|
|
- name: Setup Environment
|
|
run: |
|
|
case "${{ needs.analyze.outputs.project_type }}" in
|
|
nodejs)
|
|
echo "Setting up Node.js..."
|
|
;;
|
|
python)
|
|
echo "Setting up Python..."
|
|
pip install pytest pytest-cov 2>/dev/null || true
|
|
;;
|
|
go)
|
|
echo "Go is pre-installed"
|
|
;;
|
|
rust)
|
|
echo "Installing Rust..."
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
;;
|
|
esac
|
|
|
|
- name: Install Dependencies
|
|
run: |
|
|
case "${{ needs.analyze.outputs.project_type }}" in
|
|
nodejs)
|
|
npm ci --ignore-scripts 2>/dev/null || npm install --ignore-scripts 2>/dev/null || true
|
|
;;
|
|
python)
|
|
[ -f "requirements.txt" ] && pip install -r requirements.txt 2>/dev/null || true
|
|
[ -f "pyproject.toml" ] && pip install -e . 2>/dev/null || true
|
|
;;
|
|
go)
|
|
go mod download 2>/dev/null || true
|
|
;;
|
|
rust)
|
|
cargo fetch 2>/dev/null || true
|
|
;;
|
|
esac
|
|
|
|
- name: Run Tests
|
|
id: test
|
|
continue-on-error: true
|
|
run: |
|
|
RESULT="skipped"
|
|
|
|
if [ "${{ needs.analyze.outputs.has_tests }}" = "true" ]; then
|
|
case "${{ needs.analyze.outputs.project_type }}" in
|
|
nodejs)
|
|
npm test 2>&1 && RESULT="passed" || RESULT="failed"
|
|
;;
|
|
python)
|
|
pytest -v 2>&1 && RESULT="passed" || python -m unittest discover 2>&1 && RESULT="passed" || RESULT="failed"
|
|
;;
|
|
go)
|
|
go test ./... 2>&1 && RESULT="passed" || RESULT="failed"
|
|
;;
|
|
rust)
|
|
cargo test 2>&1 && RESULT="passed" || RESULT="failed"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
echo "result=$RESULT" >> $GITHUB_OUTPUT
|
|
echo "Test result: $RESULT"
|
|
|
|
- name: Run Build
|
|
id: build
|
|
continue-on-error: true
|
|
run: |
|
|
RESULT="skipped"
|
|
|
|
if [ "${{ needs.analyze.outputs.has_build }}" = "true" ]; then
|
|
case "${{ needs.analyze.outputs.project_type }}" in
|
|
nodejs)
|
|
npm run build 2>&1 && RESULT="passed" || RESULT="failed"
|
|
;;
|
|
rust)
|
|
cargo build --release 2>&1 && RESULT="passed" || RESULT="failed"
|
|
;;
|
|
cloudflare-worker)
|
|
npx wrangler build 2>/dev/null && RESULT="passed" || RESULT="skipped"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
echo "result=$RESULT" >> $GITHUB_OUTPUT
|
|
echo "Build result: $RESULT"
|
|
|
|
# ============================================
|
|
# Stage 3: Security & Quality
|
|
# ============================================
|
|
security-scan:
|
|
name: "Security Scan"
|
|
needs: analyze
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
vulnerabilities: ${{ steps.scan.outputs.vulns }}
|
|
severity: ${{ steps.scan.outputs.max_severity }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
|
|
- name: Run Security Scanners
|
|
id: scan
|
|
run: |
|
|
VULNS=0
|
|
MAX_SEVERITY="none"
|
|
|
|
# Scan for secrets
|
|
echo "Scanning for secrets..."
|
|
if grep -rn --include="*.js" --include="*.ts" --include="*.py" --include="*.json" \
|
|
-E "(password|secret|api_key|token)\s*[:=]\s*['\"][^'\"]+['\"]" . 2>/dev/null | \
|
|
grep -v node_modules | grep -v ".git" | head -5; then
|
|
VULNS=$((VULNS + 1))
|
|
MAX_SEVERITY="critical"
|
|
fi
|
|
|
|
# Check for common vulnerabilities
|
|
echo "Checking for common vulnerabilities..."
|
|
if [ -f "package.json" ]; then
|
|
npm audit --json 2>/dev/null | jq -r '.metadata.vulnerabilities | to_entries[] | select(.value > 0)' && VULNS=$((VULNS + 1))
|
|
fi
|
|
|
|
echo "vulns=$VULNS" >> $GITHUB_OUTPUT
|
|
echo "max_severity=$MAX_SEVERITY" >> $GITHUB_OUTPUT
|
|
|
|
- name: Auto-fix Security Issues
|
|
if: env.AUTO_FIX == 'true' && steps.scan.outputs.vulns != '0'
|
|
run: |
|
|
echo "Attempting auto-fix..."
|
|
|
|
# Try to fix npm vulnerabilities
|
|
if [ -f "package.json" ]; then
|
|
npm audit fix 2>/dev/null || true
|
|
fi
|
|
|
|
# Check if changes were made
|
|
if [ -n "$(git status --porcelain)" ]; then
|
|
git config user.name "BlackRoad Bot"
|
|
git config user.email "bot@blackroad.ai"
|
|
git add -A
|
|
git commit -m "fix(security): Auto-fix security vulnerabilities
|
|
|
|
Automated security fixes applied by BlackRoad Autonomous Agent.
|
|
|
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>"
|
|
git push || echo "Push failed - may need PR"
|
|
fi
|
|
|
|
# ============================================
|
|
# Stage 4: Code Review (PRs only)
|
|
# ============================================
|
|
code-review:
|
|
name: "AI Code Review"
|
|
needs: [analyze, test-and-build]
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Get Changed Files
|
|
id: changed
|
|
run: |
|
|
FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} 2>/dev/null | head -50)
|
|
echo "files<<EOF" >> $GITHUB_OUTPUT
|
|
echo "$FILES" >> $GITHUB_OUTPUT
|
|
echo "EOF" >> $GITHUB_OUTPUT
|
|
|
|
- name: AI Code Analysis
|
|
id: ai_review
|
|
run: |
|
|
# Call BlackRoad AI for code review
|
|
REVIEW=$(curl -s -X POST "${{ env.BLACKROAD_AGENT_API }}/review" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"repo": "${{ github.repository }}",
|
|
"pr_number": ${{ github.event.pull_request.number }},
|
|
"files": ${{ toJSON(steps.changed.outputs.files) }},
|
|
"test_result": "${{ needs.test-and-build.outputs.test_result }}",
|
|
"build_result": "${{ needs.test-and-build.outputs.build_result }}"
|
|
}' 2>/dev/null || echo "Review completed")
|
|
|
|
echo "AI Review: $REVIEW"
|
|
|
|
- name: Post Review Comment
|
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
|
|
with:
|
|
script: |
|
|
const testResult = '${{ needs.test-and-build.outputs.test_result }}';
|
|
const buildResult = '${{ needs.test-and-build.outputs.build_result }}';
|
|
const healthScore = '${{ needs.analyze.outputs.health_score }}';
|
|
|
|
let status = '';
|
|
if (testResult === 'passed' && buildResult !== 'failed') {
|
|
status = '### Status: Ready to Merge';
|
|
} else if (testResult === 'failed') {
|
|
status = '### Status: Tests Failing - Needs Fix';
|
|
} else {
|
|
status = '### Status: Review Needed';
|
|
}
|
|
|
|
const body = `## Autonomous Agent Review
|
|
|
|
${status}
|
|
|
|
| Check | Result |
|
|
|-------|--------|
|
|
| Tests | ${testResult === 'passed' ? 'Passed' : testResult === 'failed' ? 'Failed' : 'Skipped'} |
|
|
| Build | ${buildResult === 'passed' ? 'Passed' : buildResult === 'failed' ? 'Failed' : 'Skipped'} |
|
|
| Health Score | ${healthScore}/100 |
|
|
|
|
---
|
|
*Autonomous review by BlackRoad Agent*`;
|
|
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: context.payload.pull_request.number,
|
|
body: body
|
|
});
|
|
|
|
# ============================================
|
|
# Stage 5: Auto-Merge
|
|
# ============================================
|
|
auto-merge:
|
|
name: "Auto-Merge"
|
|
needs: [analyze, test-and-build, security-scan, code-review]
|
|
if: |
|
|
github.event_name == 'pull_request' &&
|
|
needs.test-and-build.outputs.test_result != 'failed' &&
|
|
needs.test-and-build.outputs.build_result != 'failed' &&
|
|
needs.security-scan.outputs.severity != 'critical'
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Enable Auto-Merge
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
echo "Enabling auto-merge for PR #${{ github.event.pull_request.number }}"
|
|
|
|
# Try multiple merge strategies
|
|
gh pr merge ${{ github.event.pull_request.number }} \
|
|
--repo ${{ github.repository }} \
|
|
--auto \
|
|
--squash \
|
|
--delete-branch 2>/dev/null || \
|
|
gh pr merge ${{ github.event.pull_request.number }} \
|
|
--repo ${{ github.repository }} \
|
|
--squash 2>/dev/null || \
|
|
echo "Auto-merge queued - waiting for required checks"
|
|
|
|
# ============================================
|
|
# Stage 6: Auto-Deploy
|
|
# ============================================
|
|
auto-deploy:
|
|
name: "Auto-Deploy"
|
|
needs: [analyze, test-and-build, security-scan]
|
|
if: |
|
|
github.event_name == 'push' &&
|
|
github.ref == 'refs/heads/main' &&
|
|
needs.test-and-build.outputs.test_result != 'failed' &&
|
|
needs.security-scan.outputs.severity != 'critical'
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
|
|
- name: Determine Deploy Target
|
|
id: target
|
|
run: |
|
|
TARGET="none"
|
|
|
|
# Check for deployment configs
|
|
[ -f "wrangler.toml" ] && TARGET="cloudflare"
|
|
[ -f "vercel.json" ] && TARGET="vercel"
|
|
[ -f "railway.toml" ] && TARGET="railway"
|
|
[ -f "Dockerfile" ] && TARGET="docker"
|
|
|
|
echo "target=$TARGET" >> $GITHUB_OUTPUT
|
|
|
|
- name: Deploy to Cloudflare
|
|
if: steps.target.outputs.target == 'cloudflare'
|
|
env:
|
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
run: |
|
|
if [ -n "$CLOUDFLARE_API_TOKEN" ]; then
|
|
npx wrangler deploy 2>/dev/null || echo "Cloudflare deploy skipped"
|
|
fi
|
|
|
|
- name: Deploy to Vercel
|
|
if: steps.target.outputs.target == 'vercel'
|
|
env:
|
|
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
|
|
run: |
|
|
if [ -n "$VERCEL_TOKEN" ]; then
|
|
npx vercel --prod --token=$VERCEL_TOKEN 2>/dev/null || echo "Vercel deploy skipped"
|
|
fi
|
|
|
|
# ============================================
|
|
# Stage 7: Memory Persistence
|
|
# ============================================
|
|
persist-memory:
|
|
name: "Persist Memory"
|
|
needs: [test-and-build, security-scan]
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Save Run Memory
|
|
run: |
|
|
curl -s -X POST "${{ env.BLACKROAD_AGENT_API }}/memory" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"repo": "${{ github.repository }}",
|
|
"run_id": "${{ github.run_id }}",
|
|
"event": "${{ github.event_name }}",
|
|
"results": {
|
|
"test": "${{ needs.test-and-build.outputs.test_result }}",
|
|
"build": "${{ needs.test-and-build.outputs.build_result }}",
|
|
"security": "${{ needs.security-scan.outputs.severity }}"
|
|
},
|
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
|
}' 2>/dev/null || echo "Memory save queued"
|
|
|
|
# ============================================
|
|
# Stage 8: Issue Triage (Issues only)
|
|
# ============================================
|
|
issue-triage:
|
|
name: "Issue Triage"
|
|
needs: analyze
|
|
if: github.event_name == 'issues' && github.event.action == 'opened'
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: AI Issue Analysis
|
|
id: analyze
|
|
run: |
|
|
TITLE="${{ github.event.issue.title }}"
|
|
BODY="${{ github.event.issue.body }}"
|
|
|
|
# Determine labels based on content
|
|
LABELS=""
|
|
|
|
echo "$TITLE $BODY" | grep -qi "bug\|error\|broken\|fix" && LABELS="$LABELS,bug"
|
|
echo "$TITLE $BODY" | grep -qi "feature\|add\|new\|enhance" && LABELS="$LABELS,enhancement"
|
|
echo "$TITLE $BODY" | grep -qi "question\|how\|help" && LABELS="$LABELS,question"
|
|
echo "$TITLE $BODY" | grep -qi "security\|vulnerability\|cve" && LABELS="$LABELS,security"
|
|
echo "$TITLE $BODY" | grep -qi "urgent\|critical\|asap" && LABELS="$LABELS,priority:high"
|
|
|
|
LABELS=$(echo "$LABELS" | sed 's/^,//')
|
|
echo "labels=$LABELS" >> $GITHUB_OUTPUT
|
|
|
|
- name: Apply Labels
|
|
if: steps.analyze.outputs.labels != ''
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
for label in $(echo "${{ steps.analyze.outputs.labels }}" | tr ',' ' '); do
|
|
gh issue edit ${{ github.event.issue.number }} --add-label "$label" 2>/dev/null || true
|
|
done
|
|
|
|
- name: Auto-Respond
|
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
|
|
with:
|
|
script: |
|
|
const labels = '${{ steps.analyze.outputs.labels }}'.split(',').filter(l => l);
|
|
|
|
let response = `Thanks for opening this issue!\n\n`;
|
|
response += `**Automated Triage:**\n`;
|
|
if (labels.length > 0) {
|
|
response += `- Labels applied: ${labels.map(l => '`' + l + '`').join(', ')}\n`;
|
|
}
|
|
response += `\nA team member will review this shortly. In the meantime:\n`;
|
|
response += `- Check if there's a similar issue already open\n`;
|
|
response += `- Provide additional context if available\n\n`;
|
|
response += `*Triaged by BlackRoad Autonomous Agent*`;
|
|
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: context.payload.issue.number,
|
|
body: response
|
|
});
|
|
|
|
# ============================================
|
|
# Stage 9: Scheduled Maintenance
|
|
# ============================================
|
|
maintenance:
|
|
name: "Scheduled Maintenance"
|
|
needs: analyze
|
|
if: github.event_name == 'schedule'
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
|
|
- name: Update Dependencies
|
|
run: |
|
|
if [ -f "package.json" ]; then
|
|
# Check for outdated packages
|
|
npm outdated --json > outdated.json 2>/dev/null || true
|
|
|
|
# Auto-update patch versions
|
|
npm update 2>/dev/null || true
|
|
|
|
if [ -n "$(git status --porcelain package-lock.json)" ]; then
|
|
git config user.name "BlackRoad Bot"
|
|
git config user.email "bot@blackroad.ai"
|
|
git add package.json package-lock.json
|
|
git commit -m "chore(deps): Auto-update dependencies
|
|
|
|
Automated dependency updates by BlackRoad Agent.
|
|
|
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>"
|
|
git push || echo "Would create PR for updates"
|
|
fi
|
|
fi
|
|
|
|
- name: Clean Stale Branches
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
# List merged branches older than 30 days
|
|
git fetch --all --prune
|
|
for branch in $(git branch -r --merged origin/main | grep -v main | grep -v HEAD); do
|
|
LAST_COMMIT=$(git log -1 --format=%ct "$branch" 2>/dev/null || echo 0)
|
|
NOW=$(date +%s)
|
|
DAYS_OLD=$(( (NOW - LAST_COMMIT) / 86400 ))
|
|
|
|
if [ $DAYS_OLD -gt 30 ]; then
|
|
BRANCH_NAME=$(echo "$branch" | sed 's|origin/||')
|
|
echo "Deleting stale branch: $BRANCH_NAME ($DAYS_OLD days old)"
|
|
git push origin --delete "$BRANCH_NAME" 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
- name: Health Report
|
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
|
|
with:
|
|
script: |
|
|
const healthScore = '${{ needs.analyze.outputs.health_score }}';
|
|
|
|
// Only create issue if health is poor
|
|
if (parseInt(healthScore) < 70) {
|
|
await github.rest.issues.create({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
title: `[Automated] Repository Health Alert (Score: ${healthScore}/100)`,
|
|
body: `## Repository Health Report
|
|
|
|
**Current Health Score:** ${healthScore}/100
|
|
|
|
The autonomous agent has detected potential issues with this repository.
|
|
|
|
### Recommended Actions
|
|
- Review and address any security warnings
|
|
- Update outdated dependencies
|
|
- Add missing documentation
|
|
|
|
---
|
|
*Generated by BlackRoad Autonomous Agent*`,
|
|
labels: ['maintenance', 'automated']
|
|
});
|
|
}
|