forked from OurHike/OurHike
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexpected-settings.yml
More file actions
323 lines (300 loc) · 15.9 KB
/
Copy pathexpected-settings.yml
File metadata and controls
323 lines (300 loc) · 15.9 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# Every GitHub Actions secret and variable this repository's workflows read,
# and which tab each one is supposed to live on.
#
# The page this describes is Settings -> Secrets and variables -> Actions.
# Nothing in a checkout can see it: a secret's value is write-only once set,
# so neither the API nor the maintainer who set it can read one back. The
# consequence is that a name set on the wrong tab, or never set at all, stays
# invisible until some workflow needs it - which for the R2 credentials means
# halfway through a publish, and for DATA_BASE_URL meant an app that built and
# installed cleanly and then could not download a map.
#
# This file and .github/tests/test_repository_settings.py close that gap
# between them: this says what is supposed to be there, and the tests say
# whether it is - the live half from inside Actions, which is the only place
# that can see.
#
# One home per item (CONTRIBUTING.md): no entry here lists which workflows use
# it. The tests derive that from .github/workflows/, and a hand-kept copy is
# exactly the half that would go stale. LAUNCH_CHECKLIST.md steps 1.3 and 2
# stay the prose instructions for setting these up; this is the same statement
# in a form a test can read, not a second copy to keep in sync.
#
# where:
# secret the Secrets tab - a value nobody outside CI should read.
# variable the Variables tab - public enough to appear in a build
# log, and worth keeping readable there.
# github-provided minted per run by GitHub, never configured by hand. The
# live check does not look for these in the settings.
#
# required: whether a workflow genuinely cannot do its job without it.
# why: what breaks when it is missing, not what the name already says.
settings:
R2_ENDPOINT_URL:
where: secret
required: true
why: >-
The account-scoped S3 endpoint every R2 call is addressed to. Without it
publish.py has nowhere to send an upload, and the account id in it is
not something to hand out.
R2_BUCKET:
where: secret
required: true
why: >-
Which bucket a publish overwrites. A publish is a PutObject over live
keys (pipeline/DATA_RELEASES.md), so this naming the wrong bucket is
worse than it naming none.
R2_ACCESS_KEY_ID:
where: secret
required: true
why: >-
Half of the Object Read & Write token pair from LAUNCH_CHECKLIST.md 1.2.
R2_SECRET_ACCESS_KEY:
where: secret
required: true
why: >-
The other half, and the one shown exactly once at creation. If this is
missing it cannot be looked up anywhere - the token has to be reminted.
DATA_BASE_URL:
where: variable
also-accepted-as-secret: true
required: true
why: >-
The public bucket base the client is built against and the freshness
check reads build_state.json from. It belongs on the Variables tab
because it is a public URL and a variable is the only form whose value
survives into a build log where it can be checked - keeping a copy on
the Secrets tab masks it as *** everywhere it would have been readable.
pages.yml accepts either rather than failing on the wrong tab, so this
is declared the same way: a secret-only DATA_BASE_URL works, and both
that and a redundant duplicate are worth saying out loud.
SUPABASE_URL:
where: variable
also-accepted-as-secret: true
required: true
why: >-
The Supabase project the client signs in against and the config check
reads. On the Variables tab for the same reason as DATA_BASE_URL: it is
a public URL, it is inlined into a JS bundle anyone can read, and a
secret would only mask it in the build log where it is worth checking.
The name carries no VITE_ prefix - the workflows add that when handing
it to the build, and naming the variable VITE_SUPABASE_URL is a real
mistake that costs an app which builds fine and cannot sign anyone in.
SUPABASE_ANON_KEY:
where: variable
also-accepted-as-secret: true
required: true
why: >-
The publishable key the client authenticates with. Public by design -
it ships inside the bundle, and what makes that safe is row-level
security rather than the key being hard to find (LAUNCH_CHECKLIST.md
5a). Not to be confused with SUPABASE_JWT_SECRET, which is a real
secret, belongs only to the backend's runtime environment, and is
deliberately absent from this file because no workflow reads it.
AUTH_PROVIDERS:
where: variable
required: false
why: >-
Which sign-in buttons a build offers. Optional because the client
defaults to google when it is unset, so a missing value is a working
app rather than a broken one. Worth declaring anyway: every name in it
must be a provider actually enabled in the Supabase dashboard, and one
that is not is a button reaching an error page. The Supabase config
check is what compares those two lists.
The default was google,email until #397 decided v1's provider set.
Email was in it because switching it on costs nothing, which was true
of the setup and false of the result - Supabase's built-in sender is
not a delivery path this project ships on, so an unset variable built
an app offering a sign-in that could not complete. Setting this to
`google` explicitly is still worth doing; since #397 it agrees with the
default rather than rescuing it.
API_BASE_URL:
where: variable
required: false
why: >-
Where the client sends queued reports - backend/'s FastAPI service, not
the R2 bucket DATA_BASE_URL names and not the Supabase project. Optional
because unset is a genuinely supported state and is what every build
currently produces: the backend is not deployed anywhere yet (#95), and
with no value the app still maps, downloads and writes reports, which
wait in the offline outbox with their authored timestamps instead of
being sent. On the Variables tab because it is a public URL that ships
inside the bundle, the same reasoning as DATA_BASE_URL. Read only by
pages.yml - pr-preview.yml deliberately does not, so a preview cannot
file test reports into the moderation queue a club works from.
UA_DATA_BASE_URL:
where: variable
required: false
why: >-
An explicit override for which published data UA reads, for whoever
needs UA to point somewhere other than its own environment - production's
raw data, or a different bucket entirely. ua.yml computes
`${DATA_BASE_URL}/environments/ua` on its own by default
(features/DATA_ENVIRONMENTS.md), the prefix publish.py writes a UA
publish to, so this is optional in the ordinary case and read only when
someone needs to override that. A prefix in the same bucket rather than
a bucket of its own, still, and for the reason RELEASING.md 14.2 gave:
one bucket means UA is served through the same CORS policy, host and
range machinery a phone uses, so what UA verifies is delivered the way
production is delivered.
UA_SUPABASE_URL:
where: variable
required: false
why: >-
UA's own Supabase project, so that a tester filing a test report is not
writing to the database a club moderates. Optional, and its absence is
the documented fallback rather than a fault: ua.yml uses production's
project for authentication and says so in the log, at the stated cost
that UA testers appear in production's user list (RELEASING.md 3d).
Sharing authentication is survivable; sharing the database is what
UA_API_BASE_URL below exists to prevent.
UA_SUPABASE_ANON_KEY:
where: variable
required: false
why: >-
The other half of UA's Supabase project, and public by design for the
same reason SUPABASE_ANON_KEY is. ua.yml uses the pair or neither - one
without the other falls back to production rather than building a UA
that cannot sign anyone in.
UA_API_BASE_URL:
where: variable
required: false
why: >-
Where UA sends queued reports - the UA backend, never production's.
Optional because unset is a supported and currently normal state: with no
value UA queues reports in the outbox with their authored timestamps,
which is behaviour worth exercising anyway. The reason this exists at all
rather than ua.yml reading API_BASE_URL is that API_BASE_URL names the
production backend, and a UA build that could send would file test
reports into the moderation queue a club works from. ua.yml deliberately
has no fallback for this one setting.
CLOUDFLARE_API_TOKEN:
where: secret
required: true
why: >-
Lets pr-preview.yml upload a preview build to Cloudflare Pages. Needs the
"Cloudflare Pages: Edit" permission and nothing else - it is not the R2
token above and should not be made one, because a token that can publish
previews has no business overwriting the live map data. Required for the
same reason the R2 credentials are: this one is configured now, and a
token can be revoked or expire without anyone touching this repository.
The workflow still degrades politely if it goes missing, which is exactly
why nothing else would notice - the next sign would be someone wondering
why a pull request had no preview.
CLOUDFLARE_ACCOUNT_ID:
where: secret
required: true
why: >-
Which Cloudflare account the preview is uploaded to. On the Secrets tab
for the same reason R2_ENDPOINT_URL is - an account id is not secret in
the way a token is, but it is not something to hand out either, and these
two name the same account. A repository secret rather than an
environment-scoped one, like the R2 four: the settings check resolves
these with no `environment:` set, so a scoped copy would be invisible to
the job whose whole purpose is noticing it had gone.
CLOUDFLARE_PAGES_PROJECT:
where: variable
required: true
why: >-
The Pages project previews are uploaded to, and the middle of every
preview hostname - `https://pr-<n>.<project>.pages.dev`. A variable
rather than a secret because it is public by construction: it is in the
URL posted to every pull request. Declared even though it is only a name,
because it has to match a project that already exists - wrangler does not
create one from CI, and a typo here is a deploy that fails saying the
project was not found.
UA_MIGRATION_DATABASE_URL:
where: secret
required: false
why: >-
UA's Postgres, for migrate.yml to apply the Alembic chain to and for
schema-drift.yml to read back. The MIGRATION_ in the name is not
decoration: this is Supavisor SESSION mode (port 5432 on
aws-<region>.pooler.supabase.com), not the transaction pooler on 6543
that the running app uses - CREATE TABLE, ALTER TABLE and Alembic's
advisory lock all need one session that stays put. Not the direct
endpoint either, from CI: Supabase serves that over IPv6 unless the
project has the IPv4 add-on, and GitHub's hosted runners are IPv4-only.
Optional because unset is the current state and both workflows skip
politely rather than turning every merge to main red - but migrate.yml's
production job refuses to run when this is missing, so an unconfigured
UA blocks production rather than waving it through. A secret rather than
a variable: it carries the database password.
PRODUCTION_MIGRATION_DATABASE_URL:
where: secret
required: false
why: >-
Production's Postgres, same session-mode rule as the UA entry above and
the same reason. Held by a job in the `production` environment so
applying it waits for a reviewer, but declared here as a repository
secret rather than an environment one, like the R2 four - the settings
check resolves these with no `environment:` set, and a scoped copy would
be invisible to it. Optional for now because no schema has ever been
applied (LAUNCH_CHECKLIST.md 5); it stops being optional the day a hiker
can sign in.
PRODUCTION_CONDITIONS_DATABASE_URL:
where: secret
required: false
why: >-
Production's Postgres again, read-only, for publish-conditions.yml to
bake verified closures and public reports into the artifacts the client
reads as its baseline (features/CONDITIONS_DELIVERY.md). A third database URL rather
than a reuse of either MIGRATION one, for the reason those two are
already separate from the DATABASE_URL the app holds: this connects as
`ourhike_conditions_reader`, whose only privilege is SELECT on
`closures` and `reports` behind an RLS policy pinned to verified rows.
A credential that cannot write is a credential a scheduled job may hold.
Deliberately no grant on `profiles` - the artifact names nobody (#430),
and no grant means the exporter could not resolve a person even if a
later edit tried to. Either pooler works here, unlike a migration: this
runs two SELECTs and disconnects, so transaction mode's prepared-
statement problem never arises. Session mode on 5432 is the
recommendation only because it is simpler to hold in your head.
THE USERNAME MUST BE TENANT-QUALIFIED - `ourhike_conditions_reader.
<project-ref>`, not the bare role name. Supavisor routes on the part
after the dot, so a bare role fails at connect with
`FATAL: (ENOTFOUND) tenant/user ... not found`, which reads like the
role was never created rather than like a URL format problem - that is
exactly how the first real run of this workflow failed (2026-08-08).
Optional because unset is the current state and the workflow skips with
a warning rather than failing every night until somebody does the
account work.
UA_CONDITIONS_DATABASE_URL:
where: secret
required: false
why: >-
UA's Postgres, read-only, for publish-conditions.yml's UA leg to bake
UA's own closures and reports from - the same role, grants and RLS the
production entry above describes, in UA's project. It exists because
conditions are the one published data source built from a *database*:
without it there is no way for a tester to verify a closure and see it
appear, except by verifying it in the database a club moderates
(features/DATA_ENVIRONMENTS.md). Optional, and unset is the current
state - the UA Supabase project is account work that has not been done
(#371), so the UA leg warns and publishes nothing while the production
leg runs normally. UA's ATC trail-updates artifact is published either
way, because that one is baked from a reviewed file in git and needs no
credential at all.
PROTECTIONS_READ_TOKEN:
where: secret
required: false
why: >-
A fine-grained PAT with Administration: read, so protections-check.yml can
read branch protection and the environments list. It exists because
GITHUB_TOKEN cannot: repository administration is not among the scopes a
workflow `permissions:` block can request, and asking for one that does
not exist makes the whole file invalid rather than warning - three runs
failed at startup with zero jobs before that was noticed. Optional, and
its absence is a declared state rather than a fault: without it the check
still reads the labels, says which sections it could not see, and stays
green instead of turning main red for something already tracked in #375.
Present but refused is different and fails the run - that means the PAT's
scopes are wrong, which is a thing to fix rather than a thing to accept.
GITHUB_TOKEN:
where: github-provided
required: true
why: >-
Minted per run, never configured. Listed only so that a workflow reading
it does not look like a reference to a setting somebody forgot to
create. What it may do is set per workflow in a permissions: block, not
here.