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 {...@@ -778,7 +778,7 @@ pub const ReadableResource = struct {
778 .tar => try unpackTarball(allocator, prog_reader.reader(), tmp_directory.handle, dep_location_tok, report),778 .tar => try unpackTarball(allocator, prog_reader.reader(), tmp_directory.handle, dep_location_tok, report),
779 .@"tar.gz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, dep_location_tok, report, std.compress.gzip),779 .@"tar.gz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, dep_location_tok, report, std.compress.gzip),
780 .@"tar.xz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, dep_location_tok, report, std.compress.xz),780 .@"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),
782 }782 }
783 } else {783 } else {
784 // Recursive directory copy.784 // Recursive directory copy.
...@@ -1220,6 +1220,8 @@ fn unpackGitPack(...@@ -1220,6 +1220,8 @@ fn unpackGitPack(
1220 reader: anytype,1220 reader: anytype,
1221 want_oid: git.Oid,1221 want_oid: git.Oid,
1222 out_dir: fs.Dir,1222 out_dir: fs.Dir,
1223 dep_location_tok: std.zig.Ast.TokenIndex,
1224 report: Report,
1223) !void {1225) !void {
1224 // The .git directory is used to store the packfile and associated index, but1226 // The .git directory is used to store the packfile and associated index, but
1225 // we do not attempt to replicate the exact structure of a real .git1227 // we do not attempt to replicate the exact structure of a real .git
...@@ -1251,7 +1253,32 @@ fn unpackGitPack(...@@ -1251,7 +1253,32 @@ fn unpackGitPack(
1251 checkout_prog_node.activate();1253 checkout_prog_node.activate();
1252 var repository = try git.Repository.init(gpa, pack_file, index_file);1254 var repository = try git.Repository.init(gpa, pack_file, index_file);
1253 defer repository.deinit();1255 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 }
1255 }1282 }
1256 }1283 }
12571284
src/git.zig+50-3
...@@ -38,6 +38,32 @@ test parseOid {...@@ -38,6 +38,32 @@ test parseOid {
38 try testing.expectError(error.InvalidOid, parseOid("HEAD"));38 try testing.expectError(error.InvalidOid, parseOid("HEAD"));
39}39}
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
41pub const Repository = struct {67pub const Repository = struct {
42 odb: Odb,68 odb: Odb,
4369
...@@ -55,6 +81,7 @@ pub const Repository = struct {...@@ -55,6 +81,7 @@ pub const Repository = struct {
55 repository: *Repository,81 repository: *Repository,
56 worktree: std.fs.Dir,82 worktree: std.fs.Dir,
57 commit_oid: Oid,83 commit_oid: Oid,
84 diagnostics: *Diagnostics,
58 ) !void {85 ) !void {
59 try repository.odb.seekOid(commit_oid);86 try repository.odb.seekOid(commit_oid);
60 const tree_oid = tree_oid: {87 const tree_oid = tree_oid: {
...@@ -62,7 +89,7 @@ pub const Repository = struct {...@@ -62,7 +89,7 @@ pub const Repository = struct {
62 if (commit_object.type != .commit) return error.NotACommit;89 if (commit_object.type != .commit) return error.NotACommit;
63 break :tree_oid try getCommitTree(commit_object.data);90 break :tree_oid try getCommitTree(commit_object.data);
64 };91 };
65 try repository.checkoutTree(worktree, tree_oid);92 try repository.checkoutTree(worktree, tree_oid, "", diagnostics);
66 }93 }
6794
68 /// Checks out the tree at `tree_oid` to `worktree`.95 /// Checks out the tree at `tree_oid` to `worktree`.
...@@ -70,6 +97,8 @@ pub const Repository = struct {...@@ -70,6 +97,8 @@ pub const Repository = struct {
70 repository: *Repository,97 repository: *Repository,
71 dir: std.fs.Dir,98 dir: std.fs.Dir,
72 tree_oid: Oid,99 tree_oid: Oid,
100 current_path: []const u8,
101 diagnostics: *Diagnostics,
73 ) !void {102 ) !void {
74 try repository.odb.seekOid(tree_oid);103 try repository.odb.seekOid(tree_oid);
75 const tree_object = try repository.odb.readObject();104 const tree_object = try repository.odb.readObject();
...@@ -87,7 +116,9 @@ pub const Repository = struct {...@@ -87,7 +116,9 @@ pub const Repository = struct {
87 try dir.makeDir(entry.name);116 try dir.makeDir(entry.name);
88 var subdir = try dir.openDir(entry.name, .{});117 var subdir = try dir.openDir(entry.name, .{});
89 defer subdir.close();118 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);
91 },122 },
92 .file => {123 .file => {
93 var file = try dir.createFile(entry.name, .{});124 var file = try dir.createFile(entry.name, .{});
...@@ -98,7 +129,23 @@ pub const Repository = struct {...@@ -98,7 +129,23 @@ pub const Repository = struct {
98 try file.writeAll(file_object.data);129 try file.writeAll(file_object.data);
99 try file.sync();130 try file.sync();
100 },131 },
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 },
102 .gitlink => {149 .gitlink => {
103 // Consistent with git archive behavior, create the directory but150 // Consistent with git archive behavior, create the directory but
104 // do nothing else151 // do nothing else