Skip to content

Commit 78131f4

Browse files
committed
Merge the live verification suite
2 parents 1c54533 + dbcb048 commit 78131f4

44 files changed

Lines changed: 2098 additions & 488 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,59 @@
1-
query($base: String!, $head: String!) {
2-
packages { items { name path } }
3-
affectedTasks(base: $base, head: $head, tasks: [
4-
"test", "build", "type-check", "ci:integration", "ci:desktop-live",
5-
"ci:e2e-scim", "ci:e2e-cli", "ci:e2e-stop-after", "ci:e2e-desktop-inbox",
6-
"ci:e2e-assistant-actions", "ci:e2e-organization-search",
7-
"ci:e2e-docs-platform",
8-
"ci:e2e-browser-extension", "ci:e2e-project-files", "ci:e2e-mobile", "ci:e2e-seo"
9-
]) {
1+
query ($base: String!, $head: String!) {
2+
packages {
3+
items {
4+
name
5+
path
6+
}
7+
}
8+
affectedTasks(
9+
base: $base
10+
head: $head
11+
tasks: [
12+
"test"
13+
"build"
14+
"type-check"
15+
"ci:integration"
16+
"ci:desktop-live"
17+
"ci:e2e-scim"
18+
"ci:e2e-cli"
19+
"ci:e2e-stop-after"
20+
"ci:e2e-desktop-inbox"
21+
"ci:e2e-assistant-actions"
22+
"ci:e2e-organization-search"
23+
"ci:e2e-docs-platform"
24+
"ci:e2e-browser-extension"
25+
"ci:e2e-project-files"
26+
"ci:e2e-mobile"
27+
"ci:e2e-seo"
28+
]
29+
) {
1030
items {
1131
name
1232
fullName
13-
package { name }
33+
package {
34+
name
35+
}
1436
reason {
1537
__typename
16-
... on TaskFileChanged { filePath }
17-
... on TaskGlobalFileChanged { filePath }
18-
... on TaskGlobalDepsChanged { filePath }
19-
... on TaskDependencyTaskChanged { taskName packageName }
20-
... on TaskPackageDependencyChanged { packageName }
21-
... on TaskAllChanged { description }
38+
... on TaskFileChanged {
39+
filePath
40+
}
41+
... on TaskGlobalFileChanged {
42+
filePath
43+
}
44+
... on TaskGlobalDepsChanged {
45+
filePath
46+
}
47+
... on TaskDependencyTaskChanged {
48+
taskName
49+
packageName
50+
}
51+
... on TaskPackageDependencyChanged {
52+
packageName
53+
}
54+
... on TaskAllChanged {
55+
description
56+
}
2257
}
2358
}
2459
}

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

Lines changed: 114 additions & 2 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|browser-extension-native|browser-extension-navigation|browser-extension-composer|browser-extension-access|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-navigation|browser-extension-composer|browser-extension-access|project-files|mobile|seo|platform-live-zone|platform-live-flag-off> (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|browser-extension-native|browser-extension-navigation|browser-extension-composer|browser-extension-access|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-navigation|browser-extension-composer|browser-extension-access|project-files|mobile|seo|platform-live-zone|platform-live-flag-off>}
2424
report_dir="$RUNNER_TEMP/e2e"
2525
ready_timeout_seconds=600
2626
mkdir -p "$report_dir"
@@ -32,6 +32,10 @@ status_log=''
3232
realtime_pid=''
3333
realtime_tag=''
3434
realtime_log=''
35+
platform_pid=''
36+
platform_tag=''
37+
platform_log=''
38+
agent_pid=''
3539
browsers_pid=''
3640
local_app_url=''
3741
# `dev` or `start`; a group sets it before start_app.
@@ -63,6 +67,17 @@ finish() {
6367
tail -n 100 "$realtime_log"
6468
fi
6569
fi
70+
if [ -n "$platform_pid" ]; then
71+
bash "$GITHUB_WORKSPACE/.github/scripts/stop-session.sh" "$platform_pid" "$platform_tag" || status=1
72+
wait "$platform_pid" 2>/dev/null || true
73+
if [ "$status" -ne 0 ]; then
74+
tail -n 100 "$platform_log"
75+
fi
76+
fi
77+
if [ -n "$agent_pid" ]; then
78+
kill "$agent_pid" 2>/dev/null || true
79+
wait "$agent_pid" 2>/dev/null || true
80+
fi
6681
if [ -n "$browsers_pid" ]; then
6782
kill -- "-$browsers_pid" 2>/dev/null || true
6883
wait "$browsers_pid" 2>/dev/null || true
@@ -133,6 +148,72 @@ start_realtime() {
133148
wait_ready "$realtime_pid" "http://127.0.0.1:$port/health" "$name realtime server"
134149
}
135150

151+
# stop_app: stops the app start_app started, so a group can start it again under other flags.
152+
stop_app() {
153+
bash "$GITHUB_WORKSPACE/.github/scripts/stop-session.sh" "$server_pid" "$app_tag"
154+
wait "$server_pid" 2>/dev/null || true
155+
server_pid=''
156+
}
157+
158+
# start_platform <port>: the production build of apps/sim/platform, behind the main app's rewrite.
159+
start_platform() {
160+
local port=$1
161+
platform_tag="platform-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT-$$"
162+
platform_log="$report_dir/platform-live-platform.log"
163+
E2E_APP="$platform_tag" SIM_PLATFORM_API_ORIGIN="$local_app_url" SIM_PLATFORM_BASE_PATH=/org setsid node \
164+
../../node_modules/next/dist/bin/next start platform --hostname 127.0.0.1 --port "$port" \
165+
> "$platform_log" 2>&1 &
166+
platform_pid=$!
167+
wait_ready "$platform_pid" "http://127.0.0.1:$port/org" 'Platform zone'
168+
}
169+
170+
# platform_live_environment: one hosted Enterprise organization, the deterministic seed its specs
171+
# read, and a scripted worker in place of the model. Hosted is decided by the app's hostname, which
172+
# the job maps to loopback. Google and Microsoft answer only to real credentials, so those clients
173+
# are fixtures, and what no fixture can reach is reported by the specs as a skip.
174+
platform_live_environment() {
175+
app_server=start
176+
export REDIS_URL=redis://127.0.0.1:6379
177+
export E2E_APP_PUBLIC_URL=http://platform-live.sim.ai:3027
178+
export NEXT_PUBLIC_APP_URL="$E2E_APP_PUBLIC_URL"
179+
export INTERNAL_API_BASE_URL=http://127.0.0.1:3027 TRUSTED_ORIGINS=http://127.0.0.1:3027
180+
export BILLING_ENABLED=true NEXT_PUBLIC_BILLING_ENABLED=true
181+
export ENTERPRISE_ENABLED=true NEXT_PUBLIC_ENTERPRISE_ENABLED=true
182+
export SSO_ENABLED=true NEXT_PUBLIC_SSO_ENABLED=true
183+
export SCIM_ENABLED=true NEXT_PUBLIC_SCIM_ENABLED=true
184+
export ORGANIZATIONS_ENABLED=true NEXT_PUBLIC_ORGANIZATIONS_ENABLED=true
185+
export KNOWLEDGE_MEMBER_ACCESS=true CREDENTIAL_GROUPS=true ACCESS_CONTROL_ENABLED=true
186+
export NEXT_PUBLIC_ACCESS_CONTROL_ENABLED=true
187+
export SIM_LINKS=true PEOPLE=true
188+
export INTERNAL_API_SECRET=platform-live-ci-local-secret-at-least-32-characters
189+
export COPILOT_API_KEY=platform-live-ci-local-copilot-key MOTHERSHIP_SIM_TRANSPORT=direct
190+
export SIM_AGENT_API_URL=http://127.0.0.1:3029
191+
export GOOGLE_CLIENT_ID=platform-live-google-client GOOGLE_CLIENT_SECRET=platform-live-google-secret
192+
export MICROSOFT_CLIENT_ID=platform-live-microsoft-client MICROSOFT_CLIENT_SECRET=platform-live-microsoft-secret
193+
export STRIPE_SECRET_KEY=sk_test_platform_live STRIPE_WEBHOOK_SECRET=whsec_platform_live
194+
195+
export PLATFORM_LIVE_ORIGIN="$E2E_APP_PUBLIC_URL"
196+
export PLATFORM_LIVE_PASSWORD=platform-live-password-1
197+
export PLATFORM_LIVE_ORGANIZATION_ID=platform-live-organization
198+
export PLATFORM_LIVE_ORGANIZATION_NAME='Sim Live Org'
199+
export PLATFORM_LIVE_OWNER_EMAIL=olive@platform-live.test
200+
export PLATFORM_LIVE_EMAIL=ada@platform-live.test
201+
export PLATFORM_LIVE_MEMBER_EMAIL=mia@platform-live.test
202+
export PLATFORM_LIVE_CONNECTED_EMAIL=cory@platform-live.test
203+
export PLATFORM_LIVE_ROLE_TARGET_EMAIL=rae@platform-live.test
204+
export PLATFORM_LIVE_REMOVABLE_EMAIL=remy@platform-live.test
205+
export PLATFORM_LIVE_REFUSED_INVITE_EMAIL=otto@platform-live.test
206+
export PLATFORM_LIVE_ORCHESTRATOR=1
207+
bun --no-env-file scripts/platform-live-seed.ts
208+
}
209+
210+
# run_platform_live <project> <report-name>
211+
run_platform_live() {
212+
local project=$1 name=$2
213+
PLATFORM_LIVE_REPORT_PATH="$report_dir/$name/report.json" \
214+
bun run test:platform:live --project="$project" ${PLATFORM_LIVE_GREP:+--grep "$PLATFORM_LIVE_GREP"}
215+
}
216+
136217
# wait_ready <pid> <health-url> <label>
137218
wait_ready() {
138219
local pid=$1 url=$2 label=$3
@@ -349,6 +430,37 @@ JS
349430
bun run test:landing-seo:e2e
350431
;;
351432

433+
# The zone as deployed: flag on, /org rewritten to the Platform build. Then the same build
434+
# with the flag off, which must put everyone back on the legacy surface. Both report, whichever
435+
# fails.
436+
platform-live-zone)
437+
platform_live_environment
438+
export SIM_PLATFORM_ORIGIN=http://127.0.0.1:3028
439+
start_browser_install platform-live --only-shell chromium
440+
PLATFORM_ORG_UI=true start_app platform-live 3027 'Platform main' record-http-status
441+
start_platform 3028
442+
bun --no-env-file platform/checks/live/support/scripted-agent.ts 3029 \
443+
> "$report_dir/platform-live-agent.log" 2>&1 &
444+
agent_pid=$!
445+
wait_for_browsers
446+
live_status=0
447+
run_platform_live zone platform-live || live_status=$?
448+
stop_app
449+
start_app platform-live-flag-off 3027 'Platform main, flag off' record-http-status
450+
run_platform_live flag-off platform-live-flag-off || live_status=$?
451+
exit "$live_status"
452+
;;
453+
454+
# No zone at all: built without SIM_PLATFORM_ORIGIN, so even with the flag on nothing may change.
455+
platform-live-flag-off)
456+
platform_live_environment
457+
export PLATFORM_ORG_UI=true
458+
start_browser_install platform-live --only-shell chromium
459+
start_app platform-live-flag-off 3027 'Main, no zone' record-http-status
460+
wait_for_browsers
461+
run_platform_live flag-off platform-live-flag-off
462+
;;
463+
352464
*)
353465
echo "::error::Unknown end-to-end group: $group" >&2
354466
exit 2

‎.github/workflows/platform-design.yml‎

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,25 @@ on:
44
pull_request:
55
paths:
66
- 'apps/sim/platform/**'
7+
- '!apps/sim/platform/docs/**'
8+
- '!apps/sim/platform/README.md'
79
- 'apps/sim/package.json'
810
- 'scripts/check-platform-design*'
911
- 'scripts/platform-variant-usage.ts'
1012
- 'scripts/sync-platform-*.ts'
1113
- '.github/workflows/platform-design.yml'
1214
- 'bun.lock'
15+
- '.github/scripts/http-e2e.sh'
1316
workflow_dispatch:
17+
inputs:
18+
live_only:
19+
description: Run only the live jobs, skipping the fixture suites.
20+
type: boolean
21+
default: false
22+
grep:
23+
description: Narrow the live suite to the specs matching this pattern.
24+
type: string
25+
default: ''
1426

1527
permissions:
1628
contents: read
@@ -21,6 +33,7 @@ concurrency:
2133

2234
jobs:
2335
browser:
36+
if: ${{ !inputs.live_only }}
2437
runs-on: ubuntu-24.04
2538
timeout-minutes: 15
2639
steps:
@@ -44,6 +57,7 @@ jobs:
4457
retention-days: 7
4558

4659
desktop:
60+
if: ${{ !inputs.live_only }}
4761
runs-on: macos-26
4862
timeout-minutes: 25
4963
steps:
@@ -69,3 +83,92 @@ jobs:
6983
name: platform-desktop
7084
path: ${{ runner.temp }}/platform-desktop
7185
retention-days: 7
86+
87+
# The Platform against the real stack: Postgres, Redis, migrations, the production main app and
88+
# the production Platform. `zone` is the deployed topology, and then that same build with the flag
89+
# off. `flag-off` builds with no zone at all, so it is the only proof /org is truly absent.
90+
# Group environment and seed: `.github/scripts/http-e2e.sh`.
91+
live:
92+
name: live (${{ matrix.topology }})
93+
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-16vcpu-ubuntu-2404' || 'linux-x64-8-core' }}
94+
timeout-minutes: 75
95+
strategy:
96+
fail-fast: false
97+
matrix:
98+
topology: [zone, flag-off]
99+
services:
100+
postgres:
101+
image: pgvector/pgvector:pg17
102+
env:
103+
POSTGRES_USER: postgres
104+
POSTGRES_PASSWORD: postgres
105+
POSTGRES_DB: sim_test
106+
ports:
107+
- 5432:5432
108+
options: >-
109+
--health-cmd "pg_isready -U postgres -d sim_test"
110+
--health-interval 5s
111+
--health-timeout 5s
112+
--health-retries 10
113+
redis:
114+
image: redis:8.2-alpine
115+
ports:
116+
- 6379:6379
117+
options: >-
118+
--health-cmd "redis-cli ping"
119+
--health-interval 5s
120+
--health-timeout 5s
121+
--health-retries 10
122+
env:
123+
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/sim_test
124+
BETTER_AUTH_SECRET: http-e2e-ci-secret-at-least-32-characters
125+
ENCRYPTION_KEY: '0000000000000000000000000000000000000000000000000000000000000000'
126+
PLATFORM_LIVE_GREP: ${{ inputs.grep }}
127+
steps:
128+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
129+
with:
130+
persist-credentials: false
131+
- uses: ./.github/actions/setup
132+
with:
133+
provider: ${{ vars.CI_PROVIDER }}
134+
turbo-cache-key: turbo-cache-platform-live-${{ matrix.topology }}
135+
- run: bun run check:platform-parity
136+
- name: Provision the database through migrations
137+
working-directory: packages/db
138+
run: bun run db:migrate
139+
- name: Build the main app
140+
env:
141+
NODE_OPTIONS: '--no-warnings --max-old-space-size=8192'
142+
NEXT_PUBLIC_APP_URL: 'http://localhost:3000'
143+
STRIPE_SECRET_KEY: 'dummy_key_for_ci_only'
144+
STRIPE_WEBHOOK_SECRET: 'dummy_secret_for_ci_only'
145+
RESEND_API_KEY: 'dummy_key_for_ci_only'
146+
AWS_REGION: 'us-west-2'
147+
TURBO_CACHE_DIR: .turbo
148+
NEXT_BUILD_SKIP_TYPE_CHECK: 'true'
149+
run: |
150+
if [ "${{ matrix.topology }}" = zone ]; then export SIM_PLATFORM_ORIGIN=http://127.0.0.1:3028; fi
151+
bunx turbo run build --filter=@sim/app
152+
- name: Build the Platform under its base path
153+
if: matrix.topology == 'zone'
154+
env:
155+
SIM_PLATFORM_BASE_PATH: /org
156+
run: bun run --cwd apps/sim build:platform
157+
- name: Map the hosted app to loopback
158+
run: printf '127.0.0.1 platform-live.sim.ai\n' | sudo tee -a /etc/hosts
159+
- name: Run the live suite
160+
working-directory: apps/sim
161+
run: bash ../../.github/scripts/http-e2e.sh platform-live-${{ matrix.topology }}
162+
- name: Hold the parity ledger to this run
163+
if: always() && matrix.topology == 'zone'
164+
run: >-
165+
bun run check:platform-parity
166+
--report "$RUNNER_TEMP/e2e/platform-live/report.json"
167+
--report "$RUNNER_TEMP/e2e/platform-live-flag-off/report.json"
168+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
169+
if: always()
170+
with:
171+
name: platform-live-${{ matrix.topology }}
172+
path: ${{ runner.temp }}/e2e/
173+
if-no-files-found: warn
174+
retention-days: 7

‎apps/sim/lib/api/contracts/organization.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,8 @@ const organizationSurfaceResponseSchema = z.object({
554554
selfHosted: z.record(z.string(), z.boolean().optional()),
555555
}),
556556
deployment: deploymentShapeSchema,
557+
/** Whether the Platform zone is open to this organization at all. */
558+
platformOrgUi: z.boolean(),
557559
/** Which destinations the viewer's navigation lists; none of these authorizes the page behind it. */
558560
navFlags: z.object({
559561
people: z.boolean(),

0 commit comments

Comments
 (0)