Skip to content

Merge pull request #1 from up2itnow0822/fix/hooks-install-no-args-crit #9

Merge pull request #1 from up2itnow0822/fix/hooks-install-no-args-crit

Merge pull request #1 from up2itnow0822/fix/hooks-install-no-args-crit #9

Workflow file for this run

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