Initial commit
This commit is contained in:
265
bot-docs-update-with-canva.yml
Normal file
265
bot-docs-update-with-canva.yml
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
name: Bot - Documentation Update with Canva
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- 'lib/**'
|
||||||
|
- '*.md'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 1' # Weekly on Monday
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-docs-with-visuals:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Install Canva integration
|
||||||
|
run: |
|
||||||
|
pip install requests
|
||||||
|
|
||||||
|
- name: Generate documentation
|
||||||
|
id: gen_docs
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Extract functions and create docs
|
||||||
|
function extractDocs(filePath) {
|
||||||
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const docRegex = /\/\*\*([\s\S]*?)\*\/\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)/g;
|
||||||
|
const docs = [];
|
||||||
|
|
||||||
|
let match;
|
||||||
|
while ((match = docRegex.exec(content)) !== null) {
|
||||||
|
docs.push({
|
||||||
|
name: match[2],
|
||||||
|
doc: match[1].trim(),
|
||||||
|
file: filePath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanForFunctions(dir) {
|
||||||
|
const docs = [];
|
||||||
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
|
||||||
|
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
docs.push(...scanForFunctions(fullPath));
|
||||||
|
} else if (entry.name.endsWith('.js') || entry.name.endsWith('.ts')) {
|
||||||
|
docs.push(...extractDocs(fullPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync('src')) {
|
||||||
|
const docs = scanForFunctions('src');
|
||||||
|
|
||||||
|
if (docs.length > 0) {
|
||||||
|
let apiDoc = '# API Documentation\n\n';
|
||||||
|
apiDoc += '> Auto-generated from source code\n\n';
|
||||||
|
|
||||||
|
// Create diagram request for Canva
|
||||||
|
const diagramRequest = {
|
||||||
|
repository: context.repo.repo,
|
||||||
|
functions: docs.map(d => d.name),
|
||||||
|
functionCount: docs.length
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync('canva-diagram-request.json', JSON.stringify(diagramRequest, null, 2));
|
||||||
|
|
||||||
|
for (const doc of docs) {
|
||||||
|
apiDoc += `## ${doc.name}\n\n`;
|
||||||
|
apiDoc += `**Source:** \`${doc.file}\`\n\n`;
|
||||||
|
apiDoc += doc.doc.replace(/\s*\*\s*/g, '\n') + '\n\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
apiDoc += '\n---\n🤖 Generated by BlackRoad Bot System\n';
|
||||||
|
apiDoc += '🎨 Visuals by Canva Integration\n';
|
||||||
|
|
||||||
|
fs.writeFileSync('API.md', apiDoc);
|
||||||
|
|
||||||
|
core.setOutput('docs_generated', 'true');
|
||||||
|
core.setOutput('function_count', docs.length);
|
||||||
|
|
||||||
|
console.log(`Generated API documentation with ${docs.length} functions`);
|
||||||
|
} else {
|
||||||
|
core.setOutput('docs_generated', 'false');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Generate architecture diagram with Canva
|
||||||
|
if: steps.gen_docs.outputs.docs_generated == 'true'
|
||||||
|
env:
|
||||||
|
CANVA_ACCESS_TOKEN: ${{ secrets.CANVA_ACCESS_TOKEN }}
|
||||||
|
run: |
|
||||||
|
python3 << 'PYTHON_SCRIPT'
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Check if Canva integration is available
|
||||||
|
canva_client_path = os.path.expanduser('~/canva-integration/canva_client.py')
|
||||||
|
if not os.path.exists(canva_client_path):
|
||||||
|
print("⚠️ Canva integration not found")
|
||||||
|
print("Skipping diagram generation")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Add canva-integration to path
|
||||||
|
sys.path.insert(0, os.path.expanduser('~/canva-integration'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
from canva_client import CanvaClient
|
||||||
|
except ImportError:
|
||||||
|
print("⚠️ Cannot import CanvaClient")
|
||||||
|
print("Skipping diagram generation")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Load diagram request
|
||||||
|
with open('canva-diagram-request.json', 'r') as f:
|
||||||
|
request = json.load(f)
|
||||||
|
|
||||||
|
print(f"📊 Creating architecture diagram for {request['repository']}")
|
||||||
|
print(f" Functions: {request['functionCount']}")
|
||||||
|
|
||||||
|
# Initialize Canva client
|
||||||
|
access_token = os.environ.get('CANVA_ACCESS_TOKEN')
|
||||||
|
if not access_token:
|
||||||
|
print("⚠️ CANVA_ACCESS_TOKEN not set")
|
||||||
|
print("Skipping diagram generation")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
client = CanvaClient(access_token=access_token)
|
||||||
|
|
||||||
|
# Create design (architecture diagram template)
|
||||||
|
design_data = {
|
||||||
|
"design_type": "Poster",
|
||||||
|
"title": f"{request['repository']} Architecture"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
design = client.create_design(design_data)
|
||||||
|
design_id = design.get('design', {}).get('id')
|
||||||
|
|
||||||
|
print(f"✅ Created Canva design: {design_id}")
|
||||||
|
|
||||||
|
# Export as PNG
|
||||||
|
export_data = {
|
||||||
|
"format": "png"
|
||||||
|
}
|
||||||
|
|
||||||
|
export_result = client.export_design(design_id, export_data)
|
||||||
|
export_url = export_result.get('export', {}).get('url')
|
||||||
|
|
||||||
|
if export_url:
|
||||||
|
print(f"✅ Diagram exported: {export_url}")
|
||||||
|
|
||||||
|
# Download diagram
|
||||||
|
import urllib.request
|
||||||
|
urllib.request.urlretrieve(export_url, 'architecture-diagram.png')
|
||||||
|
|
||||||
|
print("✅ Diagram downloaded to architecture-diagram.png")
|
||||||
|
else:
|
||||||
|
print("⚠️ No export URL received")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Canva API error: {e}")
|
||||||
|
print("Continuing without diagram")
|
||||||
|
|
||||||
|
PYTHON_SCRIPT
|
||||||
|
|
||||||
|
- name: Update README with diagram
|
||||||
|
if: steps.gen_docs.outputs.docs_generated == 'true'
|
||||||
|
run: |
|
||||||
|
if [ -f architecture-diagram.png ]; then
|
||||||
|
# Add diagram to README
|
||||||
|
if [ -f README.md ]; then
|
||||||
|
# Check if diagram section exists
|
||||||
|
if ! grep -q "## Architecture" README.md; then
|
||||||
|
echo "" >> README.md
|
||||||
|
echo "## Architecture" >> README.md
|
||||||
|
echo "" >> README.md
|
||||||
|
echo "" >> README.md
|
||||||
|
echo "" >> README.md
|
||||||
|
echo "🎨 *Auto-generated by Canva Integration*" >> README.md
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create PR with visual docs
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const status = execSync('git status --porcelain').toString();
|
||||||
|
|
||||||
|
if (status.trim()) {
|
||||||
|
const branchName = `bot/docs-with-visuals-${Date.now()}`;
|
||||||
|
|
||||||
|
execSync(`git config user.name "BlackRoad Bot"`);
|
||||||
|
execSync(`git config user.email "bot@blackroad.io"`);
|
||||||
|
execSync(`git checkout -b ${branchName}`);
|
||||||
|
execSync(`git add .`);
|
||||||
|
execSync(`git commit -m "🤖 docs: Auto-update documentation with visual diagrams
|
||||||
|
|
||||||
|
- Generated API documentation
|
||||||
|
- Created architecture diagram via Canva
|
||||||
|
- Updated README with visuals
|
||||||
|
|
||||||
|
Generated by: BlackRoad Bot System + Canva Integration"`);
|
||||||
|
execSync(`git push origin ${branchName}`);
|
||||||
|
|
||||||
|
await github.rest.pulls.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: '🤖 🎨 docs: Auto-update documentation with visuals',
|
||||||
|
body: `## Documentation Updates with Visuals
|
||||||
|
|
||||||
|
This PR includes automated documentation updates with visual diagrams:
|
||||||
|
- ✅ API documentation generation
|
||||||
|
- ✅ Architecture diagram (Canva-generated)
|
||||||
|
- ✅ README improvements
|
||||||
|
- ✅ Visual assets
|
||||||
|
|
||||||
|
**Generated by:**
|
||||||
|
- BlackRoad Bot System (documentation)
|
||||||
|
- Canva Integration (visuals)
|
||||||
|
|
||||||
|
**Functions documented:** ${steps.gen_docs.outputs.function_count || 0}
|
||||||
|
|
||||||
|
cc: @${context.repo.owner}`,
|
||||||
|
head: branchName,
|
||||||
|
base: 'main'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Log to memory
|
||||||
|
run: |
|
||||||
|
if [ -f ~/memory-system.sh ]; then
|
||||||
|
~/memory-system.sh log bot-action "docs-with-canva" "Generated visual docs for ${{ github.repository }}: ${steps.gen_docs.outputs.function_count || 0} functions documented with Canva diagrams"
|
||||||
|
fi
|
||||||
199
bot-docs-update.yml
Normal file
199
bot-docs-update.yml
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
name: Bot - Documentation Update
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- 'lib/**'
|
||||||
|
- '*.md'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 1' # Weekly on Monday
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-docs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check README exists
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
if (!fs.existsSync('README.md')) {
|
||||||
|
const template = `# ${context.repo.repo}
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
[Add project description]
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
\`\`\`bash
|
||||||
|
npm install
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
[Add usage instructions]
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
See CONTRIBUTING.md
|
||||||
|
|
||||||
|
## License
|
||||||
|
[Add license]
|
||||||
|
|
||||||
|
---
|
||||||
|
🤖 Generated by BlackRoad Bot System
|
||||||
|
`;
|
||||||
|
|
||||||
|
fs.writeFileSync('README.md', template);
|
||||||
|
|
||||||
|
await github.rest.repos.createOrUpdateFileContents({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
path: 'README.md',
|
||||||
|
message: '🤖 docs: Add README template',
|
||||||
|
content: Buffer.from(template).toString('base64')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Generate API docs
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Simple JSDoc extraction
|
||||||
|
function extractDocs(filePath) {
|
||||||
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const docRegex = /\/\*\*([\s\S]*?)\*\/\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)/g;
|
||||||
|
const docs = [];
|
||||||
|
|
||||||
|
let match;
|
||||||
|
while ((match = docRegex.exec(content)) !== null) {
|
||||||
|
docs.push({
|
||||||
|
name: match[2],
|
||||||
|
doc: match[1].trim()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanForFunctions(dir) {
|
||||||
|
const docs = [];
|
||||||
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
|
||||||
|
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
docs.push(...scanForFunctions(fullPath));
|
||||||
|
} else if (entry.name.endsWith('.js') || entry.name.endsWith('.ts')) {
|
||||||
|
docs.push(...extractDocs(fullPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync('src')) {
|
||||||
|
const docs = scanForFunctions('src');
|
||||||
|
|
||||||
|
if (docs.length > 0) {
|
||||||
|
let apiDoc = '# API Documentation\n\n';
|
||||||
|
apiDoc += '> Auto-generated from source code\n\n';
|
||||||
|
|
||||||
|
for (const doc of docs) {
|
||||||
|
apiDoc += `## ${doc.name}\n\n`;
|
||||||
|
apiDoc += doc.doc.replace(/\s*\*\s*/g, '\n') + '\n\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
apiDoc += '\n---\n🤖 Generated by BlackRoad Bot System\n';
|
||||||
|
|
||||||
|
fs.writeFileSync('API.md', apiDoc);
|
||||||
|
|
||||||
|
console.log(`Generated API documentation with ${docs.length} functions`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Update documentation links
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
if (fs.existsSync('README.md')) {
|
||||||
|
let readme = fs.readFileSync('README.md', 'utf8');
|
||||||
|
|
||||||
|
// Add links section if missing
|
||||||
|
if (!readme.includes('## Links') && !readme.includes('## Resources')) {
|
||||||
|
const links = `
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- [Documentation](https://docs.blackroad.io)
|
||||||
|
- [API Reference](./API.md)
|
||||||
|
- [Contributing](./CONTRIBUTING.md)
|
||||||
|
- [BlackRoad OS](https://github.com/BlackRoad-OS)
|
||||||
|
`;
|
||||||
|
readme += links;
|
||||||
|
fs.writeFileSync('README.md', readme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Create PR for doc updates
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Check if we made changes
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const status = execSync('git status --porcelain').toString();
|
||||||
|
|
||||||
|
if (status.trim()) {
|
||||||
|
const branchName = `bot/docs-update-${Date.now()}`;
|
||||||
|
|
||||||
|
execSync(`git config user.name "BlackRoad Bot"`);
|
||||||
|
execSync(`git config user.email "bot@blackroad.io"`);
|
||||||
|
execSync(`git checkout -b ${branchName}`);
|
||||||
|
execSync(`git add .`);
|
||||||
|
execSync(`git commit -m "🤖 docs: Auto-update documentation"`);
|
||||||
|
execSync(`git push origin ${branchName}`);
|
||||||
|
|
||||||
|
await github.rest.pulls.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: '🤖 docs: Auto-update documentation',
|
||||||
|
body: `## Documentation Updates
|
||||||
|
|
||||||
|
This PR includes automated documentation updates:
|
||||||
|
- README improvements
|
||||||
|
- API documentation generation
|
||||||
|
- Link updates
|
||||||
|
|
||||||
|
**Generated by:** BlackRoad Bot System
|
||||||
|
**Review:** Please verify changes before merging
|
||||||
|
|
||||||
|
cc: @${context.repo.owner}`,
|
||||||
|
head: branchName,
|
||||||
|
base: 'main'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Log to memory
|
||||||
|
run: |
|
||||||
|
if [ -f ~/memory-system.sh ]; then
|
||||||
|
~/memory-system.sh log bot-action "docs-update" "Documentation updated for ${{ github.repository }}"
|
||||||
|
fi
|
||||||
122
bot-issue-triage.yml
Normal file
122
bot-issue-triage.yml
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
name: Bot - Issue Triage
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened, edited, reopened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
triage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Auto-label issue
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const issue = context.payload.issue;
|
||||||
|
const title = issue.title.toLowerCase();
|
||||||
|
const body = issue.body ? issue.body.toLowerCase() : '';
|
||||||
|
const labels = [];
|
||||||
|
|
||||||
|
// Auto-detect issue type
|
||||||
|
if (title.includes('bug') || body.includes('error') || body.includes('broken')) {
|
||||||
|
labels.push('bug');
|
||||||
|
}
|
||||||
|
if (title.includes('feature') || title.includes('enhancement')) {
|
||||||
|
labels.push('enhancement');
|
||||||
|
}
|
||||||
|
if (title.includes('docs') || title.includes('documentation')) {
|
||||||
|
labels.push('documentation');
|
||||||
|
}
|
||||||
|
if (title.includes('security') || body.includes('vulnerability')) {
|
||||||
|
labels.push('security');
|
||||||
|
}
|
||||||
|
if (title.includes('performance') || body.includes('slow')) {
|
||||||
|
labels.push('performance');
|
||||||
|
}
|
||||||
|
if (title.includes('test')) {
|
||||||
|
labels.push('testing');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority detection
|
||||||
|
if (title.includes('urgent') || title.includes('critical') || body.includes('production down')) {
|
||||||
|
labels.push('priority:high');
|
||||||
|
} else if (title.includes('minor') || title.includes('trivial')) {
|
||||||
|
labels.push('priority:low');
|
||||||
|
} else {
|
||||||
|
labels.push('priority:medium');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add labels
|
||||||
|
if (labels.length > 0) {
|
||||||
|
await github.rest.issues.addLabels({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
labels: labels
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add triage comment
|
||||||
|
const comment = `👋 Thanks for opening this issue!
|
||||||
|
|
||||||
|
🤖 **Bot Triage:**
|
||||||
|
- Auto-labeled as: ${labels.join(', ')}
|
||||||
|
- Assigned priority based on content
|
||||||
|
- A team member will review soon
|
||||||
|
|
||||||
|
This is an automated message from the BlackRoad bot system.`;
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
body: comment
|
||||||
|
});
|
||||||
|
|
||||||
|
- name: Check for duplicates
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const issue = context.payload.issue;
|
||||||
|
const title = issue.title.toLowerCase();
|
||||||
|
|
||||||
|
// Search for similar issues
|
||||||
|
const { data: issues } = await github.rest.issues.listForRepo({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'all',
|
||||||
|
per_page: 100
|
||||||
|
});
|
||||||
|
|
||||||
|
const similar = issues.filter(i =>
|
||||||
|
i.number !== issue.number &&
|
||||||
|
i.title.toLowerCase().includes(title.split(' ')[0])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (similar.length > 0) {
|
||||||
|
const duplicateList = similar.slice(0, 5).map(i => `- #${i.number}: ${i.title}`).join('\n');
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
body: `🤖 **Possible Duplicates Detected:**
|
||||||
|
|
||||||
|
${duplicateList}
|
||||||
|
|
||||||
|
Please check if any of these issues match your report.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Log to memory
|
||||||
|
run: |
|
||||||
|
if [ -f ~/memory-system.sh ]; then
|
||||||
|
~/memory-system.sh log bot-action "issue-triage" "Triaged issue #${{ github.event.issue.number }} in ${{ github.repository }}"
|
||||||
|
fi
|
||||||
171
bot-pr-review.yml
Normal file
171
bot-pr-review.yml
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
name: Bot - PR Review
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
checks: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Auto-label PR
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const pr = context.payload.pull_request;
|
||||||
|
const title = pr.title.toLowerCase();
|
||||||
|
const labels = [];
|
||||||
|
|
||||||
|
// Detect PR type from title
|
||||||
|
if (title.startsWith('feat:') || title.startsWith('feature:')) {
|
||||||
|
labels.push('feature');
|
||||||
|
}
|
||||||
|
if (title.startsWith('fix:') || title.startsWith('bugfix:')) {
|
||||||
|
labels.push('bugfix');
|
||||||
|
}
|
||||||
|
if (title.startsWith('docs:')) {
|
||||||
|
labels.push('documentation');
|
||||||
|
}
|
||||||
|
if (title.startsWith('refactor:')) {
|
||||||
|
labels.push('refactoring');
|
||||||
|
}
|
||||||
|
if (title.startsWith('test:')) {
|
||||||
|
labels.push('testing');
|
||||||
|
}
|
||||||
|
if (title.startsWith('chore:')) {
|
||||||
|
labels.push('chore');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size labels
|
||||||
|
const changedFiles = pr.changed_files;
|
||||||
|
if (changedFiles <= 5) {
|
||||||
|
labels.push('size:small');
|
||||||
|
} else if (changedFiles <= 20) {
|
||||||
|
labels.push('size:medium');
|
||||||
|
} else {
|
||||||
|
labels.push('size:large');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (labels.length > 0) {
|
||||||
|
await github.rest.issues.addLabels({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: pr.number,
|
||||||
|
labels: labels
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Check PR description
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const pr = context.payload.pull_request;
|
||||||
|
const body = pr.body || '';
|
||||||
|
|
||||||
|
if (body.length < 50) {
|
||||||
|
await github.rest.pulls.createReview({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
pull_number: pr.number,
|
||||||
|
event: 'COMMENT',
|
||||||
|
body: `🤖 **Bot Review Notice:**
|
||||||
|
|
||||||
|
⚠️ This PR has a short description. Please add:
|
||||||
|
- What does this PR do?
|
||||||
|
- Why is this change needed?
|
||||||
|
- How was it tested?
|
||||||
|
|
||||||
|
A detailed description helps reviewers understand your changes.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Analyze code changes
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const pr = context.payload.pull_request;
|
||||||
|
|
||||||
|
// Get files changed
|
||||||
|
const { data: files } = await github.rest.pulls.listFiles({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
pull_number: pr.number
|
||||||
|
});
|
||||||
|
|
||||||
|
const insights = [];
|
||||||
|
|
||||||
|
// Check for common patterns
|
||||||
|
const hasTests = files.some(f => f.filename.includes('test') || f.filename.includes('spec'));
|
||||||
|
const hasDocs = files.some(f => f.filename.endsWith('.md'));
|
||||||
|
const hasConfig = files.some(f => f.filename.includes('config') || f.filename.endsWith('.json') || f.filename.endsWith('.yml'));
|
||||||
|
|
||||||
|
if (!hasTests && files.length > 3) {
|
||||||
|
insights.push('⚠️ No test files detected. Consider adding tests for your changes.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.length > 50) {
|
||||||
|
insights.push('⚠️ Large PR detected (50+ files). Consider breaking into smaller PRs.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretPatterns = [
|
||||||
|
/api[_-]?key/i,
|
||||||
|
/secret/i,
|
||||||
|
/password/i,
|
||||||
|
/token/i,
|
||||||
|
/credential/i
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.patch) {
|
||||||
|
for (const pattern of secretPatterns) {
|
||||||
|
if (pattern.test(file.patch)) {
|
||||||
|
insights.push(`🔒 Possible secret detected in ${file.filename}. Please verify no credentials are committed.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insights.length > 0) {
|
||||||
|
await github.rest.pulls.createReview({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
pull_number: pr.number,
|
||||||
|
event: 'COMMENT',
|
||||||
|
body: `🤖 **Bot Code Review:**
|
||||||
|
|
||||||
|
${insights.join('\n\n')}
|
||||||
|
|
||||||
|
This is an automated review. A human will review your changes soon.`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await github.rest.pulls.createReview({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
pull_number: pr.number,
|
||||||
|
event: 'COMMENT',
|
||||||
|
body: `🤖 **Bot Code Review:**
|
||||||
|
|
||||||
|
✅ No issues detected in automated review
|
||||||
|
✅ Code changes look good
|
||||||
|
✅ Ready for human review
|
||||||
|
|
||||||
|
This is an automated review.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Log to memory
|
||||||
|
run: |
|
||||||
|
if [ -f ~/memory-system.sh ]; then
|
||||||
|
~/memory-system.sh log bot-action "pr-review" "Reviewed PR #${{ github.event.pull_request.number }} in ${{ github.repository }}"
|
||||||
|
fi
|
||||||
196
bot-release.yml
Normal file
196
bot-release.yml
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
name: Bot - Release Automation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
release_type:
|
||||||
|
description: 'Release type'
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- patch
|
||||||
|
- minor
|
||||||
|
- major
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Detect version bump
|
||||||
|
id: version
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
// Check for package.json
|
||||||
|
if (!fs.existsSync('package.json')) {
|
||||||
|
console.log('No package.json, skipping release');
|
||||||
|
return { shouldRelease: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||||
|
const currentVersion = pkg.version || '0.0.0';
|
||||||
|
|
||||||
|
// Get recent commits
|
||||||
|
const commits = execSync('git log --oneline -20').toString();
|
||||||
|
|
||||||
|
// Detect version bump type from commits
|
||||||
|
let bumpType = 'patch';
|
||||||
|
|
||||||
|
if (commits.match(/BREAKING CHANGE|!:/)) {
|
||||||
|
bumpType = 'major';
|
||||||
|
} else if (commits.match(/^feat:|^feature:/m)) {
|
||||||
|
bumpType = 'minor';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override with manual input if provided
|
||||||
|
if (context.payload.inputs?.release_type) {
|
||||||
|
bumpType = context.payload.inputs.release_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.setOutput('bump_type', bumpType);
|
||||||
|
core.setOutput('current_version', currentVersion);
|
||||||
|
core.setOutput('should_release', 'true');
|
||||||
|
|
||||||
|
return { shouldRelease: true, bumpType, currentVersion };
|
||||||
|
|
||||||
|
- name: Bump version
|
||||||
|
if: steps.version.outputs.should_release == 'true'
|
||||||
|
id: bump
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||||
|
const currentVersion = pkg.version || '0.0.0';
|
||||||
|
const bumpType = '${{ steps.version.outputs.bump_type }}';
|
||||||
|
|
||||||
|
// Simple semver bump
|
||||||
|
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
||||||
|
|
||||||
|
let newVersion;
|
||||||
|
if (bumpType === 'major') {
|
||||||
|
newVersion = `${major + 1}.0.0`;
|
||||||
|
} else if (bumpType === 'minor') {
|
||||||
|
newVersion = `${major}.${minor + 1}.0`;
|
||||||
|
} else {
|
||||||
|
newVersion = `${major}.${minor}.${patch + 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg.version = newVersion;
|
||||||
|
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
||||||
|
|
||||||
|
core.setOutput('new_version', newVersion);
|
||||||
|
|
||||||
|
return { newVersion };
|
||||||
|
|
||||||
|
- name: Generate changelog
|
||||||
|
if: steps.version.outputs.should_release == 'true'
|
||||||
|
id: changelog
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
// Get commits since last tag
|
||||||
|
let commits;
|
||||||
|
try {
|
||||||
|
const lastTag = execSync('git describe --tags --abbrev=0').toString().trim();
|
||||||
|
commits = execSync(`git log ${lastTag}..HEAD --oneline`).toString();
|
||||||
|
} catch {
|
||||||
|
// No previous tags
|
||||||
|
commits = execSync('git log --oneline').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = commits.split('\n').filter(l => l.trim());
|
||||||
|
|
||||||
|
const features = lines.filter(l => l.match(/feat:|feature:/i));
|
||||||
|
const fixes = lines.filter(l => l.match(/fix:|bugfix:/i));
|
||||||
|
const chores = lines.filter(l => l.match(/chore:|refactor:/i));
|
||||||
|
const docs = lines.filter(l => l.match(/docs:|documentation:/i));
|
||||||
|
|
||||||
|
let changelog = `# Changelog\n\n`;
|
||||||
|
changelog += `## Version ${{ steps.bump.outputs.new_version }}\n\n`;
|
||||||
|
|
||||||
|
if (features.length > 0) {
|
||||||
|
changelog += `### Features\n`;
|
||||||
|
features.forEach(f => changelog += `- ${f.replace(/^[a-f0-9]+\s+/, '')}\n`);
|
||||||
|
changelog += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixes.length > 0) {
|
||||||
|
changelog += `### Bug Fixes\n`;
|
||||||
|
fixes.forEach(f => changelog += `- ${f.replace(/^[a-f0-9]+\s+/, '')}\n`);
|
||||||
|
changelog += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (docs.length > 0) {
|
||||||
|
changelog += `### Documentation\n`;
|
||||||
|
docs.forEach(d => changelog += `- ${d.replace(/^[a-f0-9]+\s+/, '')}\n`);
|
||||||
|
changelog += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chores.length > 0) {
|
||||||
|
changelog += `### Other Changes\n`;
|
||||||
|
chores.forEach(c => changelog += `- ${c.replace(/^[a-f0-9]+\s+/, '')}\n`);
|
||||||
|
changelog += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
changelog += `---\n🤖 Generated by BlackRoad Bot System\n`;
|
||||||
|
|
||||||
|
core.setOutput('changelog', changelog);
|
||||||
|
|
||||||
|
return { changelog };
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
if: steps.version.outputs.should_release == 'true'
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
const newVersion = '${{ steps.bump.outputs.new_version }}';
|
||||||
|
const changelog = `${{ steps.changelog.outputs.changelog }}`;
|
||||||
|
|
||||||
|
// Commit version bump
|
||||||
|
execSync(`git config user.name "BlackRoad Bot"`);
|
||||||
|
execSync(`git config user.email "bot@blackroad.io"`);
|
||||||
|
execSync(`git add package.json`);
|
||||||
|
execSync(`git commit -m "🤖 chore: Bump version to ${newVersion}"`);
|
||||||
|
|
||||||
|
// Create tag
|
||||||
|
execSync(`git tag -a v${newVersion} -m "Release v${newVersion}"`);
|
||||||
|
execSync(`git push origin main --tags`);
|
||||||
|
|
||||||
|
// Create GitHub release
|
||||||
|
await github.rest.repos.createRelease({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
tag_name: `v${newVersion}`,
|
||||||
|
name: `Release v${newVersion}`,
|
||||||
|
body: changelog,
|
||||||
|
draft: false,
|
||||||
|
prerelease: false
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`✅ Released v${newVersion}`);
|
||||||
|
|
||||||
|
- name: Log to memory
|
||||||
|
run: |
|
||||||
|
if [ -f ~/memory-system.sh ]; then
|
||||||
|
~/memory-system.sh log bot-action "release" "Released version ${{ steps.bump.outputs.new_version }} for ${{ github.repository }}"
|
||||||
|
fi
|
||||||
149
bot-security-scan.yml
Normal file
149
bot-security-scan.yml
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
name: Bot - Security Scan
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master, develop]
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master, develop]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 0' # Weekly on Sunday
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
security-scan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Secret scanning
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const secretPatterns = [
|
||||||
|
{ name: 'API Key', pattern: /(?:api[_-]?key|apikey)[\s:=]+['"]?([a-zA-Z0-9_-]{20,})['"]?/gi },
|
||||||
|
{ name: 'Password', pattern: /(?:password|passwd|pwd)[\s:=]+['"]?([^\s'"]{8,})['"]?/gi },
|
||||||
|
{ name: 'Token', pattern: /(?:token|auth[_-]?token)[\s:=]+['"]?([a-zA-Z0-9_-]{20,})['"]?/gi },
|
||||||
|
{ name: 'Private Key', pattern: /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/gi },
|
||||||
|
{ name: 'AWS Key', pattern: /AKIA[0-9A-Z]{16}/gi },
|
||||||
|
{ name: 'GitHub Token', pattern: /gh[ps]_[a-zA-Z0-9]{36,}/gi }
|
||||||
|
];
|
||||||
|
|
||||||
|
const findings = [];
|
||||||
|
|
||||||
|
function scanDirectory(dir) {
|
||||||
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
|
||||||
|
// Skip node_modules, .git, etc.
|
||||||
|
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
scanDirectory(fullPath);
|
||||||
|
} else if (entry.isFile()) {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(fullPath, 'utf8');
|
||||||
|
|
||||||
|
for (const { name, pattern } of secretPatterns) {
|
||||||
|
const matches = content.match(pattern);
|
||||||
|
if (matches) {
|
||||||
|
findings.push({
|
||||||
|
type: name,
|
||||||
|
file: fullPath,
|
||||||
|
count: matches.length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Skip binary files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scanDirectory('.');
|
||||||
|
|
||||||
|
if (findings.length > 0) {
|
||||||
|
const message = `🚨 **Security Alert: Potential Secrets Detected**
|
||||||
|
|
||||||
|
${findings.map(f => `- **${f.type}** found in \`${f.file}\` (${f.count} occurrence${f.count > 1 ? 's' : ''})`).join('\n')}
|
||||||
|
|
||||||
|
⚠️ **Action Required:**
|
||||||
|
1. Verify if these are real secrets
|
||||||
|
2. If yes, rotate credentials immediately
|
||||||
|
3. Remove from git history
|
||||||
|
4. Use environment variables instead
|
||||||
|
|
||||||
|
cc: @${context.repo.owner}`;
|
||||||
|
|
||||||
|
await github.rest.issues.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: '🚨 Security: Potential Secrets Detected',
|
||||||
|
body: message,
|
||||||
|
labels: ['security', 'priority:high']
|
||||||
|
});
|
||||||
|
|
||||||
|
core.setFailed('Potential secrets detected!');
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Dependency scan
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Check for package.json
|
||||||
|
if (fs.existsSync('package.json')) {
|
||||||
|
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||||
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
||||||
|
|
||||||
|
const outdatedWarnings = [];
|
||||||
|
|
||||||
|
// Known vulnerable package patterns (simplified)
|
||||||
|
const knownVulnerable = ['event-stream@3.3.6', 'lodash@<4.17.21'];
|
||||||
|
|
||||||
|
for (const [name, version] of Object.entries(deps)) {
|
||||||
|
const fullDep = `${name}@${version}`;
|
||||||
|
if (knownVulnerable.some(v => fullDep.includes(v))) {
|
||||||
|
outdatedWarnings.push(`⚠️ ${name}@${version} has known vulnerabilities`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outdatedWarnings.length > 0) {
|
||||||
|
await github.rest.issues.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: '🔒 Security: Vulnerable Dependencies Detected',
|
||||||
|
body: `**Vulnerable dependencies found:**\n\n${outdatedWarnings.join('\n')}\n\nPlease update to secure versions.`,
|
||||||
|
labels: ['security', 'dependencies']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: CodeQL Analysis
|
||||||
|
uses: github/codeql-action/init@v3
|
||||||
|
with:
|
||||||
|
languages: javascript, python, typescript
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Run CodeQL
|
||||||
|
uses: github/codeql-action/analyze@v3
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Log to memory
|
||||||
|
run: |
|
||||||
|
if [ -f ~/memory-system.sh ]; then
|
||||||
|
~/memory-system.sh log bot-action "security-scan" "Security scan completed for ${{ github.repository }}"
|
||||||
|
fi
|
||||||
191
bot-sync.yml
Normal file
191
bot-sync.yml
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
name: Bot - Workflow Sync
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 1' # Weekly on Monday
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-workflows:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Sync bot workflows
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Ensure .github/workflows exists
|
||||||
|
if (!fs.existsSync('.github/workflows')) {
|
||||||
|
fs.mkdirSync('.github/workflows', { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard labels for all repos
|
||||||
|
const standardLabels = [
|
||||||
|
{ name: 'bug', color: 'd73a4a', description: 'Something isn\'t working' },
|
||||||
|
{ name: 'enhancement', color: 'a2eeef', description: 'New feature or request' },
|
||||||
|
{ name: 'documentation', color: '0075ca', description: 'Improvements or additions to documentation' },
|
||||||
|
{ name: 'security', color: 'ff0000', description: 'Security vulnerability or concern' },
|
||||||
|
{ name: 'performance', color: 'ff9800', description: 'Performance improvement' },
|
||||||
|
{ name: 'testing', color: '4caf50', description: 'Testing related' },
|
||||||
|
{ name: 'priority:high', color: 'b60205', description: 'High priority' },
|
||||||
|
{ name: 'priority:medium', color: 'fbca04', description: 'Medium priority' },
|
||||||
|
{ name: 'priority:low', color: '0e8a16', description: 'Low priority' },
|
||||||
|
{ name: 'size:small', color: 'c5def5', description: 'Small PR (1-5 files)' },
|
||||||
|
{ name: 'size:medium', color: '7fb8d8', description: 'Medium PR (6-20 files)' },
|
||||||
|
{ name: 'size:large', color: '0052cc', description: 'Large PR (20+ files)' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create/update labels
|
||||||
|
for (const label of standardLabels) {
|
||||||
|
try {
|
||||||
|
await github.rest.issues.createLabel({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
name: label.name,
|
||||||
|
color: label.color,
|
||||||
|
description: label.description
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (err.status === 422) {
|
||||||
|
// Label exists, update it
|
||||||
|
await github.rest.issues.updateLabel({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
name: label.name,
|
||||||
|
color: label.color,
|
||||||
|
description: label.description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Synced standard labels');
|
||||||
|
|
||||||
|
- name: Sync bot config
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
const botConfig = {
|
||||||
|
bots: {
|
||||||
|
issue_triage: {
|
||||||
|
enabled: true,
|
||||||
|
auto_label: true,
|
||||||
|
assign_team: true,
|
||||||
|
detect_duplicates: true
|
||||||
|
},
|
||||||
|
pr_review: {
|
||||||
|
enabled: true,
|
||||||
|
require_tests: true,
|
||||||
|
security_scan: true,
|
||||||
|
code_quality: true
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
enabled: true,
|
||||||
|
dependabot: true,
|
||||||
|
secret_scan: true,
|
||||||
|
codeql: true
|
||||||
|
},
|
||||||
|
documentation: {
|
||||||
|
enabled: true,
|
||||||
|
auto_generate: true,
|
||||||
|
sync_readme: true
|
||||||
|
},
|
||||||
|
sync: {
|
||||||
|
enabled: true,
|
||||||
|
workflow_sync: true,
|
||||||
|
config_sync: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
organization: 'BlackRoad-OS',
|
||||||
|
contact: 'blackroad.systems@gmail.com'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!fs.existsSync('.github')) {
|
||||||
|
fs.mkdirSync('.github');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync('.github/bot-config.yml', yaml.dump(botConfig));
|
||||||
|
|
||||||
|
console.log('✅ Created bot config');
|
||||||
|
|
||||||
|
- name: Sync CODEOWNERS
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const codeowners = `# BlackRoad OS Code Owners
|
||||||
|
# Auto-synced by bot system
|
||||||
|
|
||||||
|
# Default owners for everything in the repo
|
||||||
|
* @${context.repo.owner}
|
||||||
|
|
||||||
|
# Specific paths
|
||||||
|
/.github/ @${context.repo.owner}
|
||||||
|
/docs/ @${context.repo.owner}
|
||||||
|
|
||||||
|
# Auto-generated by BlackRoad Bot System
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (!fs.existsSync('.github')) {
|
||||||
|
fs.mkdirSync('.github');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync('.github/CODEOWNERS', codeowners);
|
||||||
|
|
||||||
|
console.log('✅ Synced CODEOWNERS');
|
||||||
|
|
||||||
|
- name: Create sync PR
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const status = execSync('git status --porcelain').toString();
|
||||||
|
|
||||||
|
if (status.trim()) {
|
||||||
|
const branchName = `bot/sync-${Date.now()}`;
|
||||||
|
|
||||||
|
execSync(`git config user.name "BlackRoad Bot"`);
|
||||||
|
execSync(`git config user.email "bot@blackroad.io"`);
|
||||||
|
execSync(`git checkout -b ${branchName}`);
|
||||||
|
execSync(`git add .`);
|
||||||
|
execSync(`git commit -m "🤖 chore: Sync bot workflows and config"`);
|
||||||
|
execSync(`git push origin ${branchName}`);
|
||||||
|
|
||||||
|
await github.rest.pulls.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: '🤖 chore: Sync bot workflows and config',
|
||||||
|
body: `## Bot System Sync
|
||||||
|
|
||||||
|
This PR syncs the repository with org-wide standards:
|
||||||
|
- ✅ Standard labels
|
||||||
|
- ✅ Bot configuration
|
||||||
|
- ✅ CODEOWNERS file
|
||||||
|
|
||||||
|
**Auto-generated by:** BlackRoad Bot System
|
||||||
|
|
||||||
|
Safe to merge after review.`,
|
||||||
|
head: branchName,
|
||||||
|
base: 'main'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('✅ Everything already in sync');
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Log to memory
|
||||||
|
run: |
|
||||||
|
if [ -f ~/memory-system.sh ]; then
|
||||||
|
~/memory-system.sh log bot-action "workflow-sync" "Synced workflows for ${{ github.repository }}"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user