Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 100 additions & 6 deletions src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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", .{});
}

Expand Down Expand Up @@ -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});
Expand All @@ -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();
Expand Down