From 2696e047dabbac3bf2f611ea4979a72b77597e32 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:48:54 -0500 Subject: [PATCH 1/5] tests(e2e): Skip clerk/nextjs v4 AP tests --- .../tests/next-account-portal/clerk-v4-ap-core-1.test.ts | 2 +- .../tests/next-account-portal/clerk-v4-ap-core-2.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/tests/next-account-portal/clerk-v4-ap-core-1.test.ts b/integration/tests/next-account-portal/clerk-v4-ap-core-1.test.ts index 984f846ebf7..f08eafdb0fd 100644 --- a/integration/tests/next-account-portal/clerk-v4-ap-core-1.test.ts +++ b/integration/tests/next-account-portal/clerk-v4-ap-core-1.test.ts @@ -6,7 +6,7 @@ import type { FakeUser } from '../../testUtils'; import { createTestUtils } from '../../testUtils'; import { testSignIn, testSignUp, testSSR } from './common'; -test.describe('Next with ClerkJS V4 <-> Account Portal Core 1 @ap-flows', () => { +test.skip('Next with ClerkJS V4 <-> Account Portal Core 1 @ap-flows', () => { test.describe.configure({ mode: 'serial' }); let app: Application; let fakeUser: FakeUser; diff --git a/integration/tests/next-account-portal/clerk-v4-ap-core-2.test.ts b/integration/tests/next-account-portal/clerk-v4-ap-core-2.test.ts index 705d83ab841..ba4aae593e8 100644 --- a/integration/tests/next-account-portal/clerk-v4-ap-core-2.test.ts +++ b/integration/tests/next-account-portal/clerk-v4-ap-core-2.test.ts @@ -6,7 +6,7 @@ import type { FakeUser } from '../../testUtils'; import { createTestUtils } from '../../testUtils'; import { testSignIn, testSignUp, testSSR } from './common'; -test.describe('Next with ClerkJS V4 <-> Account Portal Core 2 @ap-flows', () => { +test.skip('Next with ClerkJS V4 <-> Account Portal Core 2 @ap-flows', () => { test.describe.configure({ mode: 'serial' }); let app: Application; let fakeUser: FakeUser; From 30607891c68aaeadce7456b8bb3e15b31e606f11 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:37:48 -0500 Subject: [PATCH 2/5] ci: add some logging --- integration/models/application.ts | 59 +++++++++++-- integration/models/longRunningApplication.ts | 2 + integration/scripts/ciDiagnostics.ts | 12 +++ integration/scripts/clerkJsServer.ts | 30 ++++++- .../scripts/detachedProcessLogMirror.ts | 84 +++++++++++++++++++ integration/scripts/index.ts | 2 + integration/tests/global.setup.ts | 2 + 7 files changed, 178 insertions(+), 13 deletions(-) create mode 100644 integration/scripts/ciDiagnostics.ts create mode 100644 integration/scripts/detachedProcessLogMirror.ts diff --git a/integration/models/application.ts b/integration/models/application.ts index c8a6b2df2aa..610f7085f69 100644 --- a/integration/models/application.ts +++ b/integration/models/application.ts @@ -1,6 +1,16 @@ import * as path from 'node:path'; -import { awaitableTreekill, createLogger, fs, getPort, run, waitForIdleProcess, waitForServer } from '../scripts'; +import { + awaitableTreekill, + createCiDiagnosticLogger, + createLogger, + fs, + getPort, + run, + startDetachedProcessLogMirror, + waitForIdleProcess, + waitForServer, +} from '../scripts'; import type { ApplicationConfig } from './applicationConfig.js'; import type { EnvironmentConfig } from './environment.js'; @@ -21,6 +31,9 @@ export const application = ( const stderrFilePath = path.resolve(appDirPath, `e2e.${now}.err.log`); let buildOutput = ''; let serveOutput = ''; + const createPhaseLogger = (phase: string) => { + return createCiDiagnosticLogger(`${appDirName} :: ${phase}`, logger.child({ prefix: phase }).info); + }; const self = { name, @@ -42,14 +55,18 @@ export const application = ( setup: async (opts?: { strategy?: 'ci' | 'i' | 'copy'; force?: boolean }) => { const { force } = opts || {}; const nodeModulesExist = await fs.pathExists(path.resolve(appDirPath, 'node_modules')); + const log = createPhaseLogger('setup'); if (force || !nodeModulesExist) { - const log = logger.child({ prefix: 'setup' }).info; + log(`Running "${scripts.setup}" in ${appDirPath}`); await run(scripts.setup, { cwd: appDirPath, log }); state.completedSetup = true; + log(`Completed "${scripts.setup}"`); + return; } + log(`Skipping "${scripts.setup}" because node_modules already exists in ${appDirPath}`); }, dev: async (opts: { port?: number; manualStart?: boolean; detached?: boolean; serverUrl?: string } = {}) => { - const log = logger.child({ prefix: 'dev' }).info; + const log = createPhaseLogger('dev'); const port = opts.port || (await getPort()); const getServerUrl = () => { if (opts.serverUrl) { @@ -68,21 +85,45 @@ export const application = ( return { port, serverUrl: runtimeServerUrl }; } + log(`Running "${scripts.dev}" in ${appDirPath}`); + if (opts.detached) { + log(`Detached process logs will be written to ${stdoutFilePath} and ${stderrFilePath}`); + } const proc = run(scripts.dev, { cwd: appDirPath, env: { PORT: port.toString() }, detached: opts.detached, stdout: opts.detached ? fs.openSync(stdoutFilePath, 'a') : undefined, stderr: opts.detached ? fs.openSync(stderrFilePath, 'a') : undefined, - log: opts.detached ? undefined : log, + log, + }); + void proc.on('error', error => { + log(`Process error: ${error.message}`); + }); + void proc.on('exit', (code, signal) => { + log(`Process exited with code ${String(code)}${signal ? ` and signal ${signal}` : ''}`); }); + const detachedLogMirror = opts.detached + ? startDetachedProcessLogMirror({ + stdoutFilePath, + stderrFilePath, + log, + }) + : undefined; const shouldExit = () => !!proc.exitCode && proc.exitCode !== 0; - await waitForServer(runtimeServerUrl, { log, maxAttempts: Infinity, shouldExit }); - log(`Server started at ${runtimeServerUrl}, pid: ${proc.pid}`); - cleanupFns.push(() => awaitableTreekill(proc.pid, 'SIGKILL')); - state.serverUrl = runtimeServerUrl; - return { port, serverUrl: runtimeServerUrl, pid: proc.pid }; + try { + await waitForServer(runtimeServerUrl, { log, maxAttempts: Infinity, shouldExit }); + log(`Server started at ${runtimeServerUrl}, pid: ${proc.pid}`); + cleanupFns.push(() => awaitableTreekill(proc.pid, 'SIGKILL')); + state.serverUrl = runtimeServerUrl; + return { port, serverUrl: runtimeServerUrl, pid: proc.pid }; + } catch (error) { + log(`Server failed to start at ${runtimeServerUrl}`); + throw error; + } finally { + await detachedLogMirror?.stop(); + } }, build: async () => { const log = logger.child({ prefix: 'build' }).info; diff --git a/integration/models/longRunningApplication.ts b/integration/models/longRunningApplication.ts index b7e17ced2df..0314121972a 100644 --- a/integration/models/longRunningApplication.ts +++ b/integration/models/longRunningApplication.ts @@ -59,6 +59,7 @@ export const longRunningApplication = (params: LongRunningApplicationParams) => // will be called by global.setup.ts and by the test runner // the first time this is called, the app starts and the state is persisted in the state file init: async () => { + console.log(`Starting long running app "${id}"`); try { const publishableKey = params.env.publicVariables.get('CLERK_PUBLISHABLE_KEY'); const secretKey = params.env.privateVariables.get('CLERK_SECRET_KEY'); @@ -102,6 +103,7 @@ export const longRunningApplication = (params: LongRunningApplicationParams) => try { const { port, serverUrl, pid } = await app.dev({ detached: true }); stateFile.addLongRunningApp({ port, serverUrl, pid, id, appDir: app.appDir, env: params.env.toJson() }); + console.log(`Long running app "${id}" started at ${serverUrl} (pid: ${pid})`); } catch (error) { console.error('Error during app dev:', error); throw error; diff --git a/integration/scripts/ciDiagnostics.ts b/integration/scripts/ciDiagnostics.ts new file mode 100644 index 00000000000..0070a99f0aa --- /dev/null +++ b/integration/scripts/ciDiagnostics.ts @@ -0,0 +1,12 @@ +/* eslint-disable turbo/no-undeclared-env-vars */ + +export const shouldLogCiDiagnostics = () => Boolean(process.env.CI); + +export const createCiDiagnosticLogger = (prefix: string, debugLog?: (msg: string) => void) => { + return (msg: string) => { + debugLog?.(msg); + if (shouldLogCiDiagnostics()) { + console.log(`[${prefix}] ${msg}`); + } + }; +}; diff --git a/integration/scripts/clerkJsServer.ts b/integration/scripts/clerkJsServer.ts index 5e837f9a9cd..aba482ae1f9 100644 --- a/integration/scripts/clerkJsServer.ts +++ b/integration/scripts/clerkJsServer.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import { constants } from '../constants'; import { stateFile } from '../models/stateFile'; -import { awaitableTreekill, fs, waitForServer } from '.'; +import { awaitableTreekill, createCiDiagnosticLogger, fs, startDetachedProcessLogMirror, waitForServer } from '.'; import { run } from './run'; export const startClerkJsHttpServer = async () => { @@ -37,23 +37,45 @@ const copyClerkJsToTempDir = async () => { }; const serveFromTempDir = async () => { - console.log('Serving clerkJs from temp dir'); + const log = createCiDiagnosticLogger('clerkJsHttpServer'); + log('Serving clerkJs from temp dir'); const port = 18211; const serverUrl = `http://localhost:${port}`; const now = Date.now(); const stdoutFilePath = path.resolve(constants.TMP_DIR, `clerkJsHttpServer.${now}.log`); const stderrFilePath = path.resolve(constants.TMP_DIR, `clerkJsHttpServer.${now}.err.log`); const clerkJsTempDir = getClerkJsTempDir(); + log(`Running "node_modules/.bin/http-server ${clerkJsTempDir} -d --gzip --cors -a localhost" in ${process.cwd()}`); + log(`Detached process logs will be written to ${stdoutFilePath} and ${stderrFilePath}`); const proc = run(`node_modules/.bin/http-server ${clerkJsTempDir} -d --gzip --cors -a localhost`, { cwd: process.cwd(), env: { PORT: port.toString() }, detached: true, stdout: fs.openSync(stdoutFilePath, 'a'), stderr: fs.openSync(stderrFilePath, 'a'), + log, + }); + void proc.on('error', error => { + log(`Process error: ${error.message}`); + }); + void proc.on('exit', (code, signal) => { + log(`Process exited with code ${String(code)}${signal ? ` and signal ${signal}` : ''}`); + }); + const detachedLogMirror = startDetachedProcessLogMirror({ + stdoutFilePath, + stderrFilePath, + log, }); stateFile.setClerkJsHttpServerPid(proc.pid); - await waitForServer(serverUrl, { log: console.log, maxAttempts: Infinity }); - console.log('clerk.browser.js is being served from', serverUrl); + try { + await waitForServer(serverUrl, { log, maxAttempts: Infinity }); + log(`clerk.browser.js is being served from ${serverUrl}`); + } catch (error) { + log(`clerk.browser.js failed to start from ${serverUrl}`); + throw error; + } finally { + await detachedLogMirror.stop(); + } }; // The location where the clerk.browser.js is served from diff --git a/integration/scripts/detachedProcessLogMirror.ts b/integration/scripts/detachedProcessLogMirror.ts new file mode 100644 index 00000000000..f30640e6169 --- /dev/null +++ b/integration/scripts/detachedProcessLogMirror.ts @@ -0,0 +1,84 @@ +import { readFile } from 'node:fs/promises'; + +type DetachedProcessLogMirrorOptions = { + stdoutFilePath?: string; + stderrFilePath?: string; + log: (msg: string) => void; + intervalInMs?: number; +}; + +type FileReadState = { + contentsLength: number; + hasLoggedReadError: boolean; +}; + +const readNewLogOutput = async (filePath: string, label: string, log: (msg: string) => void, state: FileReadState) => { + try { + const contents = await readFile(filePath, 'utf8'); + if (contents.length < state.contentsLength) { + state.contentsLength = 0; + } + if (contents.length === state.contentsLength) { + return; + } + + const nextChunk = contents.slice(state.contentsLength); + state.contentsLength = contents.length; + state.hasLoggedReadError = false; + + nextChunk + .split(/\r?\n/) + .map(line => line.trimEnd()) + .filter(Boolean) + .forEach(line => log(`${label} ${line}`)); + } catch (error) { + const readError = error as NodeJS.ErrnoException; + if (readError.code === 'ENOENT') { + return; + } + if (!state.hasLoggedReadError) { + log(`${label} Failed to read ${filePath}: ${readError.message}`); + state.hasLoggedReadError = true; + } + } +}; + +export const startDetachedProcessLogMirror = (opts: DetachedProcessLogMirrorOptions) => { + const { stdoutFilePath, stderrFilePath, log, intervalInMs = 1000 } = opts; + const stdoutState: FileReadState = { contentsLength: 0, hasLoggedReadError: false }; + const stderrState: FileReadState = { contentsLength: 0, hasLoggedReadError: false }; + let isReading = false; + let isStopped = false; + + const flush = async () => { + await Promise.all([ + stdoutFilePath ? readNewLogOutput(stdoutFilePath, '[stdout]', log, stdoutState) : Promise.resolve(), + stderrFilePath ? readNewLogOutput(stderrFilePath, '[stderr]', log, stderrState) : Promise.resolve(), + ]); + }; + + const timer = setInterval(() => { + if (isStopped || isReading) { + return; + } + isReading = true; + void flush().finally(() => { + isReading = false; + }); + }, intervalInMs); + + timer.unref?.(); + + return { + stop: async () => { + isStopped = true; + clearInterval(timer); + + while (isReading) { + await new Promise(resolve => setTimeout(resolve, 25)); + } + + await flush(); + }, + }; +}; diff --git a/integration/scripts/index.ts b/integration/scripts/index.ts index e87e998628b..3b742515635 100644 --- a/integration/scripts/index.ts +++ b/integration/scripts/index.ts @@ -6,10 +6,12 @@ export const getPort = _getPort; export const chalk = _chalk; export const fs = _fs; export { createLogger } from './logger'; +export { createCiDiagnosticLogger, shouldLogCiDiagnostics } from './ciDiagnostics'; export { waitForIdleProcess } from './waitForIdleProcess'; export { range } from './range'; export { chunkLogger, run } from './run'; +export { startDetachedProcessLogMirror } from './detachedProcessLogMirror'; export * from './setup'; export * from './waitForServer'; diff --git a/integration/tests/global.setup.ts b/integration/tests/global.setup.ts index 52a373ebe71..c6acbd5375e 100644 --- a/integration/tests/global.setup.ts +++ b/integration/tests/global.setup.ts @@ -12,8 +12,10 @@ setup('start long running apps', async () => { await startClerkJsHttpServer(); const { appIds } = parseEnvOptions(); + console.log(`Preparing long running apps for E2E_APP_ID=${appIds.join(',') || ''}`); if (appIds.length) { const apps = appConfigs.longRunningApps.getByPattern(appIds); + console.log(`Matched long running apps: ${apps.map(app => app.id).join(', ')}`); // state cannot be shared using playwright, // so we save the state in a file using a strategy similar to `storageState` await Promise.all(apps.map(app => app.init())); From 4ebbf489ce2fbd6126db46df12c12fd84e1c868d Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:01:45 -0500 Subject: [PATCH 3/5] tests(repo): pin expo-crypto deps --- integration/templates/expo-web/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integration/templates/expo-web/package.json b/integration/templates/expo-web/package.json index d26d26f63d8..cad7a441bcf 100644 --- a/integration/templates/expo-web/package.json +++ b/integration/templates/expo-web/package.json @@ -16,7 +16,9 @@ "@expo/vector-icons": "^14.0.2", "@react-navigation/native": "^6.0.2", "expo": "~51.0.17", + "expo-auth-session": "~5.5.2", "expo-constants": "~16.0.2", + "expo-crypto": "~13.0.2", "expo-font": "~12.0.7", "expo-linking": "~6.3.1", "expo-router": "~3.5.17", From 24866e8200aa5135018c3ee9894cd98b87077306 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:12:21 -0500 Subject: [PATCH 4/5] Revert "ci: add some logging" This reverts commit 30607891c68aaeadce7456b8bb3e15b31e606f11. --- integration/models/application.ts | 59 ++----------- integration/models/longRunningApplication.ts | 2 - integration/scripts/ciDiagnostics.ts | 12 --- integration/scripts/clerkJsServer.ts | 30 +------ .../scripts/detachedProcessLogMirror.ts | 84 ------------------- integration/scripts/index.ts | 2 - integration/tests/global.setup.ts | 2 - 7 files changed, 13 insertions(+), 178 deletions(-) delete mode 100644 integration/scripts/ciDiagnostics.ts delete mode 100644 integration/scripts/detachedProcessLogMirror.ts diff --git a/integration/models/application.ts b/integration/models/application.ts index 610f7085f69..c8a6b2df2aa 100644 --- a/integration/models/application.ts +++ b/integration/models/application.ts @@ -1,16 +1,6 @@ import * as path from 'node:path'; -import { - awaitableTreekill, - createCiDiagnosticLogger, - createLogger, - fs, - getPort, - run, - startDetachedProcessLogMirror, - waitForIdleProcess, - waitForServer, -} from '../scripts'; +import { awaitableTreekill, createLogger, fs, getPort, run, waitForIdleProcess, waitForServer } from '../scripts'; import type { ApplicationConfig } from './applicationConfig.js'; import type { EnvironmentConfig } from './environment.js'; @@ -31,9 +21,6 @@ export const application = ( const stderrFilePath = path.resolve(appDirPath, `e2e.${now}.err.log`); let buildOutput = ''; let serveOutput = ''; - const createPhaseLogger = (phase: string) => { - return createCiDiagnosticLogger(`${appDirName} :: ${phase}`, logger.child({ prefix: phase }).info); - }; const self = { name, @@ -55,18 +42,14 @@ export const application = ( setup: async (opts?: { strategy?: 'ci' | 'i' | 'copy'; force?: boolean }) => { const { force } = opts || {}; const nodeModulesExist = await fs.pathExists(path.resolve(appDirPath, 'node_modules')); - const log = createPhaseLogger('setup'); if (force || !nodeModulesExist) { - log(`Running "${scripts.setup}" in ${appDirPath}`); + const log = logger.child({ prefix: 'setup' }).info; await run(scripts.setup, { cwd: appDirPath, log }); state.completedSetup = true; - log(`Completed "${scripts.setup}"`); - return; } - log(`Skipping "${scripts.setup}" because node_modules already exists in ${appDirPath}`); }, dev: async (opts: { port?: number; manualStart?: boolean; detached?: boolean; serverUrl?: string } = {}) => { - const log = createPhaseLogger('dev'); + const log = logger.child({ prefix: 'dev' }).info; const port = opts.port || (await getPort()); const getServerUrl = () => { if (opts.serverUrl) { @@ -85,45 +68,21 @@ export const application = ( return { port, serverUrl: runtimeServerUrl }; } - log(`Running "${scripts.dev}" in ${appDirPath}`); - if (opts.detached) { - log(`Detached process logs will be written to ${stdoutFilePath} and ${stderrFilePath}`); - } const proc = run(scripts.dev, { cwd: appDirPath, env: { PORT: port.toString() }, detached: opts.detached, stdout: opts.detached ? fs.openSync(stdoutFilePath, 'a') : undefined, stderr: opts.detached ? fs.openSync(stderrFilePath, 'a') : undefined, - log, - }); - void proc.on('error', error => { - log(`Process error: ${error.message}`); - }); - void proc.on('exit', (code, signal) => { - log(`Process exited with code ${String(code)}${signal ? ` and signal ${signal}` : ''}`); + log: opts.detached ? undefined : log, }); - const detachedLogMirror = opts.detached - ? startDetachedProcessLogMirror({ - stdoutFilePath, - stderrFilePath, - log, - }) - : undefined; const shouldExit = () => !!proc.exitCode && proc.exitCode !== 0; - try { - await waitForServer(runtimeServerUrl, { log, maxAttempts: Infinity, shouldExit }); - log(`Server started at ${runtimeServerUrl}, pid: ${proc.pid}`); - cleanupFns.push(() => awaitableTreekill(proc.pid, 'SIGKILL')); - state.serverUrl = runtimeServerUrl; - return { port, serverUrl: runtimeServerUrl, pid: proc.pid }; - } catch (error) { - log(`Server failed to start at ${runtimeServerUrl}`); - throw error; - } finally { - await detachedLogMirror?.stop(); - } + await waitForServer(runtimeServerUrl, { log, maxAttempts: Infinity, shouldExit }); + log(`Server started at ${runtimeServerUrl}, pid: ${proc.pid}`); + cleanupFns.push(() => awaitableTreekill(proc.pid, 'SIGKILL')); + state.serverUrl = runtimeServerUrl; + return { port, serverUrl: runtimeServerUrl, pid: proc.pid }; }, build: async () => { const log = logger.child({ prefix: 'build' }).info; diff --git a/integration/models/longRunningApplication.ts b/integration/models/longRunningApplication.ts index 0314121972a..b7e17ced2df 100644 --- a/integration/models/longRunningApplication.ts +++ b/integration/models/longRunningApplication.ts @@ -59,7 +59,6 @@ export const longRunningApplication = (params: LongRunningApplicationParams) => // will be called by global.setup.ts and by the test runner // the first time this is called, the app starts and the state is persisted in the state file init: async () => { - console.log(`Starting long running app "${id}"`); try { const publishableKey = params.env.publicVariables.get('CLERK_PUBLISHABLE_KEY'); const secretKey = params.env.privateVariables.get('CLERK_SECRET_KEY'); @@ -103,7 +102,6 @@ export const longRunningApplication = (params: LongRunningApplicationParams) => try { const { port, serverUrl, pid } = await app.dev({ detached: true }); stateFile.addLongRunningApp({ port, serverUrl, pid, id, appDir: app.appDir, env: params.env.toJson() }); - console.log(`Long running app "${id}" started at ${serverUrl} (pid: ${pid})`); } catch (error) { console.error('Error during app dev:', error); throw error; diff --git a/integration/scripts/ciDiagnostics.ts b/integration/scripts/ciDiagnostics.ts deleted file mode 100644 index 0070a99f0aa..00000000000 --- a/integration/scripts/ciDiagnostics.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint-disable turbo/no-undeclared-env-vars */ - -export const shouldLogCiDiagnostics = () => Boolean(process.env.CI); - -export const createCiDiagnosticLogger = (prefix: string, debugLog?: (msg: string) => void) => { - return (msg: string) => { - debugLog?.(msg); - if (shouldLogCiDiagnostics()) { - console.log(`[${prefix}] ${msg}`); - } - }; -}; diff --git a/integration/scripts/clerkJsServer.ts b/integration/scripts/clerkJsServer.ts index aba482ae1f9..5e837f9a9cd 100644 --- a/integration/scripts/clerkJsServer.ts +++ b/integration/scripts/clerkJsServer.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import { constants } from '../constants'; import { stateFile } from '../models/stateFile'; -import { awaitableTreekill, createCiDiagnosticLogger, fs, startDetachedProcessLogMirror, waitForServer } from '.'; +import { awaitableTreekill, fs, waitForServer } from '.'; import { run } from './run'; export const startClerkJsHttpServer = async () => { @@ -37,45 +37,23 @@ const copyClerkJsToTempDir = async () => { }; const serveFromTempDir = async () => { - const log = createCiDiagnosticLogger('clerkJsHttpServer'); - log('Serving clerkJs from temp dir'); + console.log('Serving clerkJs from temp dir'); const port = 18211; const serverUrl = `http://localhost:${port}`; const now = Date.now(); const stdoutFilePath = path.resolve(constants.TMP_DIR, `clerkJsHttpServer.${now}.log`); const stderrFilePath = path.resolve(constants.TMP_DIR, `clerkJsHttpServer.${now}.err.log`); const clerkJsTempDir = getClerkJsTempDir(); - log(`Running "node_modules/.bin/http-server ${clerkJsTempDir} -d --gzip --cors -a localhost" in ${process.cwd()}`); - log(`Detached process logs will be written to ${stdoutFilePath} and ${stderrFilePath}`); const proc = run(`node_modules/.bin/http-server ${clerkJsTempDir} -d --gzip --cors -a localhost`, { cwd: process.cwd(), env: { PORT: port.toString() }, detached: true, stdout: fs.openSync(stdoutFilePath, 'a'), stderr: fs.openSync(stderrFilePath, 'a'), - log, - }); - void proc.on('error', error => { - log(`Process error: ${error.message}`); - }); - void proc.on('exit', (code, signal) => { - log(`Process exited with code ${String(code)}${signal ? ` and signal ${signal}` : ''}`); - }); - const detachedLogMirror = startDetachedProcessLogMirror({ - stdoutFilePath, - stderrFilePath, - log, }); stateFile.setClerkJsHttpServerPid(proc.pid); - try { - await waitForServer(serverUrl, { log, maxAttempts: Infinity }); - log(`clerk.browser.js is being served from ${serverUrl}`); - } catch (error) { - log(`clerk.browser.js failed to start from ${serverUrl}`); - throw error; - } finally { - await detachedLogMirror.stop(); - } + await waitForServer(serverUrl, { log: console.log, maxAttempts: Infinity }); + console.log('clerk.browser.js is being served from', serverUrl); }; // The location where the clerk.browser.js is served from diff --git a/integration/scripts/detachedProcessLogMirror.ts b/integration/scripts/detachedProcessLogMirror.ts deleted file mode 100644 index f30640e6169..00000000000 --- a/integration/scripts/detachedProcessLogMirror.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { readFile } from 'node:fs/promises'; - -type DetachedProcessLogMirrorOptions = { - stdoutFilePath?: string; - stderrFilePath?: string; - log: (msg: string) => void; - intervalInMs?: number; -}; - -type FileReadState = { - contentsLength: number; - hasLoggedReadError: boolean; -}; - -const readNewLogOutput = async (filePath: string, label: string, log: (msg: string) => void, state: FileReadState) => { - try { - const contents = await readFile(filePath, 'utf8'); - if (contents.length < state.contentsLength) { - state.contentsLength = 0; - } - if (contents.length === state.contentsLength) { - return; - } - - const nextChunk = contents.slice(state.contentsLength); - state.contentsLength = contents.length; - state.hasLoggedReadError = false; - - nextChunk - .split(/\r?\n/) - .map(line => line.trimEnd()) - .filter(Boolean) - .forEach(line => log(`${label} ${line}`)); - } catch (error) { - const readError = error as NodeJS.ErrnoException; - if (readError.code === 'ENOENT') { - return; - } - if (!state.hasLoggedReadError) { - log(`${label} Failed to read ${filePath}: ${readError.message}`); - state.hasLoggedReadError = true; - } - } -}; - -export const startDetachedProcessLogMirror = (opts: DetachedProcessLogMirrorOptions) => { - const { stdoutFilePath, stderrFilePath, log, intervalInMs = 1000 } = opts; - const stdoutState: FileReadState = { contentsLength: 0, hasLoggedReadError: false }; - const stderrState: FileReadState = { contentsLength: 0, hasLoggedReadError: false }; - let isReading = false; - let isStopped = false; - - const flush = async () => { - await Promise.all([ - stdoutFilePath ? readNewLogOutput(stdoutFilePath, '[stdout]', log, stdoutState) : Promise.resolve(), - stderrFilePath ? readNewLogOutput(stderrFilePath, '[stderr]', log, stderrState) : Promise.resolve(), - ]); - }; - - const timer = setInterval(() => { - if (isStopped || isReading) { - return; - } - isReading = true; - void flush().finally(() => { - isReading = false; - }); - }, intervalInMs); - - timer.unref?.(); - - return { - stop: async () => { - isStopped = true; - clearInterval(timer); - - while (isReading) { - await new Promise(resolve => setTimeout(resolve, 25)); - } - - await flush(); - }, - }; -}; diff --git a/integration/scripts/index.ts b/integration/scripts/index.ts index 3b742515635..e87e998628b 100644 --- a/integration/scripts/index.ts +++ b/integration/scripts/index.ts @@ -6,12 +6,10 @@ export const getPort = _getPort; export const chalk = _chalk; export const fs = _fs; export { createLogger } from './logger'; -export { createCiDiagnosticLogger, shouldLogCiDiagnostics } from './ciDiagnostics'; export { waitForIdleProcess } from './waitForIdleProcess'; export { range } from './range'; export { chunkLogger, run } from './run'; -export { startDetachedProcessLogMirror } from './detachedProcessLogMirror'; export * from './setup'; export * from './waitForServer'; diff --git a/integration/tests/global.setup.ts b/integration/tests/global.setup.ts index c6acbd5375e..52a373ebe71 100644 --- a/integration/tests/global.setup.ts +++ b/integration/tests/global.setup.ts @@ -12,10 +12,8 @@ setup('start long running apps', async () => { await startClerkJsHttpServer(); const { appIds } = parseEnvOptions(); - console.log(`Preparing long running apps for E2E_APP_ID=${appIds.join(',') || ''}`); if (appIds.length) { const apps = appConfigs.longRunningApps.getByPattern(appIds); - console.log(`Matched long running apps: ${apps.map(app => app.id).join(', ')}`); // state cannot be shared using playwright, // so we save the state in a file using a strategy similar to `storageState` await Promise.all(apps.map(app => app.init())); From 07ff2d90781d30b0f9e27afc0ef3b7aee89c0e55 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:15:06 -0500 Subject: [PATCH 5/5] chore(repo): Add empty changeset --- .changeset/chilly-shoes-hang.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/chilly-shoes-hang.md diff --git a/.changeset/chilly-shoes-hang.md b/.changeset/chilly-shoes-hang.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/chilly-shoes-hang.md @@ -0,0 +1,2 @@ +--- +---