Skip to content

chore: install dogfood image tooling via mise.toml#25282

Merged
ThomasK33 merged 13 commits into
mainfrom
thomas/dogfood-mise-migration
May 15, 2026
Merged

chore: install dogfood image tooling via mise.toml#25282
ThomasK33 merged 13 commits into
mainfrom
thomas/dogfood-mise-migration

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

This PR replaces the hand-rolled curl | tar | go install | cargo install chains in the dogfood Ubuntu 22.04 and 26.04 Dockerfiles with a single mise install driven by a new repo-root mise.toml.

The previous Dockerfiles installed ~25 CLIs across three multi-stage builds with versions hardcoded inline. Version bumps were scattered across the Dockerfiles, the root mise.toml (added in #24618 but otherwise unused at runtime), and CI's setup actions; build-time network failures came from a dozen distinct endpoints; and mise itself sat in the image with no manifest to install from.

The new flow:

  • The repo's mise.toml is the single source of truth for image tool versions. The Dockerfiles COPY it to /etc/mise/config.toml and run a single mise install as the coder user.
  • Tools are installed into /opt/mise/data rather than the default /home/coder/.local/share/mise, so they live in the image (not on the persistent home volume) and reach every workspace on recreate.
  • Build context moves to the repo root so the Dockerfile can COPY mise.toml; an allowlist .dockerignore keeps the transferred context to ~24 kB.
  • Optional --secret id=github_token plumbing through the Makefile and .github/workflows/dogfood.yaml lifts aqua's GitHub API quota from 60/hr unauthenticated to 1000/hr with secrets.GITHUB_TOKEN.
  • MISE_TRUSTED_CONFIG_PATHS=/home/coder:/etc/mise is set as an ENV so users who clone the coder repo into their workspace home aren't prompted to mise trust.

Net diff for the two Ubuntu Dockerfiles: -399 / +244 lines (~200 lines shorter each). The FROM rust-utils, FROM go, and FROM proto multi-stage builds are gone; so are the NVM/Node block, the bulk binary-install block (golangci-lint, helm, kubectx, syft, cosign, bun), the gh .deb/lazygit/doctl tarball installs, the gofmt update-alternatives line, and the yqyq4 rename (scripts/lib.sh:267-275 already auto-detects either name).

Both images were built and smoke-tested with Apple's container CLI on macOS — every migrated tool resolves to the expected pinned version including outside the cloned coder repo (e.g. gh from /home/coder, matching the workspace startup script in dogfood/coder/main.tf), sqlc runs (proving CGO_ENABLED=1 was honoured at install), yq --version reports v4 for scripts/lib.sh's detection, and gofmt resolves via the mise shim.

Follow-ups (out of scope here):

  • Commit a multi-platform mise.lock so gh = "latest" and the other floating versions resolve deterministically across rebuilds and dev machines.
  • Migrate CI's setup-go / setup-node actions to consume mise.toml so image and CI versions stop being able to drift.

Replace the curl-tar-go-install chains across the dogfood Ubuntu
22.04 and 26.04 Dockerfiles with a single `mise install` driven by
a new repo-root `mise.toml`. The previous Dockerfiles installed
~25 CLIs across three multi-stage builds with versions hardcoded
inline; version bumps were scattered, build-time network failures
came from a dozen distinct endpoints, and the mise binary added in

The Dockerfiles now COPY `mise.toml` to `/etc/mise/config.toml`
and install everything into `/opt/mise/data` (image-owned, outside
the persistent home volume, so version bumps reach every workspace
on recreate). Build context moves to the repo root so the
Dockerfile can reach `mise.toml`; an allowlist `.dockerignore`
keeps the transferred context to ~24 kB. The build accepts an
optional `--secret id=github_token` so aqua's GitHub API quota
goes from 60/hr unauthenticated to 1000/hr with the workflow's
`secrets.GITHUB_TOKEN`. The dogfood CI workflow is updated to
pass that secret and to point at the new build context + file.

Change-Id: Ia8b93019fd34ef89a804e50ec4bb1b10c1efecd6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
ThomasK33 and others added 5 commits May 13, 2026 14:50
`scripts/check_go_versions.sh` and the lint job in
`.github/workflows/ci.yaml` previously grepped `ARG GO_VERSION=`
and `ARG GOLANGCI_LINT_VERSION=` out of the dogfood Dockerfiles.
The migration to `mise.toml` removed those ARGs, so both grep
calls returned the empty string and the downstream commands
failed (Go version mismatch / `go install ...@v`).

Point both readers at `mise.toml`, which is the single source of
truth for image tool versions after the migration.

Change-Id: I49bb4948303f4c6359eace4e45d57213021bc33f
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
The previous commit added a 2-line comment above the
golangci-lint cache-dir step in ci.yaml, shifting the
`actions/cache` line from 188 to 190 and invalidating the
line-anchored zizmor suppression.

Change-Id: Id41fa6a2c4f9939eb7b594b5b4f75ffbbe08feaa
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Aggregated fixes from a code review of the dogfood mise migration:

- Restore `gh` to apt-installed /usr/bin/gh (and remove from mise.toml)
  so the `files/usr/local/bin/gh` wrapper continues to bridge
  `coder external-auth` into gh authentication. Mise's shim at
  ${MISE_DATA_DIR}/shims would otherwise win PATH precedence and
  bypass the wrapper.
- Replace the line-anchored zizmor `cache-poisoning` suppression
  in `.github/zizmor.yml` with an inline `# zizmor: ignore` comment
  on the offending step in `ci.yaml`, so the suppression survives
  future line shifts.
- Fix the stale `GOLANGCI_LINT_VERSION=` grep in `Makefile:748`'s
  `lint/go` target; matches the same `mise.toml` read used in
  `.github/workflows/ci.yaml`.
- Replace literal `/opt/mise/data` / `/home/coder:/etc/mise`
  occurrences in the dogfood Dockerfiles with the existing
  `$MISE_DATA_DIR` and `$MISE_TRUSTED_CONFIG_PATHS` ENV references.
- Drop the pointless `bash -c 'set -euo pipefail; …'` wrapper that
  was only protecting a single `mise install` invocation.
- Trim a narrative comment in `mise.toml` and replace an emdash so
  `make lint/emdash` stays happy.

Change-Id: Id4358de0710390d39f0e6d8637ac175785435263
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
The inline `# zizmor: ignore[cache-poisoning]` was on the same line
as `- name:`, but zizmor anchors the cache-poisoning finding to the
`uses: actions/cache@…` line and ignores comments on the step name.
Move the comment to its own line directly above the `uses:` line,
matching the pattern already used in `.github/workflows/contrib.yaml`
for `dangerous-triggers`.

Change-Id: I7ca0b1023db2227fb2a8945d1f99614319f0c9be
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
…risk

The single `actions/cache` step ran on every trigger including
`pull_request`, so a PR could write content into the cache that
subsequent runs (on main or other PRs) would restore from. The
previous workaround was a zizmor `cache-poisoning` suppression on
that line.

Replace it with the standard restore/save split:

- `actions/cache/restore` runs unconditionally (PRs may read trusted
  caches saved by main).
- `actions/cache/save` only runs on `refs/heads/main` and only when
  there was no exact-key hit, so PRs cannot write to the cache.

This makes the suppression unnecessary; both have been removed.

Change-Id: I1698113632c267b13df1c36b98f75ef4404f3050
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33 ThomasK33 marked this pull request as ready for review May 13, 2026 15:26
@ThomasK33 ThomasK33 requested a review from aslilac May 13, 2026 15:26

@aslilac aslilac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd love it if we could maintain the ability to use mise while the workspace is running in addition to using it for image prep

Comment thread mise.toml Outdated
Comment thread dogfood/coder/ubuntu-22.04/Dockerfile Outdated
Comment thread dogfood/coder/ubuntu-22.04/Dockerfile
ThomasK33 and others added 3 commits May 13, 2026 18:11
Reverts the MISE_DATA_DIR location from /opt/mise/data (image-only)
back to ~/.local/share/mise, which sits under /home/coder and is
backed by the per-workspace home volume declared in
dogfood/coder/main.tf. With this change:

- Fresh workspaces still start with every tool pre-installed: Docker's
  first-mount-copy seeds the empty home volume from /home/coder in
  the image, so the workspace gets the image-baked mise data dir
  immediately with no `mise install` downloads.
- `mise install`s the user performs at runtime persist across
  workspace recreates because the home volume is per-workspace and
  long-lived.
- Image tool-version bumps reach *new* workspaces (fresh volumes copy
  from the image) but not existing workspaces, matching the trade-off
  Homebrew already accepts on this image — see the /home/linuxbrew
  volume comment in main.tf.

Also tightened the surrounding comment blocks: dropped the running
narrative about "why we did this differently" and condensed each
block to the minimum needed to understand the invariant at that line.

Change-Id: I036bb884480d4cd15406ea0732b968cfbd5aab85
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
v4.15.1 transitively requires github.com/mitchellh/osext, whose
upstream GitHub repo has been deleted, so `mise install` fails to
resolve the module graph. v4.16.0+ drops the dep, and v4.19.0 matches
the version already pinned in go.mod.

Change-Id: I825e43bf2bc963333a23f28af5382ceb48e827f5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I37df49e9b1470ed592b4e4063b90b48626cf8dd6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33 ThomasK33 requested a review from aslilac May 13, 2026 16:38

@aslilac aslilac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this just seems like a super nice w. I'm always on board for simplifying a Dockerfile, and this is a huuuuuge simplification. good work!

Comment thread dogfood/coder/ubuntu-26.04/Dockerfile
Comment thread dogfood/coder/ubuntu-26.04/Dockerfile

@jdomeracki-coder jdomeracki-coder left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

This review was generated by Coder Agents on behalf of @jdomeracki-coder.

Security review: no critical issues. Two high-severity supply-chain findings, two medium. Net security posture improves (cache-poisoning fix, allowlist .dockerignore, correct BuildKit secret usage, digest-pinned base images).

Comment thread mise.toml Outdated
Comment thread dogfood/coder/ubuntu-22.04/Dockerfile
Comment thread dogfood/coder/ubuntu-26.04/Dockerfile
Comment thread dogfood/coder/Makefile Outdated
ThomasK33 and others added 4 commits May 14, 2026 12:24
- mise.toml: pin the five tools previously set to "latest"
  (lazygit 0.61.1, doctl 1.158.0, jj 0.41.0, typos 1.46.1,
  watchexec 2.5.1) so a compromised upstream release is not
  auto-consumed at the next image build.
- mise.toml: re-add `[settings] lockfile = true` and commit
  the generated `mise.lock`. Plain `mise lock` populates all
  reasonable platforms (linux-{arm64,arm64-musl,x64,x64-musl},
  macos-{arm64,x64}, windows-x64), so the repo now carries an
  auditable per-tool checksum baseline that replaces the
  hand-rolled `sha256sum -c` we used to do for Go.
- ubuntu-22.04 / ubuntu-26.04 Dockerfiles: narrow
  `MISE_TRUSTED_CONFIG_PATHS` from `/home/coder` to
  `/home/coder/coder` so a malicious mise.toml dropped
  anywhere else under the workspace home no longer auto-runs
  its [hooks] / [tasks] without a `mise trust` prompt.
- dogfood/coder/Makefile: quote `$(GITHUB_TOKEN_FILE)` in the
  build-secret arg so a path with shell metacharacters can't
  manipulate the `docker build` command.

Change-Id: I5de42fbe5fe4145342c1f91702c8faefb3f72898
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Mount mise.lock alongside /etc/mise/config.toml so the dogfood image
build verifies download checksums during `mise install` (closes the
build-time integrity half of jdomeracki-coder's review).

Mechanics:

- COPY mise.toml /etc/mise/config.toml + COPY mise.lock
  /etc/mise/mise.lock. mise reads the lockfile via name-match next to
  the system config (verified empirically with a deliberately-bad
  checksum probe).
- Pre-create /etc/mise as 0755 before the COPYs. BuildKit's
  --chmod=0644 on a COPY whose destination dir does not exist
  apparently propagates the mode to the implicitly-created parent,
  leaving /etc/mise without the x bit and unreachable for the coder
  user (which silently produced "0 tools to install"). Use
  --chmod=0644 explicitly on the file COPYs so a future regen under
  a tight umask doesn't ship an unreadable lockfile.
- MISE_GLOBAL_CONFIG_FILE is NOT overridden — `mise use --global`
  must continue to write to ~/.config/mise/config.toml on the
  per-workspace home volume so user-installed tools persist.
- Pin helm to "v3.12.0" so the manifest version matches the
  upstream tarball naming (mise's aqua plugin still drops the `v`
  from URL templates, so the lockfile URL is patched by
  scripts/mise_lock.sh after each `mise lock`).

Adds scripts/mise_lock.sh to wrap `mise lock` with the helm URL
workaround so future regens don't reintroduce 404s.

Change-Id: I499605a1231901bc84ffe46532db57cf121f8512
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
aqua's helm plugin emits an URL without the `v` prefix (404).
github backend can't resolve a binary (helm only ships .asc files
on GitHub). ubi is deprecated in favor of github. http backend
with `mise.jdx.dev/dev-tools/backends/http.html` templating lets
us point at get.helm.sh directly with a single URL that handles
linux + macos via {{os()}}/{{arch()}} remapping.

Drops scripts/mise_lock.sh — no sed wrapper needed anymore since
the http backend doesn't have the URL-templating bug.

Trade-off: mise.lock doesn't auto-populate per-platform checksums
for http-backend tools (only the version pin is recorded). For
helm specifically, we accept "URL is pinned, checksum is not". The
other ~30 tools still get full URL+checksum verification.

Windows-x64 is omitted from the helm template because helm's
windows release uses .zip and would need a separate platform
override. The dogfood image is linux/amd64-only and nobody on the
team runs helm via mise on Windows.

Change-Id: I2020d8d23b5cad70777dcb7597ea13d0f4357640
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
`make lint/emdash` rejected four emdash characters I introduced in
mise-related comments on ubuntu-22.04 and ubuntu-26.04 Dockerfiles
during this PR. Replace with semicolons per the lint rule's example.

Change-Id: I93c36ee572db212a9e8956288f2c374d0a905f32
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33 ThomasK33 merged commit 5f9b322 into main May 15, 2026
52 of 54 checks passed
@ThomasK33 ThomasK33 deleted the thomas/dogfood-mise-migration branch May 15, 2026 09:36
@github-actions github-actions Bot locked and limited conversation to collaborators May 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants