chore(workflows): Deploy autonomous workflow system
Deployed workflows: - autonomous-orchestrator.yml - autonomous-self-healer.yml - autonomous-cross-repo.yml - autonomous-dependency-manager.yml - autonomous-issue-manager.yml These workflows enable: - Autonomous testing and building - Self-healing on failures - AI-powered code review - Intelligent dependency updates - Smart issue triage and management - Cross-repo coordination Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>
This commit is contained in:
324
.github/workflows/autonomous-cross-repo.yml
vendored
Normal file
324
.github/workflows/autonomous-cross-repo.yml
vendored
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
# .github/workflows/autonomous-cross-repo.yml
|
||||||
|
# Cross-repository coordination for synchronized changes
|
||||||
|
|
||||||
|
name: "Autonomous Cross-Repo Coordinator"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
paths:
|
||||||
|
- 'shared/**'
|
||||||
|
- 'packages/**'
|
||||||
|
- 'lib/**'
|
||||||
|
- '*.config.*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
sync_type:
|
||||||
|
description: 'Type of sync'
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- config
|
||||||
|
- dependencies
|
||||||
|
- workflows
|
||||||
|
- all
|
||||||
|
target_repos:
|
||||||
|
description: 'Target repos (comma-separated, or "all")'
|
||||||
|
required: false
|
||||||
|
default: 'all'
|
||||||
|
dry_run:
|
||||||
|
description: 'Dry run (no actual changes)'
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
BLACKROAD_AGENT_API: https://blackroad-agents.amundsonalexa.workers.dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ============================================
|
||||||
|
# Identify Affected Repositories
|
||||||
|
# ============================================
|
||||||
|
identify-repos:
|
||||||
|
name: "Identify Affected Repos"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
repos: ${{ steps.find.outputs.repos }}
|
||||||
|
sync_files: ${{ steps.changes.outputs.files }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Get Changed Files
|
||||||
|
id: changes
|
||||||
|
run: |
|
||||||
|
FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null | head -50 || echo "")
|
||||||
|
echo "files<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "$FILES" >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Find Related Repositories
|
||||||
|
id: find
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
# Default BlackRoad repos that should stay in sync
|
||||||
|
CORE_REPOS='[
|
||||||
|
"BlackRoad-OS/blackroad-os-web",
|
||||||
|
"BlackRoad-OS/blackroad-os-docs",
|
||||||
|
"BlackRoad-OS/blackroad-cli",
|
||||||
|
"BlackRoad-OS/blackroad-agents",
|
||||||
|
"BlackRoad-OS/blackroad-os-mesh",
|
||||||
|
"BlackRoad-OS/blackroad-os-helper",
|
||||||
|
"BlackRoad-OS/blackroad-os-core"
|
||||||
|
]'
|
||||||
|
|
||||||
|
if [ "${{ github.event.inputs.target_repos }}" = "all" ] || [ -z "${{ github.event.inputs.target_repos }}" ]; then
|
||||||
|
REPOS="$CORE_REPOS"
|
||||||
|
else
|
||||||
|
# Convert comma-separated to JSON array
|
||||||
|
REPOS=$(echo '${{ github.event.inputs.target_repos }}' | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))')
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "repos=$REPOS" >> $GITHUB_OUTPUT
|
||||||
|
echo "Repos to sync: $REPOS"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Sync Workflows
|
||||||
|
# ============================================
|
||||||
|
sync-workflows:
|
||||||
|
name: "Sync Workflows"
|
||||||
|
needs: identify-repos
|
||||||
|
if: github.event.inputs.sync_type == 'workflows' || github.event.inputs.sync_type == 'all' || contains(needs.identify-repos.outputs.sync_files, '.github/workflows')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
repo: ${{ fromJSON(needs.identify-repos.outputs.repos) }}
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 5
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Source
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: source
|
||||||
|
|
||||||
|
- name: Checkout Target
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: ${{ matrix.repo }}
|
||||||
|
path: target
|
||||||
|
token: ${{ secrets.CROSS_REPO_TOKEN || secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Sync Workflow Files
|
||||||
|
run: |
|
||||||
|
# Copy autonomous workflows
|
||||||
|
mkdir -p target/.github/workflows
|
||||||
|
|
||||||
|
# Copy the orchestrator and self-healer
|
||||||
|
for workflow in autonomous-orchestrator.yml autonomous-self-healer.yml blackroad-agents.yml; do
|
||||||
|
if [ -f "source/.github/workflows-autonomous/$workflow" ]; then
|
||||||
|
cp "source/.github/workflows-autonomous/$workflow" "target/.github/workflows/"
|
||||||
|
elif [ -f "source/.github/workflows/$workflow" ]; then
|
||||||
|
cp "source/.github/workflows/$workflow" "target/.github/workflows/"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Synced workflows to ${{ matrix.repo }}"
|
||||||
|
|
||||||
|
- name: Create PR
|
||||||
|
if: github.event.inputs.dry_run != 'true'
|
||||||
|
working-directory: target
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.CROSS_REPO_TOKEN || secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
BRANCH="sync-workflows-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
git config user.name "BlackRoad Cross-Repo Bot"
|
||||||
|
git config user.email "crossrepo@blackroad.ai"
|
||||||
|
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore(workflows): Sync autonomous workflows from central repo
|
||||||
|
|
||||||
|
Synced workflows:
|
||||||
|
- autonomous-orchestrator.yml
|
||||||
|
- autonomous-self-healer.yml
|
||||||
|
- blackroad-agents.yml
|
||||||
|
|
||||||
|
Source: ${{ github.repository }}
|
||||||
|
|
||||||
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>"
|
||||||
|
|
||||||
|
git push -u origin "$BRANCH"
|
||||||
|
|
||||||
|
gh pr create \
|
||||||
|
--title "chore(workflows): Sync autonomous workflows" \
|
||||||
|
--body "## Workflow Sync
|
||||||
|
|
||||||
|
Synced autonomous workflows from central repository.
|
||||||
|
|
||||||
|
**Source:** ${{ github.repository }}
|
||||||
|
**Sync Type:** workflows
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- Updated autonomous-orchestrator.yml
|
||||||
|
- Updated autonomous-self-healer.yml
|
||||||
|
- Updated blackroad-agents.yml
|
||||||
|
|
||||||
|
---
|
||||||
|
*Automated by BlackRoad Cross-Repo Coordinator*" \
|
||||||
|
--label "automated,infrastructure"
|
||||||
|
else
|
||||||
|
echo "No workflow changes needed for ${{ matrix.repo }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Sync Configurations
|
||||||
|
# ============================================
|
||||||
|
sync-config:
|
||||||
|
name: "Sync Configurations"
|
||||||
|
needs: identify-repos
|
||||||
|
if: github.event.inputs.sync_type == 'config' || github.event.inputs.sync_type == 'all'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
repo: ${{ fromJSON(needs.identify-repos.outputs.repos) }}
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 5
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Source
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: source
|
||||||
|
|
||||||
|
- name: Checkout Target
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: ${{ matrix.repo }}
|
||||||
|
path: target
|
||||||
|
token: ${{ secrets.CROSS_REPO_TOKEN || secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Sync Config Files
|
||||||
|
run: |
|
||||||
|
# Sync common configs that should be consistent
|
||||||
|
SYNC_FILES=(
|
||||||
|
".eslintrc.js"
|
||||||
|
".prettierrc"
|
||||||
|
".editorconfig"
|
||||||
|
"tsconfig.base.json"
|
||||||
|
".github/CODEOWNERS"
|
||||||
|
".github/ISSUE_TEMPLATE/bug_report.yml"
|
||||||
|
".github/ISSUE_TEMPLATE/feature_request.yml"
|
||||||
|
)
|
||||||
|
|
||||||
|
for file in "${SYNC_FILES[@]}"; do
|
||||||
|
if [ -f "source/$file" ]; then
|
||||||
|
mkdir -p "target/$(dirname $file)"
|
||||||
|
cp "source/$file" "target/$file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Create PR
|
||||||
|
if: github.event.inputs.dry_run != 'true'
|
||||||
|
working-directory: target
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.CROSS_REPO_TOKEN || secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
BRANCH="sync-config-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
git config user.name "BlackRoad Cross-Repo Bot"
|
||||||
|
git config user.email "crossrepo@blackroad.ai"
|
||||||
|
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore(config): Sync configurations from central repo
|
||||||
|
|
||||||
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>"
|
||||||
|
|
||||||
|
git push -u origin "$BRANCH"
|
||||||
|
|
||||||
|
gh pr create \
|
||||||
|
--title "chore(config): Sync configurations" \
|
||||||
|
--body "## Configuration Sync
|
||||||
|
|
||||||
|
Synced common configurations from central repository.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Automated by BlackRoad Cross-Repo Coordinator*" \
|
||||||
|
--label "automated,config"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Sync Dependencies
|
||||||
|
# ============================================
|
||||||
|
sync-deps:
|
||||||
|
name: "Sync Dependencies"
|
||||||
|
needs: identify-repos
|
||||||
|
if: github.event.inputs.sync_type == 'dependencies' || github.event.inputs.sync_type == 'all'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Analyze Dependencies
|
||||||
|
id: deps
|
||||||
|
run: |
|
||||||
|
# Extract common dependencies and their versions
|
||||||
|
if [ -f "package.json" ]; then
|
||||||
|
DEPS=$(jq -r '.dependencies // {} | to_entries[] | "\(.key)@\(.value)"' package.json | head -20)
|
||||||
|
echo "deps<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "$DEPS" >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Report Dependencies
|
||||||
|
run: |
|
||||||
|
echo "## Dependencies to Sync"
|
||||||
|
echo "${{ steps.deps.outputs.deps }}"
|
||||||
|
|
||||||
|
# Log to coordination API
|
||||||
|
curl -s -X POST "${{ env.BLACKROAD_AGENT_API }}/coordinate" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"action": "sync_deps",
|
||||||
|
"source": "${{ github.repository }}",
|
||||||
|
"repos": ${{ needs.identify-repos.outputs.repos }},
|
||||||
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
||||||
|
}' 2>/dev/null || true
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Broadcast Changes
|
||||||
|
# ============================================
|
||||||
|
broadcast:
|
||||||
|
name: "Broadcast Changes"
|
||||||
|
needs: [sync-workflows, sync-config, sync-deps]
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Notify Coordination System
|
||||||
|
run: |
|
||||||
|
curl -s -X POST "${{ env.BLACKROAD_AGENT_API }}/broadcast" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"event": "cross_repo_sync_complete",
|
||||||
|
"source": "${{ github.repository }}",
|
||||||
|
"sync_type": "${{ github.event.inputs.sync_type || 'auto' }}",
|
||||||
|
"repos": ${{ needs.identify-repos.outputs.repos || '[]' }},
|
||||||
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
||||||
|
}' 2>/dev/null || echo "Broadcast queued"
|
||||||
|
|
||||||
|
- name: Summary
|
||||||
|
run: |
|
||||||
|
echo "## Cross-Repo Sync Complete"
|
||||||
|
echo "- Source: ${{ github.repository }}"
|
||||||
|
echo "- Sync Type: ${{ github.event.inputs.sync_type || 'auto' }}"
|
||||||
|
echo "- Dry Run: ${{ github.event.inputs.dry_run || 'false' }}"
|
||||||
297
.github/workflows/autonomous-dependency-manager.yml
vendored
Normal file
297
.github/workflows/autonomous-dependency-manager.yml
vendored
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
# .github/workflows/autonomous-dependency-manager.yml
|
||||||
|
# Intelligent dependency management with bundled updates
|
||||||
|
|
||||||
|
name: "Autonomous Dependency Manager"
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 3 * * 1' # Every Monday at 3 AM
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
update_type:
|
||||||
|
description: 'Update type'
|
||||||
|
required: false
|
||||||
|
default: 'safe'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- safe # Patch versions only
|
||||||
|
- minor # Minor + patch
|
||||||
|
- major # All updates (risky)
|
||||||
|
- security # Security updates only
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
security-events: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
BLACKROAD_AGENT_API: https://blackroad-agents.amundsonalexa.workers.dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ============================================
|
||||||
|
# Analyze Current State
|
||||||
|
# ============================================
|
||||||
|
analyze:
|
||||||
|
name: "Analyze Dependencies"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
has_npm: ${{ steps.detect.outputs.npm }}
|
||||||
|
has_python: ${{ steps.detect.outputs.python }}
|
||||||
|
has_go: ${{ steps.detect.outputs.go }}
|
||||||
|
has_rust: ${{ steps.detect.outputs.rust }}
|
||||||
|
outdated_count: ${{ steps.check.outputs.count }}
|
||||||
|
security_issues: ${{ steps.security.outputs.count }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Detect Package Managers
|
||||||
|
id: detect
|
||||||
|
run: |
|
||||||
|
echo "npm=$([[ -f package.json ]] && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||||
|
echo "python=$([[ -f requirements.txt || -f pyproject.toml ]] && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||||
|
echo "go=$([[ -f go.mod ]] && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||||
|
echo "rust=$([[ -f Cargo.toml ]] && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Check Outdated (npm)
|
||||||
|
id: check
|
||||||
|
if: steps.detect.outputs.npm == 'true'
|
||||||
|
run: |
|
||||||
|
npm outdated --json > outdated.json 2>/dev/null || true
|
||||||
|
COUNT=$(jq 'length' outdated.json 2>/dev/null || echo 0)
|
||||||
|
echo "count=$COUNT" >> $GITHUB_OUTPUT
|
||||||
|
echo "Found $COUNT outdated packages"
|
||||||
|
|
||||||
|
- name: Security Audit
|
||||||
|
id: security
|
||||||
|
run: |
|
||||||
|
ISSUES=0
|
||||||
|
|
||||||
|
if [ -f "package.json" ]; then
|
||||||
|
npm audit --json > npm-audit.json 2>/dev/null || true
|
||||||
|
NPM_VULNS=$(jq '.metadata.vulnerabilities | .low + .moderate + .high + .critical' npm-audit.json 2>/dev/null || echo 0)
|
||||||
|
ISSUES=$((ISSUES + NPM_VULNS))
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "count=$ISSUES" >> $GITHUB_OUTPUT
|
||||||
|
echo "Found $ISSUES security issues"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Update npm Dependencies
|
||||||
|
# ============================================
|
||||||
|
update-npm:
|
||||||
|
name: "Update npm Dependencies"
|
||||||
|
needs: analyze
|
||||||
|
if: needs.analyze.outputs.has_npm == 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: npm ci --ignore-scripts 2>/dev/null || npm install --ignore-scripts
|
||||||
|
|
||||||
|
- name: Update Based on Type
|
||||||
|
id: update
|
||||||
|
run: |
|
||||||
|
UPDATE_TYPE="${{ github.event.inputs.update_type || 'safe' }}"
|
||||||
|
|
||||||
|
case "$UPDATE_TYPE" in
|
||||||
|
safe)
|
||||||
|
# Only patch updates
|
||||||
|
npm update 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
minor)
|
||||||
|
# Minor and patch updates
|
||||||
|
npx npm-check-updates -u --target minor 2>/dev/null || npm update
|
||||||
|
npm install
|
||||||
|
;;
|
||||||
|
major)
|
||||||
|
# All updates (risky)
|
||||||
|
npx npm-check-updates -u 2>/dev/null || true
|
||||||
|
npm install
|
||||||
|
;;
|
||||||
|
security)
|
||||||
|
# Security updates only
|
||||||
|
npm audit fix 2>/dev/null || true
|
||||||
|
npm audit fix --force 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Check what changed
|
||||||
|
if [ -n "$(git status --porcelain package.json package-lock.json)" ]; then
|
||||||
|
echo "changes=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "changes=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
if: steps.update.outputs.changes == 'true'
|
||||||
|
id: test
|
||||||
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
npm test 2>&1 && echo "result=passed" >> $GITHUB_OUTPUT || echo "result=failed" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create PR
|
||||||
|
if: steps.update.outputs.changes == 'true' && steps.test.outputs.result != 'failed'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
BRANCH="deps/npm-update-$(date +%Y%m%d)"
|
||||||
|
git config user.name "BlackRoad Dependency Bot"
|
||||||
|
git config user.email "deps@blackroad.ai"
|
||||||
|
|
||||||
|
# Check if branch already exists
|
||||||
|
if git ls-remote --exit-code origin "$BRANCH" 2>/dev/null; then
|
||||||
|
echo "Branch already exists, updating..."
|
||||||
|
git fetch origin "$BRANCH"
|
||||||
|
git checkout "$BRANCH"
|
||||||
|
git merge main --no-edit || true
|
||||||
|
else
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git add package.json package-lock.json
|
||||||
|
git commit -m "chore(deps): Update npm dependencies
|
||||||
|
|
||||||
|
Update type: ${{ github.event.inputs.update_type || 'safe' }}
|
||||||
|
Tests: ${{ steps.test.outputs.result || 'not run' }}
|
||||||
|
|
||||||
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>" || true
|
||||||
|
|
||||||
|
git push -u origin "$BRANCH" --force
|
||||||
|
|
||||||
|
# Check if PR already exists
|
||||||
|
EXISTING_PR=$(gh pr list --head "$BRANCH" --json number -q '.[0].number')
|
||||||
|
if [ -z "$EXISTING_PR" ]; then
|
||||||
|
gh pr create \
|
||||||
|
--title "chore(deps): Weekly npm dependency updates" \
|
||||||
|
--body "## Dependency Updates
|
||||||
|
|
||||||
|
**Update Type:** ${{ github.event.inputs.update_type || 'safe' }}
|
||||||
|
**Test Status:** ${{ steps.test.outputs.result || 'not run' }}
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
Updated npm dependencies according to the configured update strategy.
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
- [ ] Tests pass
|
||||||
|
- [ ] Build succeeds
|
||||||
|
- [ ] No breaking changes
|
||||||
|
|
||||||
|
---
|
||||||
|
*Automated by BlackRoad Dependency Manager*" \
|
||||||
|
--label "dependencies,automated"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Update Python Dependencies
|
||||||
|
# ============================================
|
||||||
|
update-python:
|
||||||
|
name: "Update Python Dependencies"
|
||||||
|
needs: analyze
|
||||||
|
if: needs.analyze.outputs.has_python == 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Update Dependencies
|
||||||
|
id: update
|
||||||
|
run: |
|
||||||
|
pip install pip-tools safety 2>/dev/null || true
|
||||||
|
|
||||||
|
if [ -f "requirements.txt" ]; then
|
||||||
|
# Backup original
|
||||||
|
cp requirements.txt requirements.txt.bak
|
||||||
|
|
||||||
|
# Update all packages
|
||||||
|
pip install --upgrade $(cat requirements.txt | grep -v "^#" | cut -d'=' -f1 | tr '\n' ' ') 2>/dev/null || true
|
||||||
|
|
||||||
|
# Regenerate requirements with updated versions
|
||||||
|
pip freeze > requirements.txt.new
|
||||||
|
|
||||||
|
# Check for changes
|
||||||
|
if ! diff -q requirements.txt requirements.txt.new > /dev/null 2>&1; then
|
||||||
|
mv requirements.txt.new requirements.txt
|
||||||
|
echo "changes=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "changes=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
if: steps.update.outputs.changes == 'true'
|
||||||
|
id: test
|
||||||
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pytest 2>&1 && echo "result=passed" >> $GITHUB_OUTPUT || \
|
||||||
|
python -m unittest discover 2>&1 && echo "result=passed" >> $GITHUB_OUTPUT || \
|
||||||
|
echo "result=skipped" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create PR
|
||||||
|
if: steps.update.outputs.changes == 'true'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
BRANCH="deps/python-update-$(date +%Y%m%d)"
|
||||||
|
git config user.name "BlackRoad Dependency Bot"
|
||||||
|
git config user.email "deps@blackroad.ai"
|
||||||
|
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
git add requirements.txt
|
||||||
|
git commit -m "chore(deps): Update Python dependencies
|
||||||
|
|
||||||
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>"
|
||||||
|
git push -u origin "$BRANCH"
|
||||||
|
|
||||||
|
gh pr create \
|
||||||
|
--title "chore(deps): Weekly Python dependency updates" \
|
||||||
|
--body "## Dependency Updates
|
||||||
|
|
||||||
|
Updated Python dependencies.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Automated by BlackRoad Dependency Manager*" \
|
||||||
|
--label "dependencies,automated"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Report Summary
|
||||||
|
# ============================================
|
||||||
|
report:
|
||||||
|
name: "Generate Report"
|
||||||
|
needs: [analyze, update-npm, update-python]
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Create Summary
|
||||||
|
run: |
|
||||||
|
echo "## Dependency Update Summary"
|
||||||
|
echo ""
|
||||||
|
echo "| Package Manager | Outdated | Security Issues |"
|
||||||
|
echo "|-----------------|----------|-----------------|"
|
||||||
|
echo "| npm | ${{ needs.analyze.outputs.outdated_count || 'N/A' }} | ${{ needs.analyze.outputs.security_issues || 'N/A' }} |"
|
||||||
|
|
||||||
|
- name: Log to Memory
|
||||||
|
run: |
|
||||||
|
curl -s -X POST "${{ env.BLACKROAD_AGENT_API }}/memory" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"repo": "${{ github.repository }}",
|
||||||
|
"event": "dependency_update",
|
||||||
|
"outdated_count": "${{ needs.analyze.outputs.outdated_count }}",
|
||||||
|
"security_issues": "${{ needs.analyze.outputs.security_issues }}",
|
||||||
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
||||||
|
}' 2>/dev/null || true
|
||||||
399
.github/workflows/autonomous-issue-manager.yml
vendored
Normal file
399
.github/workflows/autonomous-issue-manager.yml
vendored
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
# .github/workflows/autonomous-issue-manager.yml
|
||||||
|
# Autonomous issue creation, triage, and management
|
||||||
|
|
||||||
|
name: "Autonomous Issue Manager"
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened, edited, labeled, assigned]
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 9 * * *' # Daily at 9 AM - stale check
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Autonomous Orchestrator", "Autonomous Self-Healer"]
|
||||||
|
types: [completed]
|
||||||
|
conclusions: [failure]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
action:
|
||||||
|
description: 'Action to perform'
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- triage_all
|
||||||
|
- cleanup_stale
|
||||||
|
- generate_report
|
||||||
|
- create_health_issues
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
BLACKROAD_AGENT_API: https://blackroad-agents.amundsonalexa.workers.dev
|
||||||
|
STALE_DAYS: 30
|
||||||
|
CLOSE_DAYS: 7
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ============================================
|
||||||
|
# Smart Issue Triage
|
||||||
|
# ============================================
|
||||||
|
triage:
|
||||||
|
name: "Smart Triage"
|
||||||
|
if: github.event_name == 'issues' && github.event.action == 'opened'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: AI Analysis
|
||||||
|
id: ai
|
||||||
|
run: |
|
||||||
|
TITLE="${{ github.event.issue.title }}"
|
||||||
|
BODY="${{ github.event.issue.body }}"
|
||||||
|
|
||||||
|
# Call AI for smart categorization
|
||||||
|
ANALYSIS=$(curl -s -X POST "${{ env.BLACKROAD_AGENT_API }}/analyze-issue" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"title": "'"$TITLE"'",
|
||||||
|
"body": "'"$(echo "$BODY" | head -c 2000 | jq -Rs .)"'",
|
||||||
|
"repo": "${{ github.repository }}"
|
||||||
|
}' 2>/dev/null || echo '{}')
|
||||||
|
|
||||||
|
echo "analysis=$ANALYSIS" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Parse AI response for labels
|
||||||
|
LABELS=$(echo "$ANALYSIS" | jq -r '.labels // [] | join(",")' 2>/dev/null || echo "")
|
||||||
|
PRIORITY=$(echo "$ANALYSIS" | jq -r '.priority // "normal"' 2>/dev/null || echo "normal")
|
||||||
|
ASSIGNEE=$(echo "$ANALYSIS" | jq -r '.assignee // ""' 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
echo "labels=$LABELS" >> $GITHUB_OUTPUT
|
||||||
|
echo "priority=$PRIORITY" >> $GITHUB_OUTPUT
|
||||||
|
echo "assignee=$ASSIGNEE" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Keyword-Based Labeling
|
||||||
|
id: keywords
|
||||||
|
run: |
|
||||||
|
TITLE="${{ github.event.issue.title }}"
|
||||||
|
BODY="${{ github.event.issue.body }}"
|
||||||
|
TEXT="$TITLE $BODY"
|
||||||
|
LABELS=""
|
||||||
|
|
||||||
|
# Type detection
|
||||||
|
echo "$TEXT" | grep -qi "bug\|error\|broken\|not working\|crash\|fail" && LABELS="$LABELS,bug"
|
||||||
|
echo "$TEXT" | grep -qi "feature\|add\|new\|enhance\|request" && LABELS="$LABELS,enhancement"
|
||||||
|
echo "$TEXT" | grep -qi "question\|how\|help\|what\|why" && LABELS="$LABELS,question"
|
||||||
|
echo "$TEXT" | grep -qi "doc\|documentation\|readme\|typo" && LABELS="$LABELS,documentation"
|
||||||
|
|
||||||
|
# Area detection
|
||||||
|
echo "$TEXT" | grep -qi "security\|vulnerability\|cve\|auth" && LABELS="$LABELS,security"
|
||||||
|
echo "$TEXT" | grep -qi "performance\|slow\|memory\|cpu" && LABELS="$LABELS,performance"
|
||||||
|
echo "$TEXT" | grep -qi "ui\|frontend\|css\|style\|design" && LABELS="$LABELS,frontend"
|
||||||
|
echo "$TEXT" | grep -qi "api\|backend\|server\|database" && LABELS="$LABELS,backend"
|
||||||
|
echo "$TEXT" | grep -qi "ci\|deploy\|workflow\|action" && LABELS="$LABELS,infrastructure"
|
||||||
|
|
||||||
|
# Priority detection
|
||||||
|
echo "$TEXT" | grep -qi "urgent\|critical\|asap\|important\|blocker" && LABELS="$LABELS,priority:high"
|
||||||
|
echo "$TEXT" | grep -qi "minor\|low\|when possible" && LABELS="$LABELS,priority:low"
|
||||||
|
|
||||||
|
# Clean up labels
|
||||||
|
LABELS=$(echo "$LABELS" | sed 's/^,//' | sed 's/,,/,/g')
|
||||||
|
echo "labels=$LABELS" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Apply Labels
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const aiLabels = '${{ steps.ai.outputs.labels }}'.split(',').filter(l => l);
|
||||||
|
const keywordLabels = '${{ steps.keywords.outputs.labels }}'.split(',').filter(l => l);
|
||||||
|
|
||||||
|
// Merge and dedupe labels
|
||||||
|
const allLabels = [...new Set([...aiLabels, ...keywordLabels])].filter(l => l);
|
||||||
|
|
||||||
|
if (allLabels.length > 0) {
|
||||||
|
// Ensure labels exist (create if not)
|
||||||
|
for (const label of allLabels) {
|
||||||
|
try {
|
||||||
|
await github.rest.issues.getLabel({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
name: label
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// Label doesn't exist, create it
|
||||||
|
const colors = {
|
||||||
|
'bug': 'd73a4a',
|
||||||
|
'enhancement': 'a2eeef',
|
||||||
|
'question': 'd876e3',
|
||||||
|
'documentation': '0075ca',
|
||||||
|
'security': 'b60205',
|
||||||
|
'performance': 'fbca04',
|
||||||
|
'frontend': '7057ff',
|
||||||
|
'backend': '008672',
|
||||||
|
'infrastructure': 'c5def5',
|
||||||
|
'priority:high': 'b60205',
|
||||||
|
'priority:low': 'c2e0c6'
|
||||||
|
};
|
||||||
|
|
||||||
|
await github.rest.issues.createLabel({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
name: label,
|
||||||
|
color: colors[label] || '333333'
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await github.rest.issues.addLabels({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.payload.issue.number,
|
||||||
|
labels: allLabels
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Welcome Response
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const labels = '${{ steps.keywords.outputs.labels }}'.split(',').filter(l => l);
|
||||||
|
const priority = '${{ steps.ai.outputs.priority }}';
|
||||||
|
|
||||||
|
let response = `Thanks for opening this issue! 👋\n\n`;
|
||||||
|
|
||||||
|
// Add context based on type
|
||||||
|
if (labels.includes('bug')) {
|
||||||
|
response += `This has been identified as a **bug report**. `;
|
||||||
|
response += `To help us investigate:\n`;
|
||||||
|
response += `- What version are you using?\n`;
|
||||||
|
response += `- Can you provide steps to reproduce?\n`;
|
||||||
|
response += `- Any error messages or logs?\n\n`;
|
||||||
|
} else if (labels.includes('enhancement')) {
|
||||||
|
response += `This has been identified as a **feature request**. `;
|
||||||
|
response += `We'll review and prioritize accordingly.\n\n`;
|
||||||
|
} else if (labels.includes('question')) {
|
||||||
|
response += `This has been identified as a **question**. `;
|
||||||
|
response += `Check our [documentation](https://docs.blackroad.io) while you wait for a response.\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priority === 'high') {
|
||||||
|
response += `⚠️ **High priority** - This will be reviewed soon.\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
response += `**Automated Labels Applied:** ${labels.length > 0 ? labels.map(l => '`' + l + '`').join(', ') : 'None'}\n\n`;
|
||||||
|
response += `---\n*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
|
||||||
|
});
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Stale Issue Cleanup
|
||||||
|
# ============================================
|
||||||
|
stale-cleanup:
|
||||||
|
name: "Stale Cleanup"
|
||||||
|
if: github.event_name == 'schedule' || github.event.inputs.action == 'cleanup_stale'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Find Stale Issues
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const staleDays = parseInt('${{ env.STALE_DAYS }}');
|
||||||
|
const closeDays = parseInt('${{ env.CLOSE_DAYS }}');
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
// Get open issues
|
||||||
|
const issues = await github.rest.issues.listForRepo({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'open',
|
||||||
|
per_page: 100
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const issue of issues.data) {
|
||||||
|
// Skip PRs
|
||||||
|
if (issue.pull_request) continue;
|
||||||
|
|
||||||
|
const updatedAt = new Date(issue.updated_at);
|
||||||
|
const daysSinceUpdate = Math.floor((now - updatedAt) / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
const hasStaleLabel = issue.labels.some(l => l.name === 'stale');
|
||||||
|
const isProtected = issue.labels.some(l =>
|
||||||
|
['pinned', 'security', 'priority:high', 'in-progress'].includes(l.name)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isProtected) continue;
|
||||||
|
|
||||||
|
// Already marked stale - check if should close
|
||||||
|
if (hasStaleLabel && daysSinceUpdate >= closeDays) {
|
||||||
|
await github.rest.issues.update({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
state: 'closed',
|
||||||
|
state_reason: 'not_planned'
|
||||||
|
});
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
body: `This issue has been automatically closed due to inactivity.\n\nIf this is still relevant, please reopen it with additional context.\n\n---\n*Closed by BlackRoad Autonomous Agent*`
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Closed stale issue #${issue.number}`);
|
||||||
|
}
|
||||||
|
// Mark as stale
|
||||||
|
else if (!hasStaleLabel && daysSinceUpdate >= staleDays) {
|
||||||
|
await github.rest.issues.addLabels({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
labels: ['stale']
|
||||||
|
});
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
body: `This issue has been automatically marked as **stale** because it has not had recent activity.\n\nIt will be closed in ${closeDays} days if no further activity occurs.\n\n---\n*Marked by BlackRoad Autonomous Agent*`
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Marked issue #${issue.number} as stale`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Auto-Create Issues from Failures
|
||||||
|
# ============================================
|
||||||
|
failure-issue:
|
||||||
|
name: "Create Failure Issue"
|
||||||
|
if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check for Existing Issue
|
||||||
|
id: check
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
// Search for existing issue about this workflow
|
||||||
|
const workflowName = '${{ github.event.workflow_run.name }}';
|
||||||
|
const searchQuery = `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open "[Automated] ${workflowName}" in:title`;
|
||||||
|
|
||||||
|
const results = await github.rest.search.issuesAndPullRequests({
|
||||||
|
q: searchQuery
|
||||||
|
});
|
||||||
|
|
||||||
|
core.setOutput('exists', results.data.total_count > 0);
|
||||||
|
if (results.data.total_count > 0) {
|
||||||
|
core.setOutput('issue_number', results.data.items[0].number);
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Create or Update Issue
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const workflowName = '${{ github.event.workflow_run.name }}';
|
||||||
|
const runId = '${{ github.event.workflow_run.id }}';
|
||||||
|
const runUrl = '${{ github.event.workflow_run.html_url }}';
|
||||||
|
const exists = '${{ steps.check.outputs.exists }}' === 'true';
|
||||||
|
const existingNumber = '${{ steps.check.outputs.issue_number }}';
|
||||||
|
|
||||||
|
const body = `## Workflow Failure Detected
|
||||||
|
|
||||||
|
**Workflow:** ${workflowName}
|
||||||
|
**Run ID:** ${runId}
|
||||||
|
**Run URL:** ${runUrl}
|
||||||
|
**Time:** ${new Date().toISOString()}
|
||||||
|
|
||||||
|
### Details
|
||||||
|
The autonomous orchestrator detected a failure in the ${workflowName} workflow.
|
||||||
|
|
||||||
|
### Suggested Actions
|
||||||
|
1. Review the [workflow run logs](${runUrl})
|
||||||
|
2. Check recent commits for potential causes
|
||||||
|
3. Run the self-healer workflow if appropriate
|
||||||
|
|
||||||
|
---
|
||||||
|
*Created by BlackRoad Autonomous Agent*`;
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
// Add comment to existing issue
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: parseInt(existingNumber),
|
||||||
|
body: `### New Failure Detected\n\n**Run:** ${runUrl}\n**Time:** ${new Date().toISOString()}`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Create new issue
|
||||||
|
await github.rest.issues.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: `[Automated] ${workflowName} Workflow Failure`,
|
||||||
|
body: body,
|
||||||
|
labels: ['bug', 'automated', 'ci-failure']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Generate Report
|
||||||
|
# ============================================
|
||||||
|
report:
|
||||||
|
name: "Generate Issue Report"
|
||||||
|
if: github.event.inputs.action == 'generate_report'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Generate Statistics
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
// Get all issues
|
||||||
|
const issues = await github.rest.issues.listForRepo({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'all',
|
||||||
|
per_page: 100
|
||||||
|
});
|
||||||
|
|
||||||
|
const stats = {
|
||||||
|
total: issues.data.length,
|
||||||
|
open: issues.data.filter(i => i.state === 'open' && !i.pull_request).length,
|
||||||
|
closed: issues.data.filter(i => i.state === 'closed' && !i.pull_request).length,
|
||||||
|
bugs: issues.data.filter(i => i.labels.some(l => l.name === 'bug')).length,
|
||||||
|
enhancements: issues.data.filter(i => i.labels.some(l => l.name === 'enhancement')).length,
|
||||||
|
stale: issues.data.filter(i => i.labels.some(l => l.name === 'stale')).length
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Issue Statistics:', stats);
|
||||||
|
|
||||||
|
// Create summary issue
|
||||||
|
await github.rest.issues.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: `[Report] Issue Statistics - ${new Date().toISOString().split('T')[0]}`,
|
||||||
|
body: `## Issue Statistics Report
|
||||||
|
|
||||||
|
| Metric | Count |
|
||||||
|
|--------|-------|
|
||||||
|
| Total Issues | ${stats.total} |
|
||||||
|
| Open | ${stats.open} |
|
||||||
|
| Closed | ${stats.closed} |
|
||||||
|
| Bugs | ${stats.bugs} |
|
||||||
|
| Enhancements | ${stats.enhancements} |
|
||||||
|
| Stale | ${stats.stale} |
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated by BlackRoad Autonomous Agent*`,
|
||||||
|
labels: ['report', 'automated']
|
||||||
|
});
|
||||||
671
.github/workflows/autonomous-orchestrator.yml
vendored
Normal file
671
.github/workflows/autonomous-orchestrator.yml
vendored
Normal file
@@ -0,0 +1,671 @@
|
|||||||
|
# .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@v4
|
||||||
|
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@v4
|
||||||
|
|
||||||
|
- 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@v4
|
||||||
|
|
||||||
|
- 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@v4
|
||||||
|
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@v7
|
||||||
|
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@v4
|
||||||
|
|
||||||
|
- 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@v7
|
||||||
|
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@v4
|
||||||
|
|
||||||
|
- 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@v7
|
||||||
|
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']
|
||||||
|
});
|
||||||
|
}
|
||||||
377
.github/workflows/autonomous-self-healer.yml
vendored
Normal file
377
.github/workflows/autonomous-self-healer.yml
vendored
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
# .github/workflows/autonomous-self-healer.yml
|
||||||
|
# Self-healing agent that automatically fixes common issues
|
||||||
|
|
||||||
|
name: "Autonomous Self-Healer"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Autonomous Orchestrator", "CI", "Test"]
|
||||||
|
types: [completed]
|
||||||
|
conclusions: [failure]
|
||||||
|
schedule:
|
||||||
|
- cron: '30 */6 * * *' # Every 6 hours, offset from orchestrator
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
fix_type:
|
||||||
|
description: 'Type of fix to attempt'
|
||||||
|
required: false
|
||||||
|
default: 'all'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- all
|
||||||
|
- tests
|
||||||
|
- build
|
||||||
|
- lint
|
||||||
|
- deps
|
||||||
|
- security
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
actions: read
|
||||||
|
checks: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
BLACKROAD_AGENT_API: https://blackroad-agents.amundsonalexa.workers.dev
|
||||||
|
MAX_FIX_ATTEMPTS: 3
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ============================================
|
||||||
|
# Diagnose the Failure
|
||||||
|
# ============================================
|
||||||
|
diagnose:
|
||||||
|
name: "Diagnose Failure"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
failure_type: ${{ steps.analyze.outputs.type }}
|
||||||
|
failure_details: ${{ steps.analyze.outputs.details }}
|
||||||
|
fixable: ${{ steps.analyze.outputs.fixable }}
|
||||||
|
fix_strategy: ${{ steps.strategy.outputs.approach }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get Failed Run Logs
|
||||||
|
id: logs
|
||||||
|
if: github.event.workflow_run.id
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
# Download workflow run logs
|
||||||
|
gh run view ${{ github.event.workflow_run.id }} --log 2>/dev/null > run_logs.txt || true
|
||||||
|
echo "logs_retrieved=true" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Analyze Failure
|
||||||
|
id: analyze
|
||||||
|
run: |
|
||||||
|
TYPE="unknown"
|
||||||
|
DETAILS=""
|
||||||
|
FIXABLE="false"
|
||||||
|
|
||||||
|
if [ -f "run_logs.txt" ]; then
|
||||||
|
# Test failures
|
||||||
|
if grep -qi "test.*fail\|jest.*fail\|pytest.*fail\|assertion.*error" run_logs.txt; then
|
||||||
|
TYPE="test_failure"
|
||||||
|
DETAILS=$(grep -i "fail\|error" run_logs.txt | head -10)
|
||||||
|
FIXABLE="maybe"
|
||||||
|
# Build failures
|
||||||
|
elif grep -qi "build.*fail\|compile.*error\|typescript.*error" run_logs.txt; then
|
||||||
|
TYPE="build_failure"
|
||||||
|
DETAILS=$(grep -i "error" run_logs.txt | head -10)
|
||||||
|
FIXABLE="maybe"
|
||||||
|
# Lint failures
|
||||||
|
elif grep -qi "lint.*error\|eslint.*error\|prettier" run_logs.txt; then
|
||||||
|
TYPE="lint_failure"
|
||||||
|
FIXABLE="true"
|
||||||
|
# Dependency failures
|
||||||
|
elif grep -qi "npm.*err\|pip.*error\|dependency.*not found\|module.*not found" run_logs.txt; then
|
||||||
|
TYPE="dependency_failure"
|
||||||
|
DETAILS=$(grep -i "not found\|missing" run_logs.txt | head -5)
|
||||||
|
FIXABLE="true"
|
||||||
|
# Security failures
|
||||||
|
elif grep -qi "vulnerability\|security\|cve-" run_logs.txt; then
|
||||||
|
TYPE="security_failure"
|
||||||
|
FIXABLE="true"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "type=$TYPE" >> $GITHUB_OUTPUT
|
||||||
|
echo "details<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "$DETAILS" >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "fixable=$FIXABLE" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
echo "Diagnosed: $TYPE (fixable=$FIXABLE)"
|
||||||
|
|
||||||
|
- name: Determine Fix Strategy
|
||||||
|
id: strategy
|
||||||
|
run: |
|
||||||
|
APPROACH="manual"
|
||||||
|
|
||||||
|
case "${{ steps.analyze.outputs.type }}" in
|
||||||
|
lint_failure)
|
||||||
|
APPROACH="auto_lint_fix"
|
||||||
|
;;
|
||||||
|
dependency_failure)
|
||||||
|
APPROACH="reinstall_deps"
|
||||||
|
;;
|
||||||
|
security_failure)
|
||||||
|
APPROACH="security_patch"
|
||||||
|
;;
|
||||||
|
test_failure)
|
||||||
|
APPROACH="ai_assisted_fix"
|
||||||
|
;;
|
||||||
|
build_failure)
|
||||||
|
APPROACH="ai_assisted_fix"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "approach=$APPROACH" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Auto-Fix: Lint Issues
|
||||||
|
# ============================================
|
||||||
|
fix-lint:
|
||||||
|
name: "Fix Lint Issues"
|
||||||
|
needs: diagnose
|
||||||
|
if: needs.diagnose.outputs.fix_strategy == 'auto_lint_fix'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.workflow_run.head_branch || github.ref }}
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: npm ci --ignore-scripts 2>/dev/null || npm install --ignore-scripts
|
||||||
|
|
||||||
|
- name: Run Lint Fix
|
||||||
|
run: |
|
||||||
|
# Try multiple linting tools
|
||||||
|
npm run lint:fix 2>/dev/null || \
|
||||||
|
npx eslint . --fix 2>/dev/null || \
|
||||||
|
npx prettier --write . 2>/dev/null || \
|
||||||
|
echo "No lint fix available"
|
||||||
|
|
||||||
|
- name: Commit Fixes
|
||||||
|
run: |
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
git config user.name "BlackRoad Self-Healer"
|
||||||
|
git config user.email "healer@blackroad.ai"
|
||||||
|
git add -A
|
||||||
|
git commit -m "fix(lint): Auto-fix linting issues
|
||||||
|
|
||||||
|
Automated lint fixes applied by BlackRoad Self-Healing Agent.
|
||||||
|
|
||||||
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>"
|
||||||
|
git push
|
||||||
|
echo "Lint fixes committed successfully"
|
||||||
|
else
|
||||||
|
echo "No lint issues to fix"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Auto-Fix: Dependencies
|
||||||
|
# ============================================
|
||||||
|
fix-deps:
|
||||||
|
name: "Fix Dependencies"
|
||||||
|
needs: diagnose
|
||||||
|
if: needs.diagnose.outputs.fix_strategy == 'reinstall_deps'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.workflow_run.head_branch || github.ref }}
|
||||||
|
|
||||||
|
- name: Fix Node Dependencies
|
||||||
|
if: hashFiles('package.json') != ''
|
||||||
|
run: |
|
||||||
|
# Remove node_modules and lock file, reinstall
|
||||||
|
rm -rf node_modules package-lock.json 2>/dev/null || true
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Dedupe and fix
|
||||||
|
npm dedupe 2>/dev/null || true
|
||||||
|
npm audit fix 2>/dev/null || true
|
||||||
|
|
||||||
|
- name: Fix Python Dependencies
|
||||||
|
if: hashFiles('requirements.txt') != '' || hashFiles('pyproject.toml') != ''
|
||||||
|
run: |
|
||||||
|
pip install --upgrade pip
|
||||||
|
[ -f "requirements.txt" ] && pip install -r requirements.txt
|
||||||
|
[ -f "pyproject.toml" ] && pip install -e .
|
||||||
|
|
||||||
|
- name: Commit Fixes
|
||||||
|
run: |
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
git config user.name "BlackRoad Self-Healer"
|
||||||
|
git config user.email "healer@blackroad.ai"
|
||||||
|
git add -A
|
||||||
|
git commit -m "fix(deps): Reinstall and fix dependencies
|
||||||
|
|
||||||
|
Dependency issues resolved by BlackRoad Self-Healing Agent.
|
||||||
|
|
||||||
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>"
|
||||||
|
git push
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Auto-Fix: Security Issues
|
||||||
|
# ============================================
|
||||||
|
fix-security:
|
||||||
|
name: "Fix Security Issues"
|
||||||
|
needs: diagnose
|
||||||
|
if: needs.diagnose.outputs.fix_strategy == 'security_patch'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.workflow_run.head_branch || github.ref }}
|
||||||
|
|
||||||
|
- name: Fix npm Security Issues
|
||||||
|
if: hashFiles('package.json') != ''
|
||||||
|
run: |
|
||||||
|
npm audit fix 2>/dev/null || true
|
||||||
|
npm audit fix --force 2>/dev/null || true
|
||||||
|
|
||||||
|
- name: Fix Python Security Issues
|
||||||
|
if: hashFiles('requirements.txt') != ''
|
||||||
|
run: |
|
||||||
|
pip install safety pip-audit 2>/dev/null || true
|
||||||
|
pip-audit --fix 2>/dev/null || true
|
||||||
|
|
||||||
|
- name: Create Security PR
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
BRANCH="security-fix-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
git config user.name "BlackRoad Self-Healer"
|
||||||
|
git config user.email "healer@blackroad.ai"
|
||||||
|
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
git add -A
|
||||||
|
git commit -m "fix(security): Auto-patch security vulnerabilities
|
||||||
|
|
||||||
|
Security vulnerabilities patched by BlackRoad Self-Healing Agent.
|
||||||
|
|
||||||
|
Co-Authored-By: BlackRoad Bot <bot@blackroad.ai>"
|
||||||
|
git push -u origin "$BRANCH"
|
||||||
|
|
||||||
|
gh pr create \
|
||||||
|
--title "fix(security): Auto-patch security vulnerabilities" \
|
||||||
|
--body "## Security Patch
|
||||||
|
|
||||||
|
This PR was automatically generated by the BlackRoad Self-Healing Agent.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- Applied security patches via npm audit fix / pip-audit
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
- Automated tests will verify compatibility
|
||||||
|
- Please review before merging
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated by BlackRoad Autonomous Agent*" \
|
||||||
|
--label "security,automated"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# AI-Assisted Fix
|
||||||
|
# ============================================
|
||||||
|
ai-fix:
|
||||||
|
name: "AI-Assisted Fix"
|
||||||
|
needs: diagnose
|
||||||
|
if: needs.diagnose.outputs.fix_strategy == 'ai_assisted_fix'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.workflow_run.head_branch || github.ref }}
|
||||||
|
|
||||||
|
- name: Request AI Fix
|
||||||
|
id: ai
|
||||||
|
run: |
|
||||||
|
# Send failure details to AI for analysis and fix
|
||||||
|
RESPONSE=$(curl -s -X POST "${{ env.BLACKROAD_AGENT_API }}/fix" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"repo": "${{ github.repository }}",
|
||||||
|
"failure_type": "${{ needs.diagnose.outputs.failure_type }}",
|
||||||
|
"details": ${{ toJSON(needs.diagnose.outputs.failure_details) }},
|
||||||
|
"run_id": "${{ github.event.workflow_run.id }}"
|
||||||
|
}' 2>/dev/null || echo '{"status":"queued"}')
|
||||||
|
|
||||||
|
echo "AI Response: $RESPONSE"
|
||||||
|
echo "response=$RESPONSE" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create Issue for Manual Review
|
||||||
|
if: needs.diagnose.outputs.fixable == 'maybe'
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const failureType = '${{ needs.diagnose.outputs.failure_type }}';
|
||||||
|
const details = `${{ needs.diagnose.outputs.failure_details }}`;
|
||||||
|
|
||||||
|
await github.rest.issues.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: `[Self-Healer] ${failureType.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())} Needs Review`,
|
||||||
|
body: `## Automated Failure Analysis
|
||||||
|
|
||||||
|
**Failure Type:** ${failureType}
|
||||||
|
**Run ID:** ${{ github.event.workflow_run.id || 'N/A' }}
|
||||||
|
|
||||||
|
### Error Details
|
||||||
|
\`\`\`
|
||||||
|
${details.substring(0, 2000)}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### AI Analysis
|
||||||
|
The self-healing agent attempted to analyze this issue but requires human review.
|
||||||
|
|
||||||
|
### Suggested Actions
|
||||||
|
1. Review the error logs above
|
||||||
|
2. Check recent changes that may have caused this
|
||||||
|
3. Apply appropriate fix
|
||||||
|
|
||||||
|
---
|
||||||
|
*Created by BlackRoad Self-Healing Agent*`,
|
||||||
|
labels: ['bug', 'automated', 'needs-triage']
|
||||||
|
});
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Report Results
|
||||||
|
# ============================================
|
||||||
|
report:
|
||||||
|
name: "Report Results"
|
||||||
|
needs: [diagnose, fix-lint, fix-deps, fix-security, ai-fix]
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Summarize Healing Attempt
|
||||||
|
run: |
|
||||||
|
echo "## Self-Healing Summary"
|
||||||
|
echo "Failure Type: ${{ needs.diagnose.outputs.failure_type }}"
|
||||||
|
echo "Fix Strategy: ${{ needs.diagnose.outputs.fix_strategy }}"
|
||||||
|
echo "Fixable: ${{ needs.diagnose.outputs.fixable }}"
|
||||||
|
|
||||||
|
# Log to memory
|
||||||
|
curl -s -X POST "${{ env.BLACKROAD_AGENT_API }}/memory" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"repo": "${{ github.repository }}",
|
||||||
|
"event": "self_heal_attempt",
|
||||||
|
"failure_type": "${{ needs.diagnose.outputs.failure_type }}",
|
||||||
|
"strategy": "${{ needs.diagnose.outputs.fix_strategy }}",
|
||||||
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
||||||
|
}' 2>/dev/null || true
|
||||||
Reference in New Issue
Block a user