mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-08-09 00:42:46 +08:00
213 lines
8.6 KiB
Markdown
213 lines
8.6 KiB
Markdown
# Promotion Workflow Examples
|
|
|
|
Common end-to-end promotion patterns. Every pattern begins with the mandatory validate step. All commands are keyed on record IDs — resolve names/subjects to IDs first. The promote step is async and returns a promotion ID that is polled separately before running `promotion complete`.
|
|
|
|
---
|
|
|
|
## Example 1 — Promote a single work item
|
|
|
|
**User prompt:** "Promote work item 1fkxx0000000123AAA to the QA stage."
|
|
|
|
```bash
|
|
# 1. Verify auth
|
|
sf org display --json
|
|
|
|
# 2. Mandatory validate (requires the target stage)
|
|
sf devops promotion validate \
|
|
--work-item-id 1fkxx0000000123AAA \
|
|
--target-stage-id 1QVxx0000000QA0AAA \
|
|
--target-org myorg --json
|
|
# → status 0 and .result.success == true, proceed. On non-zero exit, STOP.
|
|
|
|
# 3. Prepare (target stage is required)
|
|
sf devops work-item prepare --work-item-id 1fkxx0000000123AAA --target-stage-id 1QVxx0000000QA0AAA --target-org myorg --json
|
|
|
|
# 4. Promote (async). --skip-validation: this skill already validated (step 2)
|
|
# and prepared (step 3), so the CLI's built-in pre-promote check is redundant.
|
|
PROMOTION_ID=$(sf devops promote \
|
|
--work-item-id 1fkxx0000000123AAA \
|
|
--target-stage-id 1QVxx0000000QA0AAA \
|
|
--skip-validation \
|
|
--target-org myorg \
|
|
--json | jq -r '.result.promotionId // .result.asyncOperationId')
|
|
|
|
echo "Promotion initiated. Promotion ID: $PROMOTION_ID"
|
|
|
|
# 5. Hand off the promotion ID for status polling (a separate concern —
|
|
# do NOT busy-wait here). Only once the async deploy has CONFIRMED
|
|
# completion should the promotion be finalized:
|
|
# sf devops promotion complete --target-stage-id 1QVxx0000000QA0AAA --target-org myorg --json
|
|
```
|
|
|
|
**Report:** "Work item promotion initiated (→ QA stage). Promotion ID: `<id>`. Poll this ID to confirm the deploy completed; run `sf devops promotion complete` only after it reports success."
|
|
|
|
---
|
|
|
|
## Example 2 — Combine and promote multiple work items
|
|
|
|
**User prompt:** "Combine work items 1fkxx…101 and 1fkxx…102 into 1fkxx…100 and promote them to UAT."
|
|
|
|
```bash
|
|
# 1. Validate ALL work items against the target stage in one call — if it fails
|
|
# (non-zero exit), STOP. On success, a non-null .result.combineDetails
|
|
# confirms the work items share components and returns the parent/child
|
|
# grouping to use in step 2.
|
|
sf devops promotion validate \
|
|
--work-item-id 1fkxx0000000100AAA \
|
|
--work-item-id 1fkxx0000000101AAA \
|
|
--work-item-id 1fkxx0000000102AAA \
|
|
--target-stage-id 1QVxx000000UAT0AAA \
|
|
--target-org myorg --json
|
|
|
|
# 2. Combine children into the parent (from .result.combineDetails), targeting the destination stage
|
|
sf devops work-item combine \
|
|
--parent-work-item-id 1fkxx0000000100AAA \
|
|
--child-work-item-id 1fkxx0000000101AAA \
|
|
--child-work-item-id 1fkxx0000000102AAA \
|
|
--target-stage-id 1QVxx000000UAT0AAA \
|
|
--target-org myorg \
|
|
--json
|
|
|
|
# 3. Promote the PARENT work item (async). --skip-validation: all work items
|
|
# were validated in step 1 and combining prepared the unit.
|
|
PROMOTION_ID=$(sf devops promote \
|
|
--work-item-id 1fkxx0000000100AAA \
|
|
--target-stage-id 1QVxx000000UAT0AAA \
|
|
--skip-validation \
|
|
--target-org myorg \
|
|
--json | jq -r '.result.promotionId // .result.asyncOperationId')
|
|
|
|
echo "Combined promotion initiated. Promotion ID: $PROMOTION_ID"
|
|
|
|
# 4. Hand off the promotion ID for status polling. Finalize with
|
|
# `sf devops promotion complete --target-stage-id 1QVxx000000UAT0AAA --target-org myorg --json`
|
|
# ONLY after the async deploy has confirmed completion — do not run it here.
|
|
```
|
|
|
|
**Report:** "Work items combined into parent 1fkxx…100 and promotion initiated (→ UAT). Promotion ID: `<id>`. Poll this ID; run `sf devops promotion complete` only after the deploy reports success."
|
|
|
|
---
|
|
|
|
## Example 3 — Promote an entire stage
|
|
|
|
**User prompt:** "Promote everything in the QA stage to Production."
|
|
|
|
```bash
|
|
# 1. The validate-first gate still applies. `sf devops work-item list` takes
|
|
# --project-id (not a stage flag), so list the project's work items and
|
|
# filter to the approved ones in the QA stage with jq. Validate them against
|
|
# the target stage before promoting — if validation fails, STOP; do not promote.
|
|
#
|
|
# IMPORTANT: capture the list command into a variable with fail-fast checks
|
|
# BEFORE filtering. Do not put `sf devops work-item list | jq` directly in a
|
|
# `for` loop — command substitution in the loop header hides non-zero exits
|
|
# (and an unset/invalid project ID) as an empty iteration, which would skip
|
|
# validation entirely and fall through to `sf devops promote`.
|
|
set -euo pipefail
|
|
PROJECT_ID=1Qg0000000000001
|
|
QA_STAGE_ID=1QVxx0000000QA0AAA
|
|
PROD_STAGE_ID=1QVxx00000PROD0AAA # the target stage promotion validates against
|
|
|
|
if ! WI_LIST_JSON=$(sf devops work-item list --project-id "$PROJECT_ID" --target-org myorg --json); then
|
|
echo "Failed to list work items for project $PROJECT_ID — STOP. Do not promote." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# `sf devops work-item list` returns .result.workItems[]. Filter to the approved
|
|
# work items in the QA stage.
|
|
WI_IDS=$(printf '%s' "$WI_LIST_JSON" \
|
|
| jq -r --arg s "$QA_STAGE_ID" '.result.workItems[] | select(.stageId == $s and .status == "Approved") | .id')
|
|
|
|
if [ -z "$WI_IDS" ]; then
|
|
echo "No approved work items in QA stage $QA_STAGE_ID — STOP. Do not promote." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Validate all approved work items against the TARGET stage in one call (repeat
|
|
# --work-item-id per item). A non-zero exit means validation failed — STOP.
|
|
VALIDATE_ARGS=()
|
|
for WI_ID in $WI_IDS; do VALIDATE_ARGS+=(--work-item-id "$WI_ID"); done
|
|
if ! sf devops promotion validate "${VALIDATE_ARGS[@]}" \
|
|
--target-stage-id "$PROD_STAGE_ID" --target-org myorg --json; then
|
|
echo "Promotion validation failed — STOP. Do not promote the stage." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# 2. All QA work items validated — promote the whole source stage to the
|
|
# target stage (async). Production deploys with Apex should run tests.
|
|
PROMOTION_ID=$(sf devops promote \
|
|
--stage-id "$QA_STAGE_ID" \
|
|
--target-stage-id "$PROD_STAGE_ID" \
|
|
--test-level RunLocalTests \
|
|
--skip-validation \
|
|
--target-org myorg \
|
|
--json | jq -r '.result.promotionId // .result.asyncOperationId')
|
|
|
|
echo "Stage promotion initiated. Promotion ID: $PROMOTION_ID"
|
|
|
|
# 3. Hand off the promotion ID for status polling. Finalize with
|
|
# `sf devops promotion complete --target-stage-id "$PROD_STAGE_ID" --target-org myorg --json`
|
|
# ONLY after the async deploy has confirmed completion — do not run it here.
|
|
```
|
|
|
|
**Report:** "QA → Production stage promotion initiated after validating all approved QA work items. Promotion ID: `<id>`. Poll this ID; run `sf devops promotion complete` only after the deploy reports success."
|
|
|
|
---
|
|
|
|
## Example 4 — Validate-first gate blocks promotion
|
|
|
|
**User prompt:** "Promote work item 1fkxx0000000200AAA."
|
|
|
|
```bash
|
|
sf devops promotion validate \
|
|
--work-item-id 1fkxx0000000200AAA \
|
|
--target-stage-id 1QVxx0000000QA0AAA \
|
|
--target-org myorg --json
|
|
```
|
|
|
|
Response (non-zero exit indicates the work item cannot be promoted):
|
|
```json
|
|
{
|
|
"status": 1,
|
|
"result": {
|
|
"success": false,
|
|
"errorType": "VCS_ERROR",
|
|
"errorDetails": "No pull request exists for the work item on the source branch."
|
|
},
|
|
"warnings": []
|
|
}
|
|
```
|
|
|
|
**Action:** STOP. Do NOT prepare/combine/promote/complete. Report the exact blocking issue from `.result.errorType` / `.result.errorDetails` — here a `VCS_ERROR`: no pull request exists for the work item on the source branch. Remediate the reported cause (create/associate the pull request for the work item), then re-validate only after it is resolved.
|
|
|
|
**Report:** "Promotion blocked — validation failed (`VCS_ERROR`): no pull request exists for the work item on the source branch. Create/associate the PR, then re-validate and retry."
|
|
|
|
---
|
|
|
|
## Example 5 — Idempotent retry in CI
|
|
|
|
**Scenario:** A CI job re-runs after a transient network failure mid-promotion.
|
|
|
|
```bash
|
|
# Re-running validate + prepare is safe — prepare is a no-op if already prepared
|
|
sf devops promotion validate \
|
|
--work-item-id 1fkxx0000000300AAA \
|
|
--target-stage-id 1QVxx0000000QA0AAA \
|
|
--target-org myorg --json
|
|
sf devops work-item prepare --work-item-id 1fkxx0000000300AAA --target-stage-id 1QVxx0000000QA0AAA --target-org myorg --json
|
|
# → treat an already-prepared work item as success
|
|
|
|
# Promote again — capture the promotion ID (--skip-validation: validated and
|
|
# prepared above)
|
|
PROMOTION_ID=$(sf devops promote \
|
|
--work-item-id 1fkxx0000000300AAA \
|
|
--target-stage-id 1QVxx0000000QA0AAA \
|
|
--skip-validation \
|
|
--target-org myorg \
|
|
--json | jq -r '.result.promotionId // .result.asyncOperationId')
|
|
|
|
echo "Promotion ID: $PROMOTION_ID"
|
|
```
|
|
|
|
**Key point:** validate and prepare are idempotent — retries do not create duplicate state or double-prepare.
|