flake.nix fetches the release tarballs and carries their SRI hashes in a generated block. The cut tool builds the release locally with the toolchain gates.yml pins, in a normalized nine-variable environment, writes the hashes into flake.nix, and commits it with build.zig.zon as the single bump commit. The package job verifies the pins on the bump commit and the publish job verifies them again on the tag, before anything is uploaded. The tarballs are written by dist_stage (std.tar.Writer, flate gzip) instead of the runner's tar and gzip, and -ffile-prefix-map keeps checkout paths out of the C objects; two checkouts at different absolute paths produce byte-identical archives. nxdns version, /api/version and the admin footer report the version only: the bump commit cannot know its own sha.
69 lines
2.6 KiB
Zig
69 lines
2.6 KiB
Zig
//! `GET /api/version` — what this binary is and how long it has been running.
|
|
//!
|
|
//! Unauthenticated (ruling 18), like the other monitoring endpoints. The three
|
|
//! strings are build options, so nothing here reads the running configuration.
|
|
|
|
const std = @import("std");
|
|
|
|
const http_util = @import("../http_util.zig");
|
|
const server = @import("../server.zig");
|
|
const version = @import("../../version.zig");
|
|
|
|
pub const Body = struct {
|
|
version: []const u8,
|
|
zig_version: []const u8,
|
|
/// Seconds since the process started. Zero until `started_unix` is wired,
|
|
/// and never negative: a clock stepped backwards must not report a
|
|
/// process that started in the future.
|
|
uptime_seconds: u64,
|
|
};
|
|
|
|
pub fn handle(
|
|
state: *server.WebState,
|
|
io: std.Io,
|
|
request: *http_util.Request,
|
|
) http_util.HandlerError!void {
|
|
const now = std.Io.Clock.real.now(io).toSeconds();
|
|
return http_util.respondJson(request, .ok, body(state.version, state.started_unix, now), &.{});
|
|
}
|
|
|
|
pub fn body(version_string: []const u8, started_unix: i64, now_unix: i64) Body {
|
|
return .{
|
|
.version = if (version_string.len == 0) version.string else version_string,
|
|
.zig_version = version.zig_version_string,
|
|
.uptime_seconds = uptime(started_unix, now_unix),
|
|
};
|
|
}
|
|
|
|
fn uptime(started_unix: i64, now_unix: i64) u64 {
|
|
if (started_unix <= 0 or now_unix <= started_unix) return 0;
|
|
return @intCast(now_unix - started_unix);
|
|
}
|
|
|
|
const testing = std.testing;
|
|
|
|
test "the body carries the build strings and the elapsed time" {
|
|
const out = body("", 1_000, 1_060);
|
|
try testing.expectEqualStrings(version.string, out.version);
|
|
try testing.expectEqualStrings(version.zig_version_string, out.zig_version);
|
|
try testing.expectEqual(@as(u64, 60), out.uptime_seconds);
|
|
}
|
|
|
|
test "the state's version string wins over the compiled-in one" {
|
|
try testing.expectEqualStrings("9.9.9", body("9.9.9", 0, 0).version);
|
|
}
|
|
|
|
test "an unset start time and a clock that stepped back both read as zero uptime" {
|
|
try testing.expectEqual(@as(u64, 0), body("", 0, 5_000).uptime_seconds);
|
|
try testing.expectEqual(@as(u64, 0), body("", 5_000, 4_000).uptime_seconds);
|
|
}
|
|
|
|
test "the body serializes with snake_case field names" {
|
|
var buffer: [256]u8 = undefined;
|
|
var writer: std.Io.Writer = .fixed(&buffer);
|
|
try std.json.Stringify.value(body("1.2.3", 10, 20), .{}, &writer);
|
|
const text = writer.buffered();
|
|
try testing.expect(std.mem.containsAtLeast(u8, text, 1, "\"zig_version\":"));
|
|
try testing.expect(std.mem.containsAtLeast(u8, text, 1, "\"uptime_seconds\":10"));
|
|
}
|