authorgravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2023-10-02 20:59:00-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-02 18:14:57-07:00
log573a13f8be24276a02761c30396fd75fc10ebdb0
treed275cd7cd2896bb107258bab64b7d59a12985036
parent21181181bf1060d5e55738651c109d7c47647633

Support symlinks for git+http(s) dependencies


2 files changed, 79 insertions(+), 5 deletions(-)

src/Package.zig+29-2
......@@ -778,7 +778,7 @@ pub const ReadableResource = struct {
778778 .tar => try unpackTarball(allocator, prog_reader.reader(), tmp_directory.handle, dep_location_tok, report),
779779 .@"tar.gz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, dep_location_tok, report, std.compress.gzip),
780780 .@"tar.xz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, dep_location_tok, report, std.compress.xz),
781 .git_pack => try unpackGitPack(allocator, &prog_reader, git.parseOid(rr.path) catch unreachable, tmp_directory.handle),
781 .git_pack => try unpackGitPack(allocator, &prog_reader, git.parseOid(rr.path) catch unreachable, tmp_directory.handle, dep_location_tok, report),
782782 }
783783 } else {
784784 // Recursive directory copy.
......@@ -1220,6 +1220,8 @@ fn unpackGitPack(
12201220 reader: anytype,
12211221 want_oid: git.Oid,
12221222 out_dir: fs.Dir,
1223 dep_location_tok: std.zig.Ast.TokenIndex,
1224 report: Report,
12231225) !void {
12241226 // The .git directory is used to store the packfile and associated index, but
12251227 // we do not attempt to replicate the exact structure of a real .git
......@@ -1251,7 +1253,32 @@ fn unpackGitPack(
12511253 checkout_prog_node.activate();
12521254 var repository = try git.Repository.init(gpa, pack_file, index_file);
12531255 defer repository.deinit();
1254 try repository.checkout(out_dir, want_oid);
1256 var diagnostics: git.Diagnostics = .{ .allocator = gpa };
1257 defer diagnostics.deinit();
1258 try repository.checkout(out_dir, want_oid, &diagnostics);
1259
1260 if (diagnostics.errors.items.len > 0) {
1261 const notes_len: u32 = @intCast(diagnostics.errors.items.len);
1262 try report.addErrorWithNotes(notes_len, .{
1263 .tok = dep_location_tok,
1264 .off = 0,
1265 .msg = "unable to unpack packfile",
1266 });
1267 const eb = report.error_bundle;
1268 const notes_start = try eb.reserveNotes(notes_len);
1269 for (diagnostics.errors.items, notes_start..) |item, note_i| {
1270 switch (item) {
1271 .unable_to_create_sym_link => |info| {
1272 eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
1273 .msg = try eb.printString("unable to create symlink from '{s}' to '{s}': {s}", .{
1274 info.file_name, info.link_name, @errorName(info.code),
1275 }),
1276 }));
1277 },
1278 }
1279 }
1280 return error.InvalidGitPack;
1281 }
12551282 }
12561283 }
12571284
src/git.zig+50-3
......@@ -38,6 +38,32 @@ test parseOid {
3838 try testing.expectError(error.InvalidOid, parseOid("HEAD"));
3939}
4040
41pub const Diagnostics = struct {
42 allocator: Allocator,
43 errors: std.ArrayListUnmanaged(Error) = .{},
44
45 pub const Error = union(enum) {
46 unable_to_create_sym_link: struct {
47 code: anyerror,
48 file_name: []const u8,
49 link_name: []const u8,
50 },
51 };
52
53 pub fn deinit(d: *Diagnostics) void {
54 for (d.errors.items) |item| {
55 switch (item) {
56 .unable_to_create_sym_link => |info| {
57 d.allocator.free(info.file_name);
58 d.allocator.free(info.link_name);
59 },
60 }
61 }
62 d.errors.deinit(d.allocator);
63 d.* = undefined;
64 }
65};
66
4167pub const Repository = struct {
4268 odb: Odb,
4369
......@@ -55,6 +81,7 @@ pub const Repository = struct {
5581 repository: *Repository,
5682 worktree: std.fs.Dir,
5783 commit_oid: Oid,
84 diagnostics: *Diagnostics,
5885 ) !void {
5986 try repository.odb.seekOid(commit_oid);
6087 const tree_oid = tree_oid: {
......@@ -62,7 +89,7 @@ pub const Repository = struct {
6289 if (commit_object.type != .commit) return error.NotACommit;
6390 break :tree_oid try getCommitTree(commit_object.data);
6491 };
65 try repository.checkoutTree(worktree, tree_oid);
92 try repository.checkoutTree(worktree, tree_oid, "", diagnostics);
6693 }
6794
6895 /// Checks out the tree at `tree_oid` to `worktree`.
......@@ -70,6 +97,8 @@ pub const Repository = struct {
7097 repository: *Repository,
7198 dir: std.fs.Dir,
7299 tree_oid: Oid,
100 current_path: []const u8,
101 diagnostics: *Diagnostics,
73102 ) !void {
74103 try repository.odb.seekOid(tree_oid);
75104 const tree_object = try repository.odb.readObject();
......@@ -87,7 +116,9 @@ pub const Repository = struct {
87116 try dir.makeDir(entry.name);
88117 var subdir = try dir.openDir(entry.name, .{});
89118 defer subdir.close();
90 try repository.checkoutTree(subdir, entry.oid);
119 const sub_path = try std.fs.path.join(repository.odb.allocator, &.{ current_path, entry.name });
120 defer repository.odb.allocator.free(sub_path);
121 try repository.checkoutTree(subdir, entry.oid, sub_path, diagnostics);
91122 },
92123 .file => {
93124 var file = try dir.createFile(entry.name, .{});
......@@ -98,7 +129,23 @@ pub const Repository = struct {
98129 try file.writeAll(file_object.data);
99130 try file.sync();
100131 },
101 .symlink => return error.SymlinkNotSupported,
132 .symlink => {
133 try repository.odb.seekOid(entry.oid);
134 var symlink_object = try repository.odb.readObject();
135 if (symlink_object.type != .blob) return error.InvalidFile;
136 const link_name = symlink_object.data;
137 dir.symLink(link_name, entry.name, .{}) catch |e| {
138 const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name });
139 errdefer diagnostics.allocator.free(file_name);
140 const link_name_dup = try diagnostics.allocator.dupe(u8, link_name);
141 errdefer diagnostics.allocator.free(link_name_dup);
142 try diagnostics.errors.append(diagnostics.allocator, .{ .unable_to_create_sym_link = .{
143 .code = e,
144 .file_name = file_name,
145 .link_name = link_name_dup,
146 } });
147 };
148 },
102149 .gitlink => {
103150 // Consistent with git archive behavior, create the directory but
104151 // do nothing else