Skip to content

Commit b12c44f

Browse files
committed
improvement(ci): select docs site and platform acceptance from the task graph
1 parent 060bc15 commit b12c44f

8 files changed

Lines changed: 88 additions & 4 deletions

File tree

‎.github/CI.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ The `changes` job runs Turborepo 2.9.14's `affectedTasks` query before installin
44

55
A PR compares its merge base with the checked-out merge commit. Main/staging pushes execute all checks; the parent `ci` workflow skips checks on `dev` pushes, while PRs targeting `dev` still run checks. A manual `checks` run executes all checks unless supplied a full base commit SHA to reproduce a scoped plan. Missing history, an empty diff, unknown files outside the workspace graph, root script/CI changes, or a planner error run every check. Missing selection outputs fail the aggregate gate. The gate also rejects an omitted dependency, any failed/cancelled job, and any skip that its explicit selection flag did not authorize.
66

7+
Dedicated docs verification uses the same planner, comparing pushes with their previous commit and PRs with their merge base. Site acceptance follows `docs#build` and its transitive package dependencies. The app's docs-consumer task depends on server runtime and that docs build: backend-only changes run the platform consumer against the docs app, while site changes also run corpus and browser checks. Independent SDK and unit-only edits skip those production jobs. The required `docs HTTP` gate verifies explicit site/platform selections before accepting any skip.
8+
79
| Change | Expensive checks selected |
810
| --- | --- |
911
| Independent SDK | That workspace's tests and type-check; no app build, services, or browsers |
@@ -38,6 +40,8 @@ Desktop inbox protocol, assistant actions UI, and organization search UI run as
3840

3941
Live Electron acceptance tracks its live-app suite, fixtures, and Playwright configuration separately from shell-only smoke tests. Changing a shell-only smoke case runs the dedicated desktop workflow without starting the app's production build and live lease checks.
4042

43+
Desktop smoke weights also stay outside the desktop runtime task. Editing weights reruns desktop partition coverage without selecting live-app acceptance.
44+
4145
Browser extension acceptance runs native interaction and access-revalidation groups in parallel jobs with isolated databases, apps, browser profiles, and reports. Both retain the same task boundary, so an affected extension harness selects both groups. Native streaming and first-open capture checks retain real timers. Access checks install Playwright's clock before the app mounts: advancing production polling timers preserves real HTTP and database authorization checks without waiting through each polling interval. Capture timestamps originate in Chromium's isolated world, so the harness restores wall time after each advance. The local `browser-extension` group still runs the complete suite; live-provider runs require this complete mode and retain native timing throughout.
4246

4347
Integration shard weights come from successful reports for both push and migration provisioning paths. Refresh them with `scripts/update-integration-durations.ts`, then check the new partition against a separate complete run before adopting it. Weights only change scheduling; discovery still owns coverage. The current refresh uses runs [41348786822](https://github.com/simstudioai/sim/actions/runs/41348786822) and [41377145857](https://github.com/simstudioai/sim/actions/runs/41377145857), with [41377810718](https://github.com/simstudioai/sim/actions/runs/41377810718) held out for validation.

‎.github/scripts/ci-plan.gql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ query($base: String!, $head: String!) {
44
"test", "build", "type-check", "ci:integration", "ci:desktop-live",
55
"ci:e2e-scim", "ci:e2e-cli", "ci:e2e-stop-after", "ci:e2e-desktop-inbox",
66
"ci:e2e-assistant-actions", "ci:e2e-organization-search",
7+
"ci:e2e-docs-platform",
78
"ci:e2e-browser-extension", "ci:e2e-project-files", "ci:e2e-mobile", "ci:e2e-seo"
89
]) {
910
items {

‎.github/workflows/cache-cleanup.yml‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ jobs:
2525
GH_REPO: ${{ github.repository }}
2626
PR_NUMBER: ${{ github.event.pull_request.number }}
2727
run: |
28-
if [ "$(gh pr view "$PR_NUMBER" --json state --jq .state)" = OPEN ]; then
29-
exit 0
30-
fi
28+
state=$(gh pr view "$PR_NUMBER" --json state --jq .state)
29+
case "$state" in
30+
OPEN) exit 0 ;;
31+
CLOSED|MERGED) ;;
32+
*) echo "::error::Unexpected pull request state: $state"; exit 1 ;;
33+
esac
3134
gh cache delete --all --ref "refs/pull/${PR_NUMBER}/merge" --succeed-on-no-caches

‎apps/desktop/turbo.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
22
"extends": ["//"],
33
"tasks": {
4+
"ci:runtime": {
5+
"inputs": ["$TURBO_EXTENDS$", "!e2e-durations.json"]
6+
},
47
"build": {
58
"inputs": [
69
"$TURBO_DEFAULT$",

‎apps/sim/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"ci:e2e-desktop-inbox": "bash ../../.github/scripts/http-e2e.sh desktop-inbox",
5454
"ci:e2e-assistant-actions": "bash ../../.github/scripts/http-e2e.sh assistant-actions",
5555
"ci:e2e-organization-search": "bash ../../.github/scripts/http-e2e.sh organization-search",
56+
"ci:e2e-docs-platform": "bun --no-env-file scripts/test-docs-search-provider-e2e.ts",
5657
"ci:e2e-project-files": "bash ../../.github/scripts/http-e2e.sh project-files",
5758
"ci:e2e-mobile": "bash ../../.github/scripts/http-e2e.sh mobile",
5859
"ci:e2e-seo": "bash ../../.github/scripts/http-e2e.sh seo",

‎apps/sim/turbo.json‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"!scripts/test-desktop-inbox-e2e.ts",
2828
"!scripts/test-assistant-actions-e2e.ts",
2929
"!scripts/test-organization-search-e2e.ts",
30+
"!scripts/test-docs-search-provider-e2e.ts",
3031
"!scripts/test-file-*-e2e.ts",
3132
"!scripts/test-landing-seo-e2e.ts",
3233
"!scripts/test-mcp-host-e2e.ts",
@@ -133,6 +134,11 @@
133134
"inputs": ["scripts/test-organization-search-e2e.ts"],
134135
"cache": false
135136
},
137+
"ci:e2e-docs-platform": {
138+
"dependsOn": ["ci:server", "docs#build"],
139+
"inputs": ["scripts/test-docs-search-provider-e2e.ts"],
140+
"cache": false
141+
},
136142
"ci:e2e-project-files": {
137143
"dependsOn": ["ci:server", "@sim/realtime#ci:runtime"],
138144
"inputs": ["scripts/test-project-file*-e2e.ts", "scripts/test-file-*-e2e.ts"],
@@ -165,6 +171,7 @@
165171
"!scripts/test-desktop-inbox-e2e.ts",
166172
"!scripts/test-assistant-actions-e2e.ts",
167173
"!scripts/test-organization-search-e2e.ts",
174+
"!scripts/test-docs-search-provider-e2e.ts",
168175
"!scripts/test-file-*-e2e.ts",
169176
"!scripts/test-landing-seo-e2e.ts",
170177
"!scripts/test-mcp-host-e2e.ts",

‎scripts/ci-changes.test.ts‎

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ interface Plan {
1515
production: { group: string }[]
1616
typecheckPackages: string[]
1717
testPackages: string[]
18+
docsGroups: string[]
1819
}
1920
function git(cwd: string, ...args: string[]) {
2021
const result = spawnSync('git', args, { cwd, encoding: 'utf8' })
@@ -48,12 +49,18 @@ function fixture() {
4849
['apps/realtime', '@sim/realtime', { '@sim/auth': 'workspace:*' }],
4950
['apps/desktop', '@sim/desktop', {}],
5051
['apps/extension', '@sim/extension', {}],
51-
['apps/docs', 'docs', {}],
52+
['apps/docs', 'docs', { '@sim/workflow-renderer': 'workspace:*' }],
5253
['packages/auth', '@sim/auth', { '@sim/security': 'workspace:*' }],
5354
['packages/security', '@sim/security', {}],
5455
['packages/db', '@sim/db', {}],
5556
['packages/ts-sdk', 'simstudio-ts-sdk', {}],
5657
['packages/sim-cli', 'sim', {}],
58+
[
59+
'packages/workflow-renderer',
60+
'@sim/workflow-renderer',
61+
{ '@sim/workflow-types': 'workspace:*' },
62+
],
63+
['packages/workflow-types', '@sim/workflow-types', {}],
5764
] as const
5865
const appScripts = JSON.parse(
5966
readFileSync(resolve(root, 'apps/sim/package.json'), 'utf8')
@@ -84,6 +91,7 @@ function fixture() {
8491
for (const file of [
8592
'turbo.json',
8693
'apps/sim/turbo.json',
94+
'apps/desktop/turbo.json',
8795
'.github/ci-suites.json',
8896
'.github/scripts/ci-plan.gql',
8997
]) {
@@ -138,6 +146,7 @@ describe('CI task attribution through real Turborepo', () => {
138146
build: false,
139147
desktop_live: false,
140148
e2e_build: false,
149+
docs: false,
141150
})
142151
})
143152
it('selects integration and browser checks for workspace code shared with desktop execution', () => {
@@ -190,9 +199,53 @@ describe('CI task attribution through real Turborepo', () => {
190199
build: false,
191200
integration: false,
192201
desktop_live: false,
202+
docs: true,
193203
})
194204
expect(plan.testPackages).toEqual(['docs'])
195205
})
206+
it('selects docs acceptance for a transitive rendering dependency', () => {
207+
const plan = classifyFiles(['packages/workflow-types/src/index.ts'])
208+
expect(plan.fallback).toBeUndefined()
209+
expect(plan.flags.docs).toBe(true)
210+
expect(plan.docsGroups).toEqual(['http', 'corpus', 'chromium', 'webkit'])
211+
})
212+
it('keeps docs unit-only edits out of production acceptance', () => {
213+
const plan = classifyFiles(['apps/docs/lib/content/links.test.ts'])
214+
expect(plan.fallback).toBeUndefined()
215+
expect(plan.flags.docs).toBe(false)
216+
expect(plan.testPackages).toEqual(['docs'])
217+
})
218+
it('selects docs acceptance for its standalone browser harness', () => {
219+
const plan = classifyFiles(['apps/docs/scripts/test-navigation-e2e.ts'])
220+
expect(plan.fallback).toBeUndefined()
221+
expect(plan.flags.docs).toBe(true)
222+
})
223+
it('selects only the platform docs boundary for a consumer harness change', () => {
224+
const plan = classifyFiles(['apps/sim/scripts/test-docs-search-provider-e2e.ts'])
225+
expect(plan.fallback).toBeUndefined()
226+
expect(groups(plan)).toEqual([])
227+
expect(plan.docsGroups).toEqual(['platform'])
228+
expect(plan.flags).toMatchObject({
229+
docs: true,
230+
docs_site: false,
231+
docs_platform: true,
232+
test_app: false,
233+
e2e_build: false,
234+
integration: false,
235+
desktop_live: false,
236+
})
237+
})
238+
it('selects platform docs for backend changes without site browser checks', () => {
239+
const plan = classifyFiles(['apps/sim/lib/mothership/docs/search.ts'])
240+
expect(plan.fallback).toBeUndefined()
241+
expect(plan.flags).toMatchObject({ docs: true, docs_site: false, docs_platform: true })
242+
expect(plan.docsGroups).toEqual(['platform'])
243+
})
244+
it('keeps desktop smoke weights out of live runtime attribution', () => {
245+
const plan = classifyFiles(['apps/desktop/e2e-durations.json'])
246+
expect(plan.fallback).toBeUndefined()
247+
expect(plan.flags).toMatchObject({ desktop_live: false, e2e_build: false })
248+
})
196249
it('does not build or run E2E for an app unit-test-only edit', () => {
197250
const plan = classifyFiles(['apps/sim/lib/auth/session.test.ts'])
198251
expect(plan.fallback).toBeUndefined()

‎scripts/ci-plan.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ const http = suites.http.filter((suite) => affected(suite.task))
9898
const production = suites.production.filter((suite) => affected(suite.task))
9999
const desktopLive = affected('ci:desktop-live')
100100
const appTest = affected('test')
101+
const docsSite = fallback !== undefined || selected.has('docs#build')
102+
const docsPlatform = affected('ci:e2e-docs-platform')
103+
const docsGroups = docsSite
104+
? ['http', 'corpus', 'chromium', 'webkit']
105+
: docsPlatform
106+
? ['platform']
107+
: []
101108
function taskPackages(name: string, omitApp = false): string[] {
102109
return fallback
103110
? ['*']
@@ -112,6 +119,9 @@ function taskPackages(name: string, omitApp = false): string[] {
112119
const packages = taskPackages('test', true)
113120
const typecheckPackages = taskPackages('type-check')
114121
const flags = {
122+
docs: docsSite || docsPlatform,
123+
docs_site: docsSite,
124+
docs_platform: docsPlatform,
115125
integration: affected('ci:integration'),
116126
http: http.length > 0,
117127
production: production.length > 0,
@@ -128,6 +138,7 @@ const outputs = {
128138
test_matrix: JSON.stringify({ shard: appTest ? [1, 2] : [1] }),
129139
test_packages: JSON.stringify(packages),
130140
typecheck_packages: JSON.stringify(typecheckPackages),
141+
docs_matrix: JSON.stringify({ include: docsGroups.map((group) => ({ group })) }),
131142
}
132143
const report = {
133144
base,
@@ -140,6 +151,7 @@ const report = {
140151
production,
141152
testPackages: packages,
142153
typecheckPackages,
154+
docsGroups,
143155
}
144156
if (process.env.CI_PLAN_PATH)
145157
writeFileSync(process.env.CI_PLAN_PATH, `${JSON.stringify(report, null, 2)}\n`)

0 commit comments

Comments
 (0)