Summary
After the recent commit 0d8f90d ("React API Cutover ahead of Opensource PR Merge"), mieweb/launchpad@main fails for a previously-working pull_request preview deploy. The container is reported "created" but with an empty job ID, after which Phase 2 polls GET /api/v1/jobs/ (no id), receives a non-JSON body (HTTP 200), logs Response is not valid JSON — retrying 30 times, and then fails with Timed out waiting for job to complete after 300s.
Consuming workflow: mieweb/FHIRTogether PR #51 preview deploy.
Observed logs (from the action, secrets redacted)
Querying API: ***/api/v1/sites/1/containers?hostname=mieweb-fhirtogether-feat-systemd-deployment
--- RAW API RESPONSE ---
{"data":[]}
------------------------
Creating container...
--- Create Payload ---
{ "hostname": "mieweb-fhirtogether-feat-systemd-deployment", "template": "ghcr.io/mieweb/fhirtogether:pr-51", "services": [ ... ] }
----------------------
Container created successfully (ID: , Job ID: ). <-- both empty
...
Attempt 1/30 — HTTP 200
Response is not valid JSON — retrying.
...
Attempt 30/30 — HTTP 200
Response is not valid JSON — retrying.
::error::Timed out waiting for job to complete after 300s.
Root cause (response-schema drift)
The backend's JSON schema changed in the React cutover, but action.yml's parsing wasn't fully updated to match:
-
List endpoint now returns {"data":[ ... ]}, but the action reads .containers[0].id:
GET /api/v1/sites/$SITE_ID/containers?hostname=... -> {"data":[]}
CONTAINER_ID=$(echo "$EXISTING_CONTAINER" | jq -r '.containers[0].id // empty')
.containers no longer exists, so this is always empty.
-
Create response no longer exposes .container.id / .id / .jobId where the action looks:
NEW_ID=$(echo "$CREATE_BODY" | jq -r '.container.id // .id // empty')
JOB_ID=$(echo "$CREATE_BODY" | jq -r '.jobId // empty')
Result: Container created successfully (ID: , Job ID: ). — both empty.
-
Empty JOB_ID then makes Phase 2 poll GET /api/v1/jobs/ (trailing-empty id). With no job id, the API returns the SPA HTML page (HTTP 200), so jq empty rejects it as non-JSON on every one of the 30 attempts, then the step times out.
So the failure isn't a transient outage — it's a consistent schema mismatch introduced by the API cutover while @main is consumed mid-migration.
Suggested fixes
- Update
action.yml parsing to the new schema (e.g. read the list from .data[...] and the create response/job id from their new locations), or
- Have the API keep returning the legacy keys (
containers, container.id, jobId) until the action is updated, and
- Defensive: if
JOB_ID is empty after create, fail fast with a clear error instead of polling /api/v1/jobs/ (which returns HTML and wastes 5 minutes). Optionally validate that the jobs endpoint returns application/json before parsing.
Environment
- Action ref:
mieweb/launchpad@main (failing commit ≈ 0d8f90d)
- Trigger:
pull_request (opened/synchronize)
- Endpoints in use:
/api/v1/sites/1/containers, /api/v1/jobs/<id>
Summary
After the recent commit
0d8f90d("React API Cutover ahead of Opensource PR Merge"),mieweb/launchpad@mainfails for a previously-workingpull_requestpreview deploy. The container is reported "created" but with an empty job ID, after which Phase 2 pollsGET /api/v1/jobs/(no id), receives a non-JSON body (HTTP 200), logsResponse is not valid JSON — retrying30 times, and then fails withTimed out waiting for job to complete after 300s.Consuming workflow:
mieweb/FHIRTogetherPR #51 preview deploy.Observed logs (from the action, secrets redacted)
Root cause (response-schema drift)
The backend's JSON schema changed in the React cutover, but
action.yml's parsing wasn't fully updated to match:List endpoint now returns
{"data":[ ... ]}, but the action reads.containers[0].id:.containersno longer exists, so this is always empty.Create response no longer exposes
.container.id/.id/.jobIdwhere the action looks:Result:
Container created successfully (ID: , Job ID: ).— both empty.Empty
JOB_IDthen makes Phase 2 pollGET /api/v1/jobs/(trailing-empty id). With no job id, the API returns the SPA HTML page (HTTP 200), sojq emptyrejects it as non-JSON on every one of the 30 attempts, then the step times out.So the failure isn't a transient outage — it's a consistent schema mismatch introduced by the API cutover while
@mainis consumed mid-migration.Suggested fixes
action.ymlparsing to the new schema (e.g. read the list from.data[...]and the create response/job id from their new locations), orcontainers,container.id,jobId) until the action is updated, andJOB_IDis empty after create, fail fast with a clear error instead of polling/api/v1/jobs/(which returns HTML and wastes 5 minutes). Optionally validate that the jobs endpoint returnsapplication/jsonbefore parsing.Environment
mieweb/launchpad@main(failing commit ≈0d8f90d)pull_request(opened/synchronize)/api/v1/sites/1/containers,/api/v1/jobs/<id>