Repository navigation
477 lines (468 loc) · 20.5 KB
/
Copy pathdocs-negotiation.yml
File metadata and controls
477 lines (468 loc) · 20.5 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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
name: docs content negotiation
on:
pull_request:
paths:
- 'apps/docs/**'
- 'apps/sim/**'
- 'packages/**'
- 'turbo.json'
- 'scripts/ci-plan.ts'
- '.github/scripts/ci-changes.sh'
- '.github/scripts/ci-plan.gql'
- '.github/ci-suites.json'
- 'patches/next@*.patch'
- 'package.json'
- 'bun.lock'
- '.github/workflows/docs-negotiation.yml'
- '.github/actions/docs-content-cache/**'
- '.github/actions/cache/**'
- '.github/actions/setup/**'
- '.github/scripts/stop-session.sh'
push:
branches: [staging]
paths:
- 'apps/docs/**'
- 'apps/sim/**'
- 'packages/**'
- 'turbo.json'
- 'scripts/ci-plan.ts'
- '.github/scripts/ci-changes.sh'
- '.github/scripts/ci-plan.gql'
- '.github/ci-suites.json'
- 'patches/next@*.patch'
- 'package.json'
- 'bun.lock'
- '.github/workflows/docs-negotiation.yml'
- '.github/actions/docs-content-cache/**'
- '.github/actions/cache/**'
- '.github/actions/setup/**'
- '.github/scripts/stop-session.sh'
workflow_dispatch:
inputs:
benchmark:
description: Run bounded production navigation, image and Lighthouse measurements
type: boolean
default: false
build_ref:
description: Commit to build for the benchmark (empty uses this workflow revision)
type: string
default: ''
preview_artifact:
description: Save the verified production build for local visual inspection
type: boolean
default: false
benchmark_scope:
description: Full measurements, navigation without Lighthouse, or only interactive controls
type: choice
options: [full, navigation, interactions]
default: full
benchmark_production:
description: Also measure the public production origin (only when it permits automated access)
type: boolean
default: false
permissions:
contents: read
concurrency:
group: docs-negotiation-${{ github.ref }}-${{ inputs.benchmark && 'benchmark' || 'verification' }}
cancel-in-progress: ${{ !inputs.benchmark }}
jobs:
benchmark:
if: github.event_name == 'workflow_dispatch' && inputs.benchmark
name: docs benchmark (${{ matrix.target }})
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'linux-x64-8-core' }}
timeout-minutes: 150
strategy:
fail-fast: false
max-parallel: 2
matrix:
target: ${{ fromJSON(inputs.benchmark_production && '["build","production"]' || '["build"]') }}
env:
DATABASE_URL: ''
OPENAI_API_KEY: ''
DOCS_BENCHMARK_BASE_URL: ${{ matrix.target == 'production' && 'https://docs.sim.ai' || 'http://127.0.0.1:3001' }}
steps:
- name: Checkout measured revision
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
ref: ${{ inputs.build_ref || github.sha }}
fetch-depth: 0
filter: blob:none
- name: Setup workspace
uses: ./.github/actions/setup
with:
provider: ${{ vars.CI_PROVIDER }}
- name: Checkout benchmark harness
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
ref: ${{ github.sha }}
path: .readiness-harness
sparse-checkout: apps/docs/scripts/benchmark-readiness.ts
sparse-checkout-cone-mode: false
- name: Prepare measurements
env:
GH_TOKEN: ${{ github.token }}
MEASURED_TARGET: ${{ matrix.target }}
run: |
mkdir -p test-results/benchmark
cp .readiness-harness/apps/docs/scripts/benchmark-readiness.ts apps/docs/scripts/benchmark-readiness.ts
if [ "$MEASURED_TARGET" = production ]; then
gh api "repos/$GITHUB_REPOSITORY/deployments?environment=Production&per_page=1" >test-results/benchmark/deployment.json
# Candidate deployments do not establish which revision owns the public alias.
REVISION=unverified-public-origin
else
REVISION=$(git rev-parse HEAD)
fi
echo "DOCS_BENCHMARK_REVISION=$REVISION" >> "$GITHUB_ENV"
bunx --no-install playwright install --with-deps chromium
- name: Build measured production docs
id: build
if: matrix.target == 'build'
run: bun run --cwd apps/docs build
- name: Measure navigation and Lighthouse sequentially
env:
BENCHMARK_SCOPE: ${{ inputs.benchmark_scope || 'full' }}
run: |
if [ "$DOCS_BENCHMARK_BASE_URL" = http://127.0.0.1:3001 ]; then
bun run --cwd apps/docs start --port 3001 >test-results/benchmark/server.log 2>&1 &
DOCS_SERVER_PID=$!
trap 'kill "$DOCS_SERVER_PID" 2>/dev/null || true' EXIT
for attempt in $(seq 1 30); do
curl -fsS "$DOCS_BENCHMARK_BASE_URL/introduction" -o /dev/null && break
if [ "$attempt" -eq 30 ]; then exit 1; fi
sleep 1
done
fi
NAVIGATION_STATUS=0
LIGHTHOUSE_STATUS=0
if [ "$BENCHMARK_SCOPE" = interactions ]; then
export DOCS_BENCHMARK_SCENARIOS=interactions
fi
DOCS_BENCHMARK_MODE=navigation DOCS_BENCHMARK_REPORT_PATH="$GITHUB_WORKSPACE/test-results/benchmark/navigation.json" bun run --cwd apps/docs scripts/benchmark-readiness.ts || NAVIGATION_STATUS=$?
if [ "$BENCHMARK_SCOPE" = full ]; then
DOCS_BENCHMARK_MODE=lighthouse DOCS_BENCHMARK_REPORT_PATH="$GITHUB_WORKSPACE/test-results/benchmark/lighthouse.json" bun run --cwd apps/docs scripts/benchmark-readiness.ts || LIGHTHOUSE_STATUS=$?
fi
test "$NAVIGATION_STATUS" -eq 0 && test "$LIGHTHOUSE_STATUS" -eq 0
- name: Upload measurements
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: docs-benchmark-${{ matrix.target }}
path: test-results/benchmark/
retention-days: 14
- name: Package measured production preview
if: always() && inputs.preview_artifact && steps.build.outcome == 'success'
run: tar -czf docs-preview-build.tar.gz --exclude='.next/cache' -C apps/docs .next .content
- name: Upload measured production preview
if: always() && inputs.preview_artifact && steps.build.outcome == 'success'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: docs-preview-build
path: docs-preview-build.tar.gz
retention-days: 3
changes:
if: github.event_name != 'workflow_dispatch' || !inputs.benchmark
name: docs changes
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
docs: ${{ steps.classify.outputs.docs }}
docs_site: ${{ steps.classify.outputs.docs_site }}
docs_platform: ${{ steps.classify.outputs.docs_platform }}
docs_matrix: ${{ steps.classify.outputs.docs_matrix }}
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
fetch-depth: 0
filter: blob:none
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.4
- name: Install only the pinned task planner
continue-on-error: true
run: bun install --global "turbo@$(jq -r .devDependencies.turbo package.json)"
- name: Attribute documentation changes
id: classify
env:
BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
CI_PLAN_PATH: ${{ runner.temp }}/docs-ci-plan.json
run: bash .github/scripts/ci-changes.sh "$BASE" >> "$GITHUB_OUTPUT"
- name: Upload documentation attribution
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: docs-ci-plan
path: ${{ runner.temp }}/docs-ci-plan.json
retention-days: 7
build:
needs: changes
if: needs.changes.outputs.docs == 'true'
name: docs build
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'linux-x64-8-core' }}
timeout-minutes: 45
env:
DATABASE_URL: postgresql://docs_test:docs_test@127.0.0.1:1/docs_test
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
fetch-depth: 0
filter: blob:none
- name: Setup workspace
uses: ./.github/actions/setup
with:
provider: ${{ vars.CI_PROVIDER }}
- name: Restore incremental documentation build inputs
uses: ./.github/actions/docs-content-cache
with:
provider: ${{ vars.CI_PROVIDER }}
namespace: docs-build
generate: 'false'
next-cache: 'true'
- name: Build production docs
run: bun run --cwd apps/docs build
- name: Package the production docs
run: tar -I 'zstd -T0 -3' -cf docs-build.tar.zst --exclude='.next/cache' -C apps/docs .next .content public/llms-full.txt
- name: Upload the production docs
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: docs-build
path: docs-build.tar.zst
compression-level: 0
retention-days: 1
- name: Package production preview for visual inspection
if: inputs.preview_artifact
run: tar -czf docs-preview-build.tar.gz --exclude='.next/cache' -C apps/docs .next .content public/llms-full.txt
- name: Upload production preview
if: inputs.preview_artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: docs-preview-build
path: docs-preview-build.tar.gz
retention-days: 3
- name: Bound the incremental build cache
if: always()
run: |
bash .github/actions/docs-content-cache/prune.sh content
bash .github/actions/docs-content-cache/prune.sh next
source:
needs: changes
if: needs.changes.outputs.docs_site == 'true'
name: docs source
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'linux-x64-8-core' }}
timeout-minutes: 45
env:
DATABASE_URL: postgresql://docs_test:docs_test@127.0.0.1:1/docs_test
DOCS_GENERATORS_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/generators/report.json
DOCS_COMPATIBILITY_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/compatibility.json
DOCS_HEADER_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/docs-headers.json
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
fetch-depth: 0
filter: blob:none
- name: Setup workspace
uses: ./.github/actions/setup
with:
provider: ${{ vars.CI_PROVIDER }}
- name: Generate documentation modules
uses: ./.github/actions/docs-content-cache
with:
provider: ${{ vars.CI_PROVIDER }}
namespace: docs-source
- name: Verify generator idempotence and authored regions
id: generators
run: bun run --cwd apps/docs scripts/test-generators-e2e.ts
- name: Verify preserved routes, anchors and examples
if: ${{ !cancelled() && steps.generators.outcome != 'skipped' }}
run: bun run --cwd apps/docs content:verify-compatibility
- name: Verify native header edge cases
if: ${{ !cancelled() && steps.generators.outcome != 'skipped' }}
run: bun run --cwd apps/docs test:headers:e2e
- name: Bound the generated content cache
if: always()
run: bash .github/actions/docs-content-cache/prune.sh content
- name: Upload source regression reports
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: docs-source-reports
path: apps/docs/test-results/
if-no-files-found: warn
retention-days: 14
acceptance:
if: needs.changes.outputs.docs == 'true'
needs: [changes, build]
name: docs acceptance (${{ matrix.group }})
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'linux-x64-8-core' }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.changes.outputs.docs_matrix) }}
services:
search-postgres:
image: ${{ (matrix.group == 'http' || matrix.group == 'platform') && 'pgvector/pgvector:pg17' || '' }}
env:
POSTGRES_USER: docs_test
POSTGRES_PASSWORD: docs_test
POSTGRES_DB: docs_search_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U docs_test -d docs_search_test"
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
DATABASE_URL: postgresql://docs_test:docs_test@127.0.0.1:1/docs_test
DOCS_E2E_BASE_URL: http://127.0.0.1:3001
DOCS_NEGOTIATION_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/docs-negotiation.json
DOCS_SEARCH_E2E_DATABASE_URL: postgresql://docs_test:docs_test@127.0.0.1:5432/docs_search_test
DOCS_SEARCH_E2E_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/search-postgres/report.json
DOCS_INTERFACES_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/docs-interfaces.json
DOCS_CORPUS_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/docs-corpus.json
DOCS_NAVIGATION_BROWSER: ${{ matrix.group }}
DOCS_NAVIGATION_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/navigation-${{ matrix.group }}/docs.json
DOCS_SAMPLES_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/samples/report.json
DOCS_SAMPLES_SYNTAX_LANGUAGES: javascript,python,bash,go,java,csharp
DOCS_SAMPLES_HTTP_BASE_URL: http://127.0.0.1:3001
DOCS_PROVIDER_E2E_DATABASE_URL: postgresql://docs_test:docs_test@127.0.0.1:5432/docs_search_test
DOCS_PROVIDER_E2E_BASE_URL: http://127.0.0.1:3001
DOCS_PROVIDER_E2E_REPORT_PATH: ${{ github.workspace }}/apps/docs/test-results/platform-consumer/report.json
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
fetch-depth: 0
filter: blob:none
- name: Setup workspace
uses: ./.github/actions/setup
with:
provider: ${{ vars.CI_PROVIDER }}
- name: Download production docs
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: docs-build
- name: Extract production docs
run: tar -I zstd -xf docs-build.tar.zst -C apps/docs
- name: Set up Java for copied request examples
if: matrix.group == 'http'
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: temurin
java-version: '17'
- name: Set up .NET for copied request examples
if: matrix.group == 'http'
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
dotnet-version: '8.0.x'
- name: Set up Go for copied request examples
if: matrix.group == 'http'
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: '1.25.x'
cache: false
- name: Install the selected navigation browser
if: matrix.group == 'chromium' || matrix.group == 'webkit'
env:
BROWSER: ${{ matrix.group }}
run: bunx --no-install playwright install --with-deps "$BROWSER"
- name: Verify production documentation
env:
DOCS_ACCEPTANCE_GROUP: ${{ matrix.group }}
run: |
mkdir -p apps/docs/test-results
FEEDBACK_TLS_DIR=$(mktemp -d)
export DOCS_FEEDBACK_TEST_CERT="$FEEDBACK_TLS_DIR/cert.pem"
export DOCS_FEEDBACK_TEST_KEY="$FEEDBACK_TLS_DIR/key.pem"
openssl req -x509 -newkey rsa:2048 -nodes -days 1 -subj '/CN=127.0.0.1' -addext 'subjectAltName=IP:127.0.0.1' -keyout "$DOCS_FEEDBACK_TEST_KEY" -out "$DOCS_FEEDBACK_TEST_CERT" 2>/dev/null
export NODE_EXTRA_CA_CERTS="$DOCS_FEEDBACK_TEST_CERT"
export DOCS_FEEDBACK_WEBHOOK_URL=https://127.0.0.1:3443/feedback
DOCS_FEEDBACK_WEBHOOK_TOKEN=$(openssl rand -hex 32)
export DOCS_FEEDBACK_WEBHOOK_TOKEN
APP_TAG="docs-$DOCS_ACCEPTANCE_GROUP-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT-$$"
E2E_APP="$APP_TAG" setsid --wait bun run --cwd apps/docs start --port 3001 >apps/docs/test-results/docs-server.log 2>&1 &
DOCS_SERVER_PID=$!
finish() {
local status=$1
trap - EXIT
if bash .github/scripts/stop-session.sh "$DOCS_SERVER_PID" "$APP_TAG"; then
wait "$DOCS_SERVER_PID" 2>/dev/null || true
else
status=1
fi
rm -rf "$FEEDBACK_TLS_DIR"
exit "$status"
}
trap 'finish "$?"' EXIT
for attempt in $(seq 1 60); do
if ! kill -0 "$DOCS_SERVER_PID" 2>/dev/null; then
echo '::error::Documentation server exited before becoming ready.'
exit 1
fi
if curl -fsS --max-time 2 "$DOCS_E2E_BASE_URL/introduction" -o /dev/null; then
break
fi
if [ "$attempt" -eq 60 ]; then
echo '::error::Documentation server did not become ready.'
exit 1
fi
sleep 1
done
STATUS=0
case "$DOCS_ACCEPTANCE_GROUP" in
http)
bun run --cwd apps/docs test:search:postgres:e2e || STATUS=1
bun run --cwd apps/docs test:negotiation:e2e || STATUS=1
bun run --cwd apps/docs scripts/test-docs-interfaces-e2e.ts || STATUS=1
bun run --cwd apps/docs scripts/verify-api-samples.ts || STATUS=1
bun --cwd apps/sim --no-env-file scripts/test-docs-search-provider-e2e.ts || STATUS=1
;;
platform) bun --cwd apps/sim --no-env-file scripts/test-docs-search-provider-e2e.ts || STATUS=1 ;;
corpus) bun run --cwd apps/docs test:corpus:e2e || STATUS=1 ;;
chromium|webkit) bun run --cwd apps/docs test:navigation:e2e || STATUS=1 ;;
*) echo "::error::Unknown documentation acceptance group: $DOCS_ACCEPTANCE_GROUP"; STATUS=1 ;;
esac
finish "$STATUS"
- name: Upload acceptance regression reports
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: docs-acceptance-${{ matrix.group }}
path: apps/docs/test-results/
if-no-files-found: warn
retention-days: 14
http:
if: always() && (github.event_name != 'workflow_dispatch' || !inputs.benchmark)
needs: [changes, build, source, acceptance]
name: docs HTTP
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Require every documentation check to pass
env:
NEEDS: ${{ toJSON(needs) }}
run: |
if ! jq -e '
keys == ["acceptance", "build", "changes", "source"]
and .changes.result == "success"
and (
if .changes.outputs.docs == "true" then
(.changes.outputs.docs_site == "true" or .changes.outputs.docs_site == "false")
and (.changes.outputs.docs_platform == "true" or .changes.outputs.docs_platform == "false")
and (.changes.outputs.docs_site == "true" or .changes.outputs.docs_platform == "true")
and ([.build, .acceptance] | all(.result == "success"))
and (.source.result == (if .changes.outputs.docs_site == "true" then "success" else "skipped" end))
elif .changes.outputs.docs == "false" then
.changes.outputs.docs_site == "false"
and .changes.outputs.docs_platform == "false"
and ([.build, .source, .acceptance] | all(.result == "skipped"))
else false
end
)
' <<< "$NEEDS"; then
echo '::error::Documentation checks did not all pass.'
exit 1
fi