ci: add job-level concurrency to the cd-cdk, cd-sam and iOS deploys

cd-dotnet-eb already serialises deploys per environment; the other three
deploy reusables had no concurrency group, so back-to-back merges could
start overlapping runs against the same target. CloudFormation rejects a
concurrent update on the same stack and cd-sam/cd-cdk pre-flight already
hard-fails on an in-progress stack, so the symptom is a failed run that
needs a manual re-run rather than a corrupted deploy. Grouping makes
those deploys queue instead.

Each group key names the thing being deployed, so independent targets in
one caller repo still deploy in parallel:

  cd-sam         region + stack-name (both always non-empty)
  cd-cdk         region + stacks + stack-name
  cd-mobile-ios  working-directory + fastlane-lane (both default)

cd-cdk keys on the stack selector rather than stack-name because
seahaven-org-baseline calls it from five jobs in a single run, one per
AWS account, and two of those pass no stack-name. Keying on stack-name
alone would collapse them into one group and serialise five independent
per-account deploys.

cancel-in-progress is false on all three, matching cd-dotnet-eb: unlike
CI, cancelling a deploy midway can leave infrastructure mid-update.
This commit is contained in:
Adam Moussa 2026-07-28 12:34:17 -04:00
parent 3bdf11cd69
commit 8b8fc1ac46
No known key found for this signature in database
3 changed files with 31 additions and 0 deletions

View file

@ -56,6 +56,19 @@ jobs:
deploy: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 30 timeout-minutes: 30
# Serialise per deploy target so two pushes cannot deploy over each other.
# The target is the stack selector, NOT stack-name: a multi-account app can
# call this workflow from several jobs in one run (seahaven-org-baseline
# runs five, one per AWS account) and stack-name is optional, so keying on
# it alone would collapse the selector-only jobs into one group and
# serialise deploys that are genuinely independent. `stacks` always defaults
# to "--all", so the group is never empty and back-to-back deploys of the
# same target still queue.
# cancel-in-progress is FALSE on purpose: unlike CI, aborting midway can
# leave a stack mid-update.
concurrency:
group: cd-cdk-${{ inputs.region }}-${{ inputs.stacks }}-${{ inputs.stack-name }}
cancel-in-progress: false
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7

View file

@ -56,6 +56,15 @@ jobs:
deploy-ios: deploy-ios:
runs-on: macos-26 runs-on: macos-26
timeout-minutes: ${{ inputs.timeout-minutes }} timeout-minutes: ${{ inputs.timeout-minutes }}
# Serialise per app + lane so two pushes cannot upload over each other.
# There is no app-identifier input: the target is whatever Fastfile lives in
# working-directory, so that plus the lane is what identifies the deploy.
# Both always default ("." and "ios beta"), so the group is never empty.
# cancel-in-progress is FALSE on purpose: unlike CI, aborting midway can
# leave a half-uploaded TestFlight build.
concurrency:
group: cd-mobile-ios-${{ inputs.working-directory }}-${{ inputs.fastlane-lane }}
cancel-in-progress: false
defaults: defaults:
run: run:
working-directory: ${{ inputs.working-directory }} working-directory: ${{ inputs.working-directory }}

View file

@ -39,6 +39,15 @@ jobs:
deploy: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 timeout-minutes: 15
# Serialise per stack so two pushes cannot deploy over each other.
# stack-name is required and region always defaults, so the group is never
# empty; the pair is exactly what identifies a CloudFormation stack, so
# different stacks in the same caller repo still deploy in parallel.
# cancel-in-progress is FALSE on purpose: unlike CI, aborting midway can
# leave a stack mid-update.
concurrency:
group: cd-sam-${{ inputs.region }}-${{ inputs.stack-name }}
cancel-in-progress: false
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7