authorgravatar for christofer@nolander.meChristofer Nolander <christofer@nolander.me> 2024-05-11 23:19:35+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-05-11 14:19:35-07:00
log8f6b1f2c38422168d29a6dd6fab61f337430e74e
treebf959e2178d4374690b6590322dccb93c168c958
parent86d8688c7f14d30c66b2cb4b55826ab15b39ad55
signaturebadge-check Signed by PGP key B5690EEEBB952194

`zig fetch`: resolve branch/tag names to commit SHA (#19941)

* Revert "Revert "Merge pull request #19349 from nolanderc/save-commit"" This reverts commit 6ca4ed5948d8eaab28fc5e3706aeb1b113a210af. * update to new URI changes, rework `--save` type * initialize `latest_commit` to null everywhere

2 files changed, 63 insertions(+), 9 deletions(-)

src/Package/Fetch.zig+11-1
...@@ -44,6 +44,8 @@ omit_missing_hash_error: bool,...@@ -44,6 +44,8 @@ omit_missing_hash_error: bool,
44/// which specifies inclusion rules. This is intended to be true for the first44/// which specifies inclusion rules. This is intended to be true for the first
45/// fetch task and false for the recursive dependencies.45/// fetch task and false for the recursive dependencies.
46allow_missing_paths_field: bool,46allow_missing_paths_field: bool,
47/// If true and URL points to a Git repository, will use the latest commit.
48use_latest_commit: bool,
4749
48// Above this are fields provided as inputs to `run`.50// Above this are fields provided as inputs to `run`.
49// Below this are fields populated by `run`.51// Below this are fields populated by `run`.
...@@ -59,6 +61,10 @@ actual_hash: Manifest.Digest,...@@ -59,6 +61,10 @@ actual_hash: Manifest.Digest,
59has_build_zig: bool,61has_build_zig: bool,
60/// Indicates whether the task aborted due to an out-of-memory condition.62/// Indicates whether the task aborted due to an out-of-memory condition.
61oom_flag: bool,63oom_flag: bool,
64/// If `use_latest_commit` was true, this will be set to the commit that was used.
65/// If the resource pointed to by the location is not a Git-repository, this
66/// will be left unchanged.
67latest_commit: ?git.Oid,
6268
63// This field is used by the CLI only, untouched by this file.69// This field is used by the CLI only, untouched by this file.
6470
...@@ -699,6 +705,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {...@@ -699,6 +705,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
699 .job_queue = f.job_queue,705 .job_queue = f.job_queue,
700 .omit_missing_hash_error = false,706 .omit_missing_hash_error = false,
701 .allow_missing_paths_field = true,707 .allow_missing_paths_field = true,
708 .use_latest_commit = false,
702709
703 .package_root = undefined,710 .package_root = undefined,
704 .error_bundle = undefined,711 .error_bundle = undefined,
...@@ -707,6 +714,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {...@@ -707,6 +714,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
707 .actual_hash = undefined,714 .actual_hash = undefined,
708 .has_build_zig = false,715 .has_build_zig = false,
709 .oom_flag = false,716 .oom_flag = false,
717 .latest_commit = null,
710718
711 .module = null,719 .module = null,
712 };720 };
...@@ -994,7 +1002,9 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re...@@ -994,7 +1002,9 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re
994 }1002 }
995 return f.fail(f.location_tok, try eb.printString("ref not found: {s}", .{want_ref}));1003 return f.fail(f.location_tok, try eb.printString("ref not found: {s}", .{want_ref}));
996 };1004 };
997 if (uri.fragment == null) {1005 if (f.use_latest_commit) {
1006 f.latest_commit = want_oid;
1007 } else if (uri.fragment == null) {
998 const notes_len = 1;1008 const notes_len = 1;
999 try eb.addRootErrorMessage(.{1009 try eb.addRootErrorMessage(.{
1000 .msg = try eb.addString("url field is missing an explicit ref"),1010 .msg = try eb.addString("url field is missing an explicit ref"),
src/main.zig+52-8
...@@ -5097,6 +5097,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5097,6 +5097,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5097 .job_queue = &job_queue,5097 .job_queue = &job_queue,
5098 .omit_missing_hash_error = true,5098 .omit_missing_hash_error = true,
5099 .allow_missing_paths_field = false,5099 .allow_missing_paths_field = false,
5100 .use_latest_commit = false,
51005101
5101 .package_root = undefined,5102 .package_root = undefined,
5102 .error_bundle = undefined,5103 .error_bundle = undefined,
...@@ -5105,6 +5106,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5105,6 +5106,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5105 .actual_hash = undefined,5106 .actual_hash = undefined,
5106 .has_build_zig = true,5107 .has_build_zig = true,
5107 .oom_flag = false,5108 .oom_flag = false,
5109 .latest_commit = null,
51085110
5109 .module = build_mod,5111 .module = build_mod,
5110 };5112 };
...@@ -6894,6 +6896,8 @@ const usage_fetch =...@@ -6894,6 +6896,8 @@ const usage_fetch =
6894 \\ --debug-hash Print verbose hash information to stdout6896 \\ --debug-hash Print verbose hash information to stdout
6895 \\ --save Add the fetched package to build.zig.zon6897 \\ --save Add the fetched package to build.zig.zon
6896 \\ --save=[name] Add the fetched package to build.zig.zon as name6898 \\ --save=[name] Add the fetched package to build.zig.zon as name
6899 \\ --save-exact Add the fetched package to build.zig.zon, storing the URL verbatim
6900 \\ --save-exact=[name] Add the fetched package to build.zig.zon as name, storing the URL verbatim
6897 \\6901 \\
6898;6902;
68996903
...@@ -6908,7 +6912,11 @@ fn cmdFetch(...@@ -6908,7 +6912,11 @@ fn cmdFetch(
6908 var opt_path_or_url: ?[]const u8 = null;6912 var opt_path_or_url: ?[]const u8 = null;
6909 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);6913 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
6910 var debug_hash: bool = false;6914 var debug_hash: bool = false;
6911 var save: union(enum) { no, yes, name: []const u8 } = .no;6915 var save: union(enum) {
6916 no,
6917 yes: ?[]const u8,
6918 exact: ?[]const u8,
6919 } = .no;
69126920
6913 {6921 {
6914 var i: usize = 0;6922 var i: usize = 0;
...@@ -6926,9 +6934,13 @@ fn cmdFetch(...@@ -6926,9 +6934,13 @@ fn cmdFetch(
6926 } else if (mem.eql(u8, arg, "--debug-hash")) {6934 } else if (mem.eql(u8, arg, "--debug-hash")) {
6927 debug_hash = true;6935 debug_hash = true;
6928 } else if (mem.eql(u8, arg, "--save")) {6936 } else if (mem.eql(u8, arg, "--save")) {
6929 save = .yes;6937 save = .{ .yes = null };
6930 } else if (mem.startsWith(u8, arg, "--save=")) {6938 } else if (mem.startsWith(u8, arg, "--save=")) {
6931 save = .{ .name = arg["--save=".len..] };6939 save = .{ .yes = arg["--save=".len..] };
6940 } else if (mem.eql(u8, arg, "--save-exact")) {
6941 save = .{ .exact = null };
6942 } else if (mem.startsWith(u8, arg, "--save-exact=")) {
6943 save = .{ .exact = arg["--save=".len..] };
6932 } else {6944 } else {
6933 fatal("unrecognized parameter: '{s}'", .{arg});6945 fatal("unrecognized parameter: '{s}'", .{arg});
6934 }6946 }
...@@ -6988,6 +7000,7 @@ fn cmdFetch(...@@ -6988,6 +7000,7 @@ fn cmdFetch(
6988 .job_queue = &job_queue,7000 .job_queue = &job_queue,
6989 .omit_missing_hash_error = true,7001 .omit_missing_hash_error = true,
6990 .allow_missing_paths_field = false,7002 .allow_missing_paths_field = false,
7003 .use_latest_commit = true,
69917004
6992 .package_root = undefined,7005 .package_root = undefined,
6993 .error_bundle = undefined,7006 .error_bundle = undefined,
...@@ -6996,6 +7009,7 @@ fn cmdFetch(...@@ -6996,6 +7009,7 @@ fn cmdFetch(
6996 .actual_hash = undefined,7009 .actual_hash = undefined,
6997 .has_build_zig = false,7010 .has_build_zig = false,
6998 .oom_flag = false,7011 .oom_flag = false,
7012 .latest_commit = null,
69997013
7000 .module = null,7014 .module = null,
7001 };7015 };
...@@ -7022,12 +7036,12 @@ fn cmdFetch(...@@ -7022,12 +7036,12 @@ fn cmdFetch(
7022 try io.getStdOut().writeAll(hex_digest ++ "\n");7036 try io.getStdOut().writeAll(hex_digest ++ "\n");
7023 return cleanExit();7037 return cleanExit();
7024 },7038 },
7025 .yes => n: {7039 .yes, .exact => |name| name: {
7040 if (name) |n| break :name n;
7026 const fetched_manifest = fetch.manifest orelse7041 const fetched_manifest = fetch.manifest orelse
7027 fatal("unable to determine name; fetched package has no build.zig.zon file", .{});7042 fatal("unable to determine name; fetched package has no build.zig.zon file", .{});
7028 break :n fetched_manifest.name;7043 break :name fetched_manifest.name;
7029 },7044 },
7030 .name => |n| n,
7031 };7045 };
70327046
7033 const cwd_path = try process.getCwdAlloc(arena);7047 const cwd_path = try process.getCwdAlloc(arena);
...@@ -7052,13 +7066,43 @@ fn cmdFetch(...@@ -7052,13 +7066,43 @@ fn cmdFetch(
7052 var fixups: Ast.Fixups = .{};7066 var fixups: Ast.Fixups = .{};
7053 defer fixups.deinit(gpa);7067 defer fixups.deinit(gpa);
70547068
7069 var saved_path_or_url = path_or_url;
7070
7071 if (fetch.latest_commit) |*latest_commit| resolved: {
7072 const latest_commit_hex = try std.fmt.allocPrint(arena, "{}", .{std.fmt.fmtSliceHexLower(latest_commit)});
7073
7074 var uri = try std.Uri.parse(path_or_url);
7075
7076 if (uri.fragment) |fragment| {
7077 const target_ref = try fragment.toRawMaybeAlloc(arena);
7078
7079 // the refspec may already be fully resolved
7080 if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved;
7081
7082 std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex });
7083
7084 // include the original refspec in a query parameter, could be used to check for updates
7085 uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={%}", .{fragment}) };
7086 } else {
7087 std.log.info("resolved to commit {s}", .{latest_commit_hex});
7088 }
7089
7090 // replace the refspec with the resolved commit SHA
7091 uri.fragment = .{ .raw = latest_commit_hex };
7092
7093 switch (save) {
7094 .yes => saved_path_or_url = try std.fmt.allocPrint(arena, "{}", .{uri}),
7095 .no, .exact => {}, // keep the original URL
7096 }
7097 }
7098
7055 const new_node_init = try std.fmt.allocPrint(arena,7099 const new_node_init = try std.fmt.allocPrint(arena,
7056 \\.{{7100 \\.{{
7057 \\ .url = "{}",7101 \\ .url = "{}",
7058 \\ .hash = "{}",7102 \\ .hash = "{}",
7059 \\ }}7103 \\ }}
7060 , .{7104 , .{
7061 std.zig.fmtEscapes(path_or_url),7105 std.zig.fmtEscapes(saved_path_or_url),
7062 std.zig.fmtEscapes(&hex_digest),7106 std.zig.fmtEscapes(&hex_digest),
7063 });7107 });
70647108
...@@ -7078,7 +7122,7 @@ fn cmdFetch(...@@ -7078,7 +7122,7 @@ fn cmdFetch(
7078 if (dep.hash) |h| {7122 if (dep.hash) |h| {
7079 switch (dep.location) {7123 switch (dep.location) {
7080 .url => |u| {7124 .url => |u| {
7081 if (mem.eql(u8, h, &hex_digest) and mem.eql(u8, u, path_or_url)) {7125 if (mem.eql(u8, h, &hex_digest) and mem.eql(u8, u, saved_path_or_url)) {
7082 std.log.info("existing dependency named '{s}' is up-to-date", .{name});7126 std.log.info("existing dependency named '{s}' is up-to-date", .{name});
7083 process.exit(0);7127 process.exit(0);
7084 }7128 }