ReviewOS

also looking at this

zig-utils/zig-test-framework

ci: define and enforce the Zig toolchain policy

#16
Merged glennmichael123 wants to merge codex/issue-8-zig-toolchain-policy into main
9 files +97 -25
.github/workflows/ci.ymlmodified+51-6
Changes to .github/workflows/ci.yml
@@ -5,25 +5,31 @@ on:
55 branches: [main, develop]
66 pull_request:
77 branches: [main, develop]
8 schedule:
9 - cron: '17 6 * * 1'
10 workflow_dispatch:
811
912jobs:
1013 test:
11 name: Test on ${{ matrix.os }} with Zig ${{ matrix.zig-version }}
14 name: Test on ${{ matrix.os }} with supported Zig
1215 runs-on: ${{ matrix.os }}
1316 strategy:
1417 matrix:
1518 os: [ubuntu-latest, macos-latest]
16 zig-version: ['0.17.0-dev']
1719 fail-fast: false
1820
1921 steps:
2022 - name: Checkout code
2123 uses: actions/checkout@v6
2224
25 - name: Read supported Zig version
26 id: zig-version
27 run: echo "version=$(cat .zig-version)" >> "$GITHUB_OUTPUT"
28
2329 - name: Setup Zig
2430 uses: pantry-pm/pantry/packages/action@main
2531 with:
26 packages: zig@0.17.0-dev
32 packages: zig@${{ steps.zig-version.outputs.version }}
2733
2834 - name: Verify Zig installation
2935 run: zig version
@@ -45,10 +51,14 @@ jobs:
4551 - name: Checkout code
4652 uses: actions/checkout@v6
4753
54 - name: Read supported Zig version
55 id: zig-version
56 run: echo "version=$(cat .zig-version)" >> "$GITHUB_OUTPUT"
57
4858 - name: Setup Zig
4959 uses: pantry-pm/pantry/packages/action@main
5060 with:
51 packages: zig@0.17.0-dev
61 packages: zig@${{ steps.zig-version.outputs.version }}
5262
5363 - name: Check formatting
5464 run: zig fmt --check .
@@ -61,10 +71,14 @@ jobs:
6171 - name: Checkout code
6272 uses: actions/checkout@v6
6373
74 - name: Read supported Zig version
75 id: zig-version
76 run: echo "version=$(cat .zig-version)" >> "$GITHUB_OUTPUT"
77
6478 - name: Setup Zig
6579 uses: pantry-pm/pantry/packages/action@main
6680 with:
67 packages: zig@0.17.0-dev
81 packages: zig@${{ steps.zig-version.outputs.version }}
6882
6983 - name: Generate docs
7084 run: zig build-lib -femit-docs src/lib.zig
@@ -83,10 +97,14 @@ jobs:
8397 - name: Checkout code
8498 uses: actions/checkout@v6
8599
100 - name: Read supported Zig version
101 id: zig-version
102 run: echo "version=$(cat .zig-version)" >> "$GITHUB_OUTPUT"
103
86104 - name: Setup Zig
87105 uses: pantry-pm/pantry/packages/action@main
88106 with:
89 packages: zig@0.17.0-dev
107 packages: zig@${{ steps.zig-version.outputs.version }}
90108
91109 - name: Install kcov
92110 continue-on-error: true
@@ -121,3 +139,30 @@ jobs:
121139 - name: Coverage skipped
122140 if: steps.install-kcov.outcome != 'success'
123141 run: echo "kcov not available on this platform, skipping coverage"
142
143 latest-zig:
144 name: Compatibility with latest Zig 0.17.0-dev
145 if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
146 continue-on-error: true
147 runs-on: ubuntu-latest
148
149 steps:
150 - name: Checkout code
151 uses: actions/checkout@v6
152
153 - name: Setup latest Zig development build
154 uses: pantry-pm/pantry/packages/action@main
155 with:
156 packages: zig@0.17.0-dev
157
158 - name: Verify Zig installation
159 run: zig version
160
161 - name: Build framework
162 run: zig build
163
164 - name: Run tests
165 run: zig build test
166
167 - name: Run examples
168 run: zig build examples
.zig-versionadded+1-0
Changes to .zig-version
@@ -0,0 +1,1 @@
10.17.0-dev.2163+89ff10d56
CONTRIBUTING.mdmodified+1-1
Changes to CONTRIBUTING.md
@@ -26,7 +26,7 @@ Thank you for your interest in contributing to the Zig Test Framework! This docu
2626
2727### Prerequisites
2828
29- Zig 0.15.1 or later
29- The exact Zig development build recorded in [`.zig-version`](.zig-version)
3030- Git
3131- Basic understanding of testing frameworks (Jest, Vitest, or similar)
3232
README.mdmodified+4-1
Changes to README.md
@@ -756,7 +756,10 @@ zig build examples
756756
757757## Requirements
758758
759- Zig 0.15.1 or later
759- The exact Zig development build recorded in [`.zig-version`](.zig-version)
760
761See [TOOLCHAIN.md](TOOLCHAIN.md) for the compatibility policy and upgrade
762process.
760763
761764## Documentation
762765
TOOLCHAIN.mdadded+23-0
Changes to TOOLCHAIN.md
@@ -0,0 +1,23 @@
1# Zig toolchain policy
2
3The Zig Test Framework supports the exact Zig development build recorded in
4`.zig-version`. Required CI jobs use that version so pull requests are tested
5against a reproducible toolchain.
6
7Because the project follows Zig development builds, CI also runs a scheduled
8compatibility check against the latest `0.17.0-dev` build. That check is
9informational: an upstream API change should produce an intentional toolchain
10update rather than unexpectedly blocking unrelated pull requests.
11
12## Updating Zig
13
141. Replace the version in `.zig-version` with the full output of `zig version`.
152. Run `zig fmt --check .`.
163. Run `zig build`, `zig build test`, and `zig build examples`.
174. Run `zig build-lib -femit-docs src/lib.zig` and remove the generated local
18 artifacts after verifying the command succeeds.
195. Summarize required compatibility changes in the pull request and, for a
20 release, in the changelog.
21
22The compatibility job can also be run manually from GitHub Actions before
23updating the pinned version.
build.zigmodified+1-3
Changes to build.zig
@@ -27,9 +27,7 @@ pub fn build(b: *std.Build) void {
2727 const run_cmd = b.addRunArtifact(exe);
2828 run_cmd.step.dependOn(b.getInstallStep());
2929
30 if (b.args) |args| {
31 run_cmd.addArgs(args);
32 }
30 run_cmd.addPassthruArgs();
3331
3432 const run_step = b.step("run", "Run the test framework");
3533 run_step.dependOn(&run_cmd.step);
build.zig.zonmodified+2-0
Changes to build.zig.zon
@@ -5,9 +5,11 @@
55 .paths = .{
66 "build.zig",
77 "build.zig.zon",
8 ".zig-version",
89 "src",
910 "tests",
1011 "examples",
12 "TOOLCHAIN.md",
1113 "README.md",
1214 "LICENSE",
1315 },
src/compat.zigmodified+6-6
Changes to src/compat.zig
@@ -113,7 +113,7 @@ fn closeFd(fd: std.posix.fd_t) void {
113113/// Read entire file contents using POSIX APIs.
114114/// Replaces std.fs.cwd().openFile() + file.readToEndAlloc().
115115pub fn readFileAlloc(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
116 const path_z = try allocator.dupeZ(u8, path);
116 const path_z = try allocator.dupeSentinel(u8, path, 0);
117117 defer allocator.free(path_z);
118118
119119 const fd = std.posix.openatZ(std.posix.AT.FDCWD, path_z, .{}, 0) catch |err| {
@@ -139,7 +139,7 @@ pub fn readFileAlloc(allocator: std.mem.Allocator, path: []const u8) ![]const u8
139139/// Write content to a file using POSIX APIs.
140140/// Replaces std.fs.cwd().createFile() + file.writeAll().
141141pub fn writeFile(allocator: std.mem.Allocator, path: []const u8, content: []const u8) !void {
142 const path_z = try allocator.dupeZ(u8, path);
142 const path_z = try allocator.dupeSentinel(u8, path, 0);
143143 defer allocator.free(path_z);
144144
145145 const fd = std.posix.openatZ(std.posix.AT.FDCWD, path_z, .{
@@ -161,7 +161,7 @@ pub fn writeFile(allocator: std.mem.Allocator, path: []const u8, content: []cons
161161/// Delete a file using POSIX APIs.
162162/// Replaces std.fs.cwd().deleteFile().
163163pub fn deleteFile(allocator: std.mem.Allocator, path: []const u8) !void {
164 const path_z = try allocator.dupeZ(u8, path);
164 const path_z = try allocator.dupeSentinel(u8, path, 0);
165165 defer allocator.free(path_z);
166166 const rc = std.c.unlink(path_z);
167167 if (rc != 0) {
@@ -175,7 +175,7 @@ pub fn deleteFile(allocator: std.mem.Allocator, path: []const u8) !void {
175175/// Create directories recursively using POSIX APIs.
176176/// Replaces std.fs.cwd().makePath().
177177pub fn makePath(allocator: std.mem.Allocator, path: []const u8) !void {
178 const path_z = try allocator.dupeZ(u8, path);
178 const path_z = try allocator.dupeSentinel(u8, path, 0);
179179 defer allocator.free(path_z);
180180
181181 const rc = std.c.mkdir(path_z, 0o755);
@@ -189,7 +189,7 @@ pub fn makePath(allocator: std.mem.Allocator, path: []const u8) !void {
189189 if (sep > 0) {
190190 try makePath(allocator, path[0..sep]);
191191 // Retry creating the directory
192 const retry_z = try allocator.dupeZ(u8, path);
192 const retry_z = try allocator.dupeSentinel(u8, path, 0);
193193 defer allocator.free(retry_z);
194194 const rc2 = std.c.mkdir(retry_z, 0o755);
195195 if (rc2 != 0 and std.c.errno(rc2) != .EXIST) {
@@ -298,7 +298,7 @@ pub fn spawnAndWait(
298298 defer allocator.free(c_argv);
299299
300300 for (argv, 0..) |arg, i| {
301 const z = try allocator.dupeZ(u8, arg);
301 const z = try allocator.dupeSentinel(u8, arg, 0);
302302 c_argv[i] = z.ptr;
303303 }
304304 c_argv[argv.len] = null;
src/snapshot.zigmodified+8-8
Changes to src/snapshot.zig
@@ -251,16 +251,16 @@ pub const Snapshot = struct {
251251 },
252252 .@"struct" => {
253253 try writer.writeAll("{\n");
254 inline for (type_info.@"struct".fields, 0..) |field, i| {
254 inline for (type_info.@"struct".field_names, 0..) |field_name, i| {
255255 if (self.options.pretty_print) {
256256 try writer.writeAll(" ");
257257 }
258 try writer.print("\"{s}\": ", .{field.name});
258 try writer.print("\"{s}\": ", .{field_name});
259259
260 const field_value = @field(value, field.name);
260 const field_value = @field(value, field_name);
261261 try self.formatJsonValue(writer, field_value);
262262
263 if (i < type_info.@"struct".fields.len - 1) {
263 if (i < type_info.@"struct".field_names.len - 1) {
264264 try writer.writeAll(",");
265265 }
266266 if (self.options.pretty_print) {
@@ -307,10 +307,10 @@ pub const Snapshot = struct {
307307
308308 try writer.writeAll("{\n");
309309
310 inline for (type_info.@"struct".fields, 0..) |field, i| {
311 try writer.print(" \"{s}\": ", .{field.name});
310 inline for (type_info.@"struct".field_names, 0..) |field_name, i| {
311 try writer.print(" \"{s}\": ", .{field_name});
312312
313 const field_value = @field(value, field.name);
313 const field_value = @field(value, field_name);
314314 const FieldType = @TypeOf(field_value);
315315 const field_info = @typeInfo(FieldType);
316316
@@ -328,7 +328,7 @@ pub const Snapshot = struct {
328328 else => try writer.print("{any}", .{field_value}),
329329 }
330330
331 if (i < type_info.@"struct".fields.len - 1) {
331 if (i < type_info.@"struct".field_names.len - 1) {
332332 try writer.writeAll(",\n");
333333 } else {
334334 try writer.writeAll("\n");