Skip to content

Commit b30d23b

Browse files
mikemolinetclaude
andauthored
ci: replace stale ARGUS_STAGING_KEY with local cueapi-core in test job (#32)
* ci(test): spin up cueapi-core locally instead of hitting staging with ARGUS_STAGING_KEY The `test` job's `pytest tests/` runs the SDK's CRUD tests against a real CueAPI server. Previously that server was remote staging, authenticated by `secrets.ARGUS_STAGING_KEY`. Argus was retired 2026-05-02 (cueapi PR #539) and that key/user is no longer valid — every PR's `test` job has been red for ~3 days with `AuthenticationError: Invalid API key`, blocking PRs #30 and #31 (and any future SDK PRs). This switches the job to the same self-contained pattern that's already proven by the passing `sdk-integration` job: clone cueapi-core, install, migrate, boot uvicorn locally, register a fresh test user via POST /v1/auth/register (gated by ALLOW_REGISTER=true), capture the key, plumb it through the existing `CUEAPI_STAGING_URL` / `CUEAPI_STAGING_API_KEY` env vars (no SDK code change needed — `tests/conftest.py` already reads them from env). Workflow-only diff. No SDK behavior change. Note: `notify-merge` still references `secrets.ARGUS_CUEAPI_KEY` for the post-merge prod telemetry cue. That key is also stale, but the step runs after auto-merge so it doesn't gate the PR — leaving for a follow-up that needs a new prod key minted by an operator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(test): replace alembic with Base.metadata.create_all (matches cueapi-core conftest) First push failed because OSS migration set ends at 023 while the User model declares an `api_key_encrypted` column with no migration backing it. That's a parity drift in cueapi-core (private migration 019 in the hosted repo includes the column; the OSS port renamed/replaced it with the alert-webhook bits but kept the column on the model). `alembic upgrade head` produced a schema missing that column → register endpoint 500'd on the User SELECT. Switch the CI bootstrap to model-driven schema init via `Base.metadata.create_all`, which is the exact pattern cueapi-core's own `tests/conftest.py` uses (and which is robust to model/migration drift because the model is the source of truth for tests). Imports the same model list as conftest so all tables register before create_all runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a2b5325 commit b30d23b

1 file changed

Lines changed: 75 additions & 3 deletions

File tree

.github/workflows/feature-to-main.yml

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,89 @@ jobs:
5353
pip install -r cueapi-core/requirements.txt
5454
pip install -e .
5555
56-
- name: Run tests
56+
- name: Initialize test DB schema (model-driven, matches cueapi-core/tests/conftest.py)
57+
working-directory: cueapi-core
58+
env:
59+
DATABASE_URL: postgresql+asyncpg://runner@localhost:5432/cueapi_test
60+
REDIS_URL: redis://localhost:6379
61+
SESSION_SECRET: test-session-secret-32-chars-minimum!!
62+
ENV: test
63+
RESEND_API_KEY: ""
64+
run: |
65+
python <<'PY'
66+
import asyncio
67+
from sqlalchemy.ext.asyncio import create_async_engine
68+
from app.config import settings
69+
from app.database import Base
70+
# Import all models so Base.metadata sees them (mirrors tests/conftest.py).
71+
from app.models import ( # noqa: F401
72+
Alert, Cue, DispatchOutbox, Execution, UsageMonthly, User, Worker, DeviceCode,
73+
Agent, Message, UsageMessagesMonthly,
74+
)
75+
76+
async def _init():
77+
engine = create_async_engine(settings.DATABASE_URL)
78+
async with engine.begin() as conn:
79+
await conn.run_sync(Base.metadata.create_all)
80+
await engine.dispose()
81+
82+
asyncio.run(_init())
83+
print("Schema initialized via Base.metadata.create_all")
84+
PY
85+
86+
- name: Start cueapi-core API in background
87+
working-directory: cueapi-core
5788
env:
5889
DATABASE_URL: postgresql+asyncpg://runner@localhost:5432/cueapi_test
5990
REDIS_URL: redis://localhost:6379
6091
SESSION_SECRET: test-session-secret-32-chars-minimum!!
6192
ENV: test
6293
RESEND_API_KEY: ""
63-
CUEAPI_STAGING_URL: https://api-staging-e962.up.railway.app
64-
CUEAPI_STAGING_API_KEY: ${{ secrets.ARGUS_STAGING_KEY }}
94+
ALLOW_REGISTER: "true"
95+
BASE_URL: http://localhost:8000
96+
run: |
97+
nohup python -m uvicorn app.main:app --host 127.0.0.1 --port 8000 \
98+
> /tmp/uvicorn.log 2>&1 &
99+
echo $! > /tmp/uvicorn.pid
100+
101+
- name: Wait for API health
102+
run: |
103+
for i in $(seq 1 30); do
104+
if curl -sf http://127.0.0.1:8000/health > /dev/null; then
105+
echo "API ready after ${i}s"
106+
exit 0
107+
fi
108+
sleep 1
109+
done
110+
echo "API failed to start within 30s"
111+
cat /tmp/uvicorn.log
112+
exit 1
113+
114+
- name: Register test user and capture API key
115+
id: register_user
116+
run: |
117+
EMAIL="ci-sdk-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}@example.com"
118+
RESP=$(curl -sf -X POST http://127.0.0.1:8000/v1/auth/register \
119+
-H "Content-Type: application/json" \
120+
-d "{\"email\":\"${EMAIL}\"}")
121+
KEY=$(printf '%s' "$RESP" | python -c 'import sys,json;print(json.load(sys.stdin)["api_key"])')
122+
if [ -z "$KEY" ]; then
123+
echo "Registration failed; response: $RESP"
124+
exit 1
125+
fi
126+
echo "::add-mask::$KEY"
127+
echo "api_key=$KEY" >> "$GITHUB_OUTPUT"
128+
129+
- name: Run tests
130+
env:
131+
CUEAPI_STAGING_URL: http://127.0.0.1:8000
132+
CUEAPI_STAGING_API_KEY: ${{ steps.register_user.outputs.api_key }}
65133
run: pytest tests/ -v --tb=short
66134

135+
- name: Dump uvicorn log on failure
136+
if: failure()
137+
run: cat /tmp/uvicorn.log || true
138+
67139
auto-merge:
68140
needs: test
69141
runs-on: ubuntu-latest

0 commit comments

Comments
 (0)