fix: reclaim stale root-owned /tmp/gh-aw before AWF invocation#44022
Conversation
On persistent runners, a previous AWF run can leave /tmp/gh-aw/sandbox/ owned by root. The next run's setup.sh does `rm -rf /tmp/gh-aw` which silently fails (EACCES), then AWF's mkdirSync fails with: EACCES: permission denied, mkdir '/tmp/gh-aw/sandbox/firewall/logs' ## Changes ### actions/setup/setup.sh - Check if /tmp/gh-aw is writable before attempting removal - Fall back to `sudo rm -rf` when the directory (or its children) are not writable by the current user - Works on GitHub-hosted runners where sudo is passwordless ### pkg/workflow/engine_firewall_support.go - Add best-effort `chmod -R a+rX` in network-isolation mode (non-sudo) - Previously, the post-run chmod was skipped entirely when network-isolation was enabled, assuming rootless == no root-owned files. This is incorrect: Docker containers still write as non-runner UIDs (Squid: UID 13), and if AWF is killed by timeout/OOM, its cleanup never runs. ### pkg/workflow/engine_firewall_support_test.go - Update test assertions to expect non-sudo chmod in network-isolation mode Ref: https://github.com/github/gh-aw/actions/runs/28856007222/job/85583002595 Supersedes: #42400 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This pull request addresses intermittent EACCES failures on persistent runners caused by stale, root-owned (or otherwise non-writable) state under /tmp/gh-aw left behind by prior AWF runs, ensuring the next AWF invocation can create its sandbox directories reliably.
Changes:
- Harden
actions/setup/setup.shto reclaim/tmp/gh-awusing a sudo fallback when plainrm -rfcannot remove root-owned/non-writable trees. - Update firewall log parsing step generation to run a best-effort permission fix even when AWF network isolation is enabled.
- Adjust tests to reflect the updated network-isolation chmod behavior.
Show a summary per file
| File | Description |
|---|---|
actions/setup/setup.sh |
Adds pre-flight cleanup logic to remove /tmp/gh-aw even when prior runs left non-writable/root-owned contents. |
pkg/workflow/engine_firewall_support.go |
Emits a chmod-based post-run permission “safety net” for firewall artifacts in network-isolation mode. |
pkg/workflow/engine_firewall_support_test.go |
Updates assertions for the network-isolation firewall log parsing step behavior. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 3/3 changed files
- Comments generated: 2
- Review effort level: Low
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@copilot run pr-finisher skill |
|
🚀 Smoke Antigravity MISSION COMPLETE! Antigravity has spoken. ✨ |
|
✅ All tools validated successfully! Agent Container Smoke Test confirms agent container is ready. |
|
🚀 Smoke Gemini MISSION COMPLETE! Gemini has spoken. ✨ |
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
|
✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟 |
|
🚀 Smoke Pi MISSION COMPLETE! Pi delivered. 🥧 Smoke test completed; issue created and PR comment added. Label not added because not all tests passed. |
|
📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing... |
|
📰 BREAKING: Smoke Copilot - AOAI (Entra) is now investigating this pull request. Sources say the story is developing... |
|
📰 BREAKING: Smoke Copilot - AOAI (apikey) is now investigating this pull request. Sources say the story is developing... |
Smoke test
Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "proxy.golang.org"See Network Configuration for more information.
|
Smoke Test: Gemini Results
Overall Status: FAIL Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "localhost"See Network Configuration for more information.
|
Agent Container Tool Check
Result: 12/12 tools available ✅ Overall Status: PASS Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "awmgmcpg"See Network Configuration for more information.
|
|
Smoke Test: Codex - 28868608114\n#43967 fix: suppress osgetenvlibrary lint findings in pkg/cli and pkg/workflow\n#43957 Recreate AWF firewall v0.27.26 integration and artifact refresh\n✅/❌ results recorded in issue #44026\nOverall status: FAIL Warning Firewall blocked 6 domainsThe following domains were blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "accounts.google.com"
- "android.clients.google.com"
- "clients2.google.com"
- "contentautofill.googleapis.com"
- "safebrowsingohttpgateway.googleapis.com"
- "www.google.com"See Network Configuration for more information.
|
Comment MemoryNote This comment is managed by comment memory.It stores persistent context for this thread in the code block at the top of this comment. Warning Firewall blocked 6 domainsThe following domains were blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "accounts.google.com"
- "android.clients.google.com"
- "clients2.google.com"
- "contentautofill.googleapis.com"
- "safebrowsingohttpgateway.googleapis.com"
- "www.google.com"See Network Configuration for more information.
|
💥 Smoke Test: Claude — Run 28868607863Core #1-12: all ✅ Overall: PARTIAL (16: no unresolved threads; 18: allowed-files config; 19: no safe test PR) Warning Firewall blocked 6 domainsThe following domains were blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "accounts.google.com"
- "android.clients.google.com"
- "clients2.google.com"
- "contentautofill.googleapis.com"
- "safebrowsingohttpgateway.googleapis.com"
- "www.google.com"See Network Configuration for more information.
|
There was a problem hiding this comment.
💥 Automated smoke test review - all systems nominal!
Warning
Firewall blocked 6 domains
The following domains were blocked by the firewall during workflow execution:
accounts.google.comandroid.clients.google.comclients2.google.comcontentautofill.googleapis.comsafebrowsingohttpgateway.googleapis.comwww.google.com
To allow these domains, add them to the
network.allowedlist in your workflow frontmatter:
network:
allowed:
- defaults
- "accounts.google.com"
- "android.clients.google.com"
- "clients2.google.com"
- "contentautofill.googleapis.com"
- "safebrowsingohttpgateway.googleapis.com"
- "www.google.com"See Network Configuration for more information.
💥 [THE END] — Illustrated by Smoke Claude · 72.4 AIC · ⌖ 14.7 AIC · ⊞ 8.4K
Comment /smoke-claude to run again
| if [ -d /tmp/gh-aw ] && [ ! -w /tmp/gh-aw ]; then | ||
| debug_log "/tmp/gh-aw exists but is not writable (likely root-owned from prior run); using sudo to remove" | ||
| if command -v sudo >/dev/null 2>&1; then | ||
| sudo -n rm -rf /tmp/gh-aw |
There was a problem hiding this comment.
The passwordless sudo -n rm -rf fallback is a solid approach for GitHub-hosted runners. Consider logging when the sudo path is taken to aid future debugging.
|
|
||
| // When sudo is false (network isolation mode), AWF runs rootless so firewall files | ||
| // are not owned by root — skip the sudo chmod permission-fix step. | ||
| // When sudo is false (network isolation mode), AWF runs rootless but Docker |
There was a problem hiding this comment.
Good clarification in this comment — noting that Docker containers write as non-runner UIDs (e.g. Squid UID 13) makes the rationale much clearer.
Ran a pr-finisher pass.
CI on the current head remains unverified until a maintainer re-triggers workflows (close/reopen PR, workflow_dispatch, or maintainer push). |
There was a problem hiding this comment.
Caveman smoke pass. Left two tiny notes.
Warning
Firewall blocked 6 domains
The following domains were blocked by the firewall during workflow execution:
accounts.google.comandroid.clients.google.comclients2.google.comcontentautofill.googleapis.comsafebrowsingohttpgateway.googleapis.comwww.google.com
To allow these domains, add them to the
network.allowedlist in your workflow frontmatter:
network:
allowed:
- defaults
- "accounts.google.com"
- "android.clients.google.com"
- "clients2.google.com"
- "contentautofill.googleapis.com"
- "safebrowsingohttpgateway.googleapis.com"
- "www.google.com"See Network Configuration for more information.
📰 BREAKING: Report filed by Smoke Copilot · 73.8 AIC · ⌖ 3.43 AIC · ⊞ 19K
Comment /smoke-copilot to run again
Add label smoke to run again
|
Smoke Test Results:
Overall: FAIL Warning Firewall blocked 6 domainsThe following domains were blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "accounts.google.com"
- "android.clients.google.com"
- "clients2.google.com"
- "contentautofill.googleapis.com"
- "safebrowsingohttpgateway.googleapis.com"
- "www.google.com"See Network Configuration for more information.
|
There was a problem hiding this comment.
Warning
Firewall blocked 6 domains
The following domains were blocked by the firewall during workflow execution:
accounts.google.comandroid.clients.google.comclients2.google.comcontentautofill.googleapis.comsafebrowsingohttpgateway.googleapis.comwww.google.com
To allow these domains, add them to the
network.allowedlist in your workflow frontmatter:
network:
allowed:
- defaults
- "accounts.google.com"
- "android.clients.google.com"
- "clients2.google.com"
- "contentautofill.googleapis.com"
- "safebrowsingohttpgateway.googleapis.com"
- "www.google.com"See Network Configuration for more information.
📰 BREAKING: Report filed by Smoke Copilot - AOAI (apikey) · 44.3 AIC · ⌖ 3.01 AIC · ⊞ 17.8K
Comment /smoke-copilot-aoai-apikey to run again
Add label smoke to run again
|
Hey @lpcox 👋 — thanks for working on this fix for the stale root-owned
If you'd like to get this fix in, the recommended next step is to open an issue describing the problem and your proposed solution (you can reuse most of this PR's body — it's already very well written!). The core team can then pick it up and implement it through the standard workflow. If you'd like a hand drafting that issue, you can assign this prompt to your coding agent:
|
|
🎉 This pull request is included in a new release. Release: |
Summary
Fixes two related bugs where stale root-owned files from a previous AWF run break subsequent invocations on persistent GitHub-hosted runners.
Changes
actions/setup/setup.sh— robust/tmp/gh-awreclamationReplaces the unconditional
rm -rf /tmp/gh-awwith a two-branch conditional that handles root-owned directories:! -w /tmp/gh-aw): escalates immediately tosudo -n rm -rf.sandbox/firewall/): attempts plainrm -rf; falls back tosudo -n rm -rfonEACCES.sudois unavailable in either branch, emits a::error::annotation and exits with code 1.Root cause: on persistent runners, a prior AWF run leaves
/tmp/gh-awor its subdirectories owned by root. The plainrm -rffails silently or exits non-zero, preventing a clean workspace for the next run.pkg/workflow/engine_firewall_support.go— best-effort chmod in network isolation modeAdds an
elsebranch togenerateFirewallLogParsingStepfor theisAWFNetworkIsolationEnabledpath. Previously, the sudo-chmod step was entirely absent in rootless mode. Now the generated step includes:Root cause: Docker containers running inside AWF write files under non-runner UIDs even in rootless mode (e.g., Squid writes as UID 13). AWF's post-run cleanup (
fixArtifactPermissionsForRootless) normally corrects this, but is skipped on timeout or OOM kill, leaving firewall logs unreadable for artifact upload. Thesudo -n || chmod || truepattern is non-fatal and best-effort.pkg/workflow/engine_firewall_support_test.go— test coverageUpdates
TestGenerateFirewallLogParsingStepNetworkIsolationOmitsSudo:sudo chmodis absent (updated error message).chmod -R a+rXis present, validating the new best-effort fallback.Motivation
Both issues manifest only on persistent/reused runners where prior AWF runs leave behind root-owned state. The fixes are defensive: they tolerate common runner configurations and degrade gracefully when
sudois unavailable.Testing
sudo -nensures non-interactive, non-password-prompting escalation.Commits
5a2138b8dfix: reclaim stale root-owned /tmp/gh-aw before AWF invocationa0ef41764Potential fix for pull request finding470a2bdfcPotential fix for pull request finding