From c8bcaf9a4c2047df271098805e59cc562c959a27 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 11 Jun 2026 18:13:52 +0000 Subject: [PATCH 1/2] feat: add Nix flake packaging and prepare README for open-source --- .github/workflows/ci.yml | 10 ++++ .gitignore | 2 + README.md | 57 ++++++++++++++++++++-- flake.lock | 61 +++++++++++++++++++++++ flake.nix | 102 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea0344e..77fb0c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,3 +65,13 @@ jobs: done exit 1 - run: zig build test-all -Doptimize=ReleaseSafe --summary all + + nix: + name: nix build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: DeterminateSystems/nix-installer-action@v22 + - run: nix flake check + - run: nix build -L + - run: nix run . -- version diff --git a/.gitignore b/.gitignore index dca1103..568b828 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ zig-out/ .zig-cache/ +result +result-* diff --git a/README.md b/README.md index bbf9b4c..075e54b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,19 @@ -# boo +
+

boo

-Sessions that haunt your terminal. A GNU `screen` style terminal -multiplexer built on [libghostty](https://github.com/ghostty-org/ghostty) +Sessions that haunt your terminal. + +[Install](#install) | [Usage](#usage) | [Automation](#automation) | [Architecture](#architecture) + +[![ci](https://github.com/coder/boo/actions/workflows/ci.yml/badge.svg)](https://github.com/coder/boo/actions/workflows/ci.yml) +[![release](https://img.shields.io/github/v/release/coder/boo)](https://github.com/coder/boo/releases/latest) +[![license](https://img.shields.io/github/license/coder/boo)](./LICENSE) +[![discord](https://img.shields.io/discord/747933592273027093?label=discord)](https://discord.gg/coder) + +
+ +A GNU `screen` style terminal multiplexer built on +[libghostty](https://github.com/ghostty-org/ghostty) (`libghostty-vt`), written in Zig. Every session's output is parsed through Ghostty's terminal emulation @@ -37,6 +49,8 @@ exactly as a human would see it. ## Install +### Install script + ```sh curl -fsSL https://raw.githubusercontent.com/coder/boo/main/install.sh | sh ``` @@ -48,6 +62,19 @@ Pre-built binaries for Linux (x86_64, aarch64; fully static) and macOS install location (default: `/usr/local/bin` when writable, otherwise `~/.local/bin`). +### Nix + +With [flakes](https://wiki.nixos.org/wiki/Flakes) enabled: + +```sh +nix run github:coder/boo # try it without installing +nix profile add github:coder/boo # install into your profile +``` + +Or add `github:coder/boo` as an input to your own flake and reference +`packages..default` from your NixOS, nix-darwin, or Home +Manager configuration. + ## Building Requires [Zig](https://ziglang.org) 0.15.2. @@ -62,6 +89,9 @@ zig build test-all # everything The libghostty dependency is fetched and built from source automatically (pinned in `build.zig.zon`). +With Nix, `nix develop` opens a shell with the right Zig version, and +`nix build` builds the package to `./result/bin/boo`. + ## Usage ```sh @@ -172,6 +202,27 @@ This is a young project, not a drop-in GNU screen replacement: - No status line, monitoring, or copy mode yet. - Sessions run with `TERM=xterm-256color`. +## Support + +Feel free to [open an issue](https://github.com/coder/boo/issues/new) +if you have questions, run into bugs, or have a feature request. + +[Join the Coder Discord](https://discord.gg/coder) to chat with the +community. + +## Contributing + +Contributions are welcome: + +1. Fork and clone the repository. +2. Make your change and cover it with tests. +3. Run `zig build test-all` and + `zig fmt build.zig build.zig.zon src test`. +4. Open a pull request against `main`. + +CI runs formatting checks, unit tests, and PTY integration tests on +Linux and macOS, plus a Nix build. + ## License [MIT](LICENSE). Ghostty itself is MIT licensed. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..9140dbc --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1780749050, + "narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a799d3e3886da994fa307f817a6bc705ae538eeb", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..a18e63e --- /dev/null +++ b/flake.nix @@ -0,0 +1,102 @@ +{ + description = "Sessions that haunt your terminal. A GNU screen style terminal multiplexer built on libghostty."; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + self, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + zig = pkgs.zig_0_15; + + # Single source of truth for the version is build.zig.zon. + version = builtins.head ( + builtins.match ''.*\.version = "([^"]+)".*'' (builtins.readFile ./build.zig.zon) + ); + + # Zig package cache containing every dependency pinned in + # build.zig.zon (libghostty and its transitive dependencies). + # Pre-fetched as a fixed-output derivation so the sandboxed + # build below needs no network access. When dependencies in + # build.zig.zon change, update outputHash (set it to + # lib.fakeHash, build, and copy the hash from the error). + deps = pkgs.stdenvNoCC.mkDerivation { + pname = "boo-deps"; + inherit version; + src = ./.; + + nativeBuildInputs = [ zig ]; + + dontConfigure = true; + dontBuild = true; + dontFixup = true; + + installPhase = '' + export ZIG_GLOBAL_CACHE_DIR="$TMPDIR/zig-cache" + zig build --fetch=all + mv "$ZIG_GLOBAL_CACHE_DIR/p" "$out" + ''; + + impureEnvVars = lib.fetchers.proxyImpureEnvVars; + SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + outputHash = "sha256-2dZHdZoAap25va9ka2SN5QqoQ2xcZITJKNzwfOGmvus="; + }; + + boo = pkgs.stdenv.mkDerivation { + pname = "boo"; + inherit version; + src = ./.; + + nativeBuildInputs = [ zig.hook ]; + + # zig.hook builds with --release=safe, matching the + # optimization mode of the published release binaries. + # + # The dependency cache is copied, not symlinked: some + # ghostty build steps run helper executables with a working + # directory inside the package cache and locate their + # outputs via relative paths, which resolve incorrectly + # through a symlink into the store. + postConfigure = '' + cp -r --no-preserve=mode ${deps} "$ZIG_GLOBAL_CACHE_DIR/p" + ''; + + # Runs `zig build test` (unit tests; no TTY required). The + # PTY integration tests stay in CI via `zig build test-all`. + doCheck = true; + + meta = { + description = "Sessions that haunt your terminal. A GNU screen style terminal multiplexer built on libghostty"; + homepage = "https://github.com/coder/boo"; + license = lib.licenses.mit; + mainProgram = "boo"; + platforms = lib.platforms.linux ++ lib.platforms.darwin; + }; + }; + in + { + packages = { + default = boo; + inherit boo; + }; + + devShells.default = pkgs.mkShell { + packages = [ zig ]; + }; + + formatter = pkgs.nixfmt-tree; + } + ); +} From e2a8523d4d6f3bf77a3c430729a3c5fe10606753 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 11 Jun 2026 18:19:13 +0000 Subject: [PATCH 2/2] fix: retry dependency fetching in the Nix flake like CI --- flake.nix | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index a18e63e..313caa1 100644 --- a/flake.nix +++ b/flake.nix @@ -43,7 +43,15 @@ installPhase = '' export ZIG_GLOBAL_CACHE_DIR="$TMPDIR/zig-cache" - zig build --fetch=all + # Dependency hosts intermittently fail; retry like CI + # does. Zig resumes from its cache, so completed fetches + # are not repeated and the output stays reproducible. + for i in 1 2 3 4 5; do + zig build --fetch=all && break + if [ "$i" = 5 ]; then exit 1; fi + echo "fetch attempt $i failed; retrying in 10s" >&2 + sleep 10 + done mv "$ZIG_GLOBAL_CACHE_DIR/p" "$out" '';