Polish public repo release surface #8
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: | |
| test: | |
| name: Test (Python ${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "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 }} | |
| cache: pip | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev]" | |
| - name: Run focused regression suite | |
| env: | |
| LLM_PROVIDER: "" | |
| SCM_EMBEDDING_BACKEND: hash | |
| SCM_AUTO_SLEEP_DISABLE: "1" | |
| run: | | |
| pytest \ | |
| tests/test_scm_sdk.py \ | |
| tests/test_product_runtime_api.py \ | |
| tests/test_mcp_contract.py \ | |
| tests/test_cloud_auth.py::test_byok_decryption_round_trip \ | |
| -q --tb=short | |
| - name: Secret scan tracked files | |
| run: | | |
| python - <<'PY' | |
| import pathlib | |
| import re | |
| import subprocess | |
| import sys | |
| token_re = re.compile(r"(sk-proj-[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9_-]{20,})") | |
| binary_ext = { | |
| ".pdf", ".png", ".jpg", ".jpeg", ".gif", ".zip", ".gz", ".tar", | |
| ".sqlite", ".db", ".pyc", | |
| } | |
| tracked = subprocess.check_output(["git", "ls-files"], text=True).splitlines() | |
| hits = [] | |
| for name in tracked: | |
| path = pathlib.Path(name) | |
| if path.suffix.lower() in binary_ext: | |
| continue | |
| try: | |
| text = path.read_text(encoding="utf-8") | |
| except Exception: | |
| continue | |
| for match in token_re.finditer(text): | |
| token = match.group(1) | |
| lowered = token.lower() | |
| if "fake" in lowered or "your" in lowered: | |
| continue | |
| line = text.count("\n", 0, match.start()) + 1 | |
| hits.append(f"{name}:{line}: possible secret token") | |
| if hits: | |
| print("\n".join(hits)) | |
| sys.exit(1) | |
| PY | |
| build: | |
| name: Build distribution artifacts | |
| runs-on: ubuntu-latest | |
| needs: test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: pip | |
| - name: Install build tools | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build twine | |
| - name: Build wheel + sdist | |
| run: | | |
| rm -rf dist build *.egg-info | |
| python -m build --wheel --sdist | |
| - name: Validate distribution metadata | |
| run: twine check dist/* | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| wheel-smoke: | |
| name: Installed wheel smoke | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: pip | |
| - name: Download dist artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: Install wheel in a clean temp venv | |
| run: | | |
| python -m venv /tmp/scm-wheel | |
| /tmp/scm-wheel/bin/python -m pip install --upgrade pip | |
| /tmp/scm-wheel/bin/pip install dist/scm_memory-*.whl | |
| - name: Verify installed package data and Python SDK | |
| env: | |
| SCM_DATA_DIR: /tmp/scm-wheel-data | |
| LLM_PROVIDER: "" | |
| SCM_EMBEDDING_BACKEND: hash | |
| SCM_AUTO_SLEEP_DISABLE: "1" | |
| run: | | |
| cd /tmp | |
| /tmp/scm-wheel/bin/python - <<'PY' | |
| import importlib.resources as resources | |
| from scm import SCMEngine | |
| assert resources.files("src.core").joinpath("locales/en.json").is_file() | |
| assert resources.files("src.api").joinpath("static/app.html").is_file() | |
| engine = SCMEngine(session_id="wheel-smoke", sandbox=True, offline=True) | |
| added = engine.add_memory("Alice is allergic to peanuts.") | |
| assert added["ok"] and added["concepts_added"] >= 1 | |
| found = engine.search_memory("what should Alice avoid?") | |
| assert found["ok"] | |
| assert engine.sleep("deep")["ok"] | |
| assert engine.wake_summary()["ok"] | |
| PY | |
| - name: Verify CLI and offline quickstart outside repo | |
| env: | |
| SCM_DATA_DIR: /tmp/scm-wheel-data | |
| LLM_PROVIDER: "" | |
| SCM_EMBEDDING_BACKEND: hash | |
| SCM_AUTO_SLEEP_DISABLE: "1" | |
| run: | | |
| cd /tmp | |
| /tmp/scm-wheel/bin/scm --help | |
| /tmp/scm-wheel/bin/scm version | |
| /tmp/scm-wheel/bin/scm config | |
| /tmp/scm-wheel/bin/scm chat --help | |
| /tmp/scm-wheel/bin/scm sleep --help | |
| /tmp/scm-wheel/bin/scm wake-summary --help | |
| /tmp/scm-wheel/bin/scm serve --help | |
| /tmp/scm-wheel/bin/scm mcp --help | |
| /tmp/scm-wheel/bin/python "$GITHUB_WORKSPACE/examples/01_quickstart.py" | |
| - name: Verify REST five-tool contract | |
| env: | |
| SCM_DATA_DIR: /tmp/scm-wheel-data | |
| LLM_PROVIDER: "" | |
| SCM_EMBEDDING_BACKEND: hash | |
| SCM_AUTO_SLEEP_DISABLE: "1" | |
| IDLE_LEARNER_ENABLED: "false" | |
| run: | | |
| cd /tmp | |
| /tmp/scm-wheel/bin/scm serve --host 127.0.0.1 --port 8765 > /tmp/scm-server.log 2>&1 & | |
| SERVER_PID=$! | |
| trap 'kill $SERVER_PID || true' EXIT | |
| /tmp/scm-wheel/bin/python - <<'PY' | |
| import json | |
| import time | |
| import urllib.request | |
| BASE = "http://127.0.0.1:8765/v1" | |
| def req(method, path, payload=None): | |
| data = None | |
| headers = {} | |
| if payload is not None: | |
| data = json.dumps(payload).encode() | |
| headers["Content-Type"] = "application/json" | |
| request = urllib.request.Request(BASE + path, data=data, headers=headers, method=method) | |
| with urllib.request.urlopen(request, timeout=10) as response: | |
| return json.loads(response.read().decode()) | |
| for _ in range(60): | |
| try: | |
| if req("GET", "/health")["ok"]: | |
| break | |
| except Exception: | |
| time.sleep(1) | |
| else: | |
| raise SystemExit("SCM server did not become healthy") | |
| tools = req("GET", "/tools?format=openai")["tools"] | |
| assert [t["function"]["name"] for t in tools] == [ | |
| "add_memory", "search_memory", "sleep", "wake_summary", "forget" | |
| ] | |
| user = "ci-wheel-smoke" | |
| added = req("POST", "/memories", { | |
| "user_id": user, | |
| "text": "Alice is allergic to peanuts.", | |
| "sync": True, | |
| }) | |
| assert added["ok"] and added["concepts_added"] >= 1 | |
| assert req("POST", "/memories/search", { | |
| "user_id": user, | |
| "query": "what should Alice avoid?", | |
| "wait_for_pending": True, | |
| })["ok"] | |
| assert req("POST", "/memories/sleep", {"user_id": user, "mode": "deep"})["user_id"] == user | |
| assert req("GET", f"/wake-summary?user_id={user}&since_hours=24")["ok"] | |
| PY | |
| - name: Verify JS SDK unit smoke | |
| run: | | |
| cd sdk/js | |
| npm test |