Skip to content

Conversation

@Claude
Copy link
Contributor

@Claude Claude AI commented Feb 12, 2026

Docker mount errors occur when mounting /dev/null over credential files whose parent directories don't exist in the container filesystem. The error manifests as: error mounting "/dev/null" to rootfs at "/host/home/runner/.cargo/credentials": create mountpoint ... read-only file system

Changes

  • Credential hiding mechanism: Replaced individual file mounts (/dev/null:/path/to/file:ro) with directory-level tmpfs overlays (/path/to/dir:rw,noexec,nosuid,size=1m)

    • Normal mode: Hides ~/.docker, ~/.ssh, ~/.aws, ~/.kube, ~/.azure, ~/.config/gcloud, ~/.config/gh, ~/.cargo, ~/.composer, ~/.npmrc
    • Chroot mode: Hides same directories at /host paths
  • Type system: Added tmpfs?: string[] to DockerService interface in src/types.ts

  • Config mutation fix: Changed from storing tmpfs arrays on config object (which caused test pollution) to using local variable credentialTmpfsMounts

Technical Detail

tmpfs creates an empty in-memory filesystem overlay that doesn't require the target path to exist first, unlike /dev/null bind mounts which fail during mountpoint creation when parent directories are missing in the container.

Before:

agentVolumes.push(`/dev/null:${credFile}:ro`);  // Fails if parent dir doesn't exist

After:

credentialTmpfsMounts.push(`${credDir}:rw,noexec,nosuid,size=1m`);  // Works regardless

This fixes Docker mount errors like "read-only file system" that occur when
mounting /dev/null over credential files whose parent directories don't exist
in the container's filesystem.

The solution uses tmpfs mounts instead, which create empty in-memory filesystems
that overlay directories without requiring the target path to exist first.

Changes:
- Normal mode: Hide credential directories (~/.docker, ~/.ssh, ~/.aws, etc.) using tmpfs
- Chroot mode: Hide credential directories at /host paths using tmpfs
- Updated DockerService type to include tmpfs property
- Updated tests to verify tmpfs behavior instead of /dev/null mounts
- Fixed config mutation bug by using local variable instead of mutating config object

Closes the GitHub Actions failure where cargo credentials mounting failed with:
"error mounting "/dev/null" to rootfs at "/host/home/runner/.cargo/credentials":
create mountpoint ... read-only file system"

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
@Claude Claude AI changed the title [WIP] Fix failing GitHub Actions workflow agent fix: replace /dev/null mounts with tmpfs for credential hiding Feb 12, 2026
@Claude Claude AI requested a review from lpcox February 12, 2026 06:05
@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

💫 TO BE CONTINUED... Smoke Claude failed! Our hero faces unexpected challenges...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

📰 DEVELOPING STORY: Smoke Copilot reports failed. Our correspondents are investigating the incident...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

Chroot tests failed Smoke Chroot failed - See logs for details.

* Initial plan

* fix: prevent .cargo volume/tmpfs mount conflict in chroot mode

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: handle missing docker-compose.yml during cleanup gracefully (#741)

* Initial plan

* fix: handle missing docker-compose.yml during cleanup gracefully

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
@lpcox lpcox marked this pull request as ready for review February 12, 2026 06:33
Copilot AI review requested due to automatic review settings February 12, 2026 06:33
@lpcox lpcox merged commit 2f8fc0d into claude/fix-github-actions-workflow-again Feb 12, 2026
13 checks passed
@lpcox lpcox deleted the claude/fix-github-actions-workflow-another-one branch February 12, 2026 06:33
@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

💫 TO BE CONTINUED... Smoke Claude failed! Our hero faces unexpected challenges...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

Chroot tests failed Smoke Chroot failed - See logs for details.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

📰 DEVELOPING STORY: Smoke Copilot reports failed. Our correspondents are investigating the incident...

lpcox added a commit that referenced this pull request Feb 12, 2026
* Initial plan

* fix: only hide credential files if parent directory exists

Prevents Docker mount errors when credential file parent directories
don't exist on the host. This fixes the "read-only file system" error
when Docker tries to create mountpoints for non-existent parents.

The fix checks if the parent directory exists before adding /dev/null
mounts for credential files in both normal and chroot modes. This
maintains security while avoiding mount failures.

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: replace /dev/null mounts with tmpfs for credential hiding (#738)

* Initial plan

* fix: replace /dev/null mounts with tmpfs for credential hiding

This fixes Docker mount errors like "read-only file system" that occur when
mounting /dev/null over credential files whose parent directories don't exist
in the container's filesystem.

The solution uses tmpfs mounts instead, which create empty in-memory filesystems
that overlay directories without requiring the target path to exist first.

Changes:
- Normal mode: Hide credential directories (~/.docker, ~/.ssh, ~/.aws, etc.) using tmpfs
- Chroot mode: Hide credential directories at /host paths using tmpfs
- Updated DockerService type to include tmpfs property
- Updated tests to verify tmpfs behavior instead of /dev/null mounts
- Fixed config mutation bug by using local variable instead of mutating config object

Closes the GitHub Actions failure where cargo credentials mounting failed with:
"error mounting "/dev/null" to rootfs at "/host/home/runner/.cargo/credentials":
create mountpoint ... read-only file system"

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: prevent .cargo volume/tmpfs mount conflict in chroot mode (#740)

* Initial plan

* fix: prevent .cargo volume/tmpfs mount conflict in chroot mode

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: handle missing docker-compose.yml during cleanup gracefully (#741)

* Initial plan

* fix: handle missing docker-compose.yml during cleanup gracefully

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
lpcox added a commit that referenced this pull request Feb 12, 2026
* Initial plan

* fix: create .copilot/logs mountpoint before docker mount

The agent container mount at ~/.copilot:ro followed by
~/agent-logs:~/.copilot/logs:rw fails in GitHub Actions because Docker
cannot create the logs subdirectory inside a read-only mount.

Fix by creating ~/.copilot/logs on the host before mounting, so Docker
doesn't need to create the mountpoint inside the read-only parent.

This affects GitHub Actions runners where ~/.copilot doesn't exist
before AWF runs.

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: only hide credential files if parent directory exists (#737)

* Initial plan

* fix: only hide credential files if parent directory exists

Prevents Docker mount errors when credential file parent directories
don't exist on the host. This fixes the "read-only file system" error
when Docker tries to create mountpoints for non-existent parents.

The fix checks if the parent directory exists before adding /dev/null
mounts for credential files in both normal and chroot modes. This
maintains security while avoiding mount failures.

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: replace /dev/null mounts with tmpfs for credential hiding (#738)

* Initial plan

* fix: replace /dev/null mounts with tmpfs for credential hiding

This fixes Docker mount errors like "read-only file system" that occur when
mounting /dev/null over credential files whose parent directories don't exist
in the container's filesystem.

The solution uses tmpfs mounts instead, which create empty in-memory filesystems
that overlay directories without requiring the target path to exist first.

Changes:
- Normal mode: Hide credential directories (~/.docker, ~/.ssh, ~/.aws, etc.) using tmpfs
- Chroot mode: Hide credential directories at /host paths using tmpfs
- Updated DockerService type to include tmpfs property
- Updated tests to verify tmpfs behavior instead of /dev/null mounts
- Fixed config mutation bug by using local variable instead of mutating config object

Closes the GitHub Actions failure where cargo credentials mounting failed with:
"error mounting "/dev/null" to rootfs at "/host/home/runner/.cargo/credentials":
create mountpoint ... read-only file system"

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: prevent .cargo volume/tmpfs mount conflict in chroot mode (#740)

* Initial plan

* fix: prevent .cargo volume/tmpfs mount conflict in chroot mode

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: handle missing docker-compose.yml during cleanup gracefully (#741)

* Initial plan

* fix: handle missing docker-compose.yml during cleanup gracefully

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

🌑 The shadows whisper... Smoke Codex failed. The oracle requires further meditation...

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Docker Compose generation logic to hide host credentials more reliably inside the agent container by switching from per-file /dev/null bind mounts to tmpfs overlays, avoiding mountpoint creation failures when paths don’t exist or are read-only.

Changes:

  • Add tmpfs?: string[] to DockerService in src/types.ts and document tmpfs mount semantics.
  • Replace credential hiding from /dev/null:/path/to/file:ro mounts to tmpfs directory overlays in generateDockerCompose(); update unit tests accordingly.
  • Update stopContainers() to fall back to stopping containers by name when docker-compose.yml is missing.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/types.ts Extends compose service typing to include tmpfs mounts and documents intended usage.
src/docker-manager.ts Implements tmpfs-based credential hiding and wires credential tmpfs mounts into the agent service; also adds a stopContainers() fallback path.
src/docker-manager.test.ts Updates expectations from /dev/null volume mounts to tmpfs mounts; adds coverage for .cargo mount behavior under chroot/full-access combinations.
Comments suppressed due to low confidence (1)

src/docker-manager.ts:751

  • Same issue in chroot mode: /host${userHome}/.npmrc is a file path. A tmpfs mount here will fail if the file exists (or will create a directory and potentially break tools that expect a file). Use an empty-file bind mount (or /dev/null with safe parent-dir handling) for file targets.
    // Also hide ~/.npmrc file (NPM registry tokens) - mount as tmpfs
    credentialTmpfsMounts.push(`/host${userHome}/.npmrc:rw,noexec,nosuid,size=1m`);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +719 to +723
if (!credentialTmpfsMounts.some(mount => mount.startsWith(`${npmrcParent}:`))) {
// Only add if we're not already mounting the entire home directory
// In practice, we'll mount ~/.npmrc as a tmpfs (which will be an empty directory)
credentialTmpfsMounts.push(`${effectiveHome}/.npmrc:rw,noexec,nosuid,size=1m`);
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tmpfs mounts are directory mounts. Mounting tmpfs at ${effectiveHome}/.npmrc will fail if .npmrc exists as a file (common when users have NPM auth configured), because tmpfs can’t be mounted over a file path. Consider hiding .npmrc via a bind-mount of an empty file (or /dev/null if the parent dir is guaranteed to exist and be writable) instead of tmpfs, or adjust the approach so the mount point is always a directory.

This issue also appears on line 750 of the same file.

Copilot uses AI. Check for mistakes.
// 1. MCP server logs at /tmp/gh-aw/mcp-logs
// 2. Credential files/directories (when not using --allow-full-filesystem-access)
tmpfs: (() => {
const tmpfsMounts = [];
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const tmpfsMounts = []; infers any[] in TypeScript. Please type this as string[] (and similarly ensure the returned value is string[]) so DockerService.tmpfs stays strongly typed and you don’t lose type-safety in this block.

Suggested change
const tmpfsMounts = [];
const tmpfsMounts: string[] = [];

Copilot uses AI. Check for mistakes.
Comment on lines +1304 to +1312
// Check if workDir and docker-compose.yml exist before using docker compose
const composeFile = path.join(workDir, 'docker-compose.yml');
if (fs.existsSync(workDir) && fs.existsSync(composeFile)) {
// Normal path: use docker compose down
await execa('docker', ['compose', 'down', '-v'], {
cwd: workDir,
stdio: 'inherit',
});
logger.success('Containers stopped successfully');
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR also changes stopContainers() behavior by adding a fallback path that stops/removes containers by name when docker-compose.yml is missing. This is not mentioned in the PR description/title (which focuses on credential hiding), so it’s easy to miss during review/release notes—please either update the PR description to call this out or move it into a separate PR.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants