zig build dist and distcheck for source tarballs

This moves the source tarball creation process into the Zig build system
and follows the autotools-standard naming conventions of `dist` and
`distcheck`.

The `dist` target creates a source tarball in the `PREFIX/dist`
directory. The tarball is named `ghostty-VERSION.tar.gz` as expected by
standard source tarball conventions.

The `distcheck` target does the same as `dist`, but also takes the
resulting tarball, extracts it, and runs tests on the extracted source
to verify the source tarball works as expected.

This commit also updates CI:

  1. Tagged releases now use the new `zig build distcheck` command.
  2. Tip releases now use the new `zig build dist` command.
  3. A new test build tests that source tarball generation works on
     every commit.
This commit is contained in:
Mitchell Hashimoto
2025-03-18 12:11:05 -07:00
parent 0f2f0ab69f
commit bab8c28c8b
6 changed files with 162 additions and 17 deletions

View File

@ -77,9 +77,18 @@ jobs:
needs: [setup] needs: [setup]
env: env:
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }} GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@v1.2.0
with:
path: |
/nix
/zig
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@ -91,8 +100,8 @@ jobs:
- name: Create Tarball - name: Create Tarball
run: | run: |
git archive --format=tgz --prefix="ghostty-${GHOSTTY_VERSION}/" -o "ghostty-${GHOSTTY_VERSION}.tar.gz" HEAD nix develop -c zig build distcheck
git archive --format=tgz --prefix=ghostty-source/ -o ghostty-source.tar.gz HEAD cp zig-out/dist/ghostty-${GHOSTTY_VERSION}.tar.gz .
- name: Sign Tarball - name: Sign Tarball
run: | run: |
@ -108,8 +117,6 @@ jobs:
path: |- path: |-
ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz
ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz.minisig ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz.minisig
ghostty-source.tar.gz
ghostty-source.tar.gz.minisig
build-macos: build-macos:
needs: [setup] needs: [setup]

View File

@ -101,8 +101,17 @@ jobs:
) )
}} }}
runs-on: namespace-profile-ghostty-md runs-on: namespace-profile-ghostty-md
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@v1.2.0
with:
path: |
/nix
/zig
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@ -111,12 +120,17 @@ jobs:
name: ghostty name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Create Tarball - name: Create Tarball
run: git archive --format=tgz --prefix=ghostty-source/ -o ghostty-source.tar.gz HEAD run: |
rm -rf zig-out/dist
nix develop -c zig build distcheck
cp zig-out/dist/*.tar.gz ghostty-source.tar.gz
- name: Sign Tarball - name: Sign Tarball
run: | run: |
echo -n "${{ secrets.MINISIGN_KEY }}" > minisign.key echo -n "${{ secrets.MINISIGN_KEY }}" > minisign.key
echo -n "${{ secrets.MINISIGN_PASSWORD }}" > minisign.password echo -n "${{ secrets.MINISIGN_PASSWORD }}" > minisign.password
nix develop -c minisign -S -m ghostty-source.tar.gz -s minisign.key < minisign.password nix develop -c minisign -S -m ghostty-source.tar.gz -s minisign.key < minisign.password
- name: Update Release - name: Update Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:

View File

@ -11,6 +11,7 @@ jobs:
runs-on: namespace-profile-ghostty-sm runs-on: namespace-profile-ghostty-sm
needs: needs:
- build-bench - build-bench
- build-dist
- build-flatpak - build-flatpak
- build-linux - build-linux
- build-linux-libghostty - build-linux-libghostty
@ -209,6 +210,35 @@ jobs:
- name: Test NixOS package build - name: Test NixOS package build
run: nix build .#ghostty run: nix build .#ghostty
build-dist:
runs-on: namespace-profile-ghostty-md
needs: test
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@v1.2.0
with:
path: |
/nix
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Build and Check Source Tarball
run: nix develop -c zig build distcheck
build-macos: build-macos:
runs-on: namespace-profile-ghostty-macos runs-on: namespace-profile-ghostty-macos
needs: test needs: test

View File

@ -15,25 +15,31 @@ pub fn build(b: *std.Build) !void {
// Ghostty dependencies used by many artifacts. // Ghostty dependencies used by many artifacts.
const deps = try buildpkg.SharedDeps.init(b, &config); const deps = try buildpkg.SharedDeps.init(b, &config);
const exe = try buildpkg.GhosttyExe.init(b, &config, &deps);
if (config.emit_helpgen) deps.help_strings.install(); if (config.emit_helpgen) deps.help_strings.install();
// Ghostty executable, the actual runnable Ghostty program.
const exe = try buildpkg.GhosttyExe.init(b, &config, &deps);
// Ghostty docs // Ghostty docs
if (config.emit_docs) {
const docs = try buildpkg.GhosttyDocs.init(b, &deps); const docs = try buildpkg.GhosttyDocs.init(b, &deps);
docs.install(); if (config.emit_docs) docs.install();
}
// Ghostty webdata // Ghostty webdata
if (config.emit_webdata) {
const webdata = try buildpkg.GhosttyWebdata.init(b, &deps); const webdata = try buildpkg.GhosttyWebdata.init(b, &deps);
webdata.install(); if (config.emit_webdata) webdata.install();
}
// Ghostty bench tools // Ghostty bench tools
if (config.emit_bench) {
const bench = try buildpkg.GhosttyBench.init(b, &deps); const bench = try buildpkg.GhosttyBench.init(b, &deps);
bench.install(); if (config.emit_bench) bench.install();
// Ghostty dist tarball
const dist = try buildpkg.GhosttyDist.init(b, &config);
{
const step = b.step("dist", "Build the dist tarball");
step.dependOn(dist.install_step);
const check_step = b.step("distcheck", "Install and validate the dist tarball");
check_step.dependOn(dist.check_step);
check_step.dependOn(dist.install_step);
} }
// If we're not building libghostty, then install the exe and resources. // If we're not building libghostty, then install the exe and resources.

87
src/build/GhosttyDist.zig Normal file
View File

@ -0,0 +1,87 @@
const GhosttyDist = @This();
const std = @import("std");
const Config = @import("Config.zig");
const SharedDeps = @import("SharedDeps.zig");
/// The final source tarball.
archive: std.Build.LazyPath,
/// The step to install the tarball.
install_step: *std.Build.Step,
/// The step to depend on
archive_step: *std.Build.Step,
/// The step to depend on for checking the dist
check_step: *std.Build.Step,
pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
// git archive to create the final tarball. "git archive" is the
// easiest way I can find to create a tarball that ignores stuff
// from gitignore and also supports adding files as well as removing
// dist-only files (the "export-ignore" git attribute).
const git_archive = b.addSystemCommand(&.{
"git",
"archive",
"--format=tgz",
// 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-{}/", .{cfg.version}),
"-o",
});
const output = git_archive.addOutputFileArg(b.fmt(
"ghostty-{}.tar.gz",
.{cfg.version},
));
git_archive.addArg("HEAD");
// The install step to put the dist into the build directory.
const install = b.addInstallFile(
output,
b.fmt("dist/ghostty-{}.tar.gz", .{cfg.version}),
);
// The check step to ensure the archive works.
const check = b.addSystemCommand(&.{ "tar", "xvzf" });
check.addFileArg(output);
check.addArg("-C");
// 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-{}", .{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
// the future we could add an option to use a totally new cache but
// 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" });
step.setCwd(extract_dir);
// Must be set so that Zig knows that this command doesn't
// have side effects and is being run for its exit code check.
// Zig will cache depending on its extract dir.
step.expectExitCode(0);
// Capture stderr so it doesn't spew into the parent build.
// On the flip side, if the test fails we won't know why so
// that sucks but we should have already ran tests at this point.
_ = step.captureStdErr();
break :step step;
};
return .{
.archive = output,
.install_step = &install.step,
.archive_step = &git_archive.step,
.check_step = &check_test.step,
};
}

View File

@ -8,6 +8,7 @@ pub const GitVersion = @import("GitVersion.zig");
// Artifacts // Artifacts
pub const GhosttyBench = @import("GhosttyBench.zig"); pub const GhosttyBench = @import("GhosttyBench.zig");
pub const GhosttyDist = @import("GhosttyDist.zig");
pub const GhosttyDocs = @import("GhosttyDocs.zig"); pub const GhosttyDocs = @import("GhosttyDocs.zig");
pub const GhosttyExe = @import("GhosttyExe.zig"); pub const GhosttyExe = @import("GhosttyExe.zig");
pub const GhosttyFrameData = @import("GhosttyFrameData.zig"); pub const GhosttyFrameData = @import("GhosttyFrameData.zig");