Skip to content

feat(workers): install the GitHub CLI in coding agent images - #1403

Draft
Cedric Vidal (cedricvidal) wants to merge 2 commits into
microsoft:mainfrom
cedricvidal:feat/gh-cli-in-worker-images
Draft

Cedric Vidal (cedricvidal) wants to merge 2 commits into
microsoft:mainfrom
cedricvidal:feat/gh-cli-in-worker-images

Conversation

@cedricvidal

Copy link
Copy Markdown
Contributor

Problem

Coding agents are routinely asked to work with issues, branches and pull requests, but gh is not present in any worker image. A task that needs it can only fail, or fall back to hand-rolled REST calls.

Every coder image already ships a broad toolchain — Python, uv, git, PowerShell, Go, .NET, Rust, Java, Maven, Gradle — so the GitHub CLI is a natural and missing member of that set.

Change

Install gh, pinned to 2.100.0, in the three agent worker images:

Image Method
coder-acp-copilot release tarball, folded into the existing toolchain layer
coder-acp-claude-code release tarball, same layer
coder-acp-copilot-windows choco install -y gh --version=2.100.0, next to git

Details worth noting for review:

  • The Linux images reuse the existing ARCH=$(dpkg --print-architecture). gh's release asset names use amd64/arm64, which match that output exactly, so both build architectures resolve without a new case block.
  • Each Linux layer ends in gh --version, so a bad URL or a yanked release fails the build rather than silently producing an image without the tool.
  • The Windows image deliberately has no version check. Chocolatey's shim directory only joins PATH via the later ENV, so gh is not yet invokable at that layer — the same reason the existing choco install -y git does not verify either.
  • GH_VERSION is exported at runtime so agent version registration can report it alongside COPILOT_CLI_VERSION.
  • Pinning matches the convention already used for every other tool in these files, keeping builds reproducible.

Validation

Built the coder-acp-copilot base stage and ran the resulting image:

$ gh --version
gh version 2.100.0 (2026-09-03)

$ echo $GH_VERSION
2.100.0

$ which gh
/usr/local/bin/gh

The install layer was also built standalone on arm64 to confirm the asset-name and architecture handling.

I confirmed the Chocolatey gh package exists at 2.100.0 (it is also the current version there), but I could not build the Windows image locally, so that one is unverified beyond the pin being valid.

Coding agents are routinely asked to work with issues, branches and pull
requests, but `gh` was not available in any worker image, so a task that
needed it could only fail or fall back to hand-rolled API calls.

Install it alongside the other system tools each image already ships
(Python, Go, .NET, Java, Maven, Gradle, PowerShell), pinned to 2.100.0 for
reproducible builds:

- coder-acp-copilot and coder-acp-claude-code: fold the release tarball into
  the existing toolchain layer, reusing its `ARCH` so amd64 and arm64 both
  resolve (gh's asset names match `dpkg --print-architecture` exactly). The
  layer ends in `gh --version` so a bad URL fails the build instead of
  silently producing an image without it.
- coder-acp-copilot-windows: install through Chocolatey next to git. No
  version check here, because the Chocolatey shim directory only joins PATH
  via the later ENV, so `gh` is not yet invokable at that layer.

`GH_VERSION` is exported at runtime so the agent version registration can
report it next to COPILOT_CLI_VERSION.

Verified by building the coder-acp-copilot `base` stage and running the
resulting image:

    gh version 2.100.0 (2026-09-03)
    GH_VERSION=2.100.0
    /usr/local/bin/gh

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 507f8ebd-cc32-489c-afca-8941c5f8dba1
@github-actions

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #57

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2681 2681 0 0 0 0 0 2m14s

🎉 All tests passed!

Github Test Reporter

The coder services mount the host Docker socket and set DOCKER_HOST so the
agent can run containers during a task. `group_add: ${DOCKER_GID:-0}` gives
the right GID, but that is only half of what the socket needs.

Where the daemon host enforces SELinux — a podman machine always does, and
RHEL/Fedora Docker Engine can — the worker runs as `container_t` while the
socket is labelled `var_run_t`, and policy denies the connect. The agent then
cannot run containers at all, and the failure is easy to misread: it surfaces
as `permission denied ... /var/run/docker.sock`, which looks like a GID
problem that DOCKER_GID has already solved. Even `stat` on the socket is
denied, which is the tell that it is the label and not the mode.

Measured on a podman machine with SELinux enforcing, mounting the socket into
the copilot worker image:

    security_opt                          group_add 0   result
    (none)                                no            denied
    label=disable                         no            denied
    label=type:container_runtime_t        no            denied
    (none)                                yes           denied
    label=disable                         yes           OK
    label=type:container_runtime_t        yes           OK

So both are required. Add `security_opt: label=disable` next to the existing
group_add. Docker ignores label options on hosts without SELinux, so this is
a no-op under Docker Desktop and leaves those setups unchanged.

`label=type:container_runtime_t` works equally well and keeps the container
confined, which is preferable in principle, but it depends on container-selinux
providing that type and fails closed when it does not. For a local development
stack the portable option is the better default; the alternative is noted in a
comment.

Verified by recreating coder-acp-copilot from the compose files alone: the
container reports SecurityOpt ["label=disable"], GroupAdd ["0"], and `docker
ps` from inside the agent's user now succeeds.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 507f8ebd-cc32-489c-afca-8941c5f8dba1
@github-actions

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #58

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2681 2681 0 0 0 0 0 3m4s

🎉 All tests passed!

Github Test Reporter

@cedricvidal
Cedric Vidal (cedricvidal) marked this pull request as draft September 14, 2026 05:36

This branch has not been deployed

No deployments
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.

1 participant