Skip to content

Commit 0e09f25

Browse files
committed
improvement(ci): partition extension acceptance and trial faster integration runners
1 parent b6677c3 commit 0e09f25

6 files changed

Lines changed: 42 additions & 18 deletions

File tree

‎.github/CI.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ SEO depends on workspace UI and server code: landing pages reuse workspace prici
2424

2525
Affected type-checks follow the package graph too; repository audits and lint run whenever the checks workflow runs. Workspace-only unit jobs use a free GitHub runner and do not start the app test shards.
2626

27-
Database integration and lightweight HTTP suites run on free standard GitHub runners with provider-specific dependency setup. A same-source integration comparison of 16 jobs per provider (32 total) had a slowest shard of 6m 04s on 2-vCPU Blacksmith versus 6m 55s on free GitHub, with identical coverage. GitHub trades the observed 51 seconds for zero runner compute cost. This is one paired sample, not a long-run percentile estimate. These explicitly selected GitHub jobs do not follow `CI_PROVIDER`.
27+
Database integration trials 4-vCPU Blacksmith runners with sticky dependency disks, following `CI_PROVIDER` and falling back to standard GitHub runners with matching setup. A prior same-source comparison of 16 jobs per provider (32 total) had a slowest shard of 6m 04s on 2-vCPU Blacksmith versus 6m 55s on GitHub, with identical coverage. This is one paired sample, not a long-run percentile estimate; the 4-vCPU trial needs its own complete coverage and CI-gate comparison. Lightweight HTTP suites retain free standard GitHub runners and do not follow `CI_PROVIDER`.
2828

2929
GitHub runners install dependencies directly rather than transferring Bun and `node_modules` archives. In a same-source paired comparison, those restores took 26.2 seconds before an 82-millisecond install, while a clean install took 12.42 seconds. This is one paired observation; retain the policy only while repeated CI timings support it. Blacksmith keeps its sticky dependency disks. GitHub Turbo output caches remain enabled for jobs that request them, with a commit-specific key and a task/event/fork-scoped restore prefix, so a rerun can reuse an exact entry without creating another archive. Push jobs never restore pull-request entries.
3030

@@ -34,7 +34,7 @@ Every HTTP and browser group consumes the same production build in an isolated j
3434

3535
Desktop inbox protocol, assistant actions UI, and organization search UI run as three parallel HTTP jobs with isolated databases, apps, and reports. They retain one acceptance task boundary, so changing any of their harnesses selects all three jobs. Desktop inbox needs no browser. The UI jobs and browser extension install Chromium while their app starts, waiting for both before executing assertions; browser-install failures still fail the job and preserve the install log. No acceptance assertions are removed by the split.
3636

37-
Browser extension acceptance keeps native streaming and first-open capture checks on real timers. Its access-revalidation scenarios then reopen the persistent browser profile and install Playwright's clock before the app mounts. Advancing the 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. Live-provider runs retain native timing throughout.
37+
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.
3838

3939
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.
4040

‎.github/ci-suites.json‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
"task": "ci:e2e-desktop-inbox"
2626
},
2727
{
28-
"group": "browser-extension",
28+
"group": "browser-extension-native",
29+
"task": "ci:e2e-browser-extension"
30+
},
31+
{
32+
"group": "browser-extension-access",
2933
"task": "ci:e2e-browser-extension"
3034
},
3135
{

‎.github/scripts/http-e2e.sh‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# CI groups, which reuse the production build from `e2e-build`.
44
# The job extracts it into apps/sim/.next before running these suites.
55
#
6-
# Usage: http-e2e.sh <scim|cli|stop-after|desktop-inbox|assistant-actions|organization-search|browser-extension|project-files|mobile|seo> (run from apps/sim)
6+
# Usage: http-e2e.sh <scim|cli|stop-after|desktop-inbox|assistant-actions|organization-search|browser-extension|browser-extension-native|browser-extension-access|project-files|mobile|seo> (run from apps/sim)
77
#
88
# The job provides DATABASE_URL, BETTER_AUTH_SECRET and ENCRYPTION_KEY; each group sets the rest of
99
# its app's environment here. Reports and server logs land in $RUNNER_TEMP/e2e.
@@ -20,7 +20,7 @@
2020
# telemetry flush runs detached and still writes .next/dev).
2121
set -euo pipefail
2222

23-
group=${1:?usage: http-e2e.sh <scim|cli|stop-after|desktop-inbox|assistant-actions|organization-search|browser-extension|project-files|mobile|seo>}
23+
group=${1:?usage: http-e2e.sh <scim|cli|stop-after|desktop-inbox|assistant-actions|organization-search|browser-extension|browser-extension-native|browser-extension-access|project-files|mobile|seo>}
2424
report_dir="$RUNNER_TEMP/e2e"
2525
ready_timeout_seconds=600
2626
mkdir -p "$report_dir"
@@ -264,13 +264,18 @@ case "$group" in
264264
esac
265265
;;
266266

267-
browser-extension)
267+
browser-extension|browser-extension-native|browser-extension-access)
268+
extension_group=all
269+
case "$group" in
270+
browser-extension-native) extension_group=native ;;
271+
browser-extension-access) extension_group=access ;;
272+
esac
268273
export REDIS_URL=redis://127.0.0.1:6379
269274
export NEXT_PUBLIC_FORCE_HOSTED=false
270275
export KNOWLEDGE_MEMBER_ACCESS=true CREDENTIAL_GROUPS=true ACCESS_CONTROL_ENABLED=true
271276
export NEXT_PUBLIC_ACCESS_CONTROL_ENABLED=true
272277
export API_ENCRYPTION_KEY="$ENCRYPTION_KEY"
273-
start_browser_install browser-extension chromium
278+
start_browser_install "$group" chromium
274279
extension_identity=$(bun - <<'JS'
275280
import { generateKeyPairSync, createHash } from 'node:crypto'
276281
const { publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 })
@@ -282,11 +287,12 @@ JS
282287
)
283288
export SIM_EXTENSION_KEY="${extension_identity%%$'\n'*}"
284289
export BROWSER_EXTENSION_IDS="${extension_identity#*$'\n'}"
285-
start_app browser-extension 3007 'browser extension' record-http-status
290+
start_app "$group" 3007 'browser extension' record-http-status
286291
wait_for_browsers
287292
EXTENSION_APP_E2E_BASE_URL="$local_app_url" \
288293
EXTENSION_APP_E2E_DATABASE_URL="$DATABASE_URL" \
289-
EXTENSION_APP_E2E_REPORT_PATH="$report_dir/browser-extension/report.json" \
294+
EXTENSION_APP_E2E_GROUP="$extension_group" \
295+
EXTENSION_APP_E2E_REPORT_PATH="$report_dir/$group/report.json" \
290296
bun run test:browser-extension:e2e
291297
;;
292298

‎.github/workflows/checks.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
name: integration (${{ matrix.provision }}, ${{ matrix.shard == 'db' && 'db' || format('{0}/8', matrix.shard) }})
7777
needs: changes
7878
if: needs.changes.outputs.integration == 'true'
79-
runs-on: ubuntu-24.04
79+
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04' }}
8080
timeout-minutes: 20
8181
strategy:
8282
fail-fast: false
@@ -135,7 +135,7 @@ jobs:
135135
- name: Setup workspace
136136
uses: ./.github/actions/setup
137137
with:
138-
provider: github
138+
provider: ${{ vars.CI_PROVIDER }}
139139

140140
- name: Provision a fresh database through the supported command
141141
working-directory: packages/db
@@ -219,7 +219,7 @@ jobs:
219219
--health-retries 10
220220
# Desktop inbox, browser extension, and Project Files use Redis.
221221
redis:
222-
image: ${{ (matrix.group == 'desktop-inbox' || matrix.group == 'assistant-actions' || matrix.group == 'organization-search' || matrix.group == 'browser-extension' || matrix.group == 'project-files') && 'redis:8.2-alpine' || '' }}
222+
image: ${{ (matrix.group == 'desktop-inbox' || matrix.group == 'assistant-actions' || matrix.group == 'organization-search' || startsWith(matrix.group, 'browser-extension-') || matrix.group == 'project-files') && 'redis:8.2-alpine' || '' }}
223223
ports:
224224
- 6379:6379
225225
options: >-
@@ -298,7 +298,7 @@ jobs:
298298
name: e2e-reports-${{ matrix.group }}
299299
path: |
300300
${{ runner.temp }}/e2e/**/*.json
301-
${{ runner.temp }}/e2e/browser-extension/**/*.png
301+
${{ runner.temp }}/e2e/browser-extension*/**/*.png
302302
${{ runner.temp }}/e2e/*-ui-report.json.artifacts/*.png
303303
if-no-files-found: ignore
304304
retention-days: 7

‎.github/workflows/ci.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ name: ci
88
# A repo variable, not a committed value: during a Blacksmith outage there is no
99
# working CI to merge a switchover through. Only unset/'blacksmith' selects
1010
# Blacksmith; anything unrecognized selects GitHub so a typo can't queue jobs
11-
# against the provider you're escaping. Paid build/browser jobs and their cache actions share
12-
# this predicate. Task planning, the aggregate gate, HTTP acceptance and integration tests use free GitHub runners.
11+
# against the provider you're escaping. Integration, build and browser jobs and their cache
12+
# actions share this predicate. Task planning, the aggregate gate and HTTP acceptance use free GitHub runners.
1313
#
1414
# GitHub mode is break-glass, not a peer — cold layers, slower runs. The app image
1515
# is the one job on a paid larger runner: next build needs ~32 GB and OOM-kills

‎scripts/ci-changes.test.ts‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ describe('CI task attribution through real Turborepo', () => {
147147
expect(plan.fallback).toBeUndefined()
148148
expect(groups(plan)).toEqual([
149149
'assistant-actions',
150-
'browser-extension',
150+
'browser-extension-access',
151+
'browser-extension-native',
151152
'desktop-inbox',
152153
'mobile',
153154
'organization-search',
@@ -237,7 +238,6 @@ describe('CI task attribution through real Turborepo', () => {
237238
})
238239
it.each([
239240
['test-mobile-e2e.ts', 'mobile'],
240-
['test-browser-extension-e2e.ts', 'browser-extension'],
241241
['test-project-file-sharing-e2e.ts', 'project-files'],
242242
['test-scim-e2e.ts', 'scim'],
243243
['test-landing-seo-e2e.ts', 'seo'],
@@ -254,12 +254,26 @@ describe('CI task attribution through real Turborepo', () => {
254254
e2e_build: true,
255255
})
256256
})
257+
it('selects both isolated extension groups for an extension harness change', () => {
258+
const plan = classifyFiles(['apps/sim/scripts/test-browser-extension-e2e.ts'])
259+
expect(plan.fallback).toBeUndefined()
260+
expect(groups(plan)).toEqual(['browser-extension-access', 'browser-extension-native'])
261+
expect(plan.flags).toMatchObject({
262+
http: true,
263+
production: false,
264+
integration: false,
265+
test: false,
266+
build: false,
267+
e2e_build: true,
268+
})
269+
})
257270
it('propagates a transitive security dependency to every affected boundary', () => {
258271
const plan = classifyFiles(['packages/security/src/index.ts'])
259272
expect(plan.fallback).toBeUndefined()
260273
expect(groups(plan)).toEqual([
261274
'assistant-actions',
262-
'browser-extension',
275+
'browser-extension-access',
276+
'browser-extension-native',
263277
'cli',
264278
'desktop-inbox',
265279
'mobile',

0 commit comments

Comments
 (0)