docs: add CI/CD and Dependabot sections to both READMEs #54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| defaults: | |
| run: | |
| shell: bash | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.12", "3.13"] | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Run tests | |
| run: python3 tests/run_tests.py | |
| test-cli: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| defaults: | |
| run: | |
| shell: bash | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node-version: ["20", "22", "23"] | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| - name: Run CLI tests | |
| run: node --test tests/cli.test.mjs tests/tui.test.mjs tests/lock.test.mjs | |
| lint: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 | |
| with: | |
| node-version: "22" | |
| - name: Check Python syntax | |
| run: | | |
| for f in scripts/sync-agents.py scripts/sync_common.py scripts/update-manifest.py scripts/sync-skills.py; do | |
| python3 -c "import ast; ast.parse(open('$f').read())" | |
| done | |
| - name: Check Node.js syntax | |
| run: node --check bin/cli.mjs src/*.mjs src/tui/*.mjs | |
| - name: Install shellcheck | |
| run: sudo apt-get update && sudo apt-get install -y --no-install-recommends shellcheck | |
| - name: Lint install.sh | |
| run: shellcheck install.sh | |
| - name: Validate agent YAML frontmatter | |
| run: | | |
| python3 -c " | |
| import sys, os, re | |
| errors = [] | |
| agents_dir = 'agents' | |
| for root, dirs, files in os.walk(agents_dir): | |
| for fname in files: | |
| if not fname.endswith('.md'): | |
| continue | |
| path = os.path.join(root, fname) | |
| with open(path, 'r', encoding='utf-8') as f: | |
| content = f.read().strip() | |
| if not content.startswith('---'): | |
| errors.append(f'{path}: missing YAML frontmatter (no opening ---)') | |
| continue | |
| end = content.find('\n---', 3) | |
| if end == -1: | |
| errors.append(f'{path}: missing closing --- in frontmatter') | |
| continue | |
| frontmatter = content[3:end].strip() | |
| if not frontmatter: | |
| errors.append(f'{path}: empty frontmatter') | |
| continue | |
| # Check that each non-indented line looks like a valid key: value | |
| for i, line in enumerate(frontmatter.split('\n'), 1): | |
| if not line.strip(): | |
| continue | |
| if line.startswith(' ') or line.startswith('\t'): | |
| continue # indented = nested value, skip | |
| if not re.match(r'^[\w][\w-]*\s*:', line): | |
| errors.append(f'{path}: line {i}: invalid frontmatter key: {line!r}') | |
| print(f' OK: {path}') | |
| if errors: | |
| print('\nFrontmatter validation errors:', file=sys.stderr) | |
| for e in errors: | |
| print(f' {e}', file=sys.stderr) | |
| sys.exit(1) | |
| else: | |
| print(f'\nAll agent files have valid YAML frontmatter.') | |
| " | |
| - name: Validate manifest.json | |
| run: python3 -c "import json; json.load(open('manifest.json'))" && echo "manifest.json is valid JSON" | |
| validate-agents: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| - name: Verify sync-agents --list | |
| run: | | |
| output=$(python3 scripts/sync-agents.py --list 2>&1) | |
| echo "$output" | |
| echo "$output" | grep -q "Listing.*agents" | |
| echo "sync-agents --list succeeded." | |
| - name: Verify sync-agents --list --tier=extended | |
| run: | | |
| output=$(python3 scripts/sync-agents.py --list --tier=extended 2>&1) | |
| echo "$output" | |
| count=$(echo "$output" | grep -oP 'Listing \K[0-9]+') | |
| echo "Extended tier agent count: $count" | |
| if [ -z "$count" ] || [ "$count" -lt 1 ]; then | |
| echo "ERROR: Expected at least 1 agent in extended tier, got: $count" | |
| exit 1 | |
| fi | |
| echo "sync-agents --list --tier=extended succeeded with $count agents." | |
| - name: "Check for deprecated tools: field" | |
| run: | | |
| found=0 | |
| while IFS= read -r -d '' f; do | |
| # Extract frontmatter only (between first --- and second ---) | |
| frontmatter=$(sed -n '1,/^---$/!d; /^---$/d; p' "$f" | head -50) | |
| if echo "$frontmatter" | grep -qP '^tools\s*:'; then | |
| echo "ERROR: $f contains deprecated 'tools:' field (use 'permission:' instead)" | |
| found=1 | |
| fi | |
| done < <(find agents -name '*.md' -print0) | |
| if [ "$found" -eq 1 ]; then | |
| exit 1 | |
| fi | |
| echo "No deprecated tools: field found." | |
| - name: "Check for 'Specifically:.' artifact" | |
| run: | | |
| found=0 | |
| while IFS= read -r -d '' f; do | |
| if grep -q 'Specifically:\.' "$f"; then | |
| echo "ERROR: $f contains the artifact 'Specifically:.'" | |
| found=1 | |
| fi | |
| done < <(find agents -name '*.md' -print0) | |
| if [ "$found" -eq 1 ]; then | |
| exit 1 | |
| fi | |
| echo "No 'Specifically:.' artifacts found." | |
| - name: Validate manifest.json against actual files | |
| run: | | |
| python3 -c " | |
| import json, sys, os | |
| with open('manifest.json', 'r') as f: | |
| manifest = json.load(f) | |
| errors = [] | |
| base = manifest.get('source_path') or manifest.get('base_path', 'agents') | |
| for agent in manifest.get('agents', []): | |
| path = agent.get('path', '') | |
| md_file = os.path.join(base, path + '.md') | |
| if not os.path.isfile(md_file): | |
| errors.append(f'Missing file for agent \"{agent[\"name\"]}\": {md_file}') | |
| if errors: | |
| for e in errors: | |
| print(f'ERROR: {e}', file=sys.stderr) | |
| sys.exit(1) | |
| else: | |
| print(f'All {len(manifest[\"agents\"])} agents in manifest.json have matching files.') | |
| " |