diff --git a/.github/actions/setup-npm/action.yml b/.github/actions/setup-npm/action.yml index 8ff66b59a..e5ef6e014 100644 --- a/.github/actions/setup-npm/action.yml +++ b/.github/actions/setup-npm/action.yml @@ -11,7 +11,7 @@ runs: using: composite steps: - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: node-version: ${{ inputs.node-version }} diff --git a/.github/workflows/dbal-tests.yml b/.github/workflows/dbal-tests.yml index 370827220..824b86c8b 100644 --- a/.github/workflows/dbal-tests.yml +++ b/.github/workflows/dbal-tests.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install system deps run: | @@ -32,7 +32,7 @@ jobs: run: conan profile detect --force - name: Cache Conan packages - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ~/.conan2/p key: conan-unit-${{ hashFiles('dbal/production/build-config/conanfile.tests.py') }} @@ -67,7 +67,7 @@ jobs: run: ctest -R dbal_unit_tests --output-on-failure - name: Upload results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: always() with: name: unit-test-results @@ -82,7 +82,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Go (for testcontainers-sidecar) uses: actions/setup-go@v5 @@ -124,7 +124,7 @@ jobs: -s build_type=Release -s compiler.cppstd=20 - name: Cache Conan packages - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ~/.conan2/p key: conan-integration-${{ hashFiles('dbal/production/build-config/conanfile.tests.py') }} @@ -162,7 +162,7 @@ jobs: run: ctest -R dbal_integration_tests --output-on-failure -V - name: Upload results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: always() with: name: integration-test-results diff --git a/.github/workflows/gated-pipeline.yml b/.github/workflows/gated-pipeline.yml index 98863fd6e..7be65a4fb 100644 --- a/.github/workflows/gated-pipeline.yml +++ b/.github/workflows/gated-pipeline.yml @@ -289,7 +289,7 @@ jobs: echo "$(date -Iseconds)" > gate-artifacts/gate-1/start-time.txt - name: Upload gate start marker - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-start path: gate-artifacts/gate-1/ @@ -301,7 +301,7 @@ jobs: needs: gate-1-start steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v5 @@ -384,7 +384,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-schema-result path: gate-artifacts/gate-1/ @@ -396,7 +396,7 @@ jobs: needs: schema-check steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -418,7 +418,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-typecheck-result path: gate-artifacts/gate-1/ @@ -430,7 +430,7 @@ jobs: needs: schema-check steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -464,7 +464,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-lint-result path: gate-artifacts/gate-1/ @@ -476,7 +476,7 @@ jobs: needs: schema-check steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -499,7 +499,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-security-result path: gate-artifacts/gate-1/ @@ -511,7 +511,7 @@ jobs: needs: schema-check steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Check for oversized files run: | @@ -534,7 +534,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-filesize-result path: gate-artifacts/gate-1/ @@ -546,7 +546,7 @@ jobs: needs: schema-check steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Check code complexity run: | @@ -571,7 +571,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-complexity-result path: gate-artifacts/gate-1/ @@ -583,7 +583,7 @@ jobs: needs: schema-check steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Detect stubs and placeholder code run: | @@ -609,7 +609,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-stub-result path: gate-artifacts/gate-1/ @@ -620,7 +620,7 @@ jobs: needs: [schema-check, typecheck, lint, security-scan, file-size-check, code-complexity-check, stub-detection] steps: - name: Download all gate 1 artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: pattern: gate-1-* path: gate-artifacts/ @@ -648,7 +648,7 @@ jobs: ls -la gate-artifacts/gate-1/ - name: Upload consolidated gate 1 report - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-1-complete-report path: gate-artifacts/ @@ -674,7 +674,7 @@ jobs: echo "$(date -Iseconds)" > gate-artifacts/gate-2/start-time.txt - name: Upload gate start marker - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-2-start path: gate-artifacts/gate-2/ @@ -686,7 +686,7 @@ jobs: needs: gate-2-start steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -730,7 +730,7 @@ jobs: - name: Upload coverage report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: coverage-report path: frontends/nextjs/coverage/ @@ -745,7 +745,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-2-unit-result path: gate-artifacts/gate-2/ @@ -757,7 +757,7 @@ jobs: needs: gate-2-start steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -770,11 +770,6 @@ jobs: - name: Install Playwright Browsers run: npx playwright install --with-deps chromium - - name: Start smoke stack - run: | - docker compose -f deployment/docker-compose.smoke.yml up -d --wait --wait-timeout 180 - timeout-minutes: 5 - - name: Run Playwright tests run: | if [ -f e2e/playwright.config.ts ]; then @@ -782,14 +777,11 @@ jobs: else echo "::warning::No playwright.config.ts found — E2E tests not configured" fi - - - name: Stop smoke stack - if: always() - run: docker compose -f deployment/docker-compose.smoke.yml down -v + timeout-minutes: 10 - name: Upload test results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: playwright-report path: playwright-report/ @@ -804,7 +796,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-2-e2e-result path: gate-artifacts/gate-2/ @@ -816,7 +808,7 @@ jobs: needs: gate-2-start steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -836,7 +828,7 @@ jobs: - name: Upload daemon test report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: playwright-report-dbal-daemon path: frontends/nextjs/playwright-report/ @@ -851,7 +843,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-2-dbal-result path: gate-artifacts/gate-2/ @@ -862,7 +854,7 @@ jobs: needs: [test-unit, test-e2e, test-dbal-daemon] steps: - name: Download all gate 2 artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: pattern: gate-2-* path: gate-artifacts/ @@ -886,7 +878,7 @@ jobs: ls -la gate-artifacts/gate-2/ - name: Upload consolidated gate 2 report - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-2-complete-report path: gate-artifacts/ @@ -912,7 +904,7 @@ jobs: echo "$(date -Iseconds)" > gate-artifacts/gate-3/start-time.txt - name: Upload gate start marker - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-3-start path: gate-artifacts/gate-3/ @@ -926,7 +918,7 @@ jobs: build-success: ${{ steps.build-step.outcome }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -941,7 +933,7 @@ jobs: run: npm run build -w frontends/nextjs - name: Upload build artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: dist path: frontends/nextjs/.next/ @@ -956,7 +948,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-3-build-result path: gate-artifacts/gate-3/ @@ -969,7 +961,7 @@ jobs: if: github.event_name == 'pull_request' steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -999,7 +991,7 @@ jobs: - name: Upload validation result if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-3-quality-result path: gate-artifacts/gate-3/ @@ -1011,7 +1003,7 @@ jobs: if: always() && needs.build.result == 'success' && (needs.quality-check.result == 'success' || needs.quality-check.result == 'skipped') steps: - name: Download all gate 3 artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: pattern: gate-3-* path: gate-artifacts/ @@ -1032,7 +1024,7 @@ jobs: ls -la gate-artifacts/gate-3/ - name: Upload consolidated gate 3 report - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: gate-3-complete-report path: gate-artifacts/ @@ -1048,7 +1040,7 @@ jobs: if: github.event_name == 'pull_request' && !github.event.pull_request.draft steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -1217,7 +1209,7 @@ jobs: url: https://staging.metabuilder.example.com steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -1267,7 +1259,7 @@ jobs: url: https://metabuilder.example.com steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup npm with Nexus uses: ./.github/actions/setup-npm @@ -1336,16 +1328,16 @@ jobs: platforms: linux/amd64,linux/arm64 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -1381,7 +1373,7 @@ jobs: VCS_REF=${{ github.sha }} - name: Generate artifact attestation - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@v4 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }} subject-digest: ${{ steps.build.outputs.digest }} @@ -1404,16 +1396,16 @@ jobs: platforms: linux/amd64,linux/arm64 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -1450,7 +1442,7 @@ jobs: VCS_REF=${{ github.sha }} - name: Generate artifact attestation - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@v4 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }} subject-digest: ${{ steps.build.outputs.digest }} @@ -1463,16 +1455,16 @@ jobs: needs: container-base-tier2 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -1509,7 +1501,7 @@ jobs: VCS_REF=${{ github.sha }} - name: Generate artifact attestation - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@v4 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/devcontainer subject-digest: ${{ steps.build.outputs.digest }} @@ -1548,13 +1540,13 @@ jobs: dockerfile: ./frontends/exploded-diagrams/Dockerfile steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -1591,7 +1583,7 @@ jobs: VERSION=${{ steps.meta.outputs.version }} - name: Generate artifact attestation - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@v4 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }} subject-digest: ${{ steps.build.outputs.digest }} @@ -1623,14 +1615,14 @@ jobs: - exploded-diagrams steps: - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@0.28.0 + uses: aquasecurity/trivy-action@0.35.0 timeout-minutes: 15 with: image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }} @@ -1638,7 +1630,7 @@ jobs: output: 'trivy-results-${{ matrix.image }}.sarif' - name: Upload Trivy results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: 'trivy-results-${{ matrix.image }}.sarif' category: container-${{ matrix.image }} @@ -1653,7 +1645,7 @@ jobs: REF_NAME: ${{ github.ref_name }} steps: - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -1684,12 +1676,12 @@ jobs: language: ${{ inputs.codeql_languages == 'all' && fromJSON('["javascript-typescript","python","cpp","go"]') || fromJSON(format('["{0}"]', inputs.codeql_languages)) }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} queries: security-and-quality @@ -1697,7 +1689,7 @@ jobs: - name: Setup Node.js if: matrix.language == 'javascript-typescript' - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: node-version: '20' cache: 'npm' @@ -1715,10 +1707,10 @@ jobs: go-version: '1.21' - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{ matrix.language }}" upload: true @@ -1750,7 +1742,7 @@ jobs: if: always() steps: - name: Download all gate artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: pattern: gate-*-complete-report path: all-gate-artifacts/ @@ -1815,7 +1807,7 @@ jobs: } - name: Upload complete audit trail - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: complete-gate-audit-trail path: all-gate-artifacts/ diff --git a/e2e/deployment-smoke.spec.ts b/e2e/deployment-smoke.spec.ts index 587aa072f..f27da07b1 100644 --- a/e2e/deployment-smoke.spec.ts +++ b/e2e/deployment-smoke.spec.ts @@ -4,10 +4,10 @@ import { test, expect } from '@playwright/test' * Deployment smoke tests — validates all services are reachable * through the nginx gateway on port 80. * - * These tests require the full Docker stack to be running. - * They are NOT run in CI (excluded via playwright.config.ts testIgnore). + * CI: The smoke stack (docker-compose.smoke.yml) provides nginx + admin tools. + * Playwright dev servers bind to 0.0.0.0 so nginx can proxy via host.docker.internal. * - * To run locally: + * Local: * cd deployment && docker compose -f docker-compose.stack.yml up -d * PLAYWRIGHT_BASE_URL=http://localhost/workflowui/ npx playwright test deployment-smoke */ diff --git a/e2e/global.setup.ts b/e2e/global.setup.ts index 1339a4715..0e2b1826f 100644 --- a/e2e/global.setup.ts +++ b/e2e/global.setup.ts @@ -1,25 +1,50 @@ /** * Playwright global setup - * Runs before all tests to seed the database with package data + * + * 1. Starts the smoke-stack Docker containers via Testcontainers + * (nginx gateway + MySQL + MongoDB + Redis + admin tools) + * 2. Seeds the database via the /api/setup endpoint */ +import { DockerComposeEnvironment, Wait } from 'testcontainers' +import { resolve } from 'path' + +let environment: Awaited> | undefined async function globalSetup() { - // Wait a bit for the server to start + // ── 1. Start smoke stack via Testcontainers ────────────────────────────── + console.log('[setup] Starting smoke stack via Testcontainers...') + const composeDir = resolve(__dirname, '../deployment') + + environment = await new DockerComposeEnvironment(composeDir, 'docker-compose.smoke.yml') + .withWaitStrategy('nginx', Wait.forHealthCheck()) + .withWaitStrategy('phpmyadmin', Wait.forHealthCheck()) + .withWaitStrategy('mongo-express', Wait.forHealthCheck()) + .withWaitStrategy('redisinsight', Wait.forHealthCheck()) + .withStartupTimeout(180_000) + .up() + + console.log('[setup] Smoke stack healthy') + + // Store ref for teardown + ;(globalThis as Record).__TESTCONTAINERS_ENV__ = environment + + // ── 2. Wait for dev servers (started by Playwright webServer config) ───── await new Promise(resolve => setTimeout(resolve, 2000)) - try { - // Seed database with package data - const response = await fetch('http://localhost:3000/api/setup', { - method: 'POST', - }) + // ── 3. Seed database ──────────────────────────────────────────────────── + const setupUrl = process.env.PLAYWRIGHT_BASE_URL + ? new URL('/api/setup', process.env.PLAYWRIGHT_BASE_URL.replace(/\/workflowui\/?$/, '')).href + : 'http://localhost:3000/api/setup' + try { + const response = await fetch(setupUrl, { method: 'POST' }) if (!response.ok) { - console.error('Failed to seed database:', response.status, response.statusText) + console.error('[setup] Failed to seed database:', response.status, response.statusText) } else { - console.log('Database seeded successfully') + console.log('[setup] Database seeded successfully') } } catch (error) { - console.error('Failed to call setup endpoint:', error) + console.warn('[setup] Setup endpoint not available (non-fatal):', (error as Error).message) } } diff --git a/e2e/global.teardown.ts b/e2e/global.teardown.ts new file mode 100644 index 000000000..93098cfce --- /dev/null +++ b/e2e/global.teardown.ts @@ -0,0 +1,18 @@ +/** + * Playwright global teardown + * Stops the Testcontainers smoke stack started in global.setup.ts + */ + +async function globalTeardown() { + const environment = (globalThis as Record).__TESTCONTAINERS_ENV__ as + | { down: () => Promise } + | undefined + + if (environment) { + console.log('[teardown] Stopping smoke stack...') + await environment.down() + console.log('[teardown] Smoke stack stopped') + } +} + +export default globalTeardown diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 1c9367331..1cb0f8e40 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -15,6 +15,8 @@ const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:3000/workfl export default defineConfig({ testDir: './', testMatch: '**/*.spec.ts', + globalSetup: './global.setup.ts', + globalTeardown: './global.teardown.ts', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, @@ -26,10 +28,13 @@ export default defineConfig({ screenshot: 'only-on-failure', }, - /* Start dev servers automatically when not running against a live Docker stack */ + /* Start dev servers automatically when not running against a live Docker stack. + * In CI the smoke stack's nginx (in Docker) proxies to the host dev servers via + * host.docker.internal. On Linux this resolves to the Docker bridge gateway IP + * (e.g. 172.17.0.1), NOT 127.0.0.1 — so dev servers MUST listen on 0.0.0.0. */ webServer: process.env.PLAYWRIGHT_BASE_URL ? undefined : [ { - command: 'npm run dev -w workflowui', + command: `npm run dev -w workflowui -- --hostname ${process.env.CI ? '0.0.0.0' : 'localhost'}`, url: 'http://localhost:3000/workflowui/', reuseExistingServer: !process.env.CI, timeout: 120_000, @@ -41,7 +46,7 @@ export default defineConfig({ }, }, { - command: 'PORT=3001 npm run dev -w codesnippet', + command: `PORT=3001 npm run dev -w codesnippet -- --hostname ${process.env.CI ? '0.0.0.0' : 'localhost'}`, url: 'http://localhost:3001/pastebin/', reuseExistingServer: !process.env.CI, timeout: 120_000, diff --git a/frontends/nextjs/src/lib/workflow/multi-tenant-context.ts b/frontends/nextjs/src/lib/workflow/multi-tenant-context.ts index 5718034ac..9bd9f38ed 100644 --- a/frontends/nextjs/src/lib/workflow/multi-tenant-context.ts +++ b/frontends/nextjs/src/lib/workflow/multi-tenant-context.ts @@ -189,7 +189,7 @@ export class MultiTenantContextBuilder { triggerData: requestData?.triggerData ?? {}, variables: this.buildVariables(requestData?.variables), secrets: requestData?.secrets ?? {}, - request: this.options.captureRequestData === true ? requestData?.request as WorkflowContext['request'] : undefined, + request: this.options.captureRequestData ? requestData?.request as WorkflowContext['request'] : undefined, multiTenant: multiTenantMeta, requestMetadata: { ipAddress: this.requestContext.ipAddress, @@ -205,7 +205,7 @@ export class MultiTenantContextBuilder { this.validateContextSafety(context) // 5. Load and bind credentials - if (this.options.enforceCredentialValidation === true) { + if (this.options.enforceCredentialValidation) { this.bindCredentials(context) } @@ -213,7 +213,7 @@ export class MultiTenantContextBuilder { this.validateVariableTenantIsolation(context) // 7. Log context creation (audit) - if (this.options.enableAuditLogging === true) { + if (this.options.enableAuditLogging) { this.logContextCreation(context) } @@ -231,7 +231,7 @@ export class MultiTenantContextBuilder { // Super-admin (level 4) can access any tenant if (this.requestContext.userLevel >= 4) { - if (this.options.allowCrossTenantAccess !== true) { + if (!this.options.allowCrossTenantAccess) { throw new Error( `Cross-tenant access disabled: User ${this.requestContext.userId} ` + `cannot access workflow in tenant ${this.workflow.tenantId}` @@ -366,7 +366,7 @@ export class MultiTenantContextBuilder { const errors: string[] = [] // 1. Tenant ID must match (unless cross-tenant access is explicitly allowed) - if (context.tenantId !== this.workflow.tenantId && this.options.allowCrossTenantAccess !== true) { + if (context.tenantId !== this.workflow.tenantId && !this.options.allowCrossTenantAccess) { errors.push( `Context tenant ${context.tenantId} does not match ` + `workflow tenant ${this.workflow.tenantId}` @@ -540,7 +540,7 @@ export class MultiTenantContextBuilder { // 5. Check credentials const credentialCount = this.workflow.credentials.length - if (this.options.enforceCredentialValidation === true && credentialCount > 0) { + if (this.options.enforceCredentialValidation && credentialCount > 0) { warnings.push({ path: 'credentials', message: `${String(credentialCount)} credential(s) will be validated during execution`, diff --git a/package.json b/package.json index 219107a43..1bb689b6b 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "eslint-plugin-react-refresh": "^0.5.0", "jest": "^30.2.0", "jsdom": "^28.1.0", - "npm-run-all2": "8.0.4" + "npm-run-all2": "8.0.4", + "testcontainers": "^11.12.0" }, "dependencies": { "@hookform/resolvers": "^5.2.2",