cpflow 5.1.0 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/actions/cpflow-wait-for-health/action.yml +11 -4
- data/.github/workflows/cpflow-promote-staging-to-production.yml +224 -37
- data/.github/workflows/rspec-shared.yml +8 -1
- data/CHANGELOG.md +15 -1
- data/Gemfile.lock +1 -1
- data/README.md +4 -0
- data/docs/assets/logo/favicon.ico +0 -0
- data/docs/assets/logo/icon-1024.png +0 -0
- data/docs/assets/logo/icon-128.png +0 -0
- data/docs/assets/logo/icon-16.png +0 -0
- data/docs/assets/logo/icon-192.png +0 -0
- data/docs/assets/logo/icon-24.png +0 -0
- data/docs/assets/logo/icon-32.png +0 -0
- data/docs/assets/logo/icon-48.png +0 -0
- data/docs/assets/logo/icon-512.png +0 -0
- data/docs/assets/logo/icon-64.png +0 -0
- data/docs/assets/logo/icon-tile.svg +17 -0
- data/docs/assets/logo/mark-transparent.svg +16 -0
- data/docs/ci-automation.md +43 -2
- data/docs/commands.md +5 -1
- data/lib/command/maintenance_off.rb +1 -0
- data/lib/command/maintenance_on.rb +1 -0
- data/lib/command/run.rb +25 -5
- data/lib/core/maintenance_mode.rb +93 -6
- data/lib/cpflow/version.rb +1 -1
- data/lib/github_flow_templates/.github/cpflow-help.md +13 -1
- data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +224 -39
- metadata +14 -2
|
@@ -109,6 +109,53 @@ jobs:
|
|
|
109
109
|
variable:STAGING_APP_NAME
|
|
110
110
|
variable:PRODUCTION_APP_NAME
|
|
111
111
|
|
|
112
|
+
- name: Normalize Control Plane org names
|
|
113
|
+
id: cpln-orgs
|
|
114
|
+
env:
|
|
115
|
+
CPLN_ORG_STAGING: ${{ vars.CPLN_ORG_STAGING }}
|
|
116
|
+
CPLN_ORG_PRODUCTION: ${{ vars.CPLN_ORG_PRODUCTION }}
|
|
117
|
+
shell: bash
|
|
118
|
+
run: |
|
|
119
|
+
set -euo pipefail
|
|
120
|
+
|
|
121
|
+
sanitize_control_plane_name() {
|
|
122
|
+
local label="$1"
|
|
123
|
+
local value="$2"
|
|
124
|
+
|
|
125
|
+
value="${value#"${value%%[![:space:]]*}"}"
|
|
126
|
+
value="${value%"${value##*[![:space:]]}"}"
|
|
127
|
+
|
|
128
|
+
if [[ "${value}" == *$'\r'* || "${value}" == *$'\n'* ]]; then
|
|
129
|
+
echo "::error::${label} contains embedded line endings; remove them from the repository variable instead of relying on normalization." >&2
|
|
130
|
+
exit 1
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
printf '%s' "${value}"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
validate_control_plane_org() {
|
|
137
|
+
local label="$1"
|
|
138
|
+
local value="$2"
|
|
139
|
+
|
|
140
|
+
if ! [[ "${value}" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]]; then
|
|
141
|
+
local display_value
|
|
142
|
+
display_value="$(printf '%q' "${value}")"
|
|
143
|
+
echo "::error::${label} (${display_value}) must be a valid Control Plane org name; use lowercase alphanumeric characters and hyphens only, with no leading or trailing hyphen." >&2
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
staging_org="$(sanitize_control_plane_name "CPLN_ORG_STAGING" "${CPLN_ORG_STAGING}")"
|
|
149
|
+
production_org="$(sanitize_control_plane_name "CPLN_ORG_PRODUCTION" "${CPLN_ORG_PRODUCTION}")"
|
|
150
|
+
|
|
151
|
+
validate_control_plane_org "CPLN_ORG_STAGING" "${staging_org}"
|
|
152
|
+
validate_control_plane_org "CPLN_ORG_PRODUCTION" "${production_org}"
|
|
153
|
+
|
|
154
|
+
{
|
|
155
|
+
echo "staging=${staging_org}"
|
|
156
|
+
echo "production=${production_org}"
|
|
157
|
+
} >> "$GITHUB_OUTPUT"
|
|
158
|
+
|
|
112
159
|
- name: Capture release context
|
|
113
160
|
id: release-context
|
|
114
161
|
env:
|
|
@@ -127,7 +174,7 @@ jobs:
|
|
|
127
174
|
uses: ./.cpflow/.github/actions/cpflow-setup-environment
|
|
128
175
|
with:
|
|
129
176
|
token: ${{ secrets.CPLN_TOKEN_PRODUCTION }}
|
|
130
|
-
org: ${{
|
|
177
|
+
org: ${{ steps.cpln-orgs.outputs.production }}
|
|
131
178
|
working_directory: .cpflow
|
|
132
179
|
cpln_cli_version: ${{ vars.CPLN_CLI_VERSION }}
|
|
133
180
|
cpflow_version: ${{ vars.CPFLOW_VERSION }}
|
|
@@ -200,42 +247,100 @@ jobs:
|
|
|
200
247
|
CPLN_TOKEN_PRODUCTION: ${{ secrets.CPLN_TOKEN_PRODUCTION }}
|
|
201
248
|
STAGING_APP_NAME: ${{ vars.STAGING_APP_NAME }}
|
|
202
249
|
PRODUCTION_APP_NAME: ${{ vars.PRODUCTION_APP_NAME }}
|
|
203
|
-
CPLN_ORG_STAGING: ${{
|
|
204
|
-
CPLN_ORG_PRODUCTION: ${{
|
|
250
|
+
CPLN_ORG_STAGING: ${{ steps.cpln-orgs.outputs.staging }}
|
|
251
|
+
CPLN_ORG_PRODUCTION: ${{ steps.cpln-orgs.outputs.production }}
|
|
252
|
+
WORKLOAD_NAMES: ${{ steps.workloads.outputs.names }}
|
|
205
253
|
shell: bash
|
|
206
254
|
run: |
|
|
207
255
|
set -euo pipefail
|
|
208
256
|
|
|
209
|
-
|
|
210
|
-
|
|
257
|
+
list_gvc_env_names() {
|
|
258
|
+
local token="$1"
|
|
259
|
+
local org="$2"
|
|
260
|
+
local app="$3"
|
|
261
|
+
|
|
262
|
+
CPLN_TOKEN="${token}" cpln gvc get "${app}" --org "${org}" -o json |
|
|
263
|
+
jq -r '.spec.env // [] | .[] | .name // empty' |
|
|
264
|
+
sort -u
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
list_workload_env_names() {
|
|
268
|
+
local token="$1"
|
|
269
|
+
local org="$2"
|
|
270
|
+
local app="$3"
|
|
271
|
+
local workload="$4"
|
|
272
|
+
|
|
273
|
+
CPLN_TOKEN="${token}" cpln workload get "${workload}" --gvc "${app}" --org "${org}" -o json |
|
|
274
|
+
jq -r '.spec.containers // [] | .[] | (.env // [])[]? | .name // empty' |
|
|
275
|
+
sort -u
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
check_required_vars() {
|
|
279
|
+
local staging_scope="$1"
|
|
280
|
+
local production_scope="$2"
|
|
281
|
+
local missing_message="$3"
|
|
282
|
+
local staging_vars="$4"
|
|
283
|
+
local production_vars="$5"
|
|
284
|
+
local missing_vars
|
|
285
|
+
local production_only_vars
|
|
286
|
+
|
|
287
|
+
if [[ -z "${staging_vars}" ]]; then
|
|
288
|
+
echo "Staging ${staging_scope} exposes no environment variables; skipping parity check."
|
|
289
|
+
return
|
|
290
|
+
fi
|
|
211
291
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
292
|
+
# Treat staging as the promotion source of truth: fail when a variable
|
|
293
|
+
# present in staging is missing in production. Production-only variables
|
|
294
|
+
# are allowed, but surface them so teams can spot drift.
|
|
295
|
+
missing_vars="$(comm -23 <(printf '%s\n' "${staging_vars}") <(printf '%s\n' "${production_vars}"))"
|
|
296
|
+
production_only_vars="$(comm -13 <(printf '%s\n' "${staging_vars}") <(printf '%s\n' "${production_vars}"))"
|
|
216
297
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
production_only_vars="$(comm -13 <(printf '%s\n' "${staging_vars}") <(printf '%s\n' "${production_vars}"))"
|
|
298
|
+
if [[ -n "${production_only_vars}" ]]; then
|
|
299
|
+
echo "::warning::Production ${production_scope} has environment variables that are not present in staging:"
|
|
300
|
+
echo "${production_only_vars}"
|
|
301
|
+
fi
|
|
222
302
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
303
|
+
if [[ -n "${missing_vars}" ]]; then
|
|
304
|
+
echo "::error::${missing_message}"
|
|
305
|
+
echo "${missing_vars}"
|
|
306
|
+
env_check_failed=1
|
|
307
|
+
fi
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# check_required_vars intentionally mutates env_check_failed in this
|
|
311
|
+
# shell; keep calls outside subshells so failures aggregate before the
|
|
312
|
+
# final exit.
|
|
313
|
+
env_check_failed=0
|
|
314
|
+
|
|
315
|
+
staging_vars="$(list_gvc_env_names "${CPLN_TOKEN_STAGING}" "${CPLN_ORG_STAGING}" "${STAGING_APP_NAME}")"
|
|
316
|
+
production_vars="$(list_gvc_env_names "${CPLN_TOKEN_PRODUCTION}" "${CPLN_ORG_PRODUCTION}" "${PRODUCTION_APP_NAME}")"
|
|
317
|
+
check_required_vars \
|
|
318
|
+
"GVC '${STAGING_APP_NAME}'" \
|
|
319
|
+
"GVC '${PRODUCTION_APP_NAME}'" \
|
|
320
|
+
"Production GVC '${PRODUCTION_APP_NAME}' is missing environment variables that exist in staging" \
|
|
321
|
+
"${staging_vars}" \
|
|
322
|
+
"${production_vars}"
|
|
227
323
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
324
|
+
while IFS= read -r workload_name; do
|
|
325
|
+
[[ -n "${workload_name}" ]] || continue
|
|
326
|
+
|
|
327
|
+
staging_workload_vars="$(list_workload_env_names "${CPLN_TOKEN_STAGING}" "${CPLN_ORG_STAGING}" "${STAGING_APP_NAME}" "${workload_name}")"
|
|
328
|
+
production_workload_vars="$(list_workload_env_names "${CPLN_TOKEN_PRODUCTION}" "${CPLN_ORG_PRODUCTION}" "${PRODUCTION_APP_NAME}" "${workload_name}")"
|
|
329
|
+
check_required_vars \
|
|
330
|
+
"workload '${workload_name}'" \
|
|
331
|
+
"workload '${workload_name}'" \
|
|
332
|
+
"Production workload '${workload_name}' is missing environment variables that exist in staging" \
|
|
333
|
+
"${staging_workload_vars}" \
|
|
334
|
+
"${production_workload_vars}"
|
|
335
|
+
done < <(tr ',' '\n' <<< "${WORKLOAD_NAMES}")
|
|
336
|
+
|
|
337
|
+
exit "${env_check_failed}"
|
|
233
338
|
|
|
234
339
|
- name: Capture current production image
|
|
235
340
|
id: capture-current
|
|
236
341
|
env:
|
|
237
342
|
PRODUCTION_APP_NAME: ${{ vars.PRODUCTION_APP_NAME }}
|
|
238
|
-
CPLN_ORG_PRODUCTION: ${{
|
|
343
|
+
CPLN_ORG_PRODUCTION: ${{ steps.cpln-orgs.outputs.production }}
|
|
239
344
|
WORKLOAD_NAMES: ${{ steps.workloads.outputs.names }}
|
|
240
345
|
PRIMARY_WORKLOAD: ${{ steps.workloads.outputs.primary }}
|
|
241
346
|
shell: bash
|
|
@@ -293,7 +398,7 @@ jobs:
|
|
|
293
398
|
env:
|
|
294
399
|
CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
|
|
295
400
|
STAGING_APP_NAME: ${{ vars.STAGING_APP_NAME }}
|
|
296
|
-
CPLN_ORG_STAGING: ${{
|
|
401
|
+
CPLN_ORG_STAGING: ${{ steps.cpln-orgs.outputs.staging }}
|
|
297
402
|
WORKLOAD_NAMES: ${{ steps.workloads.outputs.names }}
|
|
298
403
|
PRIMARY_WORKLOAD: ${{ steps.workloads.outputs.primary }}
|
|
299
404
|
shell: bash
|
|
@@ -335,14 +440,17 @@ jobs:
|
|
|
335
440
|
|
|
336
441
|
echo "image=${staging_image}" >> "$GITHUB_OUTPUT"
|
|
337
442
|
|
|
443
|
+
- name: Set up Docker Buildx
|
|
444
|
+
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5
|
|
445
|
+
|
|
338
446
|
- name: Copy image from staging
|
|
447
|
+
id: copy-image
|
|
339
448
|
env:
|
|
340
|
-
# Pass the upstream token via env rather than `-t` so it doesn't appear in /proc/<pid>/cmdline.
|
|
341
449
|
CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
|
|
342
|
-
|
|
450
|
+
CPLN_TOKEN_PRODUCTION: ${{ secrets.CPLN_TOKEN_PRODUCTION }}
|
|
343
451
|
PRODUCTION_APP_NAME: ${{ vars.PRODUCTION_APP_NAME }}
|
|
344
|
-
CPLN_ORG_STAGING: ${{
|
|
345
|
-
CPLN_ORG_PRODUCTION: ${{
|
|
452
|
+
CPLN_ORG_STAGING: ${{ steps.cpln-orgs.outputs.staging }}
|
|
453
|
+
CPLN_ORG_PRODUCTION: ${{ steps.cpln-orgs.outputs.production }}
|
|
346
454
|
STAGING_IMAGE: ${{ steps.staging-image.outputs.image }}
|
|
347
455
|
shell: bash
|
|
348
456
|
run: |
|
|
@@ -362,14 +470,82 @@ jobs:
|
|
|
362
470
|
copy_image_attempts=$((copy_image_retries + 1))
|
|
363
471
|
copy_image_retry_interval=$((10#${COPY_IMAGE_RETRY_INTERVAL}))
|
|
364
472
|
|
|
365
|
-
|
|
473
|
+
staging_image="${STAGING_IMAGE}"
|
|
474
|
+
if [[ -z "${staging_image}" ]]; then
|
|
475
|
+
echo "::error::STAGING_IMAGE is not set or is empty."
|
|
476
|
+
exit 1
|
|
477
|
+
fi
|
|
478
|
+
|
|
479
|
+
if ! CPLN_TOKEN="${CPLN_TOKEN_STAGING}" cpln image get "${staging_image}" --org "${CPLN_ORG_STAGING}" -o json >/dev/null; then
|
|
366
480
|
echo "::error::Staging image '${STAGING_IMAGE}' was not found in org '${CPLN_ORG_STAGING}'; aborting promotion."
|
|
367
481
|
exit 1
|
|
368
482
|
fi
|
|
369
483
|
|
|
484
|
+
staging_tag=""
|
|
485
|
+
if [[ "${staging_image}" == *@* ]]; then
|
|
486
|
+
staging_tag="${staging_image##*@}"
|
|
487
|
+
elif [[ "${staging_image}" == *:* ]]; then
|
|
488
|
+
staging_tag="${staging_image##*:}"
|
|
489
|
+
fi
|
|
490
|
+
staging_commit=""
|
|
491
|
+
if [[ "${staging_tag}" == *_* ]]; then
|
|
492
|
+
staging_commit="${staging_tag##*_}"
|
|
493
|
+
else
|
|
494
|
+
echo "::warning::Staging image '${staging_image}' did not include a '_<commit>' suffix; production image tag will omit the commit suffix."
|
|
495
|
+
fi
|
|
496
|
+
|
|
497
|
+
# The workflow-level concurrency group serializes this sequence so two
|
|
498
|
+
# production promotions cannot derive and publish the same next tag.
|
|
499
|
+
# See the top-level concurrency group: cpflow-promote-staging-to-production.
|
|
500
|
+
latest_number="$(
|
|
501
|
+
cpln image query --org "${CPLN_ORG_PRODUCTION}" --prop "name~${PRODUCTION_APP_NAME}:" --max 0 -o json |
|
|
502
|
+
jq -r --arg prefix "${PRODUCTION_APP_NAME}:" \
|
|
503
|
+
'[.items[].name | select(startswith($prefix)) | (try capture("^[^:]+:(?<number>[0-9]+)") catch empty) | .number | tonumber] | max // 0'
|
|
504
|
+
)"
|
|
505
|
+
if ! [[ "${latest_number}" =~ ^[0-9]+$ ]]; then
|
|
506
|
+
echo "::error::Could not determine the next production image number for app '${PRODUCTION_APP_NAME}' in org '${CPLN_ORG_PRODUCTION}'."
|
|
507
|
+
exit 1
|
|
508
|
+
fi
|
|
509
|
+
|
|
510
|
+
production_image="${PRODUCTION_APP_NAME}:$((latest_number + 1))"
|
|
511
|
+
if [[ -n "${staging_commit}" ]]; then
|
|
512
|
+
production_image="${production_image}_${staging_commit}"
|
|
513
|
+
fi
|
|
514
|
+
|
|
515
|
+
staging_registry="${CPLN_ORG_STAGING}.registry.cpln.io"
|
|
516
|
+
production_registry="${CPLN_ORG_PRODUCTION}.registry.cpln.io"
|
|
517
|
+
source_image_ref="${staging_registry}/${STAGING_IMAGE}"
|
|
518
|
+
production_image_ref="${production_registry}/${production_image}"
|
|
519
|
+
|
|
520
|
+
docker_config_dir="$(mktemp -d)"
|
|
521
|
+
cleanup_copy_credentials() {
|
|
522
|
+
rm -rf "${docker_config_dir}"
|
|
523
|
+
}
|
|
524
|
+
trap cleanup_copy_credentials EXIT
|
|
525
|
+
|
|
526
|
+
export DOCKER_CONFIG="${docker_config_dir}"
|
|
527
|
+
|
|
528
|
+
if ! printf '%s' "${CPLN_TOKEN_STAGING}" |
|
|
529
|
+
docker login "${staging_registry}" -u '<token>' --password-stdin >/dev/null; then
|
|
530
|
+
echo "::error::Failed to authenticate to staging registry '${staging_registry}'."
|
|
531
|
+
exit 1
|
|
532
|
+
fi
|
|
533
|
+
|
|
534
|
+
if ! printf '%s' "${CPLN_TOKEN_PRODUCTION}" |
|
|
535
|
+
docker login "${production_registry}" -u '<token>' --password-stdin >/dev/null; then
|
|
536
|
+
echo "::error::Failed to authenticate to production registry '${production_registry}'."
|
|
537
|
+
exit 1
|
|
538
|
+
fi
|
|
539
|
+
|
|
540
|
+
if docker buildx imagetools inspect "${production_image_ref}" >/dev/null 2>&1; then
|
|
541
|
+
echo "::error::Production image '${production_image}' already exists in org '${CPLN_ORG_PRODUCTION}'; aborting to avoid overwriting it."
|
|
542
|
+
exit 1
|
|
543
|
+
fi
|
|
544
|
+
|
|
370
545
|
copy_status=1
|
|
371
546
|
for attempt in $(seq 1 "${copy_image_attempts}"); do
|
|
372
|
-
if
|
|
547
|
+
if docker buildx imagetools inspect "${source_image_ref}" >/dev/null &&
|
|
548
|
+
docker buildx imagetools create --prefer-index=false --tag "${production_image_ref}" "${source_image_ref}"; then
|
|
373
549
|
copy_status=0
|
|
374
550
|
break
|
|
375
551
|
else
|
|
@@ -389,10 +565,12 @@ jobs:
|
|
|
389
565
|
exit "${copy_status}"
|
|
390
566
|
fi
|
|
391
567
|
|
|
568
|
+
echo "image=${production_image}" >> "$GITHUB_OUTPUT"
|
|
569
|
+
|
|
392
570
|
- name: Deploy image to production
|
|
393
571
|
env:
|
|
394
572
|
PRODUCTION_APP_NAME: ${{ vars.PRODUCTION_APP_NAME }}
|
|
395
|
-
CPLN_ORG_PRODUCTION: ${{
|
|
573
|
+
CPLN_ORG_PRODUCTION: ${{ steps.cpln-orgs.outputs.production }}
|
|
396
574
|
RELEASE_PHASE_FLAG: ${{ steps.release-phase.outputs.flag }}
|
|
397
575
|
shell: bash
|
|
398
576
|
run: |
|
|
@@ -402,6 +580,9 @@ jobs:
|
|
|
402
580
|
if [[ -n "${RELEASE_PHASE_FLAG}" ]]; then
|
|
403
581
|
deploy_args+=("${RELEASE_PHASE_FLAG}")
|
|
404
582
|
fi
|
|
583
|
+
# `cpflow deploy-image` deploys the latest image for the app. The
|
|
584
|
+
# workflow-level concurrency group keeps production promotion copy and
|
|
585
|
+
# deploy steps coupled across workflow runs.
|
|
405
586
|
deploy_args+=(--org "${CPLN_ORG_PRODUCTION}" --verbose)
|
|
406
587
|
|
|
407
588
|
cpflow deploy-image "${deploy_args[@]}"
|
|
@@ -412,7 +593,7 @@ jobs:
|
|
|
412
593
|
with:
|
|
413
594
|
workload_name: ${{ steps.workloads.outputs.primary }}
|
|
414
595
|
app_name: ${{ vars.PRODUCTION_APP_NAME }}
|
|
415
|
-
org: ${{
|
|
596
|
+
org: ${{ steps.cpln-orgs.outputs.production }}
|
|
416
597
|
max_retries: ${{ env.HEALTH_CHECK_RETRIES }}
|
|
417
598
|
interval_seconds: ${{ env.HEALTH_CHECK_INTERVAL }}
|
|
418
599
|
accepted_statuses: ${{ env.HEALTH_CHECK_ACCEPTED_STATUSES }}
|
|
@@ -422,7 +603,7 @@ jobs:
|
|
|
422
603
|
env:
|
|
423
604
|
ROLLBACK_STATE: ${{ steps.capture-current.outputs.rollback_state }}
|
|
424
605
|
PRODUCTION_APP_NAME: ${{ vars.PRODUCTION_APP_NAME }}
|
|
425
|
-
CPLN_ORG_PRODUCTION: ${{
|
|
606
|
+
CPLN_ORG_PRODUCTION: ${{ steps.cpln-orgs.outputs.production }}
|
|
426
607
|
shell: bash
|
|
427
608
|
run: |
|
|
428
609
|
# Best-effort rollback: try every workload, aggregate failures, exit non-zero at the end
|
|
@@ -484,7 +665,7 @@ jobs:
|
|
|
484
665
|
env:
|
|
485
666
|
ROLLBACK_STATE: ${{ steps.capture-current.outputs.rollback_state }}
|
|
486
667
|
PRODUCTION_APP_NAME: ${{ vars.PRODUCTION_APP_NAME }}
|
|
487
|
-
CPLN_ORG_PRODUCTION: ${{
|
|
668
|
+
CPLN_ORG_PRODUCTION: ${{ steps.cpln-orgs.outputs.production }}
|
|
488
669
|
shell: bash
|
|
489
670
|
run: |
|
|
490
671
|
set -euo pipefail
|
|
@@ -510,8 +691,10 @@ jobs:
|
|
|
510
691
|
set -euo pipefail
|
|
511
692
|
ready=false
|
|
512
693
|
for attempt in $(seq 1 "${ROLLBACK_READINESS_RETRIES}"); do
|
|
513
|
-
|
|
514
|
-
|
|
694
|
+
workload_status="$(cpln workload get "${workload_name}" --gvc "${PRODUCTION_APP_NAME}" --org "${CPLN_ORG_PRODUCTION}" -o json)"
|
|
695
|
+
deployment_ready="$(echo "${workload_status}" | jq -r '.status.ready // false')"
|
|
696
|
+
latest_ready="$(echo "${workload_status}" | jq -r '.status.readyLatest // false')"
|
|
697
|
+
if [[ "${deployment_ready}" == "true" && "${latest_ready}" == "true" ]]; then
|
|
515
698
|
ready=true
|
|
516
699
|
break
|
|
517
700
|
fi
|
|
@@ -553,7 +736,7 @@ jobs:
|
|
|
553
736
|
HEALTHY: ${{ steps.health-check.outputs.healthy }}
|
|
554
737
|
PREVIOUS_IMAGE: ${{ steps.capture-current.outputs.current_image }}
|
|
555
738
|
PREVIOUS_VERSION: ${{ steps.capture-current.outputs.current_version }}
|
|
556
|
-
|
|
739
|
+
COPIED_IMAGE: ${{ steps.copy-image.outputs.image }}
|
|
557
740
|
shell: bash
|
|
558
741
|
run: |
|
|
559
742
|
{
|
|
@@ -561,13 +744,15 @@ jobs:
|
|
|
561
744
|
echo
|
|
562
745
|
if [[ "${HEALTHY}" == "true" ]]; then
|
|
563
746
|
echo "✅ Status: deployment successful"
|
|
747
|
+
deployed_image="${COPIED_IMAGE}"
|
|
564
748
|
else
|
|
565
749
|
echo "❌ Status: deployment failed"
|
|
750
|
+
deployed_image="${PREVIOUS_IMAGE}"
|
|
566
751
|
fi
|
|
567
752
|
echo
|
|
568
753
|
echo "Previous image: \`${PREVIOUS_IMAGE}\`"
|
|
569
754
|
echo "Previous version: ${PREVIOUS_VERSION}"
|
|
570
|
-
echo "Deployed image: \`${
|
|
755
|
+
echo "Deployed image: \`${deployed_image}\`"
|
|
571
756
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
572
757
|
|
|
573
758
|
create-github-release:
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cpflow
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.1.
|
|
4
|
+
version: 5.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Gordon
|
|
@@ -120,6 +120,18 @@ files:
|
|
|
120
120
|
- docs/ai-github-flow-prompt.md
|
|
121
121
|
- docs/assets/cpflow-deploying.svg
|
|
122
122
|
- docs/assets/grafana-alert.png
|
|
123
|
+
- docs/assets/logo/favicon.ico
|
|
124
|
+
- docs/assets/logo/icon-1024.png
|
|
125
|
+
- docs/assets/logo/icon-128.png
|
|
126
|
+
- docs/assets/logo/icon-16.png
|
|
127
|
+
- docs/assets/logo/icon-192.png
|
|
128
|
+
- docs/assets/logo/icon-24.png
|
|
129
|
+
- docs/assets/logo/icon-32.png
|
|
130
|
+
- docs/assets/logo/icon-48.png
|
|
131
|
+
- docs/assets/logo/icon-512.png
|
|
132
|
+
- docs/assets/logo/icon-64.png
|
|
133
|
+
- docs/assets/logo/icon-tile.svg
|
|
134
|
+
- docs/assets/logo/mark-transparent.svg
|
|
123
135
|
- docs/assets/memcached.png
|
|
124
136
|
- docs/assets/sidekiq-pre-stop-hook.png
|
|
125
137
|
- docs/ci-automation.md
|
|
@@ -272,7 +284,7 @@ licenses:
|
|
|
272
284
|
metadata:
|
|
273
285
|
rubygems_mfa_required: 'true'
|
|
274
286
|
post_install_message: |
|
|
275
|
-
cpflow 5.1.
|
|
287
|
+
cpflow 5.1.1 installed.
|
|
276
288
|
|
|
277
289
|
If this repository already uses generated cpflow GitHub Actions, update the
|
|
278
290
|
checked-in wrappers so GitHub loads the matching control-plane-flow release tag:
|