-
-
Notifications
You must be signed in to change notification settings - Fork 0
311 lines (277 loc) · 12.6 KB
/
Copy pathci.yml
File metadata and controls
311 lines (277 loc) · 12.6 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
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
# Cancel in-progress runs on the same PR ref when a new commit lands. Pushes
# to main never cancel — every merge should produce a green run.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
BUN_VERSION: "1.3.14"
jobs:
# Cheap path-filter job so downstream jobs can skip on docs-only PRs and
# so the expensive codegen-drift steps only run when GraphQL inputs
# actually moved. Jobs that depend on this and evaluate `if:` to false
# are reported as "Skipped" — required status checks treat that as
# passing, so the workflow stays compatible with branch protection.
#
# Two paths-filter steps: exclusion-based `code` needs
# predicate-quantifier: every (with the default `some`, `**` matches
# every file and `!**/*.md` never excludes — so CHANGELOG-only PRs
# still ran full lint/test). OR-style filters keep the default `some`.
changes:
name: Detect changes
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
code: ${{ steps.code.outputs.code }}
schema_inputs: ${{ steps.rest.outputs.schema_inputs }}
codegen_inputs: ${{ steps.rest.outputs.codegen_inputs }}
publishable: ${{ steps.rest.outputs.publishable }}
web_build: ${{ steps.rest.outputs.web_build }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
persist-credentials: false
# Exclusions only work with predicate-quantifier: every (dorny/paths-filter).
# Workflow files intentionally stay in scope — changing CI on a
# docs-only commit should still run CI.
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: code
with:
predicate-quantifier: every
filters: |
code:
- '**'
- '!**/*.md'
- '!docs/**'
- '!.context/**'
- '!LICENSE'
# OR-style filters — default predicate-quantifier: some.
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: rest
with:
filters: |
# GraphQL schema snapshot is generated from workers/api source.
# Skip the verify step when nothing here moved.
schema_inputs:
- 'workers/api/**'
- 'packages/core/**'
- 'packages/api-types/graphql/schema.graphql'
# web/codegen consumes the committed schema + web operations.
codegen_inputs:
- 'web/**'
- 'packages/api-types/graphql/schema.graphql'
# The two packages published to npm. A PR touching them should
# carry a changeset — the changeset-check job below only warns.
publishable:
- 'packages/core/**'
- 'packages/api-types/**'
# Mirror web/vercel.json ignoreCommand: Next production build when
# anything Vercel would rebuild has moved. Root oxlint ignores
# web/**, so missing named exports only surface on `next build`.
web_build:
- 'web/**'
- 'packages/**'
- 'scripts/**'
- 'bun.lock'
- 'package.json'
- '.github/workflows/ci.yml'
lint:
name: Lint & Format
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun install --frozen-lockfile
# oxlint lints + type-checks the workspace from the root .oxlintrc.json
# (typescript-go via oxlint-tsgolint; curated semantic rules + typeCheck).
- name: Oxlint
run: bun run lint
- name: Oxfmt
run: bun run format:check
test:
name: Test
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun install --frozen-lockfile
# workers/discovery, workers/mcp, and workers/webhooks are excluded
# from the root workspace and carry their own bun.lock — the root
# install above doesn't populate their node_modules, so per-worker
# tsc + deploy both need a local install. These steps also catch
# stale per-worker lockfiles at PR time (previously a separate set
# of steps at the end of this job; folded in so we don't install
# twice).
- name: Install workers/discovery deps
working-directory: workers/discovery
run: bun install --frozen-lockfile
- name: Install workers/mcp deps
working-directory: workers/mcp
run: bun install --frozen-lockfile
- name: Install workers/webhooks deps
working-directory: workers/webhooks
run: bun install --frozen-lockfile
# workers/mcp is excluded from the root oxlint type-check (carved-out
# workspace with its own bun.lock + Agents SDK tool Zod shapes that
# typescript-go doesn't model yet). Keep a targeted tsc gate here; UI
# sub-build has mcp:ui:typecheck separately.
- name: Type-check (workers/mcp)
working-directory: workers/mcp
run: npx tsc --noEmit
- name: Check migration filenames
if: github.event_name == 'pull_request'
env:
BASE_REF: ${{ github.base_ref }}
run: ./scripts/check-migration-filenames.sh "origin/$BASE_REF"
- name: Pair schema changes with a wrangler migration (PR only)
if: github.event_name == 'pull_request'
env:
BASE_REF: ${{ github.base_ref }}
run: |
# If a schema file changed on this PR, require at least one added
# file under workers/api/migrations/. Catches schema changes that
# ship without a paired wrangler migration.
schema_changed=$(git diff --name-only "origin/$BASE_REF"...HEAD -- \
'packages/core/src/schema.ts' \
'packages/core-internal/src/schema-coverage.ts' \
'workers/api/src/db/schema-cron.ts' \
'workers/api/src/db/schema-auth.ts' \
'workers/api/src/db/schema-follows.ts' || true)
if [ -z "$schema_changed" ]; then
echo "No schema files changed — pairing check skipped."
exit 0
fi
added=$(git diff --name-only --diff-filter=A "origin/$BASE_REF"...HEAD -- 'workers/api/migrations/*.sql' || true)
if [ -z "$added" ]; then
echo "ERROR: schema files changed, but this PR adds no file under workers/api/migrations/." >&2
echo "Changed schema files:" >&2
echo "$schema_changed" | sed 's/^/ /' >&2
echo "Hand-author the wrangler migration and commit it alongside the schema change." >&2
echo "Preview the SQL with: bun run db:generate (output goes to .drizzle-out/)." >&2
exit 1
fi
echo "Schema files changed:"
echo "$schema_changed" | sed 's/^/ /'
echo "Found new wrangler migration file(s):"
echo "$added" | sed 's/^/ /'
- name: Verify GraphQL schema snapshot is up to date
if: needs.changes.outputs.schema_inputs == 'true'
run: |
bun workers/api/scripts/print-graphql-schema.ts
if ! git diff --exit-code packages/api-types/graphql/schema.graphql; then
echo "ERROR: packages/api-types/graphql/schema.graphql is stale." >&2
echo "Regenerate with: bun workers/api/scripts/print-graphql-schema.ts" >&2
exit 1
fi
- name: Verify GraphQL operation codegen is up to date
if: needs.changes.outputs.codegen_inputs == 'true'
# graphql-codegen emits single-quoted, unformatted output; the
# committed copy is oxfmt-normalised by lint-staged on commit.
# Mirror that here before diffing or every PR would fail.
working-directory: web
run: |
bun run codegen
bunx oxfmt --write src/lib/graphql/__generated__/
if ! git diff --exit-code src/lib/graphql/__generated__/; then
echo "ERROR: web/src/lib/graphql/__generated__/ is stale." >&2
echo "Regenerate with: cd web && bun run codegen && bunx oxfmt --write src/lib/graphql/__generated__/" >&2
exit 1
fi
- name: Verify OpenAPI spec covers public-read routes
# Diffs every method registered under a publicReadRoutes prefix
# against the generated /v1/openapi.json. Fails if any public-read
# route is missing describeRoute(...) and isn't on the script's
# ALLOWLIST. Sub-second pure-JS check; no network, D1, or Wrangler.
run: bun scripts/check-openapi-coverage.ts
- name: Verify managed-agent YAML is up to date
# Re-renders the committed managed-agents/*.agent.yaml from the prompt
# builders, AGENT_TOOLS, and skill IDs, and fails if any file drifted.
# Pure codegen — no network or API calls. Regenerate locally with:
# bun scripts/render-managed-agents.ts
run: bun scripts/render-managed-agents.ts --check
- name: Run tests
run: bun run test
# Same production Next build Vercel runs for web/ (Next 16 + Turbopack). Root
# oxlint ignorePatterns include web/**, so type/export errors that fail the
# Vercel deploy never fail lint/test alone. Path filter mirrors
# web/vercel.json ignoreCommand so docs-only PRs stay skipped.
web-build:
name: Web build (Next)
needs: changes
if: needs.changes.outputs.web_build == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
env:
# Public prod API is fine for compile + static generation; no secrets
# required. Matches web/.env.example defaults for a green build.
RELEASES_API_URL: https://api.releases.sh
NEXT_PUBLIC_BETTER_AUTH_URL: https://api.releases.sh
NEXT_PUBLIC_USER_API_KEYS: "false"
NEXT_PUBLIC_DEVICE_AUTH_ENABLED: "false"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: ${{ env.BUN_VERSION }}
# Root install: web is a workspace package and consumes packages/* via
# workspace:*. Matches Vercel installCommand ("bun install") at monorepo root.
- run: bun install --frozen-lockfile
# web/package.json "build": copy install script, build .well-known assets,
# then `next build` (Turbopack in Next 16). Catches missing exports,
# bad imports, and Next compile errors before Vercel.
- name: Next production build
working-directory: web
run: bun run build
# Non-blocking reminder: a PR that touches a published package (packages/core,
# packages/api-types) should usually add a changeset so the release notes +
# version bump are captured. `continue-on-error` makes this advisory — a
# missing changeset surfaces as a warning but never fails the PR. Do NOT add
# this to branch protection.
changeset-check:
name: Changeset present?
needs: changes
if: github.event_name == 'pull_request' && needs.changes.outputs.publishable == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5
continue-on-error: true
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun install --frozen-lockfile
- name: Check for a changeset
env:
BASE_REF: ${{ github.base_ref }}
run: |
if bunx changeset status --since="origin/$BASE_REF"; then
echo "Changeset present (or no publishable change detected)."
else
echo "::warning::This PR changes a published package but adds no changeset. Run 'bun run changeset' to capture the version bump + changelog. (Advisory — not required to merge.)"
fi