diff --git a/src/root.zig b/src/root.zig index d471f0f..ef99ade 100644 --- a/src/root.zig +++ b/src/root.zig @@ -44,6 +44,72 @@ pub fn printVersion(allocator: std.mem.Allocator, package_json_path: []const u8) logNewLine(); } +/// Structure to hold git information +pub const GitInfo = struct { + branch: []const u8, + commit: []const u8, +}; + +/// Reads the current git branch and commit hash +/// Returns null if not in a git repository +pub fn getGitInfo(allocator: std.mem.Allocator) !?GitInfo { + // Read .git/HEAD to get the branch reference + const head_file = std.fs.cwd().openFile(".git/HEAD", .{}) catch |err| { + if (err == error.FileNotFound) { + return null; // Not a git repository + } + return err; + }; + defer head_file.close(); + + const head_content = try head_file.readToEndAlloc(allocator, 1024); + defer allocator.free(head_content); + + // Parse the ref from HEAD (format: "ref: refs/heads/branch-name\n") + const head_trimmed = std.mem.trim(u8, head_content, " \n\r\t"); + + var branch: []const u8 = "unknown"; + var ref_path_raw: []const u8 = undefined; + + if (std.mem.startsWith(u8, head_trimmed, "ref: ")) { + ref_path_raw = head_trimmed[5..]; // Skip "ref: " prefix + + // Extract branch name from refs/heads/branch-name + if (std.mem.startsWith(u8, ref_path_raw, "refs/heads/")) { + branch = ref_path_raw[11..]; // Skip "refs/heads/" + } + + // Build the full path to the ref file (.git/refs/heads/branch-name) + const ref_path = try std.fmt.allocPrint(allocator, ".git/{s}", .{ref_path_raw}); + defer allocator.free(ref_path); + + // Read the commit hash from the ref file + const ref_file = std.fs.cwd().openFile(ref_path, .{}) catch |err| { + if (err == error.FileNotFound) { + return null; // Ref file not found + } + return err; + }; + defer ref_file.close(); + + const commit_content = try ref_file.readToEndAlloc(allocator, 1024); + defer allocator.free(commit_content); + + const commit = std.mem.trim(u8, commit_content, " \n\r\t"); + + return GitInfo{ + .branch = try allocator.dupe(u8, branch), + .commit = try allocator.dupe(u8, commit), + }; + } else { + // Detached HEAD state - HEAD contains the commit hash directly + return GitInfo{ + .branch = try allocator.dupe(u8, "HEAD"), + .commit = try allocator.dupe(u8, head_trimmed), + }; + } +} + fn writeTypescriptHeader(writer: anytype) !void { try writer.writeAll("/**\n"); try writer.writeAll(" * Generated by script 🍺\n"); @@ -52,10 +118,21 @@ fn writeTypescriptHeader(writer: anytype) !void { } /// Writes the VERSION_INFO export statement to the output -fn writeVersionInfo(writer: anytype, version: []const u8, date_str: []const u8) !void { +fn writeVersionInfo(writer: anytype, version: []const u8, date_str: []const u8, git_info: ?GitInfo) !void { try writer.print("export const VERSION_INFO = {{\n", .{}); try writer.print(" version: \"{s}\",\n", .{version}); - try writer.print(" date: \"{s}\"\n", .{date_str}); + try writer.print(" date: \"{s}\"", .{date_str}); + + if (git_info) |info| { + try writer.print(",\n", .{}); + try writer.print(" git: {{\n", .{}); + try writer.print(" branch: \"{s}\",\n", .{info.branch}); + try writer.print(" commit: \"{s}\"\n", .{info.commit}); + try writer.print(" }}\n", .{}); + } else { + try writer.print("\n", .{}); + } + try writer.print("}};\n", .{}); } @@ -115,11 +192,22 @@ pub fn generateVersionInfo(allocator: std.mem.Allocator, package_json_path: []co log(Color.BLUE, "📦", "Version: {s}", .{version}); logNewLine(); - log(Color.MAGENTA, "⏰", "Generating timestamp...", .{}); + log(Color.CYAN, "⏰", "Generating timestamp...", .{}); const millis = std.time.milliTimestamp(); var date_buffer: [30]u8 = undefined; const date_str = try formatTimestampISO8601(&date_buffer, millis); - log(Color.MAGENTA, "📅", "Date: {s}", .{date_str}); + log(Color.CYAN, "📅", "Date: {s}", .{date_str}); + + logNewLine(); + log(Color.BRIGHT_MAGENTA, "🌿", "Reading git info...", .{}); + const git_info = try getGitInfo(allocator); + + if (git_info) |info| { + log(Color.BRIGHT_MAGENTA, "📍", "Branch: {s}", .{info.branch}); + log(Color.BRIGHT_MAGENTA, "🔖", "Commit: {s}", .{info.commit}); + } else { + log(Color.YELLOW, "⚠️ ", "Not a git repository (git info skipped)", .{}); + } logNewLine(); log(Color.BRIGHT_GREEN, "✍️ ", "Writing to {s}...", .{output_path}); @@ -137,13 +225,19 @@ pub fn generateVersionInfo(allocator: std.mem.Allocator, package_json_path: []co const writer = &stdout_writer.interface; try writeTypescriptHeader(writer); - try writeVersionInfo(writer, version, date_str); + try writeVersionInfo(writer, version, date_str, git_info); try writer.flush(); + // Free git_info memory after writing + if (git_info) |info| { + allocator.free(info.branch); + allocator.free(info.commit); + } + const end_time = std.time.milliTimestamp(); const duration_ms = end_time - start_time; - log(Color.BRIGHT_GREEN, "✅", "Successfully generated {s}!", .{output_path}); + log(Color.BRIGHT_GREEN, "✅", "Successfully generated {s}", .{output_path}); logNewLine(); log(Color.YELLOW, "⏱️ ", "Duration: {d}ms", .{duration_ms}); logNewLine();