fix(billing): USD-only checkout presentment on /pricing (Option B) #480
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
| # Fork-level CI for HaraldeRoessler/moltrust-api. | |
| # | |
| # Intentionally lightweight — exercises the things that catch the | |
| # common breakage modes without needing a running API or database: | |
| # | |
| # syntax — `python -m compileall` on every source dir. | |
| # import-smoke — actually try `from app.main import app` with all | |
| # required env vars set to placeholder values. This | |
| # is the only job that catches startup-time | |
| # RuntimeError raises (e.g. NONCE_SECRET unset). | |
| # pytest-coll — `pytest --collect-only` over the in-repo tests. | |
| # Catches ImportError / SyntaxError in test modules | |
| # without needing the API + Postgres stack online. | |
| # ruff — informational lint, never blocks merge. | |
| # bandit — informational SAST, never blocks merge. | |
| # | |
| # Triggers on every push and PR. Separate file name (`fork-ci.yml`) | |
| # from PR #14's proposed `ci.yml` so the two can co-exist if PR #14 | |
| # lands upstream later. | |
| name: Fork CI | |
| on: | |
| push: | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| security-events: write | |
| concurrency: | |
| group: fork-ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| syntax: | |
| name: syntax (compileall) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: byte-compile every source dir | |
| run: | | |
| python -m compileall -q app | |
| for d in scripts agents agent operator moltbook monitor; do | |
| [ -d "$d" ] && python -m compileall -q "$d" || true | |
| done | |
| # Also top-level scripts that aren't in a directory. | |
| python -m compileall -q seed_ecosystem.py test_protocol_compliance.py test_sandbox.py 2>/dev/null || true | |
| import-smoke: | |
| name: import smoke test (with required env vars) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: requirements.txt | |
| - name: install requirements | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: import app.main with placeholder env | |
| # The whole point of this job: catch any startup-time | |
| # RuntimeError. The security hardening pass added fail-fast | |
| # checks for NONCE_SECRET, MOLTRUST_API_KEYS, MOLTSTACK_DB_PW | |
| # — if one of those is missing, the import explodes here | |
| # rather than in production. | |
| env: | |
| MOLTRUST_API_KEYS: 'mt_ci_placeholder_key_does_not_authenticate' | |
| NONCE_SECRET: 'ci-placeholder-nonce-secret' | |
| MOLTSTACK_DB_PW: 'ci-placeholder-db-pw' | |
| MOLTRUST_ADMIN_USERS: 'ci-admin:admin:$2b$12$ciplaceholderhashthatwillnevermatchanypassword.ciplaceholderhash' | |
| MOLTRUST_ENV: 'ci' | |
| run: | | |
| python -c "from app.main import app; print('app.main imported OK, FastAPI app:', type(app).__name__)" | |
| pytest-collect: | |
| name: pytest --collect-only | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: requirements.txt | |
| - name: install requirements + pytest | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install pytest pytest-asyncio httpx | |
| - name: collect tests | |
| # Collection imports test modules without running them. Catches | |
| # ImportError / SyntaxError / fixture-parse errors without | |
| # needing the API stack online. | |
| env: | |
| MOLTRUST_API_KEYS: 'mt_ci_placeholder_key' | |
| NONCE_SECRET: 'ci-placeholder' | |
| MOLTSTACK_DB_PW: 'ci-placeholder' | |
| run: | | |
| pytest --collect-only -q tests/ test_*.py 2>&1 | tail -40 | |
| pytest-run: | |
| name: pytest (credit middleware) | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_DB: moltstack_sandbox | |
| POSTGRES_USER: moltstack | |
| POSTGRES_PASSWORD: ci-pw | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: requirements.txt | |
| - name: install requirements + pytest | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install pytest pytest-asyncio httpx | |
| - name: init test DB schema | |
| # init_db.sql uses IF NOT EXISTS — idempotent, safe to run | |
| # repeatedly. Uses plain text password via PGPASSWORD — safe | |
| # because this runs in an ephemeral CI container. | |
| env: | |
| PGPASSWORD: ci-pw | |
| PGUSER: moltstack | |
| run: | | |
| psql -h localhost -U moltstack -d moltstack_sandbox -f init_db.sql | |
| # CI schema alignment: columns/tables present on live DB but missing from init_db.sql | |
| psql -h localhost -U moltstack -d moltstack_sandbox -c "ALTER TABLE api_keys ADD COLUMN IF NOT EXISTS email TEXT;" | |
| # request_log is referenced by middleware during test runs. Table not in | |
| # init_db.sql — add minimal schema so INSERTs don't flood the log. | |
| psql -h localhost -U moltstack -d moltstack_sandbox -c "CREATE TABLE IF NOT EXISTS request_log (id BIGSERIAL PRIMARY KEY, endpoint TEXT, method TEXT, status_code INT, ip TEXT, user_agent TEXT, response_ms FLOAT, source TEXT, ip_org TEXT, ip_country TEXT, created_at TIMESTAMPTZ DEFAULT NOW());" | |
| # PR #81 wired record_spend_event() into the credit-deduct path; that | |
| # call needs agents.operator_did + agent_budget_caps + budget_spend_events. | |
| # 009 has a FK on gate_events(id) so 008 must run first. Both migrations | |
| # use IF NOT EXISTS / ADD COLUMN IF NOT EXISTS — idempotent. | |
| psql -h localhost -U moltstack -d moltstack_sandbox -v ON_ERROR_STOP=1 -f app/migrations/008_gate_events.sql | |
| psql -h localhost -U moltstack -d moltstack_sandbox -v ON_ERROR_STOP=1 -f app/migrations/009_agent_budget_caps.sql | |
| # 010: D3 aae_envelopes store (component 1). Idempotent (IF NOT EXISTS / | |
| # OR REPLACE / DROP-before-CREATE TRIGGER), additiv, kein ALTER an Bestand. | |
| psql -h localhost -U moltstack -d moltstack_sandbox -v ON_ERROR_STOP=1 -f app/migrations/010_aae_envelopes.sql | |
| # 011: D3 aae_evaluations (component 2 evaluator store + signed audit trail). | |
| # Append-only; FK aae_ref -> 010 muss zuerst laufen. Idempotent. | |
| psql -h localhost -U moltstack -d moltstack_sandbox -v ON_ERROR_STOP=1 -f app/migrations/011_aae_evaluations.sql | |
| # 012: drop FK aae_evaluations.aae_ref (FK-lock vs immutable-store REVOKE conflict); | |
| # sha256-CHECK stays. Idempotent. | |
| psql -h localhost -U moltstack -d moltstack_sandbox -v ON_ERROR_STOP=1 -f app/migrations/012_drop_aae_eval_fk.sql | |
| # 013: D-1 issuer_trust_tier column on aae_envelopes (additive). Idempotent. | |
| psql -h localhost -U moltstack -d moltstack_sandbox -v ON_ERROR_STOP=1 -f app/migrations/013_issuer_trust_tier.sql | |
| # 2026-06-15: insufficient_credit_events — caller_did-on-402 metric (dashboard_overview reads it; additive, IF NOT EXISTS). | |
| psql -h localhost -U moltstack -d moltstack_sandbox -v ON_ERROR_STOP=1 -f app/migrations/2026-06-15_insufficient_credit_events.sql | |
| - name: run credit middleware tests | |
| # These tests require a live Postgres because credit_middleware | |
| # connects via db_pool (asyncpg). The service container provides | |
| # a fresh Postgres on localhost:5432. | |
| # MOLTRUST_ADMIN_USERS needs a valid bcrypt hash for the format | |
| # check — the placeholder below passes the regex but won't match | |
| # any real password. | |
| env: | |
| MOLTRUST_API_KEYS: 'mt_ci_placeholder_key' | |
| NONCE_SECRET: 'ci-placeholder-nonce-secret' | |
| MOLTSTACK_DB_PW: 'ci-pw' | |
| DB_NAME: moltstack_sandbox | |
| DB_HOST: localhost | |
| MOLTRUST_ADMIN_USERS: 'ci-admin:admin:$2b$12$ciplaceholderhashforadminusersformatcheck' | |
| MOLTRUST_ENV: 'ci' | |
| CREDITS_ENABLED: 'true' | |
| run: | | |
| python -m pytest tests/test_credit_middleware.py -v --tb=short 2>&1 | |
| ruff: | |
| name: ruff (informational) | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - run: pip install ruff | |
| - name: ruff check | |
| # Informational only — don't block merge on lint noise. Surfaces | |
| # in the workflow log so we can clean up over time. | |
| run: ruff check app/ agents/ scripts/ monitor/ --output-format=concise || true | |
| bandit: | |
| name: bandit SAST (informational) | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - run: pip install bandit | |
| - name: bandit scan | |
| # -ll = report MEDIUM+ severity only (filters out the noise). | |
| # Findings here are a useful belt-and-suspenders signal — most | |
| # are already either fixed in this PR or dismissed in CodeQL. | |
| run: bandit -r app/ agents/ scripts/ monitor/ -ll || true |