From 7a59e966b8896065c376079d4121a9210c40e50c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 26 Mar 2026 06:48:07 -0700 Subject: [PATCH 1/4] build: strip large files from lib-vt dist tarball When emit_lib_vt is set, the dist tarball is now named ghostty-vt-.tar.gz and excludes large files that are unnecessary for building libghostty-vt. This reduces the archive from ~36MB to ~2.8MB by excluding images, macOS app resources, font assets, fuzz test corpus, crash testdata, and vendored libraries not used by lib-vt. GTK resources and frame data generation are also skipped since lib-vt does not need them, which removes the GTK build-time dependency. The distcheck step runs test-lib-vt instead of the full test suite for lib-vt archives. --- src/build/GhosttyDist.zig | 85 ++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/src/build/GhosttyDist.zig b/src/build/GhosttyDist.zig index 600aa4883..3c09c9490 100644 --- a/src/build/GhosttyDist.zig +++ b/src/build/GhosttyDist.zig @@ -18,17 +18,23 @@ archive_step: *std.Build.Step, check_step: *std.Build.Step, pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { + // The name prefix used for all paths in the archive. + const name = if (cfg.emit_lib_vt) "libghostty-vt" else "ghostty"; + // Get the resources we're going to inject into the source tarball. + // lib-vt doesn't need GTK resources or frame data. const alloc = b.allocator; var resources: std.ArrayListUnmanaged(Resource) = .empty; - { - const gtk = SharedDeps.gtkNgDistResources(b); - try resources.append(alloc, gtk.resources_c); - try resources.append(alloc, gtk.resources_h); - } - { - const framedata = GhosttyFrameData.distResources(b); - try resources.append(alloc, framedata.framedata); + if (!cfg.emit_lib_vt) { + { + const gtk = SharedDeps.gtkNgDistResources(b); + try resources.append(alloc, gtk.resources_c); + try resources.append(alloc, gtk.resources_h); + } + { + const framedata = GhosttyFrameData.distResources(b); + try resources.append(alloc, framedata.framedata); + } } // git archive to create the final tarball. "git archive" is the @@ -46,8 +52,8 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { const version = b.addWriteFiles().add("VERSION", b.fmt("{f}", .{cfg.version})); // --add-file uses the most recent --prefix to determine the path // in the archive to copy the file (the directory only). - git_archive.addArg(b.fmt("--prefix=ghostty-{f}/", .{ - cfg.version, + git_archive.addArg(b.fmt("--prefix={s}-{f}/", .{ + name, cfg.version, })); git_archive.addPrefixedFileArg("--add-file=", version); } @@ -65,8 +71,8 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { // --add-file uses the most recent --prefix to determine the path // in the archive to copy the file (the directory only). - git_archive.addArg(b.fmt("--prefix=ghostty-{f}/{s}/", .{ - cfg.version, + git_archive.addArg(b.fmt("--prefix={s}-{f}/{s}/", .{ + name, cfg.version, std.fs.path.dirname(resource.dist).?, })); git_archive.addPrefixedFileArg("--add-file=", copied); @@ -77,19 +83,28 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { // This is important. Standard source tarballs extract into // a directory named `project-version`. This is expected by // standard tooling such as debhelper and rpmbuild. - b.fmt("--prefix=ghostty-{f}/", .{cfg.version}), + b.fmt("--prefix={s}-{f}/", .{ name, cfg.version }), "-o", }); const output = git_archive.addOutputFileArg(b.fmt( - "ghostty-{f}.tar.gz", - .{cfg.version}, + "{s}-{f}.tar.gz", + .{ name, cfg.version }, )); git_archive.addArg("HEAD"); + // When building for lib-vt only, exclude large directories that + // are not needed to build libghostty-vt. This significantly reduces + // the size of the resulting archive. + if (cfg.emit_lib_vt) { + for (lib_vt_excludes) |exclude| { + git_archive.addArg(b.fmt(":(exclude){s}", .{exclude})); + } + } + // The install step to put the dist into the build directory. const install = b.addInstallFile( output, - b.fmt("dist/ghostty-{f}.tar.gz", .{cfg.version}), + b.fmt("dist/{s}-{f}.tar.gz", .{ name, cfg.version }), ); // The check step to ensure the archive works. @@ -100,8 +115,8 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { // This is the root Ghostty source dir of the extracted source tarball. // i.e. this is way `build.zig` is. const extract_dir = check - .addOutputDirectoryArg("ghostty") - .path(b, b.fmt("ghostty-{f}", .{cfg.version})); + .addOutputDirectoryArg(name) + .path(b, b.fmt("{s}-{f}", .{ name, cfg.version })); // Check that tests pass within the extracted directory. This isn't // a fully hermetic test because we're sharing the Zig cache. In @@ -109,7 +124,12 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { // in the interest of speed we don't do that for now and hope other // CI catches any issues. const check_test = step: { - const step = b.addSystemCommand(&.{ "zig", "build", "test" }); + // For lib-vt, we run the lib-vt tests instead of the full test suite. + const check_cmd = if (cfg.emit_lib_vt) + &[_][]const u8{ "zig", "build", "test-lib-vt", "-Demit-lib-vt=true" } + else + &[_][]const u8{ "zig", "build", "test" }; + const step = b.addSystemCommand(check_cmd); step.setCwd(extract_dir); // Must be set so that Zig knows that this command doesn't @@ -141,6 +161,33 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { }; } +/// Paths to exclude from the dist archive when building for lib-vt only. +/// These are large files and directories that are not needed to build or +/// test libghostty-vt, specified as git pathspec exclude patterns. +const lib_vt_excludes = &[_][]const u8{ + // App and platform resources + "images", + "macos", + "dist", + "flatpak", + "snap", + "po", + "example", + + // Test corpus (lib-vt tests use embedded testdata within src/terminal/) + "test", + + // Large binary assets + "src/font/res", + "src/crash/testdata", + "pkg/wuffs/src/too_big.jpg", + "pkg/wuffs/src/too_big.png", + "pkg/breakpad/vendor", + + // Vendored libraries not used by lib-vt + "vendor", +}; + /// A dist resource is a resource that is built and distributed as part /// of the source tarball with Ghostty. These aren't committed to the Git /// repository but are built as part of the `zig build dist` command. From 7ae1e32ecbd10d93ce0c7ddb4850a3c62a999940 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 26 Mar 2026 06:57:42 -0700 Subject: [PATCH 2/4] ci: add libghostty-vt source tarball to tip release Add a source-tarball-lib-vt job that builds the stripped lib-vt dist tarball and publishes it as libghostty-vt-source.tar.gz to the tip release. Also downsize the source-tarball runner from -md to -sm since it does not need the extra resources. --- .github/workflows/release-tip.yml | 56 ++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-tip.yml b/.github/workflows/release-tip.yml index 64ef41dc7..037f8ca3b 100644 --- a/.github/workflows/release-tip.yml +++ b/.github/workflows/release-tip.yml @@ -163,7 +163,7 @@ jobs: github.ref_name == 'main' ) ) - runs-on: namespace-profile-ghostty-md + runs-on: namespace-profile-ghostty-sm env: ZIG_LOCAL_CACHE_DIR: /zig/local-cache ZIG_GLOBAL_CACHE_DIR: /zig/global-cache @@ -206,6 +206,60 @@ jobs: ghostty-source.tar.gz.minisig token: ${{ secrets.GH_RELEASE_TOKEN }} + source-tarball-lib-vt: + needs: [setup] + if: | + needs.setup.outputs.should_skip != 'true' && + ( + github.event_name == 'workflow_dispatch' || + ( + github.repository_owner == 'ghostty-org' && + github.ref_name == 'main' + ) + ) + runs-on: namespace-profile-ghostty-sm + env: + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2 + with: + path: | + /nix + /zig + - uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + - name: Create Tarball + run: | + rm -rf zig-out/dist + nix develop -c zig build dist -Demit-lib-vt=true + cp zig-out/dist/*.tar.gz libghostty-vt-source.tar.gz + + - name: Sign Tarball + run: | + echo -n "${{ secrets.MINISIGN_KEY }}" > minisign.key + echo -n "${{ secrets.MINISIGN_PASSWORD }}" > minisign.password + nix develop -c minisign -S -m libghostty-vt-source.tar.gz -s minisign.key < minisign.password + + - name: Update Release + uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + with: + name: 'Ghostty Tip ("Nightly")' + prerelease: true + tag_name: tip + target_commitish: ${{ github.sha }} + files: | + libghostty-vt-source.tar.gz + libghostty-vt-source.tar.gz.minisig + token: ${{ secrets.GH_RELEASE_TOKEN }} + build-macos: needs: [setup] if: | From bfa3055309d5c292367c8ed3d876d59541a29e0c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 26 Mar 2026 06:59:59 -0700 Subject: [PATCH 3/4] ci: add distcheck for lib-vt source tarball Add a build-dist-lib-vt job that runs distcheck with -Demit-lib-vt=true and verifies the resulting tarball stays under 5 MB. Also downsize the build-dist runner from -md to -sm. --- .github/workflows/test.yml | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48e8ba1cf..70e903fba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -85,6 +85,7 @@ jobs: - skip - build-bench - build-dist + - build-dist-lib-vt - build-examples-zig - build-examples-cmake - build-examples-cmake-windows @@ -652,7 +653,7 @@ jobs: run: nm result/bin/.ghostty-wrapped 2>&1 | grep -q 'main_ghostty.main' build-dist: - runs-on: namespace-profile-ghostty-md + runs-on: namespace-profile-ghostty-sm needs: test outputs: artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }} @@ -693,6 +694,48 @@ jobs: path: |- ghostty-source.tar.gz + build-dist-lib-vt: + runs-on: namespace-profile-ghostty-sm + needs: test + env: + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2 + with: + path: | + /nix + /zig + + # Install Nix and use that to run our tests so our environment matches exactly. + - uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - name: Build and Check Source Tarball + run: | + rm -rf zig-out/dist + nix develop -c zig build distcheck -Demit-lib-vt=true + + - name: Verify tarball size + run: | + tarball=$(ls zig-out/dist/*.tar.gz) + size=$(stat --format=%s "$tarball") + max=$((5 * 1024 * 1024)) + echo "Tarball size: $size bytes (max: $max)" + if [ "$size" -gt "$max" ]; then + echo "ERROR: tarball exceeds 5 MB" + exit 1 + fi + trigger-snap: if: github.event_name != 'pull_request' runs-on: namespace-profile-ghostty-xsm From 96c414521a95e9c236e4ef5725fdf7f0bf7e1fe9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 26 Mar 2026 07:03:00 -0700 Subject: [PATCH 4/4] build: add cmake build verification to lib-vt distcheck Run cmake configure and build on the extracted lib-vt tarball as part of distcheck to ensure the CMake wrapper works from the stripped archive. Keep dist/cmake/ and dist/libghostty-vt/ in the archive since the CMake build needs them. --- src/build/GhosttyDist.zig | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/build/GhosttyDist.zig b/src/build/GhosttyDist.zig index 3c09c9490..448047f4b 100644 --- a/src/build/GhosttyDist.zig +++ b/src/build/GhosttyDist.zig @@ -72,7 +72,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { // --add-file uses the most recent --prefix to determine the path // in the archive to copy the file (the directory only). git_archive.addArg(b.fmt("--prefix={s}-{f}/{s}/", .{ - name, cfg.version, + name, cfg.version, std.fs.path.dirname(resource.dist).?, })); git_archive.addPrefixedFileArg("--add-file=", copied); @@ -153,6 +153,23 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { check_test.step.dependOn(&check_path.step); } + // For lib-vt, also verify the CMake build works from the tarball. + if (cfg.emit_lib_vt) { + const cmake_build_dir = extract_dir.path(b, "cmake-build"); + const cmake_configure = b.addSystemCommand(&.{ "cmake", "-B" }); + cmake_configure.addDirectoryArg(cmake_build_dir); + cmake_configure.setCwd(extract_dir); + cmake_configure.expectExitCode(0); + cmake_configure.step.dependOn(&check.step); + + const cmake_build = b.addSystemCommand(&.{ "cmake", "--build" }); + cmake_build.addDirectoryArg(cmake_build_dir); + cmake_build.expectExitCode(0); + cmake_build.step.dependOn(&cmake_configure.step); + + check_test.step.dependOn(&cmake_build.step); + } + return .{ .archive = output, .install_step = &install.step, @@ -168,7 +185,10 @@ const lib_vt_excludes = &[_][]const u8{ // App and platform resources "images", "macos", - "dist", + "dist/doxygen", + "dist/linux", + "dist/macos", + "dist/windows", "flatpak", "snap", "po",