g1t/.g1t/workflows/deploy.yml

204 lines7,768 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow1# Deploys g1t.sh from main, with g1t's own Actions. What it does is
2# scripts/deploy.mjs, the same tool a person runs; docs/DEPLOYING.md is the
3# guide.
4#
5# check the deploy manifest is consistent, and the tool's tests pass
6# plan what changed since each Worker's live commit, and pending migrations
7# migrate pending D1 migrations, before any code
8# core, edge, front the units of each stage, in jobs that share a build;
9# a stage starts only when the one before it succeeded
10#
Fast pages, required checks on the branch, self-hosted runners, honest incidents11# Needs the repository secret CLOUDFLARE_API_TOKEN (a Production row), the
12# variable CLOUDFLARE_ACCOUNT_ID, and api.cloudflare.com among the project's
13# workflow-only domains for deploy.yml in production (Settings, Guardrails),
14# and registry.cloudflare.com there too, to find the runner's image. See
15# docs/DEPLOYING.md.
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow16name: Deploy
17
18on:
19 push:
20 branches: [main]
21 workflow_dispatch:
22 inputs:
23 units:
24 description: "Units to deploy whether or not they changed, comma separated (empty: what changed)"
25 type: string
26 default: ""
27 all:
28 description: "Deploy every unit"
29 type: boolean
30 default: false
31 dry_run:
32 description: "Plan only: deploy nothing"
33 type: boolean
34 default: false
35
36# One deploy at a time, and never one cut off halfway: the next waits.
37concurrency:
38 group: deploy-production
39 cancel-in-progress: false
40
41env:
42 CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
43 CARGO_TERM_COLOR: never
44 WRANGLER_SEND_METRICS: "false"
45
46jobs:
47 check:
48 name: Check
49 runs-on: ubuntu-latest
50 timeout-minutes: 20
51 steps:
52 - uses: actions/checkout@v5
53 - name: Install Wrangler
54 run: npm ci --workspaces=false --no-audit --no-fund
55 - name: The manifest matches every wrangler.jsonc
56 run: node scripts/deploy.mjs manifest --check
57 - name: The deploy tool's tests
58 run: npm run test:deploy
59
60 plan:
61 name: Plan
62 needs: check
63 runs-on: ubuntu-latest
64 environment: production
65 timeout-minutes: 15
66 outputs:
67 migrate: ${{ steps.plan.outputs.migrate }}
68 migrate_units: ${{ steps.plan.outputs.migrate_units }}
69 has_core: ${{ steps.plan.outputs.has_core }}
70 core: ${{ steps.plan.outputs.core }}
71 has_edge: ${{ steps.plan.outputs.has_edge }}
72 edge: ${{ steps.plan.outputs.edge }}
73 has_front: ${{ steps.plan.outputs.has_front }}
74 front: ${{ steps.plan.outputs.front }}
75 steps:
76 - uses: actions/checkout@v5
77 with:
78 # Each Worker's live commit is compared with this one.
79 fetch-depth: 0
80 - name: Install Wrangler
81 run: npm ci --workspaces=false --no-audit --no-fund
82 - name: Plan
83 id: plan
84 env:
85 CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
86 UNITS: ${{ inputs.units }}
87 ALL: ${{ inputs.all }}
88 run: |
89 args=()
90 if [ -n "$UNITS" ]; then args+=(--only "$UNITS" --force); fi
91 if [ "$ALL" = "true" ]; then args+=(--all); fi
92 node scripts/deploy.mjs plan "${args[@]}" --github-output
93
94 migrate:
95 name: Migrations
96 needs: plan
97 if: ${{ needs.plan.outputs.migrate == 'true' && inputs.dry_run != true }}
98 runs-on: ubuntu-latest
99 environment: production
100 timeout-minutes: 20
101 steps:
102 - uses: actions/checkout@v5
103 - name: Install Wrangler
104 run: npm ci --workspaces=false --no-audit --no-fund
105 - name: Apply pending migrations
106 env:
107 CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
108 run: node scripts/deploy.mjs migrate --only "${{ needs.plan.outputs.migrate_units }}"
109
110 core:
111 name: core (${{ matrix.group }})
112 needs: [plan, migrate]
Fast pages, required checks on the branch, self-hosted runners, honest incidents113 # Runs when nothing before it failed: a migrate job skipped for having
114 # nothing to apply is not a failure.
115 if: ${{ !failure() && !cancelled() && needs.plan.outputs.has_core == 'true' && inputs.dry_run != true }}
116 # Rust builds get 4 vCPUs; everything else the standard machine.
117 runs-on: ${{ matrix.rust && 'g1t-4core' || 'ubuntu-latest' }}
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow118 environment: production
119 timeout-minutes: 60
120 strategy:
121 # A deploy cut off halfway is worse than one that finishes: the other
122 # jobs of a stage run on when one fails, and the next stage does not.
123 fail-fast: false
124 max-parallel: 4
125 matrix: ${{ fromJSON(needs.plan.outputs.core) }}
126 steps: &deploy
127 - uses: actions/checkout@v5
128 with:
129 fetch-depth: 0
130 # Rust workers: the wasm target, and worker-build kept between runs
131 # (its version is pinned in scripts/build-rust-worker.mjs).
132 - name: Rust for Workers
133 if: ${{ matrix.rust }}
134 run: rustup target add wasm32-unknown-unknown
135 - name: Cache worker-build
136 if: ${{ matrix.rust }}
137 uses: actions/cache@v4
138 with:
139 path: ~/.cargo/bin/worker-build
140 key: worker-build-${{ runner.os }}-${{ hashFiles('scripts/build-rust-worker.mjs') }}
141 - name: Cache worker-build's tools (wasm-bindgen, esbuild)
142 if: ${{ matrix.rust }}
143 uses: actions/cache@v4
144 with:
145 path: ~/.cache/worker-build
146 key: worker-build-tools-${{ runner.os }}-${{ hashFiles('scripts/build-rust-worker.mjs') }}
147 - name: Cache crates
148 if: ${{ matrix.rust }}
149 uses: actions/cache@v4
150 with:
151 path: ~/.cargo/registry/cache
152 key: cargo-crates-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
153 restore-keys: cargo-crates-${{ runner.os }}-
Fast pages, required checks on the branch, self-hosted runners, honest incidents154 # The compiled dependencies of this job's units, for wasm32 and the
155 # build scripts and proc macros they run. The workspace's own crates
156 # are compiled again whatever is cached (a checkout's sources are
157 # newer), so an entry is saved only when the dependencies change: a
158 # new Cargo.lock, or a new base image (base.json names its Rust).
159 # Otherwise the nearest earlier entry, of any group, is a start.
160 - name: Cache the Cargo target
161 if: ${{ matrix.rust }}
162 uses: actions/cache@v4
163 with:
164 path: |
165 target/release
166 target/wasm32-unknown-unknown/release
167 !target/**/incremental
168 !target/**/*.wasm
169 key: cargo-target-${{ runner.os }}-${{ matrix.group }}-${{ hashFiles('Cargo.lock', 'services/runner/base.json') }}
170 restore-keys: |
171 cargo-target-${{ runner.os }}-${{ matrix.group }}-
172 cargo-target-${{ runner.os }}-
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow173 - name: Install
174 run: node scripts/deploy.mjs install --only "${{ matrix.units }}"
175 - name: Deploy ${{ matrix.units }}
176 env:
177 CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
178 run: node scripts/deploy.mjs deploy --only "${{ matrix.units }}" --force --no-migrations --concurrency 2
179
180 edge:
181 name: edge (${{ matrix.group }})
182 needs: [plan, migrate, core]
Fast pages, required checks on the branch, self-hosted runners, honest incidents183 if: ${{ !failure() && !cancelled() && needs.plan.outputs.has_edge == 'true' && inputs.dry_run != true }}
184 runs-on: ${{ matrix.rust && 'g1t-4core' || 'ubuntu-latest' }}
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow185 environment: production
186 timeout-minutes: 60
187 strategy:
188 fail-fast: false
189 max-parallel: 4
190 matrix: ${{ fromJSON(needs.plan.outputs.edge) }}
191 steps: *deploy
192
193 front:
194 name: front (${{ matrix.group }})
195 needs: [plan, migrate, core, edge]
Fast pages, required checks on the branch, self-hosted runners, honest incidents196 if: ${{ !failure() && !cancelled() && needs.plan.outputs.has_front == 'true' && inputs.dry_run != true }}
197 runs-on: ${{ matrix.rust && 'g1t-4core' || 'ubuntu-latest' }}
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow198 environment: production
199 timeout-minutes: 60
200 strategy:
201 fail-fast: false
202 max-parallel: 4
203 matrix: ${{ fromJSON(needs.plan.outputs.front) }}
204 steps: *deploy