Skip to content

Commit 5f92ea3

Browse files
committed
feat(showcase): retarget push-to-main redeploy from prod to staging
Push-to-main now redeploys staging only (never prod) via redeploy-env.ts in a dedicated redeploy-staging job, guarded to tolerate per-slot build failures but suppress on wholesale build skip/cancel. Prod stays promote-only.
1 parent 7fad3c1 commit 5f92ea3

1 file changed

Lines changed: 53 additions & 26 deletions

File tree

.github/workflows/showcase_build.yml

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ on:
5656
# whole point of the decoupling: rapid pushes to main no longer cancel
5757
# in-flight builds.
5858

59-
env:
60-
RAILWAY_ENV_ID: "b14919f4-6417-429f-848d-c6ae2201e04f"
59+
# Top-level env intentionally empty: env IDs are sourced from the
60+
# showcase/scripts/railway-envs.ts SSOT by the redeploy-staging job.
6161

6262
permissions:
6363
contents: read
@@ -416,33 +416,60 @@ jobs:
416416
ghcr.io/copilotkit/${{ matrix.service.image }}:${{ github.sha }}
417417
build-args: ${{ steps.build-args.outputs.args }}
418418

419-
- name: Trigger Railway redeploy
420-
if: matrix.service.railway_id != ''
419+
redeploy-staging:
420+
name: Trigger Railway staging redeploy
421+
needs: [detect-changes, build]
422+
# Run if at least one service was in the matrix AND the build job
423+
# was not outright cancelled or skipped. We tolerate per-slot build
424+
# failures: redeploy what we did push (the script only redeploys
425+
# requested services, and slots that failed to push will simply
426+
# re-pull the previous :latest — which is a no-op). However, if the
427+
# build job was SKIPPED entirely (e.g. the verify-image-refs prod
428+
# digest-pin gate failed, blocking build), suppress the redeploy:
429+
# nothing was pushed and there is no reason to redeploy staging.
430+
# Partial build failures (fail-fast: false) still surface as
431+
# needs.build.result == 'failure', so the intent of redeploying what
432+
# did get pushed is preserved.
433+
if: ${{ !cancelled() && needs.detect-changes.outputs.has_changes == 'true' && needs.build.result != 'skipped' && needs.build.result != 'cancelled' }}
434+
runs-on: ubuntu-latest
435+
timeout-minutes: 5
436+
permissions:
437+
contents: read
438+
steps:
439+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
440+
with:
441+
persist-credentials: false
442+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
443+
with:
444+
node-version: 22.x
445+
- name: Compute changed-service list from build matrix
446+
id: changed
421447
env:
422-
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
423-
SERVICE_ID: ${{ matrix.service.railway_id }}
424-
ENV_ID: ${{ env.RAILWAY_ENV_ID }}
448+
MATRIX_JSON: ${{ needs.detect-changes.outputs.matrix }}
425449
run: |
426-
# Trigger Railway to pull the freshly-pushed :latest image.
427-
# Not all services have environmentPatchCommit auto-update
428-
# configured, so an explicit redeploy is the reliable path.
429-
RESULT=$(curl -s --retry 2 --retry-all-errors --retry-delay 1 \
430-
--fail-with-body \
431-
-X POST "https://backboard.railway.com/graphql/v2" \
432-
-H "Authorization: Bearer $RAILWAY_TOKEN" \
433-
-H "Content-Type: application/json" \
434-
-d "{\"query\":\"mutation { serviceInstanceRedeploy(serviceId: \\\"$SERVICE_ID\\\", environmentId: \\\"$ENV_ID\\\") }\"}")
435-
ERRORS=$(echo "$RESULT" | jq -r '.errors[]?.message // empty')
436-
if [ -n "$ERRORS" ]; then
437-
echo "::error::Railway redeploy failed for ${{ matrix.service.dispatch_name }}: $ERRORS"
438-
exit 1
439-
fi
440-
OK=$(echo "$RESULT" | jq -r '.data.serviceInstanceRedeploy // false')
441-
if [ "$OK" != "true" ]; then
442-
echo "::error::Railway redeploy did not confirm success for ${{ matrix.service.dispatch_name }}: $RESULT"
443-
exit 1
450+
# detect-changes.outputs.matrix is a JSON array of objects, each with
451+
# a dispatch_name field. We extract just those names into a CSV the
452+
# redeploy-env.ts --services flag understands. resolveTargetServices()
453+
# in the script accepts either SSOT keys OR dispatch_names.
454+
set -euo pipefail
455+
csv="$(echo "$MATRIX_JSON" | jq -r '[.[] | .dispatch_name] | join(",")')"
456+
if [ -z "$csv" ]; then
457+
echo "No services in matrix — skipping redeploy."
458+
echo "services=" >> "$GITHUB_OUTPUT"
459+
else
460+
echo "services=$csv" >> "$GITHUB_OUTPUT"
444461
fi
445-
echo "Railway redeploy triggered for ${{ matrix.service.dispatch_name }}"
462+
echo "Computed services CSV: $csv"
463+
- name: Redeploy changed services in staging
464+
if: steps.changed.outputs.services != ''
465+
env:
466+
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
467+
SERVICES_CSV: ${{ steps.changed.outputs.services }}
468+
run: |
469+
# Staging is non-blocking by design: the script always exits 0
470+
# and writes per-service failures into $GITHUB_STEP_SUMMARY. The
471+
# verify-deploy workflow is the real release gate.
472+
npx tsx showcase/scripts/redeploy-env.ts staging --services "$SERVICES_CSV"
446473
447474
notify:
448475
name: Notify on failure

0 commit comments

Comments
 (0)