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
79 changes: 75 additions & 4 deletions .github/workflows/_cf-worker-delivery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ name: CF Worker delivery (one target)
# deploy -> smoke -> conditional rollback of THIS kind, independent of every other kind
# (pipeline_phase deploy = deploy only; verify = smoke + rollback from the handed-over deploy result)
#
# `manual: true` (kind persistent-dev only; the public workflow sets it for the protected workflow_dispatch of the approved ref) runs the
# single `manual` job INSTEAD of deploy / smoke / rollback: the same engine in manual mode (stage all: build -> audit -> migration -> publish
# -> smoke -> ledger; NO automatic recovery) or, when dev_build_cmd is empty, the legacy two-command contract (never recorded in the
# last-good ledger). Plan and Migration do not run for a dispatch, so there is no descriptor and no migration hand-over; the job reads its
# settings from inputs_json. Its Environment `<app>/dev`, narrower permissions and concurrency group are properties of the job itself.
#
# Pre-mutation validation and the D1 migration are NOT here: the public workflow runs them once, in its single Migration job, and
# passes the result in (`migration_result` / `migration_json`).
#
Expand Down Expand Up @@ -62,9 +68,13 @@ on:
type: string
default: ''
descriptor:
description: toJSON of THIS kind's target descriptor (versioned, validated by the Plan job and re-validated by the engine). Carries the engine target, GitHub Environment, concurrency groups, expected physical D1, migration owner and smoke mode; never a secret.
description: toJSON of THIS kind's target descriptor (versioned, validated by the Plan job and re-validated by the engine). Carries the engine target, GitHub Environment, concurrency groups, expected physical D1, migration owner and smoke mode; never a secret. `{}` with manual (no Plan runs for the dispatch; unused).
type: string
required: true
manual:
description: persistent-dev only - the protected manual dispatch of the approved ref; runs the `manual` job instead of deploy / smoke / rollback
type: boolean
default: false
outputs:
handoff:
description: the validated deploy result of THIS kind (own publish in the deploy phase, the handed-over one in the verify phase)
Expand Down Expand Up @@ -108,7 +118,7 @@ jobs:
# -------------------------------------------------------------------------
deploy:
name: Deploy
if: inputs.phase != 'verify'
if: inputs.phase != 'verify' && !inputs.manual
runs-on: ${{ inputs.runner }}
timeout-minutes: 45
concurrency:
Expand Down Expand Up @@ -155,7 +165,7 @@ jobs:
smoke:
name: Smoke Tests
needs: [deploy]
if: always() && inputs.phase != 'deploy'
if: always() && inputs.phase != 'deploy' && !inputs.manual
runs-on: ${{ inputs.runner }}
timeout-minutes: 30
permissions:
Expand Down Expand Up @@ -241,7 +251,7 @@ jobs:
rollback:
name: Revert on Verify Failure
needs: [deploy, smoke]
if: always() && inputs.phase != 'deploy' && needs.smoke.outputs.smoke_status == 'failure' && (fromJSON(inputs.descriptor).kind != 'production' || fromJSON(inputs.inputs_json).production_rollback_policy == 'automatic') && fromJSON(needs.smoke.outputs.smoke_json || '{}').gate_blocked != 'true'
if: always() && inputs.phase != 'deploy' && !inputs.manual && needs.smoke.outputs.smoke_status == 'failure' && (fromJSON(inputs.descriptor).kind != 'production' || fromJSON(inputs.inputs_json).production_rollback_policy == 'automatic') && fromJSON(needs.smoke.outputs.smoke_json || '{}').gate_blocked != 'true'
runs-on: ${{ inputs.runner }}
timeout-minutes: 45
concurrency:
Expand Down Expand Up @@ -295,3 +305,64 @@ jobs:
deploy_outputs: ${{ env.HANDOFF }}
migration_outputs: ${{ env.MIGJSON }}
smoke_outputs: ${{ env.SMOKEJSON }}

# -------------------------------------------------------------------------
# Manual persistent Dev (Hybrid) — explicit dispatch of the approved ref only (the caller's `if` checks dispatch_target, strategy hybrid and
# persistent_dev_ref; the engine re-checks the ref). Never a side effect of a feature-branch push. Runs INSTEAD of deploy / smoke / rollback.
# dev_build_cmd set : the SAME delivery engine in manual mode, ONE job (build -> audit -> migration -> publish -> smoke -> ledger,
# recorded verified ONLY after the smoke passes; no automatic recovery)
# dev_build_cmd empty : the legacy two-command contract, unchanged (dev_deploy_cmd builds AND publishes, dev_smoke_cmd checks). It is never
# recorded in the Shared Dev last-good ledger, so automatic recovery can never treat it as known-good.
# The Environment `<app>/dev` is a property of this job, so its protection rules and approvals apply exactly as for the automatic kind.
# -------------------------------------------------------------------------
manual:
name: manual
if: inputs.manual
runs-on: ${{ inputs.runner }}
timeout-minutes: 45
environment: ${{ fromJSON(inputs.inputs_json).app_name }}/dev
concurrency:
group: cfv2-dev-${{ github.repository }}-${{ fromJSON(inputs.inputs_json).d1_lock_scope != '' && fromJSON(inputs.inputs_json).d1_lock_scope || fromJSON(inputs.inputs_json).app_name }}
cancel-in-progress: false
permissions:
contents: read
deployments: write # engine mode only: Dev ledger entry, recorded verified ONLY after the smoke passes
env:
GH_TOKEN: ${{ github.token }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
# ---- Engine contract (dev_build_cmd set)
- id: engine
if: fromJSON(inputs.inputs_json).dev_build_cmd != ''
uses: $/actions/cf-deploy
with:
target: dev
mode: manual
inputs_json: ${{ inputs.inputs_json }}
smoke_username: ${{ secrets.SMOKE_USERNAME }}
smoke_password: ${{ secrets.SMOKE_PASSWORD }}
# ---- Legacy contract (dev_build_cmd empty)
- uses: $/actions/setup
if: fromJSON(inputs.inputs_json).dev_build_cmd == ''
with:
setup-node: 'false'
setup-pnpm: 'false'
- name: Deploy persistent Dev
if: fromJSON(inputs.inputs_json).dev_build_cmd == ''
working-directory: ${{ fromJSON(inputs.inputs_json).app_path }}
env:
CMD: ${{ fromJSON(inputs.inputs_json).dev_deploy_cmd }}
run: |
[ -n "$CMD" ] || { echo "::error::dev_deploy_cmd required"; exit 1; }
bash -c "$CMD"
- name: Dev smoke (Cron/Queue-adjacent checks)
if: fromJSON(inputs.inputs_json).dev_build_cmd == '' && fromJSON(inputs.inputs_json).dev_smoke_cmd != ''
working-directory: ${{ fromJSON(inputs.inputs_json).app_path }}
env:
CMD: ${{ fromJSON(inputs.inputs_json).dev_smoke_cmd }}
run: bash -c "$CMD"
109 changes: 0 additions & 109 deletions .github/workflows/_cf-worker-persistent-dev.yml

This file was deleted.

35 changes: 8 additions & 27 deletions .github/workflows/_cf-worker-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ name: CF Worker Pipeline v2
# |
# Release optional, main only, only after a VERIFIED Production delivery with no rollback
#
# Event handlers outside that DAG: `lifecycle` (_cf-worker-lifecycle.yml: PR comment / branch delete), `persistent-dev`
# (_cf-worker-persistent-dev.yml: Hybrid, protected manual dispatch, same engine in manual mode) and `config-guard`.
# Event handlers outside that DAG: `lifecycle` (_cf-worker-lifecycle.yml: PR comment / branch delete) and `config-guard`.
# The Hybrid protected manual dispatch of persistent Dev is NOT a separate job: it is the `manual` entry of `delivery_persistent_dev`.
# One pipeline migrates at most one physical D1: distinct databases among the enabled targets are rejected by the plan, before any mutation.
# The run-level concurrency group below spans the whole run. Interface, design record and measurements: docs/cf-worker-v2-refactor.md.
#
Expand Down Expand Up @@ -670,11 +670,14 @@ jobs:
delivery_persistent_dev:
name: persistent-dev
needs: [plan, migration]
if: always() && needs.plan.result == 'success' && needs.plan.outputs.on_persistent_dev == 'true' && (needs.migration.result == 'success' || needs.migration.result == 'skipped')
# Two entries, one job: the automatic DAG delivery (plan-driven), or the protected manual dispatch of the approved ref (Hybrid only;
# Plan and Migration are skipped for a dispatch, so this branch needs no `needs` result and never runs on a push).
if: (always() && needs.plan.result == 'success' && needs.plan.outputs.on_persistent_dev == 'true' && (needs.migration.result == 'success' || needs.migration.result == 'skipped')) || (!cancelled() && inputs.pipeline_phase != 'verify' && github.event_name == 'workflow_dispatch' && inputs.dispatch_target == 'persistent-dev' && inputs.deployment_strategy == 'hybrid' && inputs.persistent_dev_ref != '' && github.ref == inputs.persistent_dev_ref)
uses: $/.github/workflows/_cf-worker-delivery.yml
permissions: *delivery_permissions
with:
descriptor: ${{ toJSON(fromJSON(needs.plan.outputs.descriptors)['persistent-dev']) }}
descriptor: ${{ needs.plan.outputs.descriptors && toJSON(fromJSON(needs.plan.outputs.descriptors)['persistent-dev']) || '{}' }}
manual: ${{ inputs.pipeline_phase != 'verify' && github.event_name == 'workflow_dispatch' && inputs.dispatch_target == 'persistent-dev' }}
runner: ${{ inputs.runner }}
inputs_json: ${{ inputs.pipeline_phase == 'verify' && toJSON(fromJSON(inputs.phase_state).inputs) || toJSON(inputs) }}
call_inputs_json: ${{ inputs.pipeline_phase == 'verify' && toJSON(inputs) || '' }}
Expand Down Expand Up @@ -730,7 +733,7 @@ jobs:

# -------------------------------------------------------------------------
# Event handlers outside the delivery DAG, one small reusable workflow each: `lifecycle` (same-repo PR -> comment the verified Preview URL;
# branch delete -> retire the ledger, the Preview is KEPT) and `persistent-dev` (Hybrid, explicit dispatch on the approved ref only).
# branch delete -> retire the ledger, the Preview is KEPT). The Hybrid manual dispatch of persistent Dev is the `manual` entry of `delivery_persistent_dev`.
# -------------------------------------------------------------------------
lifecycle:
name: lifecycle
Expand All @@ -745,25 +748,3 @@ jobs:
preview_worker_name: ${{ inputs.preview_worker_name }}
reserve_native_dev: ${{ inputs.shared_dev_native_preview_on_branch_push }}
runner: ${{ inputs.runner }}

persistent-dev:
name: persistent-dev
if: inputs.pipeline_phase != 'verify' && github.event_name == 'workflow_dispatch' && inputs.dispatch_target == 'persistent-dev' && inputs.deployment_strategy == 'hybrid' && inputs.persistent_dev_ref != '' && github.ref == inputs.persistent_dev_ref
uses: $/.github/workflows/_cf-worker-persistent-dev.yml
permissions:
contents: read
deployments: write
with:
inputs_json: ${{ toJSON(inputs) }}
app_name: ${{ inputs.app_name }}
app_path: ${{ inputs.app_path }}
d1_lock_scope: ${{ inputs.d1_lock_scope }}
dev_build_cmd: ${{ inputs.dev_build_cmd }}
dev_deploy_cmd: ${{ inputs.dev_deploy_cmd }}
dev_smoke_cmd: ${{ inputs.dev_smoke_cmd }}
runner: ${{ inputs.runner }}
secrets:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
SMOKE_USERNAME: ${{ secrets.SMOKE_USERNAME }}
SMOKE_PASSWORD: ${{ secrets.SMOKE_PASSWORD }}
4 changes: 1 addition & 3 deletions .github/workflows/cf-worker-v2-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ on:
- '.github/workflows/_cf-worker-ci.yml'
- '.github/workflows/_cf-worker-delivery.yml'
- '.github/workflows/_cf-worker-lifecycle.yml'
- '.github/workflows/_cf-worker-persistent-dev.yml'
- 'actions/cf-tools/**'
- '.github/workflows/cf-worker-v2-ci.yml'
- '.github/actionlint.yaml'
Expand All @@ -24,7 +23,6 @@ on:
- '.github/workflows/_cf-worker-ci.yml'
- '.github/workflows/_cf-worker-delivery.yml'
- '.github/workflows/_cf-worker-lifecycle.yml'
- '.github/workflows/_cf-worker-persistent-dev.yml'
- 'actions/cf-tools/**'
- '.github/actionlint.yaml'
- 'actions/cf-preview/**'
Expand Down Expand Up @@ -67,4 +65,4 @@ jobs:
- name: actionlint
run: |
bash <(curl -sSfL https://raw.githubusercontent.com/rhysd/actionlint/v1.7.12/scripts/download-actionlint.bash) 1.7.12
./actionlint -color .github/workflows/_cf-worker-v2.yml .github/workflows/_cf-worker-ci.yml .github/workflows/_cf-worker-delivery.yml .github/workflows/_cf-worker-lifecycle.yml .github/workflows/_cf-worker-persistent-dev.yml .github/workflows/_cf-worker-preview-gc.yml .github/workflows/cf-worker-v2-ci.yml
./actionlint -color .github/workflows/_cf-worker-v2.yml .github/workflows/_cf-worker-ci.yml .github/workflows/_cf-worker-delivery.yml .github/workflows/_cf-worker-lifecycle.yml .github/workflows/_cf-worker-preview-gc.yml .github/workflows/cf-worker-v2-ci.yml
Loading
Loading