Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions .github/workflows/sync-cloud-run-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,32 @@ concurrency:
cancel-in-progress: false

jobs:
approve-live-traffic:
name: Approve LongBridge production traffic
if: >-
github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' &&
github.run_attempt == 1 &&
((inputs.deployment_mode == 'legacy' && inputs.shift_traffic_to_commit) ||
(inputs.deployment_mode == 'sg-serving-snapshot' && inputs.target == 'SG' && inputs.account_snapshot_enabled == 'true') ||
((inputs.deployment_mode == 'image-only-no-traffic' || inputs.deployment_mode == 'image-only-notification-overlay') &&
inputs.target == 'HK' && inputs.account_snapshot_enabled == 'true'))
Comment thread
Pigbibi marked this conversation as resolved.
Resolved
environment: longbridge-live-approval
runs-on: ubuntu-latest
timeout-minutes: 3
permissions: {}
steps:
- name: Record approval
run: echo "Approved the production traffic change for this dispatch." >> "$GITHUB_STEP_SUMMARY"

image-only-no-traffic:
name: Stage ${{ inputs.target }} image without traffic
if: github.event_name == 'workflow_dispatch' && (inputs.deployment_mode == 'image-only-no-traffic' || inputs.deployment_mode == 'image-only-notification-overlay')
needs: approve-live-traffic
if: >-
!cancelled() && github.event_name == 'workflow_dispatch' &&
(inputs.deployment_mode == 'image-only-no-traffic' || inputs.deployment_mode == 'image-only-notification-overlay') &&
(needs.approve-live-traffic.result == 'success' ||
(needs.approve-live-traffic.result == 'skipped' &&
!(inputs.target == 'HK' && inputs.account_snapshot_enabled == 'true')))
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
Expand Down Expand Up @@ -652,10 +675,14 @@ jobs:
python3 scripts/render_runtime_target_matrix.py --profile sync --github-output

sync:
needs: resolve-matrix
needs: [resolve-matrix, approve-live-traffic]
name: Deploy / Sync ${{ matrix.target.label }} Cloud Run
# Production deployment and environment sync require an explicit operator dispatch.
if: github.event_name == 'workflow_dispatch' && inputs.deployment_mode == 'legacy'
if: >-
!cancelled() && github.event_name == 'workflow_dispatch' &&
inputs.deployment_mode == 'legacy' && needs.resolve-matrix.result == 'success' &&
(needs.approve-live-traffic.result == 'success' ||
(needs.approve-live-traffic.result == 'skipped' && !inputs.shift_traffic_to_commit))
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
Expand Down Expand Up @@ -2312,8 +2339,9 @@ jobs:

sg-serving-snapshot:
name: Enable only the SG read-only snapshot
needs: approve-live-traffic
if: >-
github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' &&
needs.approve-live-traffic.result == 'success' && github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' &&
github.run_attempt == 1 && inputs.deployment_mode == 'sg-serving-snapshot' &&
inputs.target == 'SG' && inputs.approved_ref == 'main' &&
inputs.account_snapshot_enabled == 'true' &&
Expand Down
10 changes: 10 additions & 0 deletions docs/runtime-write-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,13 @@ staging; this mode neither changes configuration nor moves traffic.





## Production traffic approval

LongBridge traffic promotion uses the `longbridge-live-approval` Environment
on `main`. This covers explicit `shift_traffic_to_commit` and the HK and SG snapshot
modes that promote a serving revision. Staging without traffic and
standalone readback skip this approval. The existing runtime-target,
revision and readback checks still run before or after the traffic change
as appropriate.
3 changes: 2 additions & 1 deletion tests/test_runtime_target_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ def test_monitor_and_deploy_workflows_consume_validated_matrix_output():
assert 'render_runtime_target_matrix.py --profile heartbeat --target "$MATRIX_TARGET" --github-output' in text
else:
assert f"render_runtime_target_matrix.py --profile {profile} --github-output" in text
assert "needs: resolve-matrix" in text
dependency = "needs: [resolve-matrix, approve-live-traffic]" if profile == "sync" else "needs: resolve-matrix"
assert dependency in text
assert "matrix: ${{ fromJSON(needs.resolve-matrix.outputs.matrix) }}" in text
assert "vars.RUNTIME_TARGET_ENABLED" in text
# Hardcoded PAPER/HK/SG matrix blocks must not remain in dynamic consumers.
Expand Down
9 changes: 7 additions & 2 deletions tests/test_sync_cloud_run_env_workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ grep -Fq 'name: Deploy / Sync ${{ matrix.target.label }} Cloud Run' "$workflow_f
grep -Fq 'fail-fast: false' "$workflow_file"
grep -Fq 'resolve-matrix:' "$workflow_file"
grep -Fq 'render_runtime_target_matrix.py --profile sync --github-output' "$workflow_file"
grep -Fq 'needs: resolve-matrix' "$workflow_file"
grep -Fq 'needs: [resolve-matrix, approve-live-traffic]' "$workflow_file"
grep -Fq 'matrix: ${{ fromJSON(needs.resolve-matrix.outputs.matrix) }}' "$workflow_file"
grep -Fq "inputs.target == 'PAPER' && 'longbridge-paper'" "$workflow_file"
grep -Fq "inputs.target == 'HK' && 'longbridge-hk'" "$workflow_file"
Expand Down Expand Up @@ -448,7 +448,12 @@ assert "name: Mask WIF identity components" in masking
assert "if: matrix.target.environment == 'longbridge-paper' && steps.config.outputs.enabled == 'true'" in masking
for section in (legacy, cleanup):
assert "inputs.deployment_mode == 'legacy'" in section.split(" steps:", 1)[0]
assert "needs: resolve-matrix" in legacy.split(" steps:", 1)[0]
assert "needs: [resolve-matrix, approve-live-traffic]" in legacy.split(" steps:", 1)[0]
assert "needs.approve-live-traffic.result == 'success'" in legacy.split(" steps:", 1)[0]
assert "needs.approve-live-traffic.result == 'success'" in job.split(" steps:", 1)[0]
sg_snapshot = workflow.split("\n sg-serving-snapshot:\n", 1)[1]
assert "needs: approve-live-traffic" in sg_snapshot.split(" steps:", 1)[0]
assert "needs.approve-live-traffic.result == 'success'" in sg_snapshot.split(" steps:", 1)[0]
assert job.index("name: Validate image-only dispatch") < job.index("uses: google-github-actions/auth@")
assert job.index("name: Verify exact image-only source") < job.index("uses: google-github-actions/auth@")
# Only the service selector, protected WIF identity and immutable overlay inputs are allowed;
Expand Down
Loading