-
Notifications
You must be signed in to change notification settings - Fork 236
Add Platformatic World to worlds-manifest.json #1450
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| --- | ||
|
|
||
| Add Platformatic to `worlds-manifest.json` as a community world, and add a generic `docker` service type to the community-world CI so worlds can declare arbitrary Docker containers in their manifest `services` array. Platformatic's CI job is gated by the existing `if: false` on `e2e-community` until community worlds ship CBOR queue transport support. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,7 +30,7 @@ on: | |
| type: string | ||
| default: '{}' | ||
| service-type: | ||
| description: 'Service container to run (none, mongodb, redis)' | ||
| description: 'Service container to run (none, mongodb, redis, docker)' | ||
| required: false | ||
| type: string | ||
| default: 'none' | ||
|
|
@@ -39,6 +39,11 @@ on: | |
| required: false | ||
| type: string | ||
| default: '' | ||
| services: | ||
| description: 'JSON array of service definitions from the manifest (used when service-type is docker)' | ||
| required: false | ||
| type: string | ||
| default: '[]' | ||
|
|
||
| jobs: | ||
| e2e: | ||
|
|
@@ -82,6 +87,58 @@ jobs: | |
| sleep 2 | ||
| done | ||
|
|
||
| - name: Start Docker services | ||
| if: ${{ inputs.service-type == 'docker' }} | ||
| # Services start serially in manifest order. Each service's health check | ||
| # must pass before the next one starts, so any service that depends on | ||
| # another (e.g. a workflow service that needs postgres) must be listed | ||
| # AFTER its dependency in the manifest's `services` array. | ||
| # | ||
| # `healthCheck.cmd` is executed via `sh -c` below, which means the | ||
| # manifest is a shell-execution surface. Safe here because | ||
| # `worlds-manifest.json` is in-repo and PR-reviewed. | ||
| run: | | ||
| set -euo pipefail | ||
| SERVICES='${{ inputs.services }}' | ||
| count=$(echo "$SERVICES" | jq '. | length') | ||
| for idx in $(seq 0 $((count - 1))); do | ||
| svc=$(echo "$SERVICES" | jq -c ".[$idx]") | ||
| name=$(echo "$svc" | jq -r '.name') | ||
| image=$(echo "$svc" | jq -r '.image') | ||
| health_cmd=$(echo "$svc" | jq -r '.healthCheck.cmd // empty') | ||
| retries=$(echo "$svc" | jq -r '.healthCheck.retries // 10') | ||
|
|
||
| # Build docker run as an array so env values with spaces/quotes | ||
| # survive intact (no shell re-interpretation via eval). | ||
| args=(docker run -d --name "$name" --network host) | ||
| env_lines=$(echo "$svc" | jq -r '.env // {} | to_entries[] | "\(.key)=\(.value)"') | ||
| if [ -n "$env_lines" ]; then | ||
| while IFS= read -r line; do | ||
| args+=(-e "$line") | ||
| done <<< "$env_lines" | ||
| fi | ||
| args+=("$image") | ||
|
|
||
| echo "Starting $name ($image)..." | ||
| "${args[@]}" | ||
|
|
||
| # Health check runs on the host (--network host shares the network). | ||
| if [ -n "$health_cmd" ]; then | ||
| echo "Waiting for $name to be ready..." | ||
| for i in $(seq 1 "$retries"); do | ||
| if sh -c "$health_cmd" &>/dev/null; then | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is explicitly executing the Also: the health check uses
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added an explicit comment at the top of the step calling this out so the assumption is visible. |
||
| echo "$name is ready" | ||
| break | ||
| fi | ||
| if [ "$i" -eq "$retries" ]; then | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: when a health check exhausts retries, this logs
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Now exit 1 when retries are exhausted. |
||
| echo "ERROR: $name health check did not pass after $retries retries" | ||
| exit 1 | ||
| fi | ||
| sleep 2 | ||
| done | ||
| fi | ||
| done | ||
|
|
||
| - name: Setup environment | ||
| uses: ./.github/actions/setup-workflow-dev | ||
| with: | ||
|
|
@@ -135,3 +192,8 @@ jobs: | |
| run: | | ||
| docker stop mongodb 2>/dev/null || true | ||
| docker stop redis 2>/dev/null || true | ||
| if [ '${{ inputs.service-type }}' = 'docker' ]; then | ||
| echo '${{ inputs.services }}' | jq -r '.[].name' 2>/dev/null | while read -r name; do | ||
| docker stop "$name" 2>/dev/null || true | ||
| done | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,14 +46,19 @@ const testableWorlds = manifest.worlds.filter((world) => { | |
| // Build the matrix | ||
| const matrix = { | ||
| world: testableWorlds.map((world) => { | ||
| // Determine service type based on services array | ||
| // Determine service type based on services array. | ||
| // `mongodb` and `redis` have dedicated single-service CI steps; anything | ||
| // else (or any multi-service world) goes through the generic `docker` | ||
| // path driven by the manifest's `services` array. | ||
| let serviceType = 'none'; | ||
| if (world.services && world.services.length > 0) { | ||
| // Use the first service's name as the service type | ||
| // Currently supports: mongodb, redis | ||
| const serviceName = world.services[0].name; | ||
| if (['mongodb', 'redis'].includes(serviceName)) { | ||
| serviceType = serviceName; | ||
| if (world.services.length === 1) { | ||
| const serviceName = world.services[0].name; | ||
| serviceType = ['mongodb', 'redis'].includes(serviceName) | ||
| ? serviceName | ||
| : 'docker'; | ||
| } else { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: the fallback-to-
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added an explicit services.length > 1 branch that routes straight to the docker path, so any multi-service combo (including the [mongodb, redis] case you raised) gets generic handling instead of the single-service fallback. |
||
| serviceType = 'docker'; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -64,6 +69,7 @@ const matrix = { | |
| 'service-type': serviceType, | ||
| 'env-vars': JSON.stringify(world.env || {}), | ||
| 'setup-command': world.setup || '', | ||
| services: JSON.stringify(world.services || []), | ||
| }; | ||
| }), | ||
| }; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -144,6 +144,51 @@ | |
| "services": [], | ||
| "requiresCredentials": true, | ||
| "credentialsNote": "Requires JAZZ_API_KEY, JAZZ_WORKER_ACCOUNT, and JAZZ_WORKER_SECRET from Jazz Cloud" | ||
| }, | ||
| { | ||
| "id": "platformatic", | ||
| "type": "community", | ||
| "package": "@platformatic/world", | ||
| "name": "Platformatic", | ||
| "description": "Platformatic World backed by a centralized workflow service with PostgreSQL for version-safe orchestration on Kubernetes", | ||
| "repository": "https://github.com/platformatic/platformatic-world", | ||
| "docs": "https://github.com/platformatic/platformatic-world", | ||
| "features": [], | ||
| "env": { | ||
| "WORKFLOW_TARGET_WORLD": "@platformatic/world", | ||
| "PLT_WORLD_SERVICE_URL": "http://localhost:3042", | ||
| "PLT_WORLD_APP_ID": "default", | ||
| "PLT_WORLD_DEPLOYMENT_VERSION": "local" | ||
| }, | ||
| "services": [ | ||
| { | ||
| "name": "postgres", | ||
| "image": "postgres:18-alpine", | ||
| "ports": ["5432:5432"], | ||
| "env": { | ||
| "POSTGRES_USER": "wf", | ||
| "POSTGRES_PASSWORD": "wf", | ||
| "POSTGRES_DB": "workflow" | ||
| }, | ||
| "healthCheck": { | ||
| "cmd": "pg_isready -h localhost", | ||
| "retries": 5 | ||
| } | ||
| }, | ||
| { | ||
| "name": "platformatic-workflow", | ||
| "image": "platformatic/workflow:0.7.0", | ||
| "ports": ["3042:3042"], | ||
| "env": { | ||
| "DATABASE_URL": "postgres://wf:wf@localhost:5432/workflow", | ||
| "PORT": "3042" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No explicit service dependency declaration. The platformatic-workflow container depends on postgres being ready (it opens DB connections on startup), but the workflow file starts services serially in manifest order and health-checks each one before starting the next. So this works only because postgres is listed first. If a future world entry lists services in a different order, this will silently break. Consider:
For this PR, just documenting the implicit ordering invariant somewhere would be enough.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Services now start serially and each health check must pass before the next starts, so postgres (listed first) is guaranteed ready before platformatic-workflow boots. |
||
| }, | ||
| "healthCheck": { | ||
| "cmd": "curl -sf http://localhost:3042/status", | ||
| "retries": 10 | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary
eval+ fragile quoting.Issues:
evalis unnecessary here. Every variable already works with direct expansion. Replace with an array:This avoids all shell re-interpretation of values.
Values with spaces/special chars break. If a manifest env value contains a space or quote,
cmd="$cmd -e $key=$val"concatenates it unquoted, andevalthen splits on whitespace. Not currently exploited by the Platformatic manifest, but a booby trap for future world submissions.No
set -e. A failingdocker run(e.g. image pull failure) lets the loop continue and the test run proceeds anyway. Considerset -euo pipefailat the top of therunblock.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rewrote as args=(docker run ...) array — no eval, env values with quotes/spaces survive intact. Also added set -euo pipefail so any failure in the step aborts.