Ports the base additions from pi-devbox v1.4.0 + v1.5.0 that opencode-devbox
lacked (opencode-devbox already tracks pi-devbox for CLI-toolset parity, v2.6.0):
- typst PDF engine for pandoc (v1.4.0) + the pandoc typst-template default-font
patch (v1.5.0) so 'pandoc --pdf-engine=typst' works without -V mainfont.
pandoc shipped since v2.6.0 as a front-end only (no PDF back-end). +xz-utils.
Tracks latest; --build-arg TYPST_VERSION escape hatch.
- Terminal support (v1.5.0): ncurses-term + kitty-terminfo + a compiled
xterm-ghostty alias (tic -x, use=ghostty), so wezterm/alacritty/foot/ghostty/
kitty resolve TERM over SSH instead of degrading to a dumb fallback.
- Readable Neovim colours (v1.5.0): system-wide /etc/xdg/nvim/sysinit.vim with
termguicolors.
- Host SSH reachability check at shell startup (v1.4.0): one-time probe in
.bash_aliases warning (with fix steps + inline pubkey) when the Mac host is
unreachable. The rest of the LAN stack was already present.
- .claude/settings.local.json added to the gitignore_global seed (v1.5.0).
- Repo hygiene (v1.5.0): LICENSE (MIT), THIRD_PARTY.md, hadolint CI job (pinned
v2.14.0) + .hadolint.yaml, IDEAS.md backlog.
Base-affecting (Dockerfile.base + rootfs) → base-<hash> advances, base rebuilds.
smoke-test gains typst/PDF, terminfo, and nvim-tgc assertions. Validated:
hadolint clean on both Dockerfiles, bash -n OK, base-hash guard OK, workflow
guard OK. CHANGELOG v2.7.0.
Two CI-only changes ported from pi-devbox (no runtime/image impact),
adapted to opencode-devbox's split-base 2-variant pipeline. Rides the
next release.
C — eliminate the sh-vs-bash footgun class:
- Add `defaults: run: shell: bash` workflow-wide to docker-publish-split.yml
and validate.yml. Gitea's default step shell is sh/dash, so bash-only
syntax in a step that omits `shell: bash` fails silently. All pre-existing
steps are POSIX, so bash runs them unchanged (no behavioural change).
- New .gitea/workflows/lint.yml (push/PR/dispatch): a Gitea-accurate shell
guard (scripts/check-workflow-shell.sh) + pinned actionlint + shellcheck.
The guard closes the actionlint blind spot: actionlint models GitHub
(default shell bash) so it does NOT flag bash syntax in a shell-less step.
Guard scans ALL .gitea/workflows/*.yml (hence the validate.yml default too).
Ported from pi-devbox 26384fe/d1db595.
B — promote-base-latest re-points base-latest by digest, not need_build:
The gate keyed off need_build=='true', assuming need_build==false meant
base-latest was current. A dry-run dispatch that pre-builds base-<hash>
falsifies that, leaving base-latest one base behind. Gate now runs on every
tag release / promote dispatch; the no-op optimization moved into the step
as a crane digest compare (re-tags only when base-latest != released
base-<hash>). Ported from pi-devbox b7197e8.
Validated locally: all 3 workflows YAML-parse; shell guard passes real
workflows and correctly fails a synthetic omit-shell+pipefail workflow;
actionlint (pinned 1.7.7) passes with explicit .gitea/workflows/*.yml glob.