Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
- name: All tools exist
run: |
docker run --rm squarebox:test bash -c '
for cmd in bat curl delta difft eza fd fzf gh glow gum jq just nano rg starship xh yq zoxide; do
for cmd in bat curl delta difft eza fd fzf gh glow gum jq just mise nano rg starship xh yq zoxide; do
which "$cmd" || { echo "MISSING: $cmd"; exit 1; }
done
'
Expand Down
39 changes: 39 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Create release with install scripts as assets
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME}"

# Pre-release tags carry an `-rc`, `-beta`, etc. suffix.
prerelease_flag=""
case "$tag" in
*-*) prerelease_flag="--prerelease" ;;
esac

# Idempotent: if the workflow re-runs on the same tag, replace assets.
if gh release view "$tag" >/dev/null 2>&1; then
gh release upload "$tag" install.sh install.ps1 --clobber
else
gh release create "$tag" \
--title "$tag" \
--generate-notes \
$prerelease_flag \
install.sh install.ps1
fi
10 changes: 7 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

squarebox is a containerized development environment (Docker or Podman) combining modern CLI/TUI tools with Claude Code. It uses a persistent container model — the container suspends on exit and resumes on restart, preserving state. Workspace code lives on the host at `~/squarebox/workspace` via volume mount.
squarebox is a containerized development environment (Docker or Podman) combining modern CLI/TUI tools with Claude Code. It uses a persistent container model — the container suspends on exit and resumes on restart, preserving state. Workspace code lives on the host at `~/squarebox/workspace` via bind mount; per-user state (shell history, gh auth, claude-code data, mise toolchains) lives in the `squarebox-home` named Docker volume; image-managed config (`.bashrc`, `starship.toml`, `lazygit/`) is bind-mounted from `~/squarebox/dotfiles` so repo updates flow through.

## Build & Run

Expand All @@ -21,6 +21,8 @@ docker run -it --name squarebox \
-v ~/.ssh/config:/home/dev/.ssh/config:ro \
-v ~/.ssh/known_hosts:/home/dev/.ssh/known_hosts:ro \
-v ~/squarebox/workspace:/workspace \
-v squarebox-home:/home/dev \
-v ~/squarebox/dotfiles/bashrc:/home/dev/.bashrc:ro \
-v ~/.config/git:/home/dev/.config/git \
-v ~/squarebox/.config/starship.toml:/home/dev/.config/starship.toml \
-v ~/squarebox/.config/lazygit:/home/dev/.config/lazygit \
Expand All @@ -31,6 +33,8 @@ docker run -it --name squarebox \
docker start -ai squarebox
```

The `squarebox-home` named volume holds per-user state. Bind mounts at sub-paths inside `/home/dev` (`.bashrc`, `.config/starship.toml`, `.config/lazygit`, `.config/git`) override the volume so repo-managed files stay in lockstep with the image.

Replace `docker` with `podman` above if using Podman. The `install.sh` script auto-detects the runtime; override with `SQUAREBOX_RUNTIME=docker|podman`.

The `install.sh` script automates initial setup (clone, build, create container, add `sqrbx` shell alias). A `.devcontainer/devcontainer.json` is also provided for VS Code Dev Containers / Codespaces.
Expand All @@ -47,7 +51,7 @@ The `install.sh` script automates initial setup (clone, build, create container,
4. **Text editors** — micro, edit (Microsoft), fresh, nvim (nano is always available)
5. **TUI tools** — lazygit, gh-dash, yazi (any combination)
6. **Terminal multiplexers** — tmux, zellij
7. **SDKs** — Node.js (via nvm), Python (via uv), Go, .NET, Rust (via rustup)
7. **SDKs** — Node.js, Python, Go, .NET, Rust (all installed and managed by [mise](https://github.com/jdx/mise) via `~/.config/mise/config.toml`)
8. **Shell** — bash (default) or zsh + Oh My Zsh + autosuggestions + syntax highlighting (experimental)

Selections are saved to `/workspace/.squarebox/` and reused on subsequent rebuilds.
Expand Down Expand Up @@ -79,7 +83,7 @@ sqrbx-update

`scripts/update-versions.sh` only touches the Dockerfile tier (delta, yq, xh, glow, gum, starship, just, difftastic). It fetches latest GitHub releases, downloads artifacts for both architectures, computes SHA256 checksums, and updates `checksums.txt` and the Dockerfile ARGs.

Optional tools installed by `setup.sh` (opencode, editors, TUIs, zellij, Go, nvm) are not pinned. They install the latest upstream release at setup time, so there is no checksum file or version variable to update in the repo.
Optional tools installed by `setup.sh` (opencode, editors, TUIs, zellij) are not pinned. They install the latest upstream release at setup time, so there is no checksum file or version variable to update in the repo. SDKs are installed by mise (a Dockerfile-tier pinned binary), so SDK trust is anchored to mise's own pinning rather than per-language installers.

## CI

Expand Down
8 changes: 6 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,15 @@ Dockerfile-tier tools (delta, yq, xh, glow, gum, starship, just, difftastic) are
2. Review the diff. Verify version bumps and checksums look correct.
3. Rebuild and test.

Optional tools (editors, TUIs, opencode, zellij, Go, nvm) track upstream
latest at install time. To add a new optional tool, add an entry to
Optional tools (editors, TUIs, opencode, zellij) track upstream latest at
install time. To add a new optional tool, add an entry to
`scripts/lib/tools.yaml` and call `sb_install <tool> latest` from `setup.sh`.
No checksum file update is required.

SDKs (Node, Python, Go, .NET, Rust) are installed by mise itself, which is
part of the Dockerfile-pinned tier — so SDK availability is gated by mise's
own pinning, not the SDK installers individually.
Comment on lines +78 to +85

Never skip checksum verification for the Dockerfile tier.

### Style
Expand Down
90 changes: 28 additions & 62 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
toilet-fonts \
libicu-dev \
locales \
# Runtime deps for mise-managed SDKs:
# gpg — mise verifies upstream signatures (Node, etc.)
# libatomic1 — required by official Node Linux builds
gpg \
libatomic1 \
&& sed -i '/en_US.UTF-8/s/^# //' /etc/locale.gen \
&& locale-gen \
&& rm -rf /var/lib/apt/lists/* \
Expand All @@ -41,6 +46,7 @@ ARG GLOW_VERSION=2.1.2
ARG GUM_VERSION=0.17.0
ARG JUST_VERSION=1.49.0
ARG DIFFTASTIC_VERSION=0.68.0
ARG MISE_VERSION=2026.5.4

# Validate version ARGs are non-empty
RUN test -n "$DELTA_VERSION" || { echo "Error: DELTA_VERSION is empty" >&2; exit 1; } \
Expand All @@ -50,7 +56,8 @@ RUN test -n "$DELTA_VERSION" || { echo "Error: DELTA_VERSION is empty" >&2
&& test -n "$GLOW_VERSION" || { echo "Error: GLOW_VERSION is empty" >&2; exit 1; } \
&& test -n "$GUM_VERSION" || { echo "Error: GUM_VERSION is empty" >&2; exit 1; } \
&& test -n "$JUST_VERSION" || { echo "Error: JUST_VERSION is empty" >&2; exit 1; } \
&& test -n "$DIFFTASTIC_VERSION" || { echo "Error: DIFFTASTIC_VERSION is empty" >&2; exit 1; }
&& test -n "$DIFFTASTIC_VERSION" || { echo "Error: DIFFTASTIC_VERSION is empty" >&2; exit 1; } \
&& test -n "$MISE_VERSION" || { echo "Error: MISE_VERSION is empty" >&2; exit 1; }

# Checksum verification infrastructure
COPY checksums.txt /tmp/checksums.txt
Expand All @@ -77,8 +84,8 @@ RUN mkdir -p -m 755 /etc/apt/keyrings \
# Install from repos
&& apt-get update \
&& apt-get install -y --no-install-recommends gh eza \
# Purge build-only dependency
&& apt-get purge -y --auto-remove gnupg \
# Note: gnupg is kept (also installed in layer 1 as `gpg`) — mise needs it
# at runtime to verify Node release signatures.
&& rm -rf /var/lib/apt/lists/*

# Build-time tool install helper: sources library + wires up checksum verification
Expand All @@ -93,6 +100,7 @@ RUN . /tmp/sb-init.sh && sb_install gum "$GUM_VERSION"
RUN . /tmp/sb-init.sh && sb_install starship "$STARSHIP_VERSION"
RUN . /tmp/sb-init.sh && sb_install just "$JUST_VERSION"
RUN . /tmp/sb-init.sh && sb_install difftastic "$DIFFTASTIC_VERSION"
RUN . /tmp/sb-init.sh && sb_install mise "$MISE_VERSION"

# Clean up build-time files
RUN rm -f /tmp/checksums.txt /tmp/tools.yaml /tmp/tool-lib.sh /tmp/sb-init.sh
Expand All @@ -110,18 +118,25 @@ RUN userdel -r ubuntu 2>/dev/null || true \
RUN printf '[core]\n\tpager = delta\n[interactive]\n\tdiffFilter = delta --color-only\n[delta]\n\tnavigate = true\n\tdark = true\n[merge]\n\tconflictstyle = zdiff3\n' > /etc/gitconfig

# 6. Setup script
#
# setup.sh and motd.sh live under /usr/local/lib/squarebox/ rather than
# /home/dev/ so they stay image-managed. /home/dev/ is backed by the
# squarebox-home named volume, which Docker only seeds from the image when
# the volume is first created — anything we put there would go stale after
# a `sqrbx-rebuild` against an existing volume.

COPY --chown=dev:dev motd.sh /home/dev/motd.sh
RUN chmod +x /home/dev/motd.sh

COPY --chown=dev:dev setup.sh /home/dev/setup.sh
COPY --chown=dev:dev starship.toml /home/dev/.config/starship.toml

COPY motd.sh /usr/local/lib/squarebox/motd.sh
COPY setup.sh /usr/local/lib/squarebox/setup.sh
COPY scripts/squarebox-update.sh /usr/local/bin/sqrbx-update
COPY scripts/squarebox-setup.sh /usr/local/bin/sqrbx-setup
COPY scripts/lib/tools.yaml /usr/local/lib/squarebox/tools.yaml
COPY scripts/lib/tool-lib.sh /usr/local/lib/squarebox/tool-lib.sh
RUN chmod +x /home/dev/setup.sh /usr/local/bin/sqrbx-update /usr/local/bin/sqrbx-setup
RUN chmod +x /usr/local/lib/squarebox/setup.sh \
/usr/local/lib/squarebox/motd.sh \
/usr/local/bin/sqrbx-update \
/usr/local/bin/sqrbx-setup

RUN chown -R dev:dev /home/dev/.config /home/dev/.claude \
&& mkdir -p /workspace && chown dev:dev /workspace
Expand All @@ -132,61 +147,12 @@ ENV HOME=/home/dev
ENV SQUAREBOX=1

# 7. Shell Config
# The .bashrc lives in dotfiles/ on the host so install.sh can bind-mount it
# into the container — keeping it in sync with the repo while shell history
# and per-user state stay in the squarebox-home named volume. The COPY here
# is what seeds a fresh volume; subsequent runs see the bind-mounted version.

RUN cat <<'EOFRC' >> ~/.bashrc
eval "$(starship init bash)"
eval "$(zoxide init bash --cmd cd)"
alias ls='eza --icons'
alias ll='eza -la --icons'
alias lsa='ls -a'
alias lt='eza --tree --level=2 --long --icons --git'
alias lta='lt -a'
alias cat='bat --paging=never'
alias ff="fzf --preview 'bat --style=numbers --color=always {}'"
alias eff='$EDITOR "$(ff)"'
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
export EDITOR='nano'
[ -f ~/.squarebox-ai-aliases ] && source ~/.squarebox-ai-aliases
[ -f ~/.squarebox-editor-aliases ] && source ~/.squarebox-editor-aliases
[ -f ~/.squarebox-tui-aliases ] && source ~/.squarebox-tui-aliases
[ -f ~/.squarebox-sdk-paths ] && source ~/.squarebox-sdk-paths
alias g='git'
alias gcm='git commit -m'
alias gcam='git commit -a -m'
alias gcad='git commit -a --amend'
export PATH="$HOME/.local/bin:$PATH"
# First-run setup
if [ ! -f ~/.squarebox-setup-done ]; then
if [ -n "${DEVCONTAINER:-}" ]; then
touch ~/.squarebox-setup-done
else
~/setup.sh && touch ~/.squarebox-setup-done
[ -f ~/.squarebox-ai-aliases ] && source ~/.squarebox-ai-aliases
[ -f ~/.squarebox-editor-aliases ] && source ~/.squarebox-editor-aliases
[ -f ~/.squarebox-tui-aliases ] && source ~/.squarebox-tui-aliases
[ -f ~/.squarebox-sdk-paths ] && source ~/.squarebox-sdk-paths
fi
fi
# Hand off to zsh if the user opted in via setup.sh (experimental).
# SQUAREBOX_IN_ZSH guards against re-exec loops; SQUAREBOX_NO_ZSH lets
# users force bash for one shell without removing the marker.
if [ -f ~/.squarebox-use-zsh ] && [ -z "${SQUAREBOX_IN_ZSH:-}" ] && [ -z "${SQUAREBOX_NO_ZSH:-}" ] && command -v zsh >/dev/null 2>&1; then
export SQUAREBOX_IN_ZSH=1
exec zsh -l
fi
# Hand off to fish if the user opted in via setup.sh (experimental).
# SQUAREBOX_IN_FISH guards against re-exec loops; SQUAREBOX_NO_FISH lets
# users force bash for one shell without removing the marker.
if [ -f ~/.squarebox-use-fish ] && [ -z "${SQUAREBOX_IN_FISH:-}" ] && [ -z "${SQUAREBOX_NO_FISH:-}" ] && command -v fish >/dev/null 2>&1; then
export SQUAREBOX_IN_FISH=1
exec fish -l
fi
EOFRC

# Display MOTD on interactive shell login
RUN echo '~/motd.sh' >> ~/.bashrc
COPY --chown=dev:dev dotfiles/bashrc /home/dev/.bashrc

WORKDIR /workspace
CMD ["/bin/bash"]
Loading