diff --git a/.github/workflows/test-evals-instance-ai.yml b/.github/workflows/test-evals-instance-ai.yml index bde299ea0e1..9356b26013f 100644 --- a/.github/workflows/test-evals-instance-ai.yml +++ b/.github/workflows/test-evals-instance-ai.yml @@ -74,7 +74,7 @@ jobs: - name: Start sandbox service if: ${{ inputs.sandbox-provider == 'n8n-sandbox' }} - run: pnpm tsx packages/testing/containers/start-sandbox.ts --network n8n-eval-net + run: pnpm --filter n8n-containers services --services sandbox --network n8n-eval-net --name n8n-svc-sandbox - name: Start n8n containers env: @@ -284,14 +284,13 @@ jobs: done # Sandbox service container logs (when using n8n-sandbox provider) - for c in sandbox-api sandbox-runner-1; do - if docker ps -aq --filter "name=$c" | grep -q .; then - echo "" - echo "============================================================" - echo "=== $c (last 100 lines) ===" - echo "============================================================" - docker logs "$c" 2>&1 | tail -100 || true - fi + for c in $(docker ps -aq --filter "label=com.docker.compose.project=n8n-svc-sandbox"); do + name=$(docker inspect --format '{{.Name}}' "$c" | sed 's|^/||') + echo "" + echo "============================================================" + echo "=== $name (last 100 lines) ===" + echo "============================================================" + docker logs "$c" 2>&1 | tail -100 || true done - name: Stop n8n containers @@ -302,9 +301,8 @@ jobs: docker stop "${ids[@]}" 2>/dev/null || true docker rm "${ids[@]}" 2>/dev/null || true fi - # Sandbox service cleanup (safe even if containers don't exist) - docker stop sandbox-runner-1 sandbox-api 2>/dev/null || true - docker rm sandbox-runner-1 sandbox-api 2>/dev/null || true + # Sandbox service cleanup + pnpm --filter n8n-containers services:clean 2>/dev/null || true docker network rm n8n-eval-net 2>/dev/null || true - name: Post eval results to PR diff --git a/packages/testing/containers/n8n-start-stack.ts b/packages/testing/containers/n8n-start-stack.ts index c2d994c6f32..de66096b21c 100644 --- a/packages/testing/containers/n8n-start-stack.ts +++ b/packages/testing/containers/n8n-start-stack.ts @@ -63,6 +63,7 @@ ${colors.yellow}Options:${colors.reset} --mains Number of main instances (default: 1) --workers Number of worker instances (default: 1) --name Project name for parallel runs + --network Docker network name (default: auto-generated) --env KEY=VALUE Set environment variables --plan Use performance plan preset (${Object.keys(BASE_PERFORMANCE_PLANS).join(', ')}) --help, -h Show this help @@ -154,6 +155,7 @@ async function main() { mains: { type: 'string' }, workers: { type: 'string' }, name: { type: 'string' }, + network: { type: 'string' }, env: { type: 'string', multiple: true }, plan: { type: 'string' }, }, @@ -199,6 +201,7 @@ async function main() { (servicesOnly ? `n8n-svc-${Math.random().toString(36).substring(7)}` : `n8n-stack-${Math.random().toString(36).substring(7)}`), + networkName: values.network, }; // Handle queue mode (mains > 1 or workers > 0) @@ -273,6 +276,7 @@ async function main() { const stack = await createServiceStack({ services, projectName: config.projectName, + networkName: config.networkName, }); const envVars = writeDevEnvFile(stack, services); diff --git a/packages/testing/containers/service-stack.ts b/packages/testing/containers/service-stack.ts index 7879629aa2c..196a6cab886 100644 --- a/packages/testing/containers/service-stack.ts +++ b/packages/testing/containers/service-stack.ts @@ -8,6 +8,7 @@ import { createN8NStack, type N8NStack } from './stack'; export interface ServiceStackOptions { services: ServiceName[]; projectName?: string; + networkName?: string; } /** @@ -22,7 +23,7 @@ export interface ServiceStackOptions { * await stack.stop(); */ export async function createServiceStack(options: ServiceStackOptions): Promise { - const { services, projectName } = options; + const { services, projectName, networkName } = options; return await createN8NStack({ mains: 0, @@ -30,6 +31,7 @@ export async function createServiceStack(options: ServiceStackOptions): Promise< postgres: services.includes('postgres'), services, projectName, + networkName, external: true, }); } diff --git a/packages/testing/containers/services/types.ts b/packages/testing/containers/services/types.ts index fbde9b38a20..c5864b8369d 100644 --- a/packages/testing/containers/services/types.ts +++ b/packages/testing/containers/services/types.ts @@ -77,6 +77,8 @@ export interface StackConfig { services?: readonly ServiceName[]; /** When true, services target host machine instead of Docker-internal n8n */ external?: boolean; + /** When set, the Docker network uses this exact name instead of a random UUID. */ + networkName?: string; /** * Caddy load-balancer upstream-selection policy. Only applies when `mains > 1`. * Defaults to `'first'` — sticky to main #1, useful for UI debuggability. diff --git a/packages/testing/containers/stack.ts b/packages/testing/containers/stack.ts index c269e246827..f08cf0fc89e 100644 --- a/packages/testing/containers/stack.ts +++ b/packages/testing/containers/stack.ts @@ -86,6 +86,7 @@ export async function createN8NStack(config: N8NConfig = {}): Promise workerResourceQuota, services: enabledServices = [], external = false, + networkName, } = config; const log = createElapsedLogger('stack'); @@ -115,7 +116,8 @@ export async function createN8NStack(config: N8NConfig = {}): Promise let network: StartedNetwork; try { const networkStart = performance.now(); - network = await new Network().start(); + const uuid = networkName ? { nextUuid: () => networkName } : undefined; + network = await new Network(uuid).start(); telemetry.recordNetwork(Math.round(performance.now() - networkStart)); } catch (error) { const message = error instanceof Error ? error.message : String(error); diff --git a/packages/testing/containers/start-sandbox.ts b/packages/testing/containers/start-sandbox.ts deleted file mode 100644 index e63eeeec965..00000000000 --- a/packages/testing/containers/start-sandbox.ts +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env tsx -/** - * Standalone script to start the n8n sandbox service (API + runner) using the - * testcontainers-based sandbox service definition. Creates a named Docker - * network so that separately-started n8n containers can join it. - * - * Usage: pnpm tsx packages/testing/containers/start-sandbox.ts [--network ] - */ -import { parseArgs } from 'node:util'; -import { Network } from 'testcontainers'; -import type { Uuid } from 'testcontainers'; - -import { sandbox } from './services/sandbox'; - -const { values } = parseArgs({ - options: { network: { type: 'string', default: 'n8n-eval-net' } }, - strict: false, -}); - -const networkName = values.network ?? 'n8n-eval-net'; -const projectName = 'n8n-sandbox-ci'; - -class FixedName implements Uuid { - constructor(private readonly name: string) {} - nextUuid() { - return this.name; - } -} - -async function main() { - console.log(`Creating network "${networkName}"...`); - const network = await new Network(new FixedName(networkName)).start(); - - console.log('Starting sandbox service...'); - const result = await sandbox.start(network, projectName); - - console.log(`Sandbox API URL: ${result.meta.apiUrl}`); - console.log(`Network: ${network.getName()}`); - console.log('Sandbox service is ready'); -} - -main().catch((error) => { - console.error(error); - process.exit(1); -});