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
+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 { | |
| 16 | 16 | const version = b.option([]const u8, "version", "Version string for release") orelse | |
| 17 | 17 | @as([]const u8, @import("build.zig.zon").version); | |
| 18 | 18 | ||
| 19 | - | const run_step = b.step("run", "Run the app"); | |
| 20 | - | const test_step = b.step("test", "Run unit tests"); | |
| 21 | - | ||
| 22 | 19 | var code: u8 = 0; | |
| 23 | 20 | const git_sha = std.mem.trim(u8, b.runAllowFail( | |
| 24 | 21 | &.{ "git", "rev-parse", "--short", "HEAD" }, |
| ... | ... | @@ -29,7 +26,8 @@ pub fn build(b: *std.Build) void { | |
| 29 | 26 | const options = b.addOptions(); | |
| 30 | 27 | options.addOption([]const u8, "version", version); | |
| 31 | 28 | 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); | |
| 33 | 31 | ||
| 34 | 32 | const exe_mod = b.createModule(.{ | |
| 35 | 33 | .root_source_file = b.path("src/main.zig"), |
| ... | ... | @@ -38,8 +36,6 @@ pub fn build(b: *std.Build) void { | |
| 38 | 36 | }); | |
| 39 | 37 | exe_mod.addOptions("build_options", options); | |
| 40 | 38 | ||
| 41 | - | // You'll want to use a lazy dependency here so that ghostty is only | |
| 42 | - | // downloaded if you actually need it. | |
| 43 | 39 | if (b.lazyDependency("ghostty", .{ | |
| 44 | 40 | .target = target, | |
| 45 | 41 | .optimize = optimize, |
| ... | ... | @@ -50,97 +46,109 @@ pub fn build(b: *std.Build) void { | |
| 50 | 46 | ); | |
| 51 | 47 | } | |
| 52 | 48 | ||
| 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 | - | ||
| 62 | 49 | // 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 | + | } | |
| 67 | 63 | ||
| 68 | 64 | // 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, | |
| 101 | 69 | }); | |
| 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 | + | } | |
| 110 | 73 | ||
| 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(.{ | |
| 112 | 78 | .name = "zmx", | |
| 113 | - | .root_module = release_mod, | |
| 79 | + | .root_module = exe_mod, | |
| 114 | 80 | }); | |
| 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(); | |
| 127 | 82 | ||
| 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); | |
| 136 | 87 | } | |
| 137 | 88 | ||
| 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 | + | } | |
| 140 | 144 | ||
| 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" }); | |
| 143 | 150 | ||
| 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 | + | } | |
| 146 | 154 | } |
A
pico.sh
+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 { | |
| 835 | 835 | continue; | |
| 836 | 836 | } | |
| 837 | 837 | 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}); | |
| 839 | 839 | try stdout.flush(); | |
| 840 | 840 | continue; | |
| 841 | 841 | } | |
| 842 | + | try stdout.print("completed task={s} exit_code={d}\n", .{ session.name, session.task_exit_code.? }); | |
| 843 | + | try stdout.flush(); | |
| 842 | 844 | if (session.task_exit_code != 0) { | |
| 843 | 845 | agg_exit_code = session.task_exit_code orelse 0; | |
| 844 | 846 | } |
| ... | ... | @@ -865,7 +867,11 @@ fn wait(cfg: *Cfg, session_names: std.ArrayList([]const u8)) !void { | |
| 865 | 867 | max_seen = total; | |
| 866 | 868 | ||
| 867 | 869 | 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 | + | } | |
| 869 | 875 | try stdout.flush(); | |
| 870 | 876 | std.process.exit(agg_exit_code); | |
| 871 | 877 | return; |