Merge pull request #1 from up2itnow0822/fix/hooks-install-no-args-crit #9
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] | |
| jobs: | |
| # ── Job 1: Validate plugin manifest and structure ──────────────────────────── | |
| validate: | |
| name: Validate Plugin Structure | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install PyYAML | |
| run: pip install pyyaml | |
| - name: Validate plugin.yaml schema | |
| run: | | |
| python3 - <<'EOF' | |
| import yaml, sys | |
| with open('plugin.yaml') as f: | |
| d = yaml.safe_load(f) | |
| errors = [] | |
| required = ['name', 'title', 'description', 'version'] | |
| for field in required: | |
| if not d.get(field): | |
| errors.append(f'Missing required field: {field}') | |
| import re | |
| if not re.match(r'^[a-z0-9_]+$', d.get('name', '')): | |
| errors.append(f'name must match ^[a-z0-9_]+$ got: {d["name"]}') | |
| if d.get('always_enabled') is True: | |
| errors.append('always_enabled must be false for community plugins') | |
| if 'enabled' in d: | |
| errors.append('Unknown field: enabled (not in plugin.yaml schema)') | |
| if 'prompt' in d: | |
| errors.append('Unknown field: prompt (use extensions/ for prompt injection)') | |
| if errors: | |
| print('FAIL plugin.yaml validation:') | |
| for e in errors: print(f' - {e}') | |
| sys.exit(1) | |
| print(f'PASS plugin.yaml: name={d["name"]}, version={d["version"]}') | |
| EOF | |
| - name: Validate all 7 SKILL.md files have required frontmatter | |
| run: | | |
| python3 - <<'EOF' | |
| import os, re, sys | |
| skills_dir = 'skills' | |
| required_skills = ['azpowers-memory','azpowers-payments','azpowers-wallet', | |
| 'azpowers-rsi','azpowers-swarm','azpowers-itp','azpowers-native'] | |
| errors = [] | |
| for skill in required_skills: | |
| path = os.path.join(skills_dir, skill, 'SKILL.md') | |
| if not os.path.exists(path): | |
| errors.append(f'Missing: {path}') | |
| continue | |
| with open(path) as f: | |
| content = f.read() | |
| if not content.startswith('---'): | |
| errors.append(f'{path}: missing YAML frontmatter') | |
| continue | |
| fm = content.split('---')[1] | |
| if 'name:' not in fm: | |
| errors.append(f'{path}: frontmatter missing name:') | |
| if 'description:' not in fm: | |
| errors.append(f'{path}: frontmatter missing description:') | |
| # Check name matches directory | |
| m = re.search(r'name:\s*(\S+)', fm) | |
| if m and m.group(1) != skill: | |
| errors.append(f'{path}: name={m.group(1)} does not match dir={skill}') | |
| if errors: | |
| print('FAIL SKILL.md validation:') | |
| for e in errors: print(f' - {e}') | |
| sys.exit(1) | |
| print(f'PASS all {len(required_skills)} SKILL.md files valid') | |
| EOF | |
| - name: Check LICENSE exists | |
| run: test -f LICENSE && echo 'PASS LICENSE present' || (echo 'FAIL LICENSE missing' && exit 1) | |
| - name: Check no residual CPS abbreviation (should be A0P-S) | |
| run: | | |
| # Exclude upstream repo and binary files | |
| if grep -rw 'CPS' README.md .gitignore hooks.py plugin.yaml scripts/ skills/ itp-service/ extensions/ 2>/dev/null | grep -v 'Binary'; then | |
| echo 'FAIL: Found residual CPS references (should be A0P-S)' | |
| exit 1 | |
| else | |
| echo 'PASS no residual CPS references' | |
| fi | |
| # ── Job 2: Smoke test all 7 A0P-S modules ──────────────────────────────────── | |
| smoke-test: | |
| name: Smoke Test (Node ${{ matrix.node-version }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: ['20', '22'] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| - name: Set up Python for ITP service | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install clawpowers npm package | |
| run: | | |
| mkdir -p ~/.clawpowers/runtime | |
| npm install clawpowers@2.2.6 --prefix ~/.clawpowers/runtime --no-save | |
| - name: Install ITP service dependencies | |
| run: pip install -r itp-service/requirements.txt | |
| - name: Start ITP service | |
| run: | | |
| cd itp-service | |
| uvicorn itp_server:app --host 127.0.0.1 --port 8100 & | |
| # Wait for service to be ready | |
| for i in $(seq 1 10); do | |
| curl -sf http://127.0.0.1:8100/health && break || sleep 1 | |
| done | |
| echo 'ITP service ready' | |
| - name: Run smoke test | |
| run: node scripts/smoke-test.mjs | |
| - name: Teardown ITP service | |
| if: always() | |
| run: | | |
| pkill -f 'uvicorn itp_server:app' || true | |
| echo 'ITP service stopped' | |
| # ── Job 3: Python lint and validation ──────────────────────────────────────── | |
| lint-python: | |
| name: Lint Python | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install flake8 and pylint | |
| run: pip install flake8 pylint pyflakes pyyaml | |
| - name: Flake8 — hooks.py | |
| run: flake8 hooks.py --max-line-length=120 --ignore=E501 | |
| - name: Flake8 — itp_server.py | |
| run: flake8 itp-service/itp_server.py --max-line-length=120 --ignore=E501 | |
| - name: Flake8 — extension | |
| run: flake8 extensions/python/agent_system_prompt/end/10_azpowers.py --max-line-length=120 | |
| # ── Job 4: Shell script lint ────────────────────────────────────────────────── | |
| shellcheck: | |
| name: ShellCheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install ShellCheck | |
| run: sudo apt-get install -y shellcheck | |
| - name: ShellCheck all shell scripts | |
| run: | | |
| shellcheck -e SC1091 scripts/install.sh | |
| shellcheck -e SC1091 scripts/verify.sh | |
| shellcheck -e SC1091 scripts/install-tier1.sh | |
| shellcheck -e SC1091 itp-service/start.sh | |
| echo 'PASS all shell scripts pass ShellCheck' | |
| # ── Job 5: Python unit tests ───────────────────────────────────────────────── | |
| unit-test: | |
| name: Unit Tests (Python ${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| python-version: ['3.11', '3.12'] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install dev dependencies | |
| run: pip install -r requirements-dev.txt | |
| - name: Run pytest | |
| run: pytest -q |