Repository navigation
154 lines (144 loc) · 7.58 KB
/
Copy pathdeploy-website.yml
File metadata and controls
154 lines (144 loc) · 7.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
name: Deploy Website
# The marketing site deploys on two events, for two different reasons.
#
# A nostream-website release: the changesets release PR bumps the site package,
# and merging that PR makes the changesets workflow tag + publish
# nostream-website@X.Y.Z and create the GitHub release — this workflow deploys
# that tagged build.
#
# A relay release, through the Release workflow: the site's version badge and
# its changelog page are read from the repo at build time (`website/site`'s
# VERSION is the root package.json, and changelog.astro renders the root
# CHANGELOG.md), so both go stale on every relay release while the site itself
# is unchanged. Measured 2026-11-05: the last site release was 2026-10-31, and
# nostream.dev served v3.9.0 through six relay releases to 3.12.1.
#
# The relay path cannot be a `release: published` trigger with a wider `if`.
# The `website` environment admits `main` and `nostream-website@*` only, and a
# deployment is checked against the run's own ref — for a release event that is
# the tag, so `nostream@3.12.1` is refused whatever the job does. `workflow_run`
# is the way in: it runs on the default branch, which the policy admits, and it
# starts only after the image for the release was built and pushed.
#
# Merges to main still do not deploy on their own.
on:
release:
types:
- published
# Only a completed Release run is a release that produced an image. Its
# head_branch is the tag the release named, so this selects relay releases
# (`nostream@X.Y.Z`) and leaves the website ones, which the Release workflow
# skips as `skipped`, alone.
#
# This trigger is not removable, whatever a scanner reports. The `website`
# environment admits only `main` and `nostream-website@*`, and a deployment is
# checked against its own run's ref, so a `release: published` run for
# `nostream@X.Y.Z` is refused at the environment — measured on the policy
# itself, not assumed. `workflow_run` runs on the default branch, which the
# policy admits. The escalation it is reported for is closed by the
# head_repository assertion in the job `if` below: a Release run from a fork
# can never reach this deploy.
workflow_run:
workflows: ["Release"]
types: [completed]
workflow_dispatch:
jobs:
deploy:
# Concurrency lives on the job, not the workflow: a relay release
# (v* tag) that the if below did not select must not enter this deploy
# queue, where it could replace a queued website deploy. Measured: the
# nostream@3.5.1 release run (38845857055) was cancelled with zero
# jobs at the workflow-level group, before the job if could skip it.
concurrency:
group: deploy-website
cancel-in-progress: false
# workflow_dispatch ignores every on: filter — a manual run must not
# publish a non-main branch to production, so dispatch stays main-only.
#
# The relay path has two shapes. A release event gives the tag as the run's
# head_branch. A retried image build — `Release` dispatched from main with the
# tag as an input — gives `main` instead, because a dispatch runs on a branch
# and `workflow_run` cannot see the input, so the branch is the only signal
# available to this job. Both end with a published image, which is what makes
# the site's build-time version and changelog worth redeploying.
if: >-
${{ startsWith(github.event.release.tag_name, 'nostream-website@') ||
(github.event_name == 'workflow_run' &&
github.event.workflow_run.head_repository.full_name == github.repository &&
github.event.workflow_run.conclusion == 'success' &&
(startsWith(github.event.workflow_run.head_branch || '', 'nostream@') ||
(github.event.workflow_run.event == 'workflow_dispatch' &&
github.event.workflow_run.head_branch == 'main'))) ||
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') }}
name: Build and deploy to Cloudflare Workers
runs-on: ubuntu-latest
# Deploy credentials live in this GitHub environment: CLOUDFLARE_API_TOKEN
# secret (Workers Scripts scope) and CLOUDFLARE_ACCOUNT_ID variable.
environment: website
permissions:
contents: read
steps:
# Checkout gets the release event's default: the tag that triggered the
# run (refs/tags/nostream-website@X.Y.Z). The website environment's
# branch policy admits nostream-website@* — every release deploy since
# 1.1.1 has run on the tag ref — so the build is exactly the released
# commit, even if main has advanced by the time the job starts.
- name: Checkout
uses: actions/checkout@v3
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v3
with:
node-version-file: .nvmrc
cache: pnpm
- name: Install workspace dependencies
run: pnpm install --frozen-lockfile
- name: Build the site
working-directory: website/site
env:
SITE_URL: https://nostream.dev
run: pnpm run build
- name: Deploy to Cloudflare Workers
working-directory: website/site
# The shared script retries only a transient failure (#970) and fails on
# the first attempt for an auth or validation error; timeout-minutes
# bounds the retries plus the per-attempt timeout the script applies.
timeout-minutes: 12
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
CANONICAL_HOST: nostream.dev
# The router worker: serves the production assets itself and proxies
# /preview/<n> to per-PR preview workers (deploy-website-preview.yml).
# The site answers on one host. Every other host the routes catch,
# http included, 301s to it, so www/apex cannot serve a second copy
# and split search ranking. --var keeps the deployed value from
# drifting away from the SITE_URL the build above baked in.
run: |
bash "$GITHUB_WORKSPACE/.github/scripts/deploy-with-retry.sh" \
pnpm exec wrangler deploy --config wrangler.router.jsonc --var CANONICAL_HOST:"$CANONICAL_HOST"
- name: Purge the site's CDN cache
# Same reason as the preview workflow's purge: a request that lands
# mid-deploy can keep being served the previous build after the new
# one is live. Production deploys the router, so everything changes
# and the purge is unfiltered (`purge_everything`). Guarded on the
# variable so an unconfigured environment skips instead of POSTing to
# a zone id of "" and the notice says what is missing.
if: vars.CLOUDFLARE_ZONE_ID != ''
continue-on-error: true
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ZONE_ID: ${{ vars.CLOUDFLARE_ZONE_ID }}
run: |
code=$(curl -sS -o /tmp/purge.json -w '%{http_code}' \
-X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/purge_cache" \
-H "authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H 'content-type: application/json' \
--data '{"purge_everything":true}')
if [ "$code" = "200" ]; then
echo "purged the whole nostream.dev cache"
else
echo "::warning::cache purge answered HTTP $code — $(head -c 400 /tmp/purge.json)"
fi
- name: Note a skipped purge
if: vars.CLOUDFLARE_ZONE_ID == ''
run: echo "::notice::CLOUDFLARE_ZONE_ID is not set for the website environment — deployed, cache purge skipped. Add it as an environment VARIABLE to enable purging."