use git version in metno identifier
This commit is contained in:
parent
ad93634100
commit
b2cb5d537f
3 changed files with 171 additions and 2 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const GitVersion = @import("build/GitVersion.zig");
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
@ -53,6 +54,10 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
maxminddb.installHeadersDirectory(maxminddb_upstream.path("include"), "", .{});
|
maxminddb.installHeadersDirectory(maxminddb_upstream.path("include"), "", .{});
|
||||||
|
|
||||||
|
const version = GitVersion.getVersion(b, .{});
|
||||||
|
const build_options = b.addOptions();
|
||||||
|
build_options.addOption([]const u8, "version", version);
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "wttr",
|
.name = "wttr",
|
||||||
.root_module = b.createModule(.{
|
.root_module = b.createModule(.{
|
||||||
|
|
@ -67,6 +72,7 @@ pub fn build(b: *std.Build) void {
|
||||||
exe.root_module.addAnonymousImport("airports.dat", .{
|
exe.root_module.addAnonymousImport("airports.dat", .{
|
||||||
.root_source_file = openflights.path("data/airports.dat"),
|
.root_source_file = openflights.path("data/airports.dat"),
|
||||||
});
|
});
|
||||||
|
exe.root_module.addOptions("build_options", build_options);
|
||||||
exe.root_module.addIncludePath(maxminddb_upstream.path("include"));
|
exe.root_module.addIncludePath(maxminddb_upstream.path("include"));
|
||||||
exe.root_module.addConfigHeader(maxminddb_config);
|
exe.root_module.addConfigHeader(maxminddb_config);
|
||||||
exe.linkLibrary(maxminddb);
|
exe.linkLibrary(maxminddb);
|
||||||
|
|
@ -87,6 +93,7 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
const test_options = b.addOptions();
|
const test_options = b.addOptions();
|
||||||
test_options.addOption(bool, "download_geoip", download_geoip);
|
test_options.addOption(bool, "download_geoip", download_geoip);
|
||||||
|
test_options.addOption([]const u8, "version", version);
|
||||||
|
|
||||||
const tests = b.addTest(.{
|
const tests = b.addTest(.{
|
||||||
.root_module = b.createModule(.{
|
.root_module = b.createModule(.{
|
||||||
|
|
|
||||||
161
build/GitVersion.zig
Normal file
161
build/GitVersion.zig
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Build = std.Build;
|
||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
/// Length of the short hash (default: 7, git's default)
|
||||||
|
hash_length: u6 = 7,
|
||||||
|
/// String to append when working tree is dirty
|
||||||
|
dirty_flag: []const u8 = "*",
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get git version information by reading .git files directly
|
||||||
|
pub fn getVersion(b: *Build, options: Options) []const u8 {
|
||||||
|
const allocator = b.allocator;
|
||||||
|
|
||||||
|
// Find build root by looking for build.zig
|
||||||
|
const build_root = findBuildRoot(allocator) catch return "unknown";
|
||||||
|
defer allocator.free(build_root);
|
||||||
|
|
||||||
|
// Read .git/HEAD relative to build root
|
||||||
|
const head_path = std.fmt.allocPrint(allocator, "{s}/.git/HEAD", .{build_root}) catch return "unknown";
|
||||||
|
defer allocator.free(head_path);
|
||||||
|
|
||||||
|
const head_data = std.fs.cwd().readFileAlloc(allocator, head_path, 1024) catch {
|
||||||
|
return "not under version control";
|
||||||
|
};
|
||||||
|
defer allocator.free(head_data);
|
||||||
|
|
||||||
|
const head_trimmed = std.mem.trim(u8, head_data, &std.ascii.whitespace);
|
||||||
|
|
||||||
|
// Parse HEAD - either "ref: refs/heads/branch" or direct hash
|
||||||
|
const hash_owned = if (std.mem.startsWith(u8, head_trimmed, "ref: ")) blk: {
|
||||||
|
const ref_path_rel = std.mem.trimLeft(u8, head_trimmed[5..], &std.ascii.whitespace);
|
||||||
|
const ref_file = std.fmt.allocPrint(allocator, "{s}/.git/{s}", .{ build_root, ref_path_rel }) catch return "unknown";
|
||||||
|
defer allocator.free(ref_file);
|
||||||
|
|
||||||
|
const ref_fd = std.fs.openFileAbsolute(ref_file, .{}) catch return "unknown";
|
||||||
|
defer ref_fd.close();
|
||||||
|
|
||||||
|
var ref_buf: [1024]u8 = undefined;
|
||||||
|
const bytes_read = ref_fd.readAll(&ref_buf) catch return "unknown";
|
||||||
|
const ref_data = ref_buf[0..bytes_read];
|
||||||
|
|
||||||
|
const ref_trimmed = std.mem.trim(u8, ref_data, &std.ascii.whitespace);
|
||||||
|
break :blk allocator.dupe(u8, ref_trimmed) catch return "unknown";
|
||||||
|
} else allocator.dupe(u8, head_trimmed) catch return "unknown";
|
||||||
|
defer allocator.free(hash_owned);
|
||||||
|
|
||||||
|
// Truncate to short hash
|
||||||
|
const short_hash = if (hash_owned.len > options.hash_length)
|
||||||
|
hash_owned[0..options.hash_length]
|
||||||
|
else
|
||||||
|
hash_owned;
|
||||||
|
|
||||||
|
// Check if dirty using simple heuristic:
|
||||||
|
// If any .zig files are newer than .git/index, mark as dirty
|
||||||
|
const is_dirty = isDirty(allocator, build_root) catch return "unknown";
|
||||||
|
|
||||||
|
if (is_dirty) {
|
||||||
|
return std.fmt.allocPrint(allocator, "{s}{s}", .{ short_hash, options.dirty_flag }) catch return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
return allocator.dupe(u8, short_hash) catch return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn findBuildRoot(allocator: std.mem.Allocator) ![]const u8 {
|
||||||
|
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||||
|
const start_cwd = try std.fs.cwd().realpath(".", &buf);
|
||||||
|
var cwd: []const u8 = start_cwd;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Check if build.zig exists in current directory
|
||||||
|
var dir = std.fs.openDirAbsolute(cwd, .{}) catch break;
|
||||||
|
defer dir.close();
|
||||||
|
|
||||||
|
dir.access("build.zig", .{}) catch {
|
||||||
|
// build.zig not found, try parent
|
||||||
|
const parent = std.fs.path.dirname(cwd) orelse break;
|
||||||
|
if (std.mem.eql(u8, parent, cwd)) break; // Reached root
|
||||||
|
cwd = parent;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
return allocator.dupe(u8, cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.BuildRootNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn isDirty(allocator: std.mem.Allocator, build_root: []const u8) !bool {
|
||||||
|
// Get .git/index mtime
|
||||||
|
const index_path = try std.fs.path.join(allocator, &[_][]const u8{ build_root, ".git", "index" });
|
||||||
|
defer allocator.free(index_path);
|
||||||
|
|
||||||
|
const index_stat = std.fs.cwd().statFile(index_path) catch return error.CannotDetermineDirty;
|
||||||
|
const index_mtime = index_stat.mtime;
|
||||||
|
|
||||||
|
// Read .gitignore
|
||||||
|
const ignore_path = try std.fs.path.join(allocator, &[_][]const u8{ build_root, ".gitignore" });
|
||||||
|
defer allocator.free(ignore_path);
|
||||||
|
const ignore_data = std.fs.cwd().readFileAlloc(allocator, ignore_path, 1024 * 1024) catch
|
||||||
|
try allocator.dupe(u8, "");
|
||||||
|
defer allocator.free(ignore_data);
|
||||||
|
|
||||||
|
// Walk source files in build root and check if any are newer
|
||||||
|
var dir = std.fs.openDirAbsolute(build_root, .{ .iterate = true }) catch return error.CannotDetermineDirty;
|
||||||
|
defer dir.close();
|
||||||
|
|
||||||
|
var walker = dir.walk(allocator) catch return error.CannotDetermineDirty;
|
||||||
|
defer walker.deinit();
|
||||||
|
|
||||||
|
while (walker.next() catch return error.CannotDetermineDirty) |entry| {
|
||||||
|
if (entry.kind != .file) continue;
|
||||||
|
|
||||||
|
// Always ignore .git/
|
||||||
|
if (std.mem.startsWith(u8, entry.path, ".git/")) continue;
|
||||||
|
|
||||||
|
// Check if path matches any ignore pattern
|
||||||
|
var lines = std.mem.splitScalar(u8, ignore_data, '\n');
|
||||||
|
var ignored = false;
|
||||||
|
while (lines.next()) |line| {
|
||||||
|
const trimmed = std.mem.trim(u8, line, &std.ascii.whitespace);
|
||||||
|
if (trimmed.len == 0) continue;
|
||||||
|
if (trimmed[0] == '#') continue;
|
||||||
|
|
||||||
|
if (isIgnored(entry.path, trimmed)) {
|
||||||
|
ignored = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ignored) continue;
|
||||||
|
|
||||||
|
const stat = entry.dir.statFile(entry.basename) catch continue;
|
||||||
|
if (stat.mtime > index_mtime) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles checking a gitignore style pattern to determine if a path should
|
||||||
|
/// be ignored. Mostly, globs are not handled, but an extension-like pattern
|
||||||
|
/// is common and recognized by this function, so things like '*.swp' will work
|
||||||
|
/// as expected
|
||||||
|
fn isIgnored(path: []const u8, pattern: []const u8) bool {
|
||||||
|
// TODO: Handle other glob patterns (?, [], *prefix, suffix*)
|
||||||
|
// Handle *.extension pattern
|
||||||
|
if (std.mem.startsWith(u8, pattern, "*."))
|
||||||
|
return std.mem.endsWith(u8, path, pattern[1..]);
|
||||||
|
|
||||||
|
// Pattern ending with / matches directory prefix
|
||||||
|
if (std.mem.endsWith(u8, pattern, "/"))
|
||||||
|
return std.mem.startsWith(u8, path, pattern);
|
||||||
|
|
||||||
|
// Pattern starting with / matches from root
|
||||||
|
if (std.mem.startsWith(u8, pattern, "/"))
|
||||||
|
return std.mem.eql(u8, path, pattern[1..]);
|
||||||
|
|
||||||
|
// Otherwise match as substring (file/dir name anywhere in path)
|
||||||
|
return std.mem.indexOf(u8, path, pattern) != null;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const build_options = @import("build_options");
|
||||||
const WeatherProvider = @import("Provider.zig");
|
const WeatherProvider = @import("Provider.zig");
|
||||||
const Coordinates = @import("../Coordinates.zig");
|
const Coordinates = @import("../Coordinates.zig");
|
||||||
const types = @import("types.zig");
|
const types = @import("types.zig");
|
||||||
|
|
@ -119,8 +120,8 @@ fn fetchRaw(ptr: *anyopaque, allocator: std.mem.Allocator, coords: Coordinates)
|
||||||
|
|
||||||
const user_agent = try std.fmt.allocPrint(
|
const user_agent = try std.fmt.allocPrint(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
"wttr/1.0 git.lerch.org/lobo/wttr {s}",
|
"wttr/{s} git.lerch.org/lobo/wttr {s}",
|
||||||
.{self.identifying_email},
|
.{ build_options.version, self.identifying_email },
|
||||||
);
|
);
|
||||||
defer self.allocator.free(user_agent);
|
defer self.allocator.free(user_agent);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue