Skip to content

Harden production promotion image copy#755

Merged
justin808 merged 3 commits into
masterfrom
jg-codex/fix-production-promote-copy
Jun 2, 2026
Merged

Harden production promotion image copy#755
justin808 merged 3 commits into
masterfrom
jg-codex/fix-production-promote-copy

Conversation

@justin808

@justin808 justin808 commented Jun 2, 2026

Copy link
Copy Markdown
Member

Summary

  • add a staging image preflight check before production promotion copies the image
  • retry the staging-to-production image copy before failing the promotion
  • fix rollback updates to use CPLN CLI container-name paths instead of unsupported array-index paths

Context

Follow-up to failed production promotion run: https://github.com/shakacode/react-webpack-rails-tutorial/actions/runs/26808757348

Verification

  • git diff --check -- .github/workflows/cpflow-promote-staging-to-production.yml bin/test-cpflow-github-flow bin/pin-cpflow-github-ref .github/cpflow-help.md .controlplane/readme.md
  • bin/conductor-exec bin/test-cpflow-github-flow

Note

Medium Risk
Touches production promotion and rollback paths in CI; misconfiguration could still block promotion or leave rollback ineffective, but changes reduce transient copy failures and fix broken rollback updates.

Overview
Hardens the staging → production promotion workflow so image copy and rollback are more reliable.

The Copy image from staging step now validates COPY_IMAGE_RETRIES / COPY_IMAGE_RETRY_INTERVAL, preflights the staging image with cpln image get before copying, and retries cpflow copy-image-from-upstream with warnings between attempts instead of failing on the first error.

On rollback, workload updates now set images via spec.containers.<container_name>.image (name-based --set paths) instead of array-index paths that the CPLN CLI did not support correctly.

Reviewed by Cursor Bugbot for commit 3fc0314. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Improvements
    • More reliable staging→production image promotion: promotion now verifies the staging image exists before copying and performs configurable retry attempts with warnings and wait intervals on transient failures.
    • More accurate rollback: restores container images by name during rollback, ensuring the correct containers are updated for recovery.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a7891435-3ceb-436a-b6f0-ff283399fd4d

📥 Commits

Reviewing files that changed from the base of the PR and between 60aefcc and 3fc0314.

📒 Files selected for processing (1)
  • .github/workflows/cpflow-promote-staging-to-production.yml

Walkthrough

A GitHub Actions workflow now sets COPY_IMAGE_RETRIES and COPY_IMAGE_RETRY_INTERVAL, prechecks the staging image with cpln image get, retries the staging→production image copy on failure, and refactors rollback updates to set container images by name instead of by array index.

Changes

Staging-to-Production Promotion Workflow

Layer / File(s) Summary
Image copy retry configuration and implementation
.github/workflows/cpflow-promote-staging-to-production.yml
Adds workflow env vars COPY_IMAGE_RETRIES and COPY_IMAGE_RETRY_INTERVAL (repo-overridable). The "Copy image from staging" step verifies the upstream image with cpln image get, validates retry env values, then runs cpflow copy-image-from-upstream inside a retry loop that logs warnings, sleeps between attempts, and fails after configured retries.
Rollback image selection refactoring
.github/workflows/cpflow-promote-staging-to-production.yml
Rollback now iterates previous_containers as name/image pairs and issues rollback updates using spec.containers.<container_name>.image via cpln workload update instead of index-based spec.containers[<index>].image.

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant cpln
  participant cpflow
  participant cpln_workload
  GitHubActions->>cpln: CPLN_UPSTREAM_TOKEN + cpln image get (verify staging image)
  GitHubActions->>cpflow: cpflow copy-image-from-upstream (retry loop using COPY_IMAGE_RETRIES/COPY_IMAGE_RETRY_INTERVAL)
  cpflow-->>GitHubActions: copy success or failure code
  GitHubActions->>cpln_workload: cpln workload update with spec.containers.<name>.image (rollback)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hop through YAML, retry in tow,
I peek the image where the staging rivers flow,
I try, I wait, I try once more,
Rollback by name — no index chore,
The pipeline hums, and off we go.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Harden production promotion image copy' clearly and concisely describes the main change: adding robustness to the image copy process during production promotion through preflight checks, retry logic, and improved error handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg-codex/fix-production-promote-copy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🚀 Quick Review App Commands

Welcome! Here are the commands you can use in this PR:
They require the repository to have cpflow review apps configured, including the CPLN_TOKEN_STAGING secret.

+review-app-deploy

Deploy your PR branch for testing.

+review-app-delete

Remove the review app when done.

+review-app-help

Show detailed instructions, environment setup, and configuration options.

Comment +review-app-help for full setup details.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c45e3ac6c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

break
fi

copy_status=$?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the failed copy exit status before leaving the if

When cpflow copy-image-from-upstream fails, this assignment records the exit status of the preceding if compound command, not the failed cpflow command; in bash, an if whose then branch does not run exits with status 0. In the scenario where every image-copy attempt fails, copy_status is reset to 0 on each failure, the final error block is skipped, and the workflow proceeds to deploy-image even though the staging image was never copied.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR hardens the production promotion workflow by adding a staging image preflight check, a configurable retry loop around the image copy step, and a fix to the rollback logic that switches from unsupported array-index paths (spec.containers[N].image) to CPLN CLI container-name paths (spec.containers.NAME.image).

  • Preflight check: Before the retry loop, cpln image get is called with the staging token to confirm the image exists; a failure here causes an early, clear exit before any copy is attempted.
  • Retry loop: cpflow copy-image-from-upstream is retried up to COPY_IMAGE_RETRIES (default 3) times with COPY_IMAGE_RETRY_INTERVAL (default 20 s) between attempts; the exit code of the last failed run is preserved and propagated correctly.
  • Rollback path fix: The jq query and --set path are updated to use container names rather than positional indices, matching what the CPLN CLI actually supports.

Confidence Score: 4/5

Safe to merge; all three changes address documented real failures and the core copy/rollback logic is correct.

The preflight check, retry loop, and rollback path fix are all well-formed. The only thing worth a second look is that the per-attempt warning is suppressed on the final retry, leaving a small observability gap in the logs — but this has no effect on correctness or production safety.

No files require special attention beyond the minor logging gap on the last retry attempt in the copy step.

Important Files Changed

Filename Overview
.github/workflows/cpflow-promote-staging-to-production.yml Adds staging image preflight check, retry logic for the copy step, and fixes rollback to use CPLN CLI container-name paths; logic is sound with one minor exit-code reporting nuance.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant CPLN_S as CPLN Staging API
    participant CPFLOW as cpflow CLI
    participant CPLN_P as CPLN Production API

    GH->>CPLN_S: cpln image get STAGING_IMAGE (preflight)
    alt Image not found
        CPLN_S-->>GH: error → step fails early
    else Image exists
        CPLN_S-->>GH: 200 OK

        loop Retry up to COPY_IMAGE_RETRIES times
            GH->>CPFLOW: copy-image-from-upstream
            CPFLOW->>CPLN_S: pull image
            CPFLOW->>CPLN_P: push image
            alt Copy succeeds
                CPLN_P-->>GH: success → break
            else "Copy fails (attempt < max)"
                GH->>GH: warning + sleep COPY_IMAGE_RETRY_INTERVAL
            end
        end

        alt All retries exhausted
            GH->>GH: error + exit copy_status
        else Copy succeeded
            GH->>CPLN_P: deploy-image
            alt Deploy healthy
                CPLN_P-->>GH: health check pass
            else Deploy unhealthy
                GH->>CPLN_P: cpln workload update spec.containers.NAME.image (rollback)
                GH->>GH: wait for rollback readiness
            end
        end
    end
Loading

Reviews (1): Last reviewed commit: "Harden production promotion image copy" | Re-trigger Greptile

Comment on lines +360 to +364
copy_status=$?
if [[ "${attempt}" -lt "${COPY_IMAGE_RETRIES}" ]]; then
echo "::warning::Image copy attempt ${attempt}/${COPY_IMAGE_RETRIES} failed with exit ${copy_status}; retrying in ${COPY_IMAGE_RETRY_INTERVAL}s."
sleep "${COPY_IMAGE_RETRY_INTERVAL}"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The last failed attempt is never explicitly logged before the final error message. When all retries are exhausted the loop exits silently on the last attempt (the inner if [[ "${attempt}" -lt "${COPY_IMAGE_RETRIES}" ]] guard skips both the warning and the sleep), so log consumers see only the summary error with no per-attempt context for the final try. Emitting the warning unconditionally and guarding only the sleep makes every failure visible while still skipping the redundant sleep.

Suggested change
copy_status=$?
if [[ "${attempt}" -lt "${COPY_IMAGE_RETRIES}" ]]; then
echo "::warning::Image copy attempt ${attempt}/${COPY_IMAGE_RETRIES} failed with exit ${copy_status}; retrying in ${COPY_IMAGE_RETRY_INTERVAL}s."
sleep "${COPY_IMAGE_RETRY_INTERVAL}"
fi
copy_status=$?
echo "::warning::Image copy attempt ${attempt}/${COPY_IMAGE_RETRIES} failed with exit ${copy_status}."
if [[ "${attempt}" -lt "${COPY_IMAGE_RETRIES}" ]]; then
echo "Retrying in ${COPY_IMAGE_RETRY_INTERVAL}s..."
sleep "${COPY_IMAGE_RETRY_INTERVAL}"
fi

@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Code Review

Overall: Solid defensive hardening. The preflight check and retry logic are well-structured, and the rollback path fix is a clear correctness improvement. A few things worth addressing before merge:

Correctness

  • Rollback fix looks correct. Switching from numeric index notation (spec.containers[0].image) to named-container notation (spec.containers.<name>.image) matches what the CPLN CLI actually supports. The current_names fetch is still used for the safety check that refuses rollback when the container set has changed — that logic is intact.

Edge Cases

  • COPY_IMAGE_RETRIES=0 silently skips the copy. seq 1 0 produces no output, so the loop body never executes, copy_status stays at its initialised value of 1, and the job fails with "after 0 attempt(s)" — an odd failure mode if someone misconfigures or tries to use 0 to mean "no retries". Consider guarding with [[ $COPY_IMAGE_RETRIES -gt 0 ]] || COPY_IMAGE_RETRIES=1 or documenting that 0 is unsupported.

  • Preflight failure has no custom context message. When cpln image get exits non-zero, the only output is whatever the cpln CLI prints. The copy-failure path has an explicit echo ::error:: naming the staging org and image; the preflight doesn't. Suggest adding an error echo before the cpln image get call (or as a || trap) so the Actions log is self-explanatory without reading cpln's raw output.

Minor

  • copy_status capture is correct. After if cmd; then …; fi with no else, bash sets $? to cmd's exit code when the condition was false — so copy_status=$? correctly captures cpflow's non-zero status on each failed attempt.
  • Secret exposure surface is unchanged. Using CPLN_TOKEN="${CPLN_TOKEN_STAGING}" as an inline assignment for cpln image get is consistent with the existing pattern and keeps the value out of /proc/<pid>/cmdline.

set -euo pipefail
cpflow copy-image-from-upstream -a "${PRODUCTION_APP_NAME}" --org "${CPLN_ORG_PRODUCTION}" --image "${STAGING_IMAGE}"

CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln image get "${STAGING_IMAGE}" --org "${CPLN_ORG_STAGING}" -o json >/dev/null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preflight failure has no custom error message — the only output will be whatever cpln prints. The copy-failure path (a few lines below) has a clear ::error:: annotation; suggest matching that here so the Actions log is immediately actionable:

Suggested change
CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln image get "${STAGING_IMAGE}" --org "${CPLN_ORG_STAGING}" -o json >/dev/null
CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln image get "${STAGING_IMAGE}" --org "${CPLN_ORG_STAGING}" -o json >/dev/null \
|| { echo "::error::Staging image '${STAGING_IMAGE}' not found in org '${CPLN_ORG_STAGING}'; aborting promotion." >&2; exit 1; }

CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln image get "${STAGING_IMAGE}" --org "${CPLN_ORG_STAGING}" -o json >/dev/null

copy_status=1
for attempt in $(seq 1 "${COPY_IMAGE_RETRIES}"); do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If COPY_IMAGE_RETRIES is set to 0, seq 1 0 produces no output and the loop body never runs. copy_status stays at 1 (its initial value) and the job exits with "after 0 attempt(s)" — misleading since no attempt was made. Consider clamping to a minimum of 1:

Suggested change
for attempt in $(seq 1 "${COPY_IMAGE_RETRIES}"); do
for attempt in $(seq 1 "$(( COPY_IMAGE_RETRIES > 0 ? COPY_IMAGE_RETRIES : 1 ))"); do

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/cpflow-promote-staging-to-production.yml:
- Around line 353-369: The retry loop is incorrectly capturing the exit status
of the entire if compound (so copy_status can stay 0); modify the loop around
the cpflow command used with COPY_IMAGE_RETRIES so you record the command's exit
code immediately after running cpflow copy-image-from-upstream (store it into
copy_status or a temp var), then test that recorded code to decide success (set
copy_status=0 and break on 0, otherwise keep the non‑zero value and
retry/sleep); update the references to copy_status in the subsequent error/exit
logic accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b09ccd22-3cfc-4325-b009-ca8639ea68a6

📥 Commits

Reviewing files that changed from the base of the PR and between 86f315a and c45e3ac.

📒 Files selected for processing (1)
  • .github/workflows/cpflow-promote-staging-to-production.yml

Comment thread .github/workflows/cpflow-promote-staging-to-production.yml
@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Code Review: Harden production promotion image copy

Overview

This PR makes three targeted improvements to the staging→production promotion workflow:

  1. Preflight check – validates the staging image exists before attempting a copy.
  2. Retry loop – wraps cpflow copy-image-from-upstream in a configurable retry with tunable defaults (COPY_IMAGE_RETRIES=3, COPY_IMAGE_RETRY_INTERVAL=20s).
  3. Rollback path fix – switches from unsupported array-index paths (spec.containers[${index}].image) to container-name paths (spec.containers.${container_name}.image).

The motivation is well-justified by the linked failed production run, and each change is narrowly scoped.


What works well

  • Retry structure is correct. The if cmd; then ... else copy_status=$?; fi pattern inside a set -e shell is the right way to capture failures without triggering the errexit trap. The sleep is correctly skipped after the final attempt.
  • No sleep after the last retry. The [[ "${attempt}" -lt "${COPY_IMAGE_RETRIES}" ]] guard is correct.
  • Rollback simplification is a clear improvement. The old jq expression used index() to map names→array-positions then formatted [N] paths, which the CLI doesn't support. Direct spec.containers.${container_name}.image paths are both simpler and correct.
  • Secret handling is intentional and sound. CPLN_TOKEN_STAGING (used by the preflight cpln call) and CPLN_UPSTREAM_TOKEN (used by cpflow copy-image-from-upstream) reference the same secret but serve different tools. Passing both via env rather than CLI flags keeps the token out of /proc/<pid>/cmdline.

Issues / Suggestions

1. Preflight failure is silent beyond cpln's own output (minor)

If cpln image get fails, set -e exits immediately with only the tool's native output. A quick || { echo "::error::..."; exit 1; } annotation would make the log immediately actionable without digging into cpln error text. See inline comment on line 351.

2. COPY_IMAGE_RETRIES=0 gives a confusing error (minor edge case)

seq 1 0 produces no output so the loop body never executes. copy_status stays at its initial value of 1, and the step fails with "after 0 attempt(s)" — accurate but surprising. A one-line guard ([[ "${COPY_IMAGE_RETRIES}" -ge 1 ]]) or a note in the env-var comment would prevent a head-scratcher. See inline comment on line 353.

3. Container names with dots (cosmetic / low risk)

CPLN container names follow DNS-label rules (alphanumeric + hyphens), so dots should never appear in practice. No action needed.


Security

No new attack surface. Values flow from vars/secrets into env vars consumed by trusted binaries. No dynamic string interpolation into shell commands.


Verdict

The changes are correct, well-scoped, and directly address a documented production failure. The two minor issues above are suggestions, not blockers. Ready to merge with or without the cosmetic improvements.

set -euo pipefail
cpflow copy-image-from-upstream -a "${PRODUCTION_APP_NAME}" --org "${CPLN_ORG_PRODUCTION}" --image "${STAGING_IMAGE}"

CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln image get "${STAGING_IMAGE}" --org "${CPLN_ORG_STAGING}" -o json >/dev/null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preflight is good, but if cpln image get fails the step exits immediately via set -e with only whatever cpln itself prints. Consider attaching an explicit ::error:: annotation so the failure is immediately obvious in the Actions summary without scrolling through raw cpln output:

Suggested change
CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln image get "${STAGING_IMAGE}" --org "${CPLN_ORG_STAGING}" -o json >/dev/null
CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln image get "${STAGING_IMAGE}" --org "${CPLN_ORG_STAGING}" -o json >/dev/null \
|| { echo "::error::Staging image '${STAGING_IMAGE}' not found in org '${CPLN_ORG_STAGING}'; aborting promotion."; exit 1; }

Minor UX improvement — not a blocker.

Comment on lines +353 to +354
copy_status=1
for attempt in $(seq 1 "${COPY_IMAGE_RETRIES}"); do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edge case: if COPY_IMAGE_RETRIES=0 (user misconfiguration), seq 1 0 yields nothing so the loop body never runs. copy_status stays at 1 and the step fails with "after 0 attempt(s)" — correct but confusing. A guard here makes the intent explicit:

Suggested change
copy_status=1
for attempt in $(seq 1 "${COPY_IMAGE_RETRIES}"); do
copy_status=1
[[ "${COPY_IMAGE_RETRIES}" -ge 1 ]] || { echo "::error::COPY_IMAGE_RETRIES must be >= 1 (got '${COPY_IMAGE_RETRIES}')."; exit 1; }
for attempt in $(seq 1 "${COPY_IMAGE_RETRIES}"); do

Low priority — the default of 3 protects the common case.

@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Code Review

Overall: LGTM with minor nits. This is a solid reliability improvement that directly addresses a real production failure. The logic is correct, the shell scripting is careful, and the rollback fix is a meaningful simplification.


What the PR does

  1. Preflight check – validates the staging image exists in the staging org before attempting the cross-org copy, failing fast with a clear error instead of a cryptic copy failure.
  2. Retry loop – wraps cpflow copy-image-from-upstream in a configurable retry loop (default 3 retries, 20s interval) with proper exit-status propagation.
  3. Rollback fix – replaces fragile array-index --set paths (spec.containers[N].image) with name-based paths (spec.containers.<name>.image) that the CPLN CLI actually supports.

Positives

  • 10#${COPY_IMAGE_RETRIES} forces decimal interpretation – good defence against accidental octal parsing if someone sets the var to 08 or similar.
  • copy_status=1 initialised before the loop, so a hypothetical zero-iteration loop can't silently succeed.
  • $? in the else branch of if cpflow ...; then ...; else copy_status=$?; fi correctly captures cpflow's non-zero exit – subtle but right.
  • set -euo pipefail is preserved; the retry loop intentionally avoids -e breaking out early via the if wrapper, which is the correct pattern.
  • Removing the current_names | index(...) jq for rollback eliminates a whole class of "container order changed between capture and rollback" bugs.

Issues / suggestions

Minor (line 383): The "no attempts remain" message uses ::warning:: but at this point the copy has definitively failed – ::error:: would match the severity and make the GitHub Actions annotation visually distinct from the transient retry warnings above it. (The subsequent ::error:: on line 388 catches it anyway, so this is cosmetic.)

Nit (lines 341–342): CPLN_TOKEN_STAGING and CPLN_TOKEN_UPSTREAM both resolve to secrets.CPLN_TOKEN_STAGING. The duplication is intentional (different names used for different commands in the script), but a short inline comment noting why both exist would help future readers.

See inline comments for specifics.

echo "::warning::Image copy attempt ${attempt}/${copy_image_attempts} failed with exit ${copy_status}; retrying in ${copy_image_retry_interval}s."
sleep "${copy_image_retry_interval}"
else
echo "::warning::Image copy attempt ${attempt}/${copy_image_attempts} failed with exit ${copy_status}; no attempts remain."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The final retry message uses ::warning:: even though the copy has definitively failed at this point (no attempts remain). Consider upgrading to ::error:: so it's visually grouped with the failure annotation on line 388 rather than looking like one of the transient retry warnings above it.

Suggested change
echo "::warning::Image copy attempt ${attempt}/${copy_image_attempts} failed with exit ${copy_status}; no attempts remain."
echo "::error::Image copy attempt ${attempt}/${copy_image_attempts} failed with exit ${copy_status}; no attempts remain."

Comment on lines +341 to 342
CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
CPLN_UPSTREAM_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both env vars resolve to the same secret (secrets.CPLN_TOKEN_STAGING). The duplication is intentional – CPLN_TOKEN_STAGING is used explicitly for the cpln image get preflight (CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln ...), while CPLN_UPSTREAM_TOKEN is consumed by cpflow copy-image-from-upstream internally. A short comment here would make this less surprising for future readers:

Suggested change
CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
CPLN_UPSTREAM_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }}
# CPLN_TOKEN_STAGING: used explicitly for the staging preflight (cpln image get).
# CPLN_UPSTREAM_TOKEN: consumed internally by cpflow copy-image-from-upstream.
CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
CPLN_UPSTREAM_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }}

@justin808 justin808 merged commit 8a2f155 into master Jun 2, 2026
11 checks passed
@justin808 justin808 deleted the jg-codex/fix-production-promote-copy branch June 2, 2026 18:38
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

✅ Review App Deleted

Review app for PR #755 is deleted

🎮 Control Plane Console
📋 View Workflow Logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant