diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index aea3cad900b..b74c2790a9f 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -392,6 +392,11 @@ "digest": "sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9@sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2" }, + "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26": { + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", + "digest": "sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da", + "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26@sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da" + }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28": { "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "digest": "sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7", diff --git a/pkg/actionpins/data/action_pins.json b/pkg/actionpins/data/action_pins.json index aea3cad900b..b74c2790a9f 100644 --- a/pkg/actionpins/data/action_pins.json +++ b/pkg/actionpins/data/action_pins.json @@ -392,6 +392,11 @@ "digest": "sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9@sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2" }, + "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26": { + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", + "digest": "sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da", + "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26@sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da" + }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28": { "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "digest": "sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7", diff --git a/pkg/workflow/awf_helpers.go b/pkg/workflow/awf_helpers.go index 77b76a960cf..b08791c0af1 100644 --- a/pkg/workflow/awf_helpers.go +++ b/pkg/workflow/awf_helpers.go @@ -790,6 +790,10 @@ func GetAWFCommandPrefix(workflowData *WorkflowData) string { // // ,squid=sha256:...,agent=sha256:...,api-proxy=sha256:...,cli-proxy=sha256:... // +// For arc-dind topology, build-tools is also included: +// +// ,squid=sha256:...,agent=sha256:...,api-proxy=sha256:...,cli-proxy=sha256:...,build-tools=sha256:... +// // This keeps AWF sidecar configuration aligned with digest-pinned pre-download images. func buildAWFImageTagWithDigests(imageTag string, workflowData *WorkflowData) string { if imageTag == "" { @@ -807,6 +811,9 @@ func buildAWFImageTagWithDigests(imageTag string, workflowData *WorkflowData) st {name: "api-proxy", image: constants.DefaultFirewallRegistry + "/api-proxy:" + imageTag}, {name: "cli-proxy", image: constants.DefaultFirewallRegistry + "/cli-proxy:" + imageTag}, } + if isArcDindTopology(workflowData) { + specs = append(specs, digestSpec{name: "build-tools", image: constants.DefaultFirewallRegistry + "/build-tools:" + imageTag}) + } parts := []string{imageTag} for _, spec := range specs { diff --git a/pkg/workflow/awf_helpers_test.go b/pkg/workflow/awf_helpers_test.go index 831742cb066..e625527077a 100644 --- a/pkg/workflow/awf_helpers_test.go +++ b/pkg/workflow/awf_helpers_test.go @@ -2047,6 +2047,23 @@ func TestBuildAWFImageTagWithDigests(t *testing.T) { tag := buildAWFImageTagWithDigests("0.0.1", nil) assert.Equal(t, "0.0.1", tag, "should not append digest metadata when no pins are available") }) + + t.Run("includes build-tools digest for arc-dind topology", func(t *testing.T) { + imageTag := strings.TrimPrefix(string(constants.DefaultFirewallVersion), "v") + workflowData := &WorkflowData{ + RunnerConfig: &RunnerConfig{Topology: RunnerTopologyArcDind}, + } + tag := buildAWFImageTagWithDigests(imageTag, workflowData) + + assert.Contains(t, tag, "build-tools=sha256:", "should include build-tools digest metadata for arc-dind topology") + }) + + t.Run("excludes build-tools digest without arc-dind topology", func(t *testing.T) { + imageTag := strings.TrimPrefix(string(constants.DefaultFirewallVersion), "v") + tag := buildAWFImageTagWithDigests(imageTag, nil) + + assert.NotContains(t, tag, "build-tools=", "should not include build-tools digest metadata without arc-dind topology") + }) } func TestBuildAWFArgs_ImageTagIncludesDigests(t *testing.T) { diff --git a/pkg/workflow/data/action_pins.json b/pkg/workflow/data/action_pins.json index aea3cad900b..b74c2790a9f 100644 --- a/pkg/workflow/data/action_pins.json +++ b/pkg/workflow/data/action_pins.json @@ -392,6 +392,11 @@ "digest": "sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9@sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2" }, + "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26": { + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", + "digest": "sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da", + "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26@sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da" + }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28": { "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "digest": "sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7", diff --git a/pkg/workflow/docker_firewall_pin_compile_test.go b/pkg/workflow/docker_firewall_pin_compile_test.go index d4577534e06..4f663822d4f 100644 --- a/pkg/workflow/docker_firewall_pin_compile_test.go +++ b/pkg/workflow/docker_firewall_pin_compile_test.go @@ -174,3 +174,62 @@ Test workflow.` } } } + +// TestCompileWorkflow_BuildToolsImagePinnedForArcDind is a regression test for +// gh-aw#44040: when runner.topology is arc-dind, the build-tools image must be +// digest-pinned in the compiled lock file the same way the other four gh-aw-firewall +// images (agent, api-proxy, cli-proxy, squid) are. +func TestCompileWorkflow_BuildToolsImagePinnedForArcDind(t *testing.T) { + // Strip the leading "v" to get the Docker image tag (mirrors getAWFImageTag). + imageTag := strings.TrimPrefix(string(constants.DefaultFirewallVersion), "v") + + frontmatter := `--- +on: workflow_dispatch +engine: claude +runner: + topology: arc-dind +network: + allowed: + - defaults +--- + +# Test +Test workflow.` + + tmpDir := testutil.TempDir(t, "docker-firewall-pins-arc-dind-test") + testFile := filepath.Join(tmpDir, "test-workflow.md") + if err := os.WriteFile(testFile, []byte(frontmatter), 0644); err != nil { + t.Fatal(err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(testFile); err != nil { + t.Fatalf("Failed to compile workflow: %v", err) + } + + lockFile := stringutil.MarkdownToLockFile(testFile) + yaml, err := os.ReadFile(lockFile) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + yamlStr := string(yaml) + + buildToolsImage := "ghcr.io/github/gh-aw-firewall/build-tools:" + imageTag + buildToolsDigest := "sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da" + pinnedBuildTools := buildToolsImage + "@" + buildToolsDigest + + if !strings.Contains(yamlStr, `"image":"`+buildToolsImage+`","digest":"`+buildToolsDigest+`","pinned_image":"`+pinnedBuildTools+`"`) { + t.Errorf("Expected manifest header to include pinned metadata for %s", buildToolsImage) + } + if !strings.Contains(yamlStr, "# - "+pinnedBuildTools) { + t.Errorf("Expected pinned container comment for %s", buildToolsImage) + } + if !strings.Contains(yamlStr, pinnedBuildTools) { + t.Errorf("Expected pinned download reference for %s", buildToolsImage) + } + + if !strings.Contains(yamlStr, `build-tools=`+buildToolsDigest) { + t.Errorf("Expected AWF config JSON to include build-tools=%s", buildToolsDigest) + } +} diff --git a/pkg/workflow/testdata/TestWasmGolden_AllEngines/claude.golden b/pkg/workflow/testdata/TestWasmGolden_AllEngines/claude.golden index ce33aa9b494..dc5df7fd15f 100644 --- a/pkg/workflow/testdata/TestWasmGolden_AllEngines/claude.golden +++ b/pkg/workflow/testdata/TestWasmGolden_AllEngines/claude.golden @@ -690,6 +690,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/pkg/workflow/testdata/TestWasmGolden_AllEngines/codex.golden b/pkg/workflow/testdata/TestWasmGolden_AllEngines/codex.golden index 9e617156e33..af133e8ccf4 100644 --- a/pkg/workflow/testdata/TestWasmGolden_AllEngines/codex.golden +++ b/pkg/workflow/testdata/TestWasmGolden_AllEngines/codex.golden @@ -660,6 +660,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/pkg/workflow/testdata/TestWasmGolden_AllEngines/copilot.golden b/pkg/workflow/testdata/TestWasmGolden_AllEngines/copilot.golden index 901a209eefd..a17f7648ef2 100644 --- a/pkg/workflow/testdata/TestWasmGolden_AllEngines/copilot.golden +++ b/pkg/workflow/testdata/TestWasmGolden_AllEngines/copilot.golden @@ -634,6 +634,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden b/pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden index c495fb55ffb..4cf4ef8cfb3 100644 --- a/pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden +++ b/pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden @@ -614,6 +614,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/pkg/workflow/testdata/TestWasmGolden_AllEngines/pi.golden b/pkg/workflow/testdata/TestWasmGolden_AllEngines/pi.golden index 2ef33e94aca..2df7e298845 100644 --- a/pkg/workflow/testdata/TestWasmGolden_AllEngines/pi.golden +++ b/pkg/workflow/testdata/TestWasmGolden_AllEngines/pi.golden @@ -560,6 +560,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden index 7e8bc914954..275496143ec 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden @@ -634,6 +634,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden index 4bd363f7ef7..39d792b6555 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden @@ -654,6 +654,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden index e3be7e3b55b..6bb0f85f825 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden @@ -891,6 +891,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden index c761c94842d..a829ffc9894 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden @@ -635,6 +635,8 @@ jobs: env: AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs run: | + # Best-effort permission fix for artifact upload (AWF cleanup may not have run) + sudo -n chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) if command -v awf &> /dev/null; then awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"