diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index ca7767f66b9..d726d8cf4ee 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -470,6 +470,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: blogauditor + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index be70c0b1f30..32b242686ca 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -548,6 +548,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: cloclo + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/daily-model-inventory.lock.yml b/.github/workflows/daily-model-inventory.lock.yml index 23fc8b5ab61..dcf5b40412d 100644 --- a/.github/workflows/daily-model-inventory.lock.yml +++ b/.github/workflows/daily-model-inventory.lock.yml @@ -424,6 +424,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: dailymodelinventory + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index 586c72a618b..b5925a6ef9f 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -431,6 +431,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: dailymultidevicedocstester + PLAYWRIGHT_MCP_SANDBOX: false outputs: ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} aic: ${{ steps.parse-mcp-gateway.outputs.aic }} diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index f837fb18c32..251684ad3f2 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -421,6 +421,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: docsnoobtester + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml index 3dc8eb9c594..7c85b8ab208 100644 --- a/.github/workflows/slide-deck-maintainer.lock.yml +++ b/.github/workflows/slide-deck-maintainer.lock.yml @@ -451,6 +451,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: slidedeckmaintainer + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index b8e0879386b..27ec3de6872 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -656,6 +656,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: smokeclaude + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index be632757588..e88cd4c72f9 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -532,6 +532,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: smokecodex + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml index 449bb792d7b..3ce1731f1c9 100644 --- a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml @@ -567,6 +567,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: smokecopilotaoaiapikey + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/smoke-copilot-aoai-entra.lock.yml b/.github/workflows/smoke-copilot-aoai-entra.lock.yml index 3fe6a2173e2..dcad12e63ac 100644 --- a/.github/workflows/smoke-copilot-aoai-entra.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-entra.lock.yml @@ -568,6 +568,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: smokecopilotaoaientra + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml index 00ab08b3fec..acef99f18e7 100644 --- a/.github/workflows/smoke-copilot-arm.lock.yml +++ b/.github/workflows/smoke-copilot-arm.lock.yml @@ -510,6 +510,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: smokecopilotarm + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 0277e3af28c..3a5e08ec8d6 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -573,6 +573,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: smokecopilot + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/visual-regression-checker.lock.yml b/.github/workflows/visual-regression-checker.lock.yml index 51752d6c442..71e076e8484 100644 --- a/.github/workflows/visual-regression-checker.lock.yml +++ b/.github/workflows/visual-regression-checker.lock.yml @@ -433,6 +433,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: visualregressionchecker + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/.github/workflows/weekly-editors-health-check.lock.yml b/.github/workflows/weekly-editors-health-check.lock.yml index ce87bd7740f..706ce0cee1a 100644 --- a/.github/workflows/weekly-editors-health-check.lock.yml +++ b/.github/workflows/weekly-editors-health-check.lock.yml @@ -420,6 +420,7 @@ jobs: GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: weeklyeditorshealthcheck + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/pkg/workflow/compiler_main_job_helpers.go b/pkg/workflow/compiler_main_job_helpers.go index 21295082410..f0d013b1880 100644 --- a/pkg/workflow/compiler_main_job_helpers.go +++ b/pkg/workflow/compiler_main_job_helpers.go @@ -251,9 +251,22 @@ func (c *Compiler) buildMainJobOutputs(data *WorkflowData) map[string]string { func (c *Compiler) buildMainJobEnv(data *WorkflowData) map[string]string { var env map[string]string + // Disable the Chromium process sandbox for playwright CLI mode. + // GitHub Actions runners are containerised environments where kernel namespace + // sandboxing is unavailable, which causes playwright-cli to abort with + // "Playwright can't run in this sandbox environment". + if isPlaywrightCLIMode(data.Tools) { + if env == nil { + env = make(map[string]string) + } + env["PLAYWRIGHT_MCP_SANDBOX"] = "false" + } + if data.SafeOutputs != nil { compilerMainJobLog.Printf("Configuring safe-outputs job env for main job (uploadAssets=%v)", data.SafeOutputs.UploadAssets != nil) - env = make(map[string]string) + if env == nil { + env = make(map[string]string) + } // Set GH_AW_MCP_LOG_DIR for safe outputs MCP server logging // Store in mcp-logs directory so it's included in mcp-logs artifact diff --git a/pkg/workflow/compiler_main_job_helpers_test.go b/pkg/workflow/compiler_main_job_helpers_test.go index fc275938286..b56598ddf6d 100644 --- a/pkg/workflow/compiler_main_job_helpers_test.go +++ b/pkg/workflow/compiler_main_job_helpers_test.go @@ -260,6 +260,40 @@ func TestBuildMainJobEnv(t *testing.T) { require.NotNil(t, env) assert.Contains(t, env["GH_AW_PROJECT_UTC"], "+05:30") }) + + t.Run("playwright CLI mode sets PLAYWRIGHT_MCP_SANDBOX=false", func(t *testing.T) { + c := NewCompiler() + c.repoConfigLoaded = true + c.repoConfig = &RepoConfig{} + data := &WorkflowData{ + Tools: map[string]any{ + "playwright": map[string]any{ + "mode": "cli", + }, + }, + } + env := c.buildMainJobEnv(data) + require.NotNil(t, env) + assert.Equal(t, "false", env["PLAYWRIGHT_MCP_SANDBOX"]) + }) + + t.Run("safe-outputs with playwright CLI preserves PLAYWRIGHT_MCP_SANDBOX", func(t *testing.T) { + c := NewCompiler() + c.repoConfigLoaded = true + c.repoConfig = &RepoConfig{} + data := &WorkflowData{ + Tools: map[string]any{ + "playwright": map[string]any{ + "mode": "cli", + }, + }, + SafeOutputs: &SafeOutputsConfig{}, + } + env := c.buildMainJobEnv(data) + require.NotNil(t, env) + assert.Equal(t, "false", env["PLAYWRIGHT_MCP_SANDBOX"]) + assert.Equal(t, "${{ github.event.repository.default_branch }}", env["DEFAULT_BRANCH"]) + }) } // TestBuildMainJobPermissions tests that permissions are built correctly. diff --git a/pkg/workflow/playwright_cli_test.go b/pkg/workflow/playwright_cli_test.go index b9ce11ea2fc..686c8a0ef4e 100644 --- a/pkg/workflow/playwright_cli_test.go +++ b/pkg/workflow/playwright_cli_test.go @@ -20,7 +20,7 @@ func TestGeneratePlaywrightCLIInstallSteps_DefaultVersionUsesCooldown(t *testing }, }) - require.Len(t, steps, 2, "expected npm install step plus skills install step") + require.Len(t, steps, 2, "expected npm install step and skills install step") installStep := strings.Join(steps[0], "\n") assert.Contains(t, installStep, "npm install -g @playwright/cli@"+string(constants.DefaultPlaywrightCLIVersion)) diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden index 2c42568f4e9..7607d3823a3 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden @@ -337,6 +337,7 @@ jobs: env: GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: playwrightclimode + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} diff --git a/pkg/workflow/testdata/wasm_golden/WasmBinary/playwright-cli-mode.golden b/pkg/workflow/testdata/wasm_golden/WasmBinary/playwright-cli-mode.golden index 9bca52b25d1..a3b9187574a 100644 --- a/pkg/workflow/testdata/wasm_golden/WasmBinary/playwright-cli-mode.golden +++ b/pkg/workflow/testdata/wasm_golden/WasmBinary/playwright-cli-mode.golden @@ -337,6 +337,7 @@ jobs: env: GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: playwrightclimode + PLAYWRIGHT_MCP_SANDBOX: false outputs: agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }}