fix(ci): isolate quick tunnel callback traffic

This commit is contained in:
saltbo
2026-07-31 11:27:19 -04:00
parent 4a9588e551
commit 21305cc660
5 changed files with 70 additions and 14 deletions
+11
View File
@@ -10,6 +10,17 @@ export class CloudE2eCommandError extends Error {
}
}
export function cloudE2eEndpoints(localBaseUrl, tunnelUrl) {
return {
browserBaseUrl: localBaseUrl,
publicBaseUrl: tunnelUrl ?? localBaseUrl,
}
}
export function cloudflaredQuickTunnelArgs(target) {
return ['tunnel', '--url', target, '--protocol', 'http2', '--no-autoupdate']
}
export function isRetryableQuickTunnelFailure({ commandOutput, tunnelOutput }) {
if (QUICK_TUNNEL_502.test(commandOutput)) return true
return (
+24
View File
@@ -2,6 +2,8 @@ import { describe, expect, it } from 'vitest'
import {
CloudE2eCommandError,
cloudE2eAttemptCount,
cloudE2eEndpoints,
cloudflaredQuickTunnelArgs,
isRetryableQuickTunnelFailure,
} from './cloud-e2e-resilience.mjs'
@@ -15,6 +17,28 @@ describe('cloud E2E resilience', () => {
expect(error.output).toBe('gateway response')
})
it('keeps browser traffic local and reserves the tunnel for public callbacks', () => {
expect(cloudE2eEndpoints('http://localhost:5185', 'https://callback.trycloudflare.com')).toEqual({
browserBaseUrl: 'http://localhost:5185',
publicBaseUrl: 'https://callback.trycloudflare.com',
})
expect(cloudE2eEndpoints('http://localhost:5185', null)).toEqual({
browserBaseUrl: 'http://localhost:5185',
publicBaseUrl: 'http://localhost:5185',
})
})
it('uses HTTP/2 instead of QUIC for the Quick Tunnel transport', () => {
expect(cloudflaredQuickTunnelArgs('http://localhost:5185')).toEqual([
'tunnel',
'--url',
'http://localhost:5185',
'--protocol',
'http2',
'--no-autoupdate',
])
})
it('retries a Cloudflare Quick Tunnel gateway page', () => {
expect(
isRetryableQuickTunnelFailure({
+9 -7
View File
@@ -5,6 +5,8 @@ import { createRequire } from 'node:module'
import {
CloudE2eCommandError,
cloudE2eAttemptCount,
cloudE2eEndpoints,
cloudflaredQuickTunnelArgs,
isRetryableQuickTunnelFailure,
} from './cloud-e2e-resilience.mjs'
@@ -71,17 +73,17 @@ for (let attempt = 1; attempt <= maxRunAttempts; attempt += 1) {
async function buildE2eEnv(tunnel) {
const tunnelHost = tunnel ? new URL(tunnel.url).hostname : ''
const tunnelIp = tunnel ? await waitForPublicTunnelIp(tunnelHost) : ''
const baseUrl = tunnel?.url ?? localBaseUrl
if (tunnel) await waitForPublicTunnelIp(tunnelHost)
const { browserBaseUrl, publicBaseUrl } = cloudE2eEndpoints(localBaseUrl, tunnel?.url)
return {
...cloudEnv,
E2E_BASE_URL: baseUrl,
E2E_BASE_URL: browserBaseUrl,
E2E_LOCAL_BASE_URL: localBaseUrl,
E2E_PUBLIC_BASE_URL: publicBaseUrl,
E2E_APP_PORT: String(appPort),
E2E_API_PORT: String(apiPort),
BETTER_AUTH_URL: baseUrl,
TRUSTED_ORIGINS: `${baseUrl},${localBaseUrl}`,
...(tunnel ? { E2E_CHROME_HOST_RESOLVER_RULES: `MAP ${tunnelHost} ${tunnelIp}` } : {}),
BETTER_AUTH_URL: publicBaseUrl,
TRUSTED_ORIGINS: `${publicBaseUrl},${localBaseUrl}`,
...s3MockEnv(),
...credentialsEnv,
...(runtime === 'cf' ? { E2E_RUNTIME: 'cf' } : {}),
@@ -145,7 +147,7 @@ async function startTunnel(target) {
}
function startTunnelOnce(target) {
const child = spawn(cloudflared, ['tunnel', '--url', target, '--no-autoupdate'], {
const child = spawn(cloudflared, cloudflaredQuickTunnelArgs(target), {
stdio: ['ignore', 'pipe', 'pipe'],
})
writeFileSync(pidFile, String(child.pid))