Commit fac9795

Eric Bower  ·  2026-03-23 14:16:32 -0400 EDT
parent 7cdb333
chore: dockerfile and ci file

I'm experimenting with using zmx as a job engine for CI.
5 files changed,  +144, -89
+2, -0
......@@ -0,0 +1,2 @@
1+.git
2+zig-out
+20, -0
......@@ -0,0 +1,20 @@
1+FROM alpine:3.23
2+
3+RUN apk add curl git
4+
5+ARG ZIG_VERSION=0.15.2
6+RUN curl -L -o /tmp/zig.tar.xz https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz && \
7+ cd /tmp && \
8+ tar -xvf zig.tar.xz && \
9+ mv zig-x86_64-linux-${ZIG_VERSION} /usr/local/zig && \
10+ ln -s /usr/local/zig/zig /usr/local/bin/zig
11+
12+ENV PATH=/usr/local/zig:$PATH
13+
14+WORKDIR /app
15+
16+COPY build.zig build.zig.zon src/ /app/
17+
18+RUN zig build
19+
20+CMD ["zig", "build"]
+95, -87
......@@ -16,9 +16,6 @@ pub fn build(b: *std.Build) void {
1616 const version = b.option([]const u8, "version", "Version string for release") orelse
1717 @as([]const u8, @import("build.zig.zon").version);
1818
19- const run_step = b.step("run", "Run the app");
20- const test_step = b.step("test", "Run unit tests");
21-
2219 var code: u8 = 0;
2320 const git_sha = std.mem.trim(u8, b.runAllowFail(
2421 &.{ "git", "rev-parse", "--short", "HEAD" },
......@@ -29,7 +26,8 @@ pub fn build(b: *std.Build) void {
2926 const options = b.addOptions();
3027 options.addOption([]const u8, "version", version);
3128 options.addOption([]const u8, "git_sha", git_sha);
32- options.addOption([]const u8, "ghostty_version", @import("build.zig.zon").dependencies.ghostty.hash);
29+ const ghostty_ver = @import("build.zig.zon").dependencies.ghostty.hash;
30+ options.addOption([]const u8, "ghostty_version", ghostty_ver);
3331
3432 const exe_mod = b.createModule(.{
3533 .root_source_file = b.path("src/main.zig"),
......@@ -38,8 +36,6 @@ pub fn build(b: *std.Build) void {
3836 });
3937 exe_mod.addOptions("build_options", options);
4038
41- // You'll want to use a lazy dependency here so that ghostty is only
42- // downloaded if you actually need it.
4339 if (b.lazyDependency("ghostty", .{
4440 .target = target,
4541 .optimize = optimize,
......@@ -50,97 +46,109 @@ pub fn build(b: *std.Build) void {
5046 );
5147 }
5248
53- // Exe
54- const exe = b.addExecutable(.{
55- .name = "zmx",
56- .root_module = exe_mod,
57- });
58- exe.linkLibC();
59-
60- b.installArtifact(exe);
61-
6249 // Run
63- const run_cmd = b.addRunArtifact(exe);
64- run_cmd.step.dependOn(b.getInstallStep());
65- if (b.args) |args| run_cmd.addArgs(args);
66- run_step.dependOn(&run_cmd.step);
50+ {
51+ const run_step = b.step("run", "Run the app");
52+ const exe = b.addExecutable(.{
53+ .name = "zmx",
54+ .root_module = exe_mod,
55+ });
56+ exe.linkLibC();
57+ b.installArtifact(exe);
58+ const run_cmd = b.addRunArtifact(exe);
59+ run_cmd.step.dependOn(b.getInstallStep());
60+ if (b.args) |args| run_cmd.addArgs(args);
61+ run_step.dependOn(&run_cmd.step);
62+ }
6763
6864 // Test
69- const exe_unit_tests = b.addTest(.{
70- .root_module = exe_mod,
71- });
72- const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
73- test_step.dependOn(&run_exe_unit_tests.step);
74-
75- // This is where the interesting part begins.
76- // As you can see we are re-defining the same executable but
77- // we're binding it to a dedicated build step.
78- const exe_check = b.addExecutable(.{
79- .name = "zmx",
80- .root_module = exe_mod,
81- });
82- exe_check.linkLibC();
83- // There is no `b.installArtifact(exe_check);` here.
84-
85- // Finally we add the "check" step which will be detected
86- // by ZLS and automatically enable Build-On-Save.
87- // If you copy this into your `build.zig`, make sure to rename 'foo'
88- const check = b.step("check", "Check if foo compiles");
89- check.dependOn(&exe_check.step);
90-
91- // Release step - macOS can cross-compile to Linux, but Linux cannot cross-compile to macOS (needs SDK)
92- const native_os = @import("builtin").os.tag;
93- const release_targets = if (native_os == .macos) linux_targets ++ macos_targets else linux_targets;
94- const release_step = b.step("release", "Build release binaries (macOS builds all, Linux builds Linux only)");
95- for (release_targets) |release_target| {
96- const resolved = b.resolveTargetQuery(release_target);
97- const release_mod = b.createModule(.{
98- .root_source_file = b.path("src/main.zig"),
99- .target = resolved,
100- .optimize = .ReleaseSafe,
65+ {
66+ const test_step = b.step("test", "Run unit tests");
67+ const exe_unit_tests = b.addTest(.{
68+ .root_module = exe_mod,
10169 });
102- release_mod.addOptions("build_options", options);
103-
104- if (b.lazyDependency("ghostty", .{
105- .target = resolved,
106- .optimize = .ReleaseSafe,
107- })) |dep| {
108- release_mod.addImport("ghostty-vt", dep.module("ghostty-vt"));
109- }
70+ const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
71+ test_step.dependOn(&run_exe_unit_tests.step);
72+ }
11073
111- const release_exe = b.addExecutable(.{
74+ // Check for LSP integration
75+ {
76+ const check = b.step("check", "Check if zmx compiles");
77+ const exe_check = b.addExecutable(.{
11278 .name = "zmx",
113- .root_module = release_mod,
79+ .root_module = exe_mod,
11480 });
115- release_exe.linkLibC();
116-
117- const os_name = @tagName(release_target.os_tag orelse .linux);
118- const arch_name = @tagName(release_target.cpu_arch orelse .x86_64);
119- const tarball_name = b.fmt("zmx-{s}-{s}-{s}.tar.gz", .{ version, os_name, arch_name });
120-
121- const tar = b.addSystemCommand(&.{ "tar", "--no-xattrs", "-czf" });
122-
123- const tarball = tar.addOutputFileArg(tarball_name);
124- tar.addArg("-C");
125- tar.addDirectoryArg(release_exe.getEmittedBinDirectory());
126- tar.addArg("zmx");
81+ exe_check.linkLibC();
12782
128- const shasum = b.addSystemCommand(&.{ "shasum", "-a", "256" });
129- shasum.addFileArg(tarball);
130- const shasum_output = shasum.captureStdOut();
131-
132- const install_tar = b.addInstallFile(tarball, b.fmt("dist/{s}", .{tarball_name}));
133- const install_sha = b.addInstallFile(shasum_output, b.fmt("dist/{s}.sha256", .{tarball_name}));
134- release_step.dependOn(&install_tar.step);
135- release_step.dependOn(&install_sha.step);
83+ // Finally we add the "check" step which will be detected
84+ // by ZLS and automatically enable Build-On-Save.
85+ // If you copy this into your `build.zig`, make sure to rename 'foo'
86+ check.dependOn(&exe_check.step);
13687 }
13788
138- // Upload step - rsync docs and dist to pgs.sh
139- const upload_step = b.step("upload", "Upload docs and dist to pgs.sh:/zmx");
89+ // Release step - macOS can cross-compile to Linux,
90+ // but Linux cannot cross-compile to macOS (needs SDK)
91+ {
92+ const release_step = b.step(
93+ "release",
94+ "Build release binaries (macOS builds all, Linux builds Linux only)",
95+ );
96+ const native_os = @import("builtin").os.tag;
97+ const release_targets = if (native_os == .macos) linux_targets ++ macos_targets else linux_targets;
98+ for (release_targets) |release_target| {
99+ const resolved = b.resolveTargetQuery(release_target);
100+ const release_mod = b.createModule(.{
101+ .root_source_file = b.path("src/main.zig"),
102+ .target = resolved,
103+ .optimize = .ReleaseSafe,
104+ });
105+ release_mod.addOptions("build_options", options);
106+
107+ if (b.lazyDependency("ghostty", .{
108+ .target = resolved,
109+ .optimize = .ReleaseSafe,
110+ })) |dep| {
111+ release_mod.addImport("ghostty-vt", dep.module("ghostty-vt"));
112+ }
113+
114+ const release_exe = b.addExecutable(.{
115+ .name = "zmx",
116+ .root_module = release_mod,
117+ });
118+ release_exe.linkLibC();
119+
120+ const os_name = @tagName(release_target.os_tag orelse .linux);
121+ const arch_name = @tagName(release_target.cpu_arch orelse .x86_64);
122+ const tarball_name = b.fmt("zmx-{s}-{s}-{s}.tar.gz", .{ version, os_name, arch_name });
123+
124+ const tar = b.addSystemCommand(&.{ "tar", "--no-xattrs", "-czf" });
125+
126+ const tarball = tar.addOutputFileArg(tarball_name);
127+ tar.addArg("-C");
128+ tar.addDirectoryArg(release_exe.getEmittedBinDirectory());
129+ tar.addArg("zmx");
130+
131+ const shasum = b.addSystemCommand(&.{ "shasum", "-a", "256" });
132+ shasum.addFileArg(tarball);
133+ const shasum_output = shasum.captureStdOut();
134+
135+ const install_tar = b.addInstallFile(tarball, b.fmt("dist/{s}", .{tarball_name}));
136+ const install_sha = b.addInstallFile(
137+ shasum_output,
138+ b.fmt("dist/{s}.sha256", .{tarball_name}),
139+ );
140+ release_step.dependOn(&install_tar.step);
141+ release_step.dependOn(&install_sha.step);
142+ }
143+ }
140144
141- const rsync_docs = b.addSystemCommand(&.{ "rsync", "-rv", "docs/", "pgs.sh:/zmx" });
142- const rsync_dist = b.addSystemCommand(&.{ "rsync", "-rv", "zig-out/dist/", "pgs.sh:/zmx/a" });
145+ // Upload artifacts to pgs
146+ {
147+ const upload_step = b.step("upload", "Upload docs and dist to pgs.sh:/zmx");
148+ const rsync_docs = b.addSystemCommand(&.{ "rsync", "-rv", "docs/", "pgs.sh:/zmx" });
149+ const rsync_dist = b.addSystemCommand(&.{ "rsync", "-rv", "zig-out/dist/", "pgs.sh:/zmx/a" });
143150
144- upload_step.dependOn(&rsync_docs.step);
145- upload_step.dependOn(&rsync_dist.step);
151+ upload_step.dependOn(&rsync_docs.step);
152+ upload_step.dependOn(&rsync_dist.step);
153+ }
146154 }
+19, -0
......@@ -0,0 +1,19 @@
1+#!/usr/bin/env bash
2+set -xeo pipefail
3+
4+# This is a little experiement seeing how we could use zmx as a job engine for CI
5+
6+export ZMX_SESSION_PREFIX="ci-"
7+
8+zmx run build podman build -t zig .
9+zmx wait build
10+
11+zmx run fmt podman run --rm -it -v "$(pwd)":/app zig zig fmt --check .
12+zmx run test podman run --rm -it -v "$(pwd)":/app zig zig build test --summary all
13+zmx wait fmt test
14+
15+zmx kill build
16+zmx kill fmt
17+zmx kill test
18+
19+echo "success!"
+8, -2
......@@ -835,10 +835,12 @@ fn wait(cfg: *Cfg, session_names: std.ArrayList([]const u8)) !void {
835835 continue;
836836 }
837837 if (session.task_ended_at == 0) {
838- try stdout.print("still waiting task={s}\n", .{session.name});
838+ try stdout.print("waiting task={s}\n", .{session.name});
839839 try stdout.flush();
840840 continue;
841841 }
842+ try stdout.print("completed task={s} exit_code={d}\n", .{ session.name, session.task_exit_code.? });
843+ try stdout.flush();
842844 if (session.task_exit_code != 0) {
843845 agg_exit_code = session.task_exit_code orelse 0;
844846 }
......@@ -865,7 +867,11 @@ fn wait(cfg: *Cfg, session_names: std.ArrayList([]const u8)) !void {
865867 max_seen = total;
866868
867869 if (total > 0 and total == done) {
868- try stdout.print("tasks completed!\n", .{});
870+ if (agg_exit_code == 0) {
871+ try stdout.print("task(s) completed!\n", .{});
872+ } else {
873+ try stdout.print("task(s) failed!\n", .{});
874+ }
869875 try stdout.flush();
870876 std.process.exit(agg_exit_code);
871877 return;