@@ -336,10 +336,10 @@ jobs:
336336 echo "image=${staging_image}" >> "$GITHUB_OUTPUT"
337337
338338 - name : Copy image from staging
339+ id : copy-image
339340 env :
340341 # Pass the upstream token via env rather than `-t` so it doesn't appear in /proc/<pid>/cmdline.
341342 CPLN_TOKEN_STAGING : ${{ secrets.CPLN_TOKEN_STAGING }}
342- CPLN_UPSTREAM_TOKEN : ${{ secrets.CPLN_TOKEN_STAGING }}
343343 PRODUCTION_APP_NAME : ${{ vars.PRODUCTION_APP_NAME }}
344344 CPLN_ORG_STAGING : ${{ vars.CPLN_ORG_STAGING }}
345345 CPLN_ORG_PRODUCTION : ${{ vars.CPLN_ORG_PRODUCTION }}
@@ -367,9 +367,39 @@ jobs:
367367 exit 1
368368 fi
369369
370+ staging_commit="${STAGING_IMAGE##*_}"
371+ if [[ "${staging_commit}" == "${STAGING_IMAGE}" || -z "${staging_commit}" ]]; then
372+ echo "::error::Staging image '${STAGING_IMAGE}' does not include the expected '_<commit>' suffix."
373+ exit 1
374+ fi
375+
376+ latest_number="$(
377+ cpln image query --org "${CPLN_ORG_PRODUCTION}" --prop "name~${PRODUCTION_APP_NAME}:" -o json |
378+ jq -r --arg prefix "${PRODUCTION_APP_NAME}:" \
379+ '[.items[].name | select(startswith($prefix)) | (try capture("^[^:]+:(?<number>[0-9]+)") catch empty) | .number | tonumber] | max // 0'
380+ )"
381+ production_image="${PRODUCTION_APP_NAME}:$((latest_number + 1))_${staging_commit}"
382+ source_image_ref="${CPLN_ORG_STAGING}.registry.cpln.io/${STAGING_IMAGE}"
383+
384+ upstream_profile="upstream-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
385+ cleanup_upstream_profile() {
386+ cpln profile delete "${upstream_profile}" >/dev/null 2>&1 || true
387+ }
388+ trap cleanup_upstream_profile EXIT
389+
390+ cleanup_upstream_profile
391+ CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln profile create "${upstream_profile}" >/dev/null
392+ CPLN_PROFILE="${upstream_profile}" cpln image docker-login --org "${CPLN_ORG_STAGING}" >/dev/null
393+
370394 copy_status=1
371395 for attempt in $(seq 1 "${copy_image_attempts}"); do
372- if cpflow copy-image-from-upstream -a "${PRODUCTION_APP_NAME}" --org "${CPLN_ORG_PRODUCTION}" --image "${STAGING_IMAGE}"; then
396+ if CPLN_PROFILE="${upstream_profile}" docker manifest inspect "${source_image_ref}" >/dev/null &&
397+ cpln image copy "${STAGING_IMAGE}" \
398+ --profile "${upstream_profile}" \
399+ --org "${CPLN_ORG_STAGING}" \
400+ --to-profile default \
401+ --to-org "${CPLN_ORG_PRODUCTION}" \
402+ --to-name "${production_image}"; then
373403 copy_status=0
374404 break
375405 else
@@ -389,6 +419,8 @@ jobs:
389419 exit "${copy_status}"
390420 fi
391421
422+ echo "image=${production_image}" >> "$GITHUB_OUTPUT"
423+
392424 - name : Deploy image to production
393425 env :
394426 PRODUCTION_APP_NAME : ${{ vars.PRODUCTION_APP_NAME }}
@@ -553,21 +585,23 @@ jobs:
553585 HEALTHY : ${{ steps.health-check.outputs.healthy }}
554586 PREVIOUS_IMAGE : ${{ steps.capture-current.outputs.current_image }}
555587 PREVIOUS_VERSION : ${{ steps.capture-current.outputs.current_version }}
556- DEPLOYED_IMAGE : ${{ steps.staging -image.outputs.image }}
588+ COPIED_IMAGE : ${{ steps.copy -image.outputs.image }}
557589 shell : bash
558590 run : |
559591 {
560592 echo "## Promotion Summary"
561593 echo
562594 if [[ "${HEALTHY}" == "true" ]]; then
563595 echo "✅ Status: deployment successful"
596+ deployed_image="${COPIED_IMAGE}"
564597 else
565598 echo "❌ Status: deployment failed"
599+ deployed_image="${PREVIOUS_IMAGE}"
566600 fi
567601 echo
568602 echo "Previous image: \`${PREVIOUS_IMAGE}\`"
569603 echo "Previous version: ${PREVIOUS_VERSION}"
570- echo "Deployed image: \`${DEPLOYED_IMAGE }\`"
604+ echo "Deployed image: \`${deployed_image }\`"
571605 } >> "$GITHUB_STEP_SUMMARY"
572606
573607 create-github-release :
0 commit comments