Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ CONTRIBUTING.md
*~
.DS_Store
.devcontainer
docker-compose.yml
.env
.env.example
16 changes: 16 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copy to .env and adjust — consumed by docker-compose.yml.
# cp .env.example .env

# Image tag to run. Pin a release (e.g. v1.0.0) for reproducibility, or a
# pre-release for testing (e.g. v1.0.0-rc1). `latest` tracks newest stable.
SQUAREBOX_TAG=latest

# Host user/group that should own files squarebox writes to the workspace mount.
# Unraid / most NAS shares: 99 / 100. A typical single-user Linux host: 1000 / 1000.
PUID=1000
PGID=1000

# Host path mounted as /workspace (your code). Relative paths are resolved next
# to docker-compose.yml. On Unraid, point this at an array/share path, e.g.
# /mnt/user/appdata/squarebox/workspace or /mnt/user/code.
SQUAREBOX_WORKSPACE=./workspace
17 changes: 17 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,20 @@ jobs:
run: |
docker rm -f squarebox-test 2>/dev/null || true
docker rm -f sqrbx-persist 2>/dev/null || true

# arm64 build smoke — the published image is multi-arch, so catch arm64-only
# Dockerfile / tool-asset breakage at PR time (via QEMU) rather than only on
# the release tag. Build-only; behavioural arm64 tests run in e2e on tags.
build-arm64:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/arm64
push: false
cache-from: type=gha,scope=arm64
cache-to: type=gha,mode=max,scope=arm64
89 changes: 89 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,92 @@ jobs:
name: e2e-report
path: e2e-report.md
retention-days: 90

# ── Publish (gated) ──────────────────────────────────────────────────────
# Pushes the multi-arch image to GHCR and cuts the GitHub release ONLY after
# every test job above has passed, and ONLY for version tags. `needs` without
# `if: always()` means a single failed test job skips this job — so a tag can
# never publish a broken image or advertise a release for one. Runs of the
# E2E suite via workflow_dispatch (no tag) skip it via the ref guard.
publish:
needs:
- build-amd64
- build-arm64
- host-install
- tools-verification
- shell-environment
- setup-noninteractive
- setup-editors-sdks
- container-lifecycle
- sqrbx-update
- devcontainer
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
runs-on: ubuntu-latest
permissions:
contents: write # create the GitHub release
packages: write # push to GHCR
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3

- name: Resolve image ref and tags
id: meta
run: |
set -euo pipefail
# GHCR requires a lowercase path; the org is mixed-case.
image="ghcr.io/$(echo "${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')"
ref="${GITHUB_REF_NAME}" # e.g. v1.0.0-rc1
ver="${ref#v}" # e.g. 1.0.0-rc1
tags="${image}:${ref},${image}:${ver}"
# Prerelease tags (-rc, -beta, …) must not move the :latest pointer —
# `latest` always tracks the newest stable release.
case "$ref" in
*-*) prerelease=true ;;
*) prerelease=false; tags="${tags},${image}:latest" ;;
esac
{
echo "tags=${tags}"
echo "prerelease=${prerelease}"
} >> "$GITHUB_OUTPUT"

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
# Reuse the layer caches the build-amd64 / build-arm64 jobs populated.
cache-from: |
type=gha
type=gha,scope=arm64
cache-to: type=gha,mode=max

- name: Create or update the GitHub release
env:
GH_TOKEN: ${{ github.token }}
PRERELEASE: ${{ steps.meta.outputs.prerelease }}
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME}"
prerelease_flag=""
[ "$PRERELEASE" = "true" ] && prerelease_flag="--prerelease"
# Idempotent: re-running the tag refreshes assets but preserves any
# human-edited title/notes from the first run.
if gh release view "$tag" >/dev/null 2>&1; then
gh release upload "$tag" install.sh install.ps1 uninstall.sh --clobber
else
gh release create "$tag" \
--title "$tag" \
--generate-notes \
$prerelease_flag \
install.sh install.ps1 uninstall.sh
fi
43 changes: 0 additions & 43 deletions .github/workflows/release.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# Environment and secrets
.env
.env.*
!.env.example
credentials.json

# Docker build artifacts
Expand Down
13 changes: 10 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -132,21 +132,27 @@ 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/sqrbx-learn /usr/local/bin/sqrbx-learn
COPY scripts/squarebox-entrypoint.sh /usr/local/bin/squarebox-entrypoint
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 /usr/local/lib/squarebox/setup.sh \
/usr/local/lib/squarebox/motd.sh \
/usr/local/bin/sqrbx-update \
/usr/local/bin/sqrbx-setup \
/usr/local/bin/sqrbx-learn
/usr/local/bin/sqrbx-learn \
/usr/local/bin/squarebox-entrypoint

RUN chown -R dev:dev /home/dev/.config /home/dev/.claude \
&& mkdir -p /workspace && chown dev:dev /workspace

USER dev

# The container starts as root so the entrypoint can honour PUID/PGID, then
# drops to `dev` via setpriv. With the default 1000:1000 this is a no-op and
# the running process is `dev` — identical to a plain `USER dev` image. PUID/
# PGID are declared here so docker-compose / Unraid template UIs surface them.
ENV HOME=/home/dev
ENV SQUAREBOX=1
ENV PUID=1000
ENV PGID=1000

# 7. Shell Config
# The .bashrc lives in dotfiles/ on the host so install.sh can bind-mount it
Expand All @@ -157,4 +163,5 @@ ENV SQUAREBOX=1
COPY --chown=dev:dev dotfiles/bashrc /home/dev/.bashrc

WORKDIR /workspace
ENTRYPOINT ["/usr/local/bin/squarebox-entrypoint"]
CMD ["/bin/bash"]
102 changes: 93 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ running before you continue.
Install
-------

These commands clone the repo, build the container image, and drop you into the
container (if possible). On first login, a setup script runs automatically to
configure git (pulling your name and email from the host's global git config
if available), optionally sign in to GitHub CLI, your choice of AI coding
assistant, and language SDKs.
These commands install squarebox and drop you into the container (if possible).
By default they **pull a prebuilt image** from GHCR — no local Docker build, no
build toolchain — then clone the repo into `~/squarebox` for the config files
and the `sqrbx` helper commands. On first login, a setup script runs
automatically to configure git (pulling your name and email from the host's
global git config if available), optionally sign in to GitHub CLI, your choice
of AI coding assistant, and language SDKs.

**Stable**

Expand All @@ -70,9 +72,51 @@ assistant, and language SDKs.

curl -fsSL https://github.com/SquareWaveSystems/squarebox/releases/latest/download/install.sh | bash -s -- --edge

Stable installs the latest tagged release (pre-release tags like `-rc` are skipped). Edge uses the latest commit on main. The install script itself is published as a release asset, so the URL is pinned to a tagged version of the script — pushes to `main` won't break new installs until a release is cut.
Stable pulls the prebuilt image for the latest tagged release (pre-release tags
like `-rc` are skipped). Edge builds from the latest commit on `main` — no image
is published for unreleased commits, so edge always builds from source. To build
the released version from source instead of pulling, pass `--build`. The install
script itself is published as a release asset, so the URL is pinned to a tagged
version of the script — pushes to `main` won't break new installs until a
release is cut.

If the install fails or you want to see the full docker build and git output, re-run with `--verbose`.
If the install fails or you want to see the full build/pull and git output,
re-run with `--verbose`.

<details>
<summary><strong>Advanced install options (flags &amp; environment variables)</strong></summary>

Flags: `--build` (build from source instead of pulling), `--edge` (latest
`main`), `--verbose`.

| Variable | Default | Purpose |
|----------|---------|---------|
| `SQUAREBOX_DIR` | `~/squarebox` | Install location (repo + workspace). Point at durable storage on hosts where `$HOME` is volatile — e.g. Unraid `/mnt/user/appdata/squarebox`. |
| `SQUAREBOX_WORKSPACE` | `$SQUAREBOX_DIR/workspace` | Host path mounted as `/workspace`. |
| `SQUAREBOX_TAG` | matched release / `latest` | Image tag to pull (e.g. `v1.0.0-rc1` to test a pre-release). |
| `SQUAREBOX_IMAGE` | `ghcr.io/squarewavesystems/squarebox` | Image repository to pull from. |
| `SQUAREBOX_BUILD` | `0` | `1` is equivalent to `--build`. |
| `PUID` / `PGID` | `1000` / `1000` | Host uid/gid that should own bind-mounted files. Unraid/NAS: `99` / `100`. |
| `SQUAREBOX_RUNTIME` | auto | Force `docker` or `podman`. |
| `SQUAREBOX_HOME_VOLUME` | `squarebox-home` | Name of the named volume backing `/home/dev`. |
| `SQUAREBOX_EDGE` | `0` | `1` is equivalent to `--edge`. |

**Non-interactive provisioning** — set any of these to a comma-separated list to
pre-select a toolset and install it without prompts (handy for servers and
scripted installs). Values use the same keys as `sqrbx-setup`:

| Variable | Selects |
|----------|---------|
| `SQUAREBOX_AI` | AI assistants (`claude,copilot,gemini,codex,opencode,pi`) |
| `SQUAREBOX_SDKS` | language SDKs (`node,python,go,dotnet,rust`) |
| `SQUAREBOX_EDITORS` | editors (`micro,edit,fresh,nvim`) |
| `SQUAREBOX_TUIS` | TUI tools (`lazygit,gh-dash,yazi`) |
| `SQUAREBOX_MULTIPLEXERS` | multiplexers (`tmux,zellij`) |
| `SQUAREBOX_GIT_NAME` / `SQUAREBOX_GIT_EMAIL` | git identity (when no host gitconfig) |

Example: `SQUAREBOX_AI=claude SQUAREBOX_SDKS=node,python curl -fsSL …/install.sh | bash`

</details>

**Windows (PowerShell 7+)**

Expand Down Expand Up @@ -109,6 +153,35 @@ lives in a named Docker volume (`squarebox-home`) that survives container
recreation. Image-managed config like `.bashrc` is bind-mounted from the repo
so updates flow through to the running container.

Run as a long-lived server (Unraid / NAS / VPS)
-----------------------------------------------

The `curl | bash` installer is built around an interactive desktop shell. To run
squarebox as a persistent server container you attach into on demand — on
Unraid, a NAS, or a VPS — use the prebuilt image directly with the bundled
`docker-compose.yml`:

cp .env.example .env # set PUID/PGID, the workspace path, and a tag
docker compose up -d
docker compose exec -u dev squarebox bash

Set `PUID`/`PGID` in `.env` to match your host so files squarebox writes to the
workspace mount are owned correctly — on Unraid that's `99` / `100`. The `-u dev`
on `exec` is needed because the container starts as root (to apply PUID/PGID)
then drops to the `dev` user; `exec` bypasses that, so `-u dev` lands you where
you want to be.

Per-user state (shell history, gh auth, mise toolchains, AI-assistant state)
lives in the `squarebox-home` named volume and survives image updates; your code
lives on the host at the workspace path. To update, pull a newer tag and
`docker compose up -d`. The published image is multi-arch (amd64 + arm64), so it
also runs on ARM NAS/VPS hosts.

> **Unraid note:** the host's `/root` is tmpfs and wiped on reboot, so a raw
> `curl | bash` install there won't persist. Either use compose (above) with the
> workspace path under `/mnt/user/appdata`, or run the installer with
> `SQUAREBOX_DIR` and `SQUAREBOX_WORKSPACE` pointed at appdata.

What's included
---------------

Expand Down Expand Up @@ -139,8 +212,10 @@ What's optional
----------------

Selected during first-run setup. Choose any combination, all, or none.
Selections are saved to the workspace volume and reused automatically on
container rebuilds.
Selections are saved under `/workspace/.squarebox` (on the host workspace bind
mount) and reused automatically on container rebuilds. They can also be
pre-selected non-interactively via the `SQUAREBOX_AI`/`SQUAREBOX_SDKS`/… env vars
(see *Advanced install options* above).

### AI Coding Assistants

Expand Down Expand Up @@ -259,6 +334,15 @@ to scale both the examples and the agent's coaching; you can change it later
from the menu. Lessons render through `glow` when available so the markdown
bodies display properly.

Reconfiguring
-------------

Re-run the first-run wizard at any time from inside the container with
`sqrbx-setup`. With no arguments it walks every section; pass one or more
section names to reconfigure just those: `git`, `github`, `ai`, `editors`,
`tuis`, `multiplexers`, `sdks`, `shell`, `learn`. `sqrbx-setup --list` shows your
current selections and `sqrbx-setup --help` the usage.

Aliases
-------

Expand Down
Loading
Loading