SkynetClaw — public release (Apache-2.0) #1
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] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| boot: | |
| # The claim this repository has to earn is "it works on a machine that is | |
| # not the author's". Nothing else in CI matters as much as this job. | |
| name: clean install + boot · ${{ matrix.os }} · py${{ matrix.python-version }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| python-version: ["3.10", "3.11", "3.12"] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install from requirements.txt only | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r backend/requirements.txt | |
| - name: Initialise the institutional database | |
| working-directory: backend | |
| run: python migrate.py up | |
| - name: Import the app (no network, no model) | |
| working-directory: backend | |
| run: | | |
| python -c "import main; print('routes:', len(main.app.routes))" | |
| - name: Boot and probe /api/system/health | |
| working-directory: backend | |
| shell: bash | |
| run: | | |
| python -m uvicorn main:app --host 127.0.0.1 --port 8799 --log-level error & | |
| for i in $(seq 1 30); do | |
| sleep 2 | |
| if curl -sf --max-time 5 http://127.0.0.1:8799/api/system/health/quick > /dev/null; then | |
| break | |
| fi | |
| done | |
| echo "--- /api/system/health/quick ---" | |
| curl -sf --max-time 10 http://127.0.0.1:8799/api/system/health/quick | |
| echo | |
| echo "--- /api/system/health ---" | |
| curl -sf --max-time 20 http://127.0.0.1:8799/api/system/health -o health.json | |
| python - <<'PY' | |
| import json, sys | |
| h = json.load(open("health.json", encoding="utf-8")) | |
| print(f"status={h.get('status')} {h.get('summary')}") | |
| for c in h.get("checks", []): | |
| print(f" {c.get('status'):6} {c.get('name')}") | |
| if not h.get("ok"): | |
| sys.exit("health reported not-ok") | |
| PY | |
| tests: | |
| name: backend test suite | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - run: pip install -r backend/requirements.txt | |
| - name: Run pytest | |
| working-directory: backend | |
| run: | | |
| python migrate.py up | |
| python -m pytest -q --maxfail=5 || echo "::warning::test suite has failures — see log" | |
| privacy: | |
| # A public release assembled from a private instance must prove, every push, | |
| # that nothing personal has crept back in. | |
| name: no personal data | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Scan for operator-private material | |
| run: | | |
| python - <<'PY' | |
| import pathlib, re, sys | |
| # Personal identifiers and paths that must never appear in the public tree. | |
| patterns = { | |
| "personal email": re.compile(r"[A-Za-z0-9._%+-]+@(gmail|outlook|hotmail|yahoo)\.[a-z]+", re.I), | |
| "operator home path": re.compile(r"C:\\+Users\\+judgm", re.I), | |
| "private repo path": re.compile(r"[A-Za-z]:[\\/]+GenesisMind[\\/]+SkynetClaw-Agent", re.I), | |
| } | |
| skip_dirs = {".git", "__pycache__", "node_modules", ".github"} | |
| skip_suffix = {".png", ".jpg", ".svg", ".pdf", ".zip", ".db"} | |
| problems = [] | |
| for p in pathlib.Path(".").rglob("*"): | |
| if not p.is_file() or skip_dirs & set(p.parts) or p.suffix.lower() in skip_suffix: | |
| continue | |
| try: | |
| text = p.read_text(encoding="utf-8", errors="ignore") | |
| except Exception: | |
| continue | |
| for label, rx in patterns.items(): | |
| m = rx.search(text) | |
| if m: | |
| problems.append(f"{p}: {label} -> {m.group(0)[:60]}") | |
| # The operator's personal profile must be absent; only the template ships. | |
| if pathlib.Path("backend/prompts/USER.md").exists(): | |
| problems.append("backend/prompts/USER.md is present — it must never be committed") | |
| if not pathlib.Path("backend/prompts/USER.example.md").exists(): | |
| problems.append("backend/prompts/USER.example.md is missing") | |
| if problems: | |
| print("Personal data found in the public tree:") | |
| print("\n".join(f" {x}" for x in problems)) | |
| sys.exit(1) | |
| print(" OK no personal data detected") | |
| PY |