Skip to content

Integrate with the devbox VM flow (de-brew Linux + mise) and lint the whole repo - #1

Merged
jacob-delgado merged 13 commits into
mainfrom
devbox-integration-and-linting
Jul 1, 2026
Merged

Integrate with the devbox VM flow (de-brew Linux + mise) and lint the whole repo#1
jacob-delgado merged 13 commits into
mainfrom
devbox-integration-and-linting

Conversation

@jacob-delgado

Copy link
Copy Markdown
Owner

Summary

Two connected efforts:

  1. Linux / devbox integration — make these dotfiles consumable by the
    sound-barrier/devbox Ansible flow,
    which clones the repo to ~/.dotfiles, runs ./bootstrap-debian.sh over SSH
    with no TTY as a passwordless-sudo user, then runs mise install. macOS
    keeps Homebrew.
  2. Repo-wide linting/formatting — CI + lefthook over every file type, a repo
    .editorconfig, and Dependabot.

Linux / devbox

  • New mise/ stow package → ~/.config/mise/config.toml (the exact path devbox
    stats before running mise install). Pins Node/Go + the modern CLIs;
    fzf/direnv stay on apt/brew because their oh-my-zsh plugins load before
    mise activate.
  • bootstrap-debian.sh: drop Linuxbrew + brew bundle; install via apt + mise
    (mise from its own apt repo). Location-independent (works from ~/.dotfiles),
    fully non-interactive (sudo chsh, no prompts). Stows before installing
    OMZ so its template ~/.zshrc can't collide, and an -ef (same-inode)
    collision check keeps re-runs idempotent.
  • .zshrc: activate mise before the zoxide/atuin/eza/task blocks (on Linux
    those tools exist only via mise); $(brew --prefix) on macOS.
  • New bootstrap-macos.sh counterpart (Homebrew stays the macOS source of truth).

Linting / tooling

  • .github/workflows/ci.yml: shellcheck, shfmt, yamllint, actionlint, taplo,
    markdownlint, mdformat, editorconfig-checker — all pinned, scoped via
    git ls-files.
  • lefthook.yml (repo root) with 1:1 parity to CI.
  • Formatted all Markdown (mdformat + markdownlint) and TOML (taplo); added a repo
    .editorconfig; Dependabot keeps the workflow Actions current.

Verification

  • Ran bootstrap-debian.sh end-to-end in a fresh Debian 13 (trixie) container
    as a passwordless-sudo user with no TTY: exits 0, installs no Homebrew, leaves
    go/node + the CLIs on PATH, stows ~/.zshrc and ~/.config/mise, sets the
    login shell to zsh, and ~/.zshrc sources cleanly — and a re-run is a clean
    idempotent no-op.
  • All nine linters/formatters pass locally on the exact commands CI runs.

Notes

  • The mise package is intentionally not stowed on macOS (Homebrew provides
    the tools there); per-project mise.toml pinning is unaffected.
  • Lua (nvim) linting was left out — it's hand-styled and stylua's defaults would
    rewrite it wholesale.

🤖 Generated with Claude Code

jacob-delgado and others added 13 commits June 30, 2026 23:53
.zshrc activates mise and the headless devbox flow runs `mise install`,
but the repo shipped no mise config, so mise managed nothing. This adds
mise/.config/mise/config.toml (stowed to ~/.config/mise/config.toml,
which is exactly one of the paths devbox's guest_personalize role stats
before running `mise install`).

On the Debian VMs mise is the primary tool manager — there is no
Homebrew — so the config pins the Node/Go runtimes plus the modern CLIs
the shell config references (bat, eza, fd, ripgrep, delta, yq, zoxide,
atuin, go-task). fzf and direnv are deliberately excluded: their
oh-my-zsh plugins load before `mise activate`, so they come from apt on
Linux / brew on macOS instead.

Wires the package into the usual places: STOW_PACKAGES, the Brewfile
snapshot (mise is a real brew formula here), the README/CLAUDE.md
tables, and the mdBook (src/SUMMARY.md + src/mise.md).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The devbox Ansible flow clones this repo to ~/.dotfiles and runs this
script over SSH with no TTY as a passwordless-sudo user. Two things
blocked that, and both are fixed here.

De-brew Linux. Homebrew on an ephemeral VM is a heavy, slow step and we
now want mise to own language runtimes + modern CLIs there. Remove the
Linuxbrew install, the /home/linuxbrew shellenv, and `brew bundle`.
Instead:
  * apt installs the base system and the CLIs Debian packages well,
    plus fzf and direnv — those two must precede `mise activate` because
    their oh-my-zsh plugins load at shell-init time.
  * mise is installed from its own apt repo (same deb822 pattern as the
    devbox role; skipped when devbox already installed it), and a new
    step 7 runs `mise install` to materialize ~/.config/mise/config.toml.
The Brewfile stays the macOS source of truth.

Run headless. Derive DOTFILES_DIR from the script's own path so it works
from ~/.dotfiles, not just ~/dotfiles. Replace the bare `chsh` (which
prompts via PAM and hangs with no TTY) with `sudo chsh -s <zsh> <user>`,
a no-op when zsh is already the login shell. apt already runs with -y and
there are no other prompts.

Passes shellcheck (the repo's enable=all config) and shfmt -i 2 -ci.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On the Debian VMs, mise is the only source of zoxide, atuin, eza,
go-task, etc. But `mise activate` sat at the very bottom of .zshrc, after
the `command -v zoxide`/`atuin`/`eza`/`task` checks that wire those tools
up — so on Linux they were never on PATH when their checks ran, and the
integrations silently no-op'd. Move `mise activate` up, right after the
PATH setup, so its shims are present before those blocks. macOS is
unaffected: Homebrew already puts the tools on PATH regardless of mise's
position.

Also switch the macOS GNU-tools block from a hardcoded /opt/homebrew to
$(brew --prefix) (Intel-Mac safe) and gate it on `command -v brew`, per
the repo convention. The block is already macOS-only, so Linux skips it.

Verified: zsh -n passes and a fresh interactive shell loads cleanly on
macOS (the pre-existing gitstatus notice is unrelated — it also appears
on the prior .zshrc when sourced without a TTY).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Until now CI only built the mdBook. Add .github/workflows/ci.yml that
lints on push/PR the same way the lefthook template and stowed rc files
do locally:
  * shellcheck + shfmt (pinned via mise's aqua backend — 0.11.0 / 3.13.1,
    the versions these scripts were verified against) over the tracked
    shell scripts, using shellcheck/.shellcheckrc (enable=all) and
    shfmt -i 2 -ci.
  * yamllint (pinned 1.38.0) over the tracked YAML, using the repo's
    yamllint config.

Scoping is done with `git ls-files` so the gitignored vim persistent-undo
files that merely end in .sh/.yaml are never linted. gh's generated
config.yml (its own 4-space format, not ours) is the single YAML
exclusion. permissions are read-only; no untrusted event input reaches a
run step.

Verified locally: every tracked file passes these exact commands, so the
first run is green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only the Debian bootstrap existed. Add bootstrap-macos.sh so a fresh Mac
has a single idempotent entry point too: ensure Homebrew, `brew bundle`
from the Brewfile, install OMZ + p10k + the custom plugins, and stow the
packages. Homebrew stays the macOS source of truth.

The mise package is deliberately left out of the macOS stow set — there
the tools come from Homebrew, so the global mise tool-list is a
Linux-only concern; per-project mise.toml pinning in individual repos is
unaffected.

Passes shellcheck (enable=all) and shfmt -i 2 -ci. README documents both
bootstrap scripts and drops the stale "Linuxbrew" mention.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Format every authored Markdown file with mdformat (gfm + tables plugins,
wrap=keep, consecutive list numbering) and lint with markdownlint. This
aligns all the README tables (satisfying markdownlint's new MD060 rule)
and normalizes fenced-code / list / heading syntax without reflowing
prose.

Fix the substantive markdownlint findings by hand: add languages to bare
code fences (MD040), convert kitty's bold pseudo-headings to real ###
headings (MD036); markdownlint --fix handled the blanks-around-fences and
space-in-code nits.

Config:
  * .markdownlint-cli2.jsonc — default rules, MD013 (line-length) off to
    match .editorconfig; src/ (mdBook include-wrappers) and vim/.vim/
    excluded.
  * .mdformat.toml — wrap=keep + consecutive numbering.

mdformat --check and markdownlint are both clean and mutually idempotent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reformat mise/.config/mise/config.toml with taplo (the TOML formatter now
run in CI): single-space inline comments, aligned entries. No semantic
change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a repo-root .editorconfig capturing the conventions actually used
here (2-space default; tabs for Go/Make; Markdown exempt from
trailing-whitespace and indent-width, since mdformat indents ordered-list
continuations by 3). It mirrors the global editorconfig this repo ships.

.editorconfig-checker.json excludes the files we don't hand-edit: the
vendored vim colorschemes under vim/.vim/ and the p10k.zsh written by
`p10k configure`.

Normalize the two files that violated the config: git/.gitconfig had two
stray tab-indented lines among 83 space-indented ones, and a tmux.conf
if-shell continuation used a 3-space indent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expand the lint workflow to cover the whole tree using the repo's own
configs: shellcheck + shfmt (shell), yamllint (yaml), actionlint
(workflows), taplo (toml), markdownlint + mdformat (markdown), and
editorconfig-checker.

Binary linters come pinned from mise (aqua backend); yamllint and
mdformat (+gfm/tables plugins) from pipx; markdownlint-cli2 from npm.
Scope is `git ls-files` so gitignored vim persistent-undo files are
skipped; gh's generated config.yml plus the vendored/generated files are
excluded to match the local configs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a repo-root lefthook.yml running the same nine linters as
.github/workflows/ci.yml against staged files, so a pre-commit catches
what CI would. Distinct from lefthook/lefthook.yml, which stays a
stowable Go-project template. Enable with `lefthook install`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Open a grouped weekly PR when the workflow actions (actions/checkout,
jdx/mise-action, the mdBook and Pages actions, …) release new versions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Linuxbrew removal left two stale claims: that `bootstrap-debian.sh`
runs `brew bundle`, and that `$(brew --prefix)` resolves Linuxbrew. Update
both — the Brewfile is now the macOS path (bootstrap-macos.sh), and Linux
uses apt + mise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ran bootstrap-debian.sh end-to-end in a fresh Debian 13 (trixie)
container as a passwordless-sudo user with no TTY, which surfaced two real
bugs:

  * Fresh install aborted at `stow zsh`. The oh-my-zsh installer always
    writes its template ~/.zshrc when a box has none (KEEP_ZSHRC=yes only
    preserves an *existing* one), colliding with our stow target. Fix:
    stow BEFORE installing OMZ, so ~/.zshrc is our symlink first and
    KEEP_ZSHRC keeps it.

  * Re-runs falsely refused ("Refusing to stow atuin:
    ~/.config/atuin/config.toml exists as a regular file"). stow folds a
    single-package subtree into one dir symlink (~/.config/atuin -> repo),
    so files under it look like plain files though they're already ours.
    Fix: an `-ef` (same-inode) check tells an already-stowed file apart
    from a genuine foreign one, keeping re-runs idempotent.

Also add gnupg to the apt set so mise verifies tool signatures instead of
warning. Same reorder + collision fix applied to bootstrap-macos.sh.

Verified in-container: fresh run exits 0 (no Homebrew; go/node + the CLIs
installed; ~/.zshrc and ~/.config/mise stowed; default shell zsh; .zshrc
sources cleanly; no gpg warning), and a re-run is a clean idempotent
no-op.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jacob-delgado
jacob-delgado merged commit 01cee0d into main Jul 1, 2026
3 checks passed
@jacob-delgado
jacob-delgado deleted the devbox-integration-and-linting branch July 1, 2026 07:30
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