diff options
author | 2021-10-15 19:44:53 -0700 | |
---|---|---|
committer | 2021-10-15 19:44:53 -0700 | |
commit | 10696680ff9012f7ca07ae2c1de5ed710647b4f9 (patch) | |
tree | 1732ddd3dd2a7605da8ff5f181156e9e734aaa99 /src/cli/create_command.zig | |
parent | 7e159cb5cdfc288c1aab7b0f810f5b54438b9f19 (diff) | |
download | bun-10696680ff9012f7ca07ae2c1de5ed710647b4f9.tar.gz bun-10696680ff9012f7ca07ae2c1de5ed710647b4f9.tar.zst bun-10696680ff9012f7ca07ae2c1de5ed710647b4f9.zip |
Polish
Diffstat (limited to 'src/cli/create_command.zig')
-rw-r--r-- | src/cli/create_command.zig | 654 |
1 files changed, 545 insertions, 109 deletions
diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index b70512f6f..a96d48624 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -30,10 +30,112 @@ const DotEnv = @import("../env_loader.zig"); const NPMClient = @import("../which_npm_client.zig").NPMClient; const which = @import("../which.zig").which; const clap = @import("clap"); +const Lock = @import("../lock.zig").Lock; const CopyFile = @import("../copy_file.zig"); var bun_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; +const libgit2_basename = switch (std.builtin.os.tag) { + .linux => "libgit2.so.26", + .macos => "libgit2.dylib", + .windows => "libgit2.dll", + else => "libgit2.so", +}; + +const global_libgit2_paths = switch (std.builtin.os.tag) { + .macos => if (Environment.isAarch64) + &[][:0]const u8{ + "/opt/homebrew/lib/libgit2.dylib", + "/opt/homebrew/libgit2/lib/libgit2.dylib", + "/usr/local/lib/libgit2.dylib", + "/usr/local/opt/libgit2/lib/libgit2.dylib", + } + else + &[_][:0]const u8{ + "/usr/local/lib/libgit2.dylib", + "/usr/local/opt/libgit2/lib/libgit2.dylib", + }, + + else => &[_][:0]const u8{}, +}; + +// the standard library function for this returns false when statically linking +fn getSelfExeSharedLibPaths(allocator: *std.mem.Allocator) error{OutOfMemory}![][:0]u8 { + const List = std.ArrayList([:0]u8); + const os = std.os; + const Allocator = std.mem.Allocator; + const builtin = std.builtin; + const mem = std.mem; + + switch (builtin.os.tag) { + .linux, + .freebsd, + .netbsd, + .dragonfly, + .openbsd, + => { + var paths = List.init(allocator); + errdefer { + const slice = paths.toOwnedSlice(); + for (slice) |item| { + allocator.free(item); + } + allocator.free(slice); + } + try os.dl_iterate_phdr(&paths, error{OutOfMemory}, struct { + fn callback(info: *os.dl_phdr_info, size: usize, list: *List) !void { + _ = size; + const name = info.dlpi_name orelse return; + if (name[0] == '/') { + const item = try list.allocator.dupeZ(u8, mem.spanZ(name)); + errdefer list.allocator.free(item); + try list.append(item); + } + } + }.callback); + return paths.toOwnedSlice(); + }, + .macos, .ios, .watchos, .tvos => { + var paths = List.init(allocator); + errdefer { + const slice = paths.toOwnedSlice(); + for (slice) |item| { + allocator.free(item); + } + allocator.free(slice); + } + const img_count = std.c._dyld_image_count(); + var i: u32 = 0; + while (i < img_count) : (i += 1) { + const name = std.c._dyld_get_image_name(i); + const item = try allocator.dupeZ(u8, mem.spanZ(name)); + errdefer allocator.free(item); + try paths.append(item); + } + return paths.toOwnedSlice(); + }, + // revisit if Haiku implements dl_iterat_phdr (https://dev.haiku-os.org/ticket/15743) + .haiku => { + var paths = List.init(allocator); + errdefer { + const slice = paths.toOwnedSlice(); + for (slice) |item| { + allocator.free(item); + } + allocator.free(slice); + } + + var b = "/boot/system/runtime_loader"; + const item = try allocator.dupeZ(u8, mem.spanZ(b)); + errdefer allocator.free(item); + try paths.append(item); + + return paths.toOwnedSlice(); + }, + else => @compileError("getSelfExeSharedLibPaths unimplemented for this target"), + } +} + const skip_dirs = &[_]string{ "node_modules", ".git" }; const skip_files = &[_]string{ "package-lock.json", @@ -136,6 +238,7 @@ const CreateOptions = struct { skip_install: bool = false, overwrite: bool = false, skip_git: bool = false, + verbose: bool = false, const params = [_]clap.Param(clap.Help){ clap.parseParam("--help Print this menu") catch unreachable, @@ -145,6 +248,7 @@ const CreateOptions = struct { clap.parseParam("--force Overwrite existing files") catch unreachable, clap.parseParam("--no-install Don't install node_modules") catch unreachable, clap.parseParam("--no-git Don't create a git repository") catch unreachable, + clap.parseParam("--verbose Too many logs") catch unreachable, clap.parseParam("<POS>... ") catch unreachable, }; @@ -183,17 +287,10 @@ const CreateOptions = struct { opts.npm_client = NPMClient.Tag.pnpm; } - if (args.flag("--no-install")) { - opts.skip_install = true; - } - - if (args.flag("--no-git")) { - opts.skip_git = true; - } - - if (args.flag("--force")) { - opts.overwrite = true; - } + opts.verbose = args.flag("--verbose"); + opts.skip_install = args.flag("--no-install"); + opts.skip_git = args.flag("--no-git"); + opts.overwrite = args.flag("--force"); return opts; } @@ -228,6 +325,9 @@ pub const CreateCommand = struct { home_dir_buf[outdir_path.len] = 0; var outdir_path_ = home_dir_buf[0..outdir_path.len :0]; std.fs.accessAbsoluteZ(outdir_path_, .{}) catch break :outer; + if (create_options.verbose) { + Output.prettyErrorln("reading from {s}", .{outdir_path}); + } break :brk outdir_path; } } @@ -238,6 +338,9 @@ pub const CreateCommand = struct { home_dir_buf[outdir_path.len] = 0; var outdir_path_ = home_dir_buf[0..outdir_path.len :0]; std.fs.accessAbsoluteZ(outdir_path_, .{}) catch break :outer; + if (create_options.verbose) { + Output.prettyErrorln("reading from {s}", .{outdir_path}); + } break :brk outdir_path; } @@ -248,6 +351,9 @@ pub const CreateCommand = struct { home_dir_buf[outdir_path.len] = 0; var outdir_path_ = home_dir_buf[0..outdir_path.len :0]; std.fs.accessAbsoluteZ(outdir_path_, .{}) catch break :outer; + if (create_options.verbose) { + Output.prettyErrorln("reading from {s}", .{outdir_path}); + } break :brk outdir_path; } } @@ -266,6 +372,10 @@ pub const CreateCommand = struct { // alacritty is fast if (env_loader.map.get("ALACRITTY_LOG") != null) { progress.refresh_rate_ns = std.time.ns_per_ms * 8; + + if (create_options.verbose) { + Output.prettyErrorln("your using alacritty", .{}); + } } defer { @@ -277,6 +387,10 @@ pub const CreateCommand = struct { const is_remote_template = !std.fs.path.isAbsolute(template); + if (create_options.verbose) { + Output.prettyErrorln("is_remote_template {d}", .{@boolToInt(is_remote_template)}); + } + if (is_remote_template) { var tarball_bytes: MutableString = Example.fetch(ctx, template, &progress, node) catch |err| { switch (err) { @@ -288,7 +402,8 @@ pub const CreateCommand = struct { template, }); Output.flush(); - const examples = try Example.fetchAllLocalAndRemote(ctx, &env_loader, filesystem); + + const examples = try Example.fetchAllLocalAndRemote(ctx, null, &env_loader, filesystem); Example.print(examples.items, dirname); Output.flush(); std.os.exit(1); @@ -325,6 +440,7 @@ pub const CreateCommand = struct { var archive_context = Archive.Context{ .pluckers = &pluckers, + .all_files = undefined, .overwrite_list = std.StringArrayHashMap(void).init(ctx.allocator), }; @@ -348,14 +464,15 @@ pub const CreateCommand = struct { // Thank you create-react-app for this copy (and idea) Output.prettyErrorln( - "<r><red>error<r><d>: <r>The directory <b><green>{s}<r> contains files that could conflict:", + "<r><red>error<r><d>: <r>The directory <b><blue>{s}<r>/ contains files that could conflict:\n\n", .{ std.fs.path.basename(destination), }, ); for (archive_context.overwrite_list.keys()) |path| { if (strings.endsWith(path, std.fs.path.sep_str)) { - Output.prettyErrorln("<r> <cyan>{s}<r>", .{path}); + Output.prettyError("<r> <blue>{s}<r>", .{path[0 .. std.math.max(path.len, 1) - 1]}); + Output.prettyErrorln(std.fs.path.sep_str, .{}); } else { Output.prettyErrorln("<r> {s}", .{path}); } @@ -369,6 +486,8 @@ pub const CreateCommand = struct { tarball_buf_list.items, destination, &archive_context, + void, + void{}, 1, false, false, @@ -416,48 +535,63 @@ pub const CreateCommand = struct { }; const Walker = @import("../walker_skippable.zig"); - var walker = try Walker.walk(template_dir, ctx.allocator, skip_files, skip_dirs); - defer walker.deinit(); + var walker_ = try Walker.walk(template_dir, ctx.allocator, skip_files, skip_dirs); + defer walker_.deinit(); var count: usize = 0; - while (try walker.next()) |entry| { - // TODO: make this not walk these folders entirely - // rather than checking each file path..... - if (entry.kind != .File) continue; - var outfile = destination_dir.createFile(entry.path, .{}) catch brk: { - if (std.fs.path.dirname(entry.path)) |entry_dirname| { - destination_dir.makePath(entry_dirname) catch {}; - } - break :brk destination_dir.createFile(entry.path, .{}) catch |err| { - node.end(); + const FileCopier = struct { + pub fn copy( + destination_dir_: std.fs.Dir, + walker: *Walker, + node_: *std.Progress.Node, + progress_: *std.Progress, + ) !void { + while (try walker.next()) |entry| { + // TODO: make this not walk these folders entirely + // rather than checking each file path..... + if (entry.kind != .File) continue; + + var outfile = destination_dir_.createFile(entry.path, .{}) catch brk: { + if (std.fs.path.dirname(entry.path)) |entry_dirname| { + destination_dir_.makePath(entry_dirname) catch {}; + } + break :brk destination_dir_.createFile(entry.path, .{}) catch |err| { + node_.end(); - progress.refresh(); + progress_.refresh(); - Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path }); - Output.flush(); - std.os.exit(1); - }; - }; - defer outfile.close(); - defer node.completeOne(); - - var infile = try entry.dir.openFile(entry.basename, .{ .read = true }); - defer infile.close(); - CopyFile.copy(infile.handle, outfile.handle) catch { - entry.dir.copyFile(entry.basename, destination_dir, entry.path, .{}) catch |err| { - node.end(); + Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path }); + Output.flush(); + std.os.exit(1); + }; + }; + defer outfile.close(); + defer node_.completeOne(); - progress.refresh(); + var infile = try entry.dir.openFile(entry.basename, .{ .read = true }); + defer infile.close(); - Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path }); - Output.flush(); - std.os.exit(1); - }; - }; - var stat = outfile.stat() catch continue; - _ = C.fchmod(outfile.handle, stat.mode); - } + // Assumption: you only really care about making sure something that was executable is still executable + const stat = infile.stat() catch continue; + _ = C.fchmod(outfile.handle, stat.mode); + + CopyFile.copy(infile.handle, outfile.handle) catch { + entry.dir.copyFile(entry.basename, destination_dir_, entry.path, .{}) catch |err| { + node_.end(); + + progress_.refresh(); + + Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path }); + Output.flush(); + std.os.exit(1); + }; + }; + } + } + }; + + try FileCopier.copy(destination_dir, &walker_, node, &progress); package_json_file = destination_dir.openFile("package.json", .{ .read = true, .write = true }) catch |err| { node.end(); @@ -547,6 +681,8 @@ pub const CreateCommand = struct { var preinstall_tasks = std.mem.zeroes(std.ArrayListUnmanaged([]const u8)); var postinstall_tasks = std.mem.zeroes(std.ArrayListUnmanaged([]const u8)); + var has_dependencies: bool = false; + { var i: usize = 0; var property_i: usize = 0; @@ -554,6 +690,13 @@ pub const CreateCommand = struct { const property = package_json_expr.data.e_object.properties[i]; const key = property.key.?.asString(ctx.allocator).?; + has_dependencies = has_dependencies or + ((strings.eqlComptime(key, "dependencies") or + strings.eqlComptime(key, "devDependencies") or + std.mem.indexOf(u8, key, "optionalDependencies") != null or + strings.eqlComptime(key, "peerDependencies")) and + (property.value.?.data == .e_object and property.value.?.data.e_object.properties.len > 0)); + if (key.len == 0 or !strings.eqlComptime(key, "bun-create")) { package_json_expr.data.e_object.properties[property_i] = property; property_i += 1; @@ -605,6 +748,11 @@ pub const CreateCommand = struct { } } } + package_json_expr.data.e_object.properties = package_json_expr.data.e_object.properties[0..property_i]; + } + + if (create_options.verbose) { + Output.prettyErrorln("Has dependencies? {d}", .{@boolToInt(has_dependencies)}); } var package_json_writer = JSPrinter.NewFileWriter(package_json_file); @@ -615,9 +763,38 @@ pub const CreateCommand = struct { std.os.exit(1); }; + { + var parent_dir = try std.fs.openDirAbsolute(destination, .{}); + defer parent_dir.close(); + std.os.linkat(parent_dir.fd, "gitignore", parent_dir.fd, ".gitignore", 0) catch {}; + std.os.unlinkat( + parent_dir.fd, + "gitignore", + 0, + ) catch {}; + std.os.unlinkat( + parent_dir.fd, + ".npmignore", + 0, + ) catch {}; + } + const PATH = env_loader.map.get("PATH") orelse ""; var npm_client_: ?NPMClient = null; + create_options.skip_install = create_options.skip_install or !has_dependencies; + + if (!create_options.skip_git) { + if (!create_options.skip_install) { + GitHandler.spawn(destination, PATH, create_options.verbose); + } else { + if (create_options.verbose) { + create_options.skip_git = GitHandler.run(destination, PATH, true) catch false; + } else { + create_options.skip_git = GitHandler.run(destination, PATH, false) catch false; + } + } + } if (!create_options.skip_install) { if (env_loader.map.get("NPM_CLIENT")) |npm_client_bin| { @@ -694,39 +871,8 @@ pub const CreateCommand = struct { } } - { - var parent_dir = try std.fs.openDirAbsolute(destination, .{}); - std.os.linkat(parent_dir.fd, "gitignore", parent_dir.fd, ".gitignore", 0) catch {}; - std.os.unlinkat( - parent_dir.fd, - "gitignore", - 0, - ) catch {}; - parent_dir.close(); - } - - if (!create_options.skip_git) { - if (which(&bun_path_buf, PATH, destination, "git")) |git| { - const git_commands = .{ - &[_]string{ std.mem.span(git), "init", "--quiet" }, - &[_]string{ std.mem.span(git), "add", "-A", destination, "--ignore-errors" }, - &[_]string{ std.mem.span(git), "commit", "-am", "\"Initial Commit\"", "--quiet" }, - }; - // same names, just comptime known values - - inline for (comptime std.meta.fieldNames(@TypeOf(Commands))) |command_field| { - const command: []const string = @field(git_commands, command_field); - var process = try std.ChildProcess.init(command, ctx.allocator); - process.cwd = destination; - process.stdin_behavior = .Inherit; - process.stdout_behavior = .Inherit; - process.stderr_behavior = .Inherit; - defer process.deinit(); - - var term = try process.spawnAndWait(); - _ = process.kill() catch undefined; - } - } + if (!create_options.skip_install and !create_options.skip_git) { + create_options.skip_git = !GitHandler.wait(); } Output.printError("\n", .{}); @@ -734,11 +880,13 @@ pub const CreateCommand = struct { Output.prettyErrorln(" <r><d>bun create {s}<r>", .{template}); Output.flush(); - Output.pretty( - \\ - \\<r><d>-----<r> - \\ - , .{}); + if (!create_options.skip_install) { + Output.pretty( + \\ + \\<r><d>-----<r> + \\ + , .{}); + } if (!create_options.skip_git and !create_options.skip_install) { Output.pretty( @@ -800,7 +948,7 @@ const PackageDownloadThread = struct { std.Thread.Futex.wake(&this.done, 1); } - pub fn spawn(allocator: *std.mem.Allocator, tarball_url: string) !*PackageDownloadThread { + pub fn spawn(allocator: *std.mem.Allocator, tarball_url: string, progress_node: *std.Progress.Node) !*PackageDownloadThread { var download = try allocator.create(PackageDownloadThread); download.* = PackageDownloadThread{ .allocator = allocator, @@ -811,6 +959,10 @@ const PackageDownloadThread = struct { .thread = undefined, }; + if (Output.enable_ansi_colors) { + download.client.progress_node = progress_node; + } + download.thread = try std.Thread.spawn(.{}, threadHandler, .{download}); return download; @@ -853,8 +1005,9 @@ pub const Example = struct { } } - pub fn fetchAllLocalAndRemote(ctx: Command.Context, env_loader: *DotEnv.Loader, filesystem: *fs.FileSystem) !std.ArrayList(Example) { - const remote_examples = try Example.fetchAll(ctx); + pub fn fetchAllLocalAndRemote(ctx: Command.Context, node: ?*std.Progress.Node, env_loader: *DotEnv.Loader, filesystem: *fs.FileSystem) !std.ArrayList(Example) { + const remote_examples = try Example.fetchAll(ctx, node); + if (node) |node_| node_.end(); var examples = std.ArrayList(Example).fromOwnedSlice(ctx.allocator, remote_examples); { @@ -936,6 +1089,7 @@ pub const Example = struct { url = URL.parse(try std.fmt.bufPrint(&url_buf, "https://registry.npmjs.org/@bun-examples/{s}/latest", .{name})); client = HTTPClient.init(ctx.allocator, .GET, url, .{}, ""); client.timeout = timeout; + client.progress_node = progress; var response = try client.send("", &mutable); switch (response.status_code) { @@ -1007,23 +1161,11 @@ pub const Example = struct { progress.name = "Downloading tarball"; refresher.refresh(); - var thread: *PackageDownloadThread = try PackageDownloadThread.spawn(ctx.allocator, tarball_url); - - std.Thread.Futex.wait(&thread.done, 1, std.time.ns_per_ms * 100) catch {}; - - progress.setEstimatedTotalItems(thread.client.body_size); - progress.setCompletedItems(thread.client.read_count); + var thread: *PackageDownloadThread = try PackageDownloadThread.spawn(ctx.allocator, tarball_url, progress); refresher.maybeRefresh(); - if (thread.done.load(.Acquire) == 0) { - while (true) { - std.Thread.Futex.wait(&thread.done, 1, std.time.ns_per_ms * 100) catch {}; - progress.setEstimatedTotalItems(thread.client.body_size); - progress.setCompletedItems(thread.client.read_count); - refresher.maybeRefresh(); - if (thread.done.load(.Acquire) == 1) { - break; - } - } + + while (thread.done.load(.Acquire) == 0) { + std.Thread.Futex.wait(&thread.done, 1, std.time.ns_per_ms * 10000) catch {}; } refresher.maybeRefresh(); @@ -1042,10 +1184,15 @@ pub const Example = struct { return thread.buffer; } - pub fn fetchAll(ctx: Command.Context) ![]Example { + pub fn fetchAll(ctx: Command.Context, progress_node: ?*std.Progress.Node) ![]Example { url = URL.parse(examples_url); client = HTTPClient.init(ctx.allocator, .GET, url, .{}, ""); client.timeout = timeout; + + if (Output.enable_ansi_colors) { + client.progress_node = progress_node; + } + var mutable: MutableString = try MutableString.init(ctx.allocator, 1024); var response = client.send("", &mutable) catch |err| { switch (err) { @@ -1139,9 +1286,13 @@ pub const CreateListExamplesCommand = struct { env_loader.loadProcess(); const time = std.time.nanoTimestamp(); - const examples = try Example.fetchAllLocalAndRemote(ctx, &env_loader, filesystem); + var progress = std.Progress{}; + var node = try progress.start("Fetching manifest", 0); + progress.refresh(); + + const examples = try Example.fetchAllLocalAndRemote(ctx, node, &env_loader, filesystem); Output.printStartEnd(time, std.time.nanoTimestamp()); - Output.prettyln(" <d>Fetched examples<r>", .{}); + Output.prettyln(" <d>Fetched manifest<r>", .{}); Output.prettyln("Welcome to Bun! Create a new project by pasting any of the following:\n\n", .{}); Output.flush(); @@ -1162,3 +1313,288 @@ pub const CreateListExamplesCommand = struct { Output.flush(); } }; + +const GitHandler = struct { + var success: std.atomic.Atomic(u32) = undefined; + var thread: std.Thread = undefined; + pub fn spawn( + destination: string, + PATH: string, + verbose: bool, + ) void { + success = std.atomic.Atomic(u32).init(0); + + thread = std.Thread.spawn(.{}, spawnThread, .{ destination, PATH, verbose }) catch |err| { + Output.prettyErrorln("<r><red>{s}<r>", .{@errorName(err)}); + Output.flush(); + std.os.exit(1); + }; + } + + fn spawnThread( + destination: string, + PATH: string, + verbose: bool, + ) void { + Output.Source.configureThread(); + std.Thread.setName(thread, "git") catch {}; + defer Output.flush(); + const outcome = if (verbose) + run(destination, PATH, true) catch false + else + run(destination, PATH, false) catch false; + + @fence(.Acquire); + success.store( + if (outcome) + 1 + else + 2, + .Release, + ); + std.Thread.Futex.wake(&success, 1); + } + + pub fn wait() bool { + @fence(.Release); + + while (success.load(.Acquire) == 0) { + std.Thread.Futex.wait(&success, 0, 1000) catch continue; + } + + const outcome = success.load(.Acquire) == 1; + thread.join(); + return outcome; + } + + pub fn run( + destination: string, + PATH: string, + comptime verbose: bool, + ) !bool { + const git_start = std.time.nanoTimestamp(); + + // This feature flag is disabled. + // using libgit2 is slower than the CLI. + // [481.00ms] git + // [89.00ms] git + // if (comptime FeatureFlags.use_libgit2) { + // try_to_use_libgit2: { + // if (comptime Environment.isWindows) @compileError("TODO win32"); + + // // since we link libc, it always goes through dlopen unless windows + // const DlDynlib = std.DynLib; + // var libgit2_dylib: std.DynLib = brk: { + // for (global_libgit2_paths) |lib_path| { + // break :brk DlDynlib.openZ(lib_path) catch continue; + // } + + // var lib_paths = getSelfExeSharedLibPaths(default_allocator) catch break :try_to_use_libgit2; + + // // this is a list of every dynamically loaded library on the user's computer? + // for (lib_paths) |lib_path| { + // const basename = std.fs.path.basename(std.mem.span(lib_path)); + // if (basename.len == (comptime libgit2_basename.len) and + // std.hash.Wyhash.hash(10, basename) == comptime std.hash.Wyhash.hash(10, libgit2_basename)) + // { + // break :brk DlDynlib.openZ(lib_path) catch continue; + // } + // } + // break :try_to_use_libgit2; + // }; + + // const libgit2 = @import("../deps/libgit2.zig"); + // var repo: ?*libgit2.git_repository = null; + // // https://libgit2.org/docs/examples/init/ + // // Note: git_threads_init was renamed to git_libgit2_init + // // https://mail.gnome.org/archives/commits-list/2015-January/msg03703.html + // // ...in 2015 and that doc is still out of date + + // const git_repository_init = libgit2_dylib.lookup(libgit2.git_repository_init, "git_repository_init") orelse break :try_to_use_libgit2; + // const git_repository_free = libgit2_dylib.lookup(libgit2.git_repository_free, "git_repository_free") orelse break :try_to_use_libgit2; + // const git_signature_free = libgit2_dylib.lookup(libgit2.git_signature_free, "git_signature_free") orelse break :try_to_use_libgit2; + // const git_signature_default = libgit2_dylib.lookup(libgit2.git_signature_default, "git_signature_default") orelse break :try_to_use_libgit2; + // const git_repository_index = libgit2_dylib.lookup(libgit2.git_repository_index, "git_repository_index") orelse break :try_to_use_libgit2; + // const git_index_read_tree = libgit2_dylib.lookup(libgit2.git_index_read_tree, "git_index_read_tree") orelse break :try_to_use_libgit2; + // const git_index_write_tree = libgit2_dylib.lookup(libgit2.git_index_write_tree, "git_index_write_tree") orelse break :try_to_use_libgit2; + // const git_index_write = libgit2_dylib.lookup(libgit2.git_index_write, "git_index_write") orelse break :try_to_use_libgit2; + + // const git_index_add_all = libgit2_dylib.lookup(libgit2.git_index_add_all, "git_index_add_all") orelse break :try_to_use_libgit2; + // const git_tree_lookup = libgit2_dylib.lookup(libgit2.git_tree_lookup, "git_tree_lookup") orelse break :try_to_use_libgit2; + // const git_commit_create_v = libgit2_dylib.lookup(libgit2.git_commit_create_v, "git_commit_create_v") orelse break :try_to_use_libgit2; + // const git_index_free = libgit2_dylib.lookup(libgit2.git_index_free, "git_index_free") orelse break :try_to_use_libgit2; + // const git_tree_free = libgit2_dylib.lookup(libgit2.git_tree_free, "git_tree_free") orelse break :try_to_use_libgit2; + // const git_index_add_bypath = libgit2_dylib.lookup(libgit2.git_index_add_bypath, "git_index_add_bypath") orelse break :try_to_use_libgit2; + // const git_status_should_ignore = libgit2_dylib.lookup(libgit2.git_status_should_ignore, "git_status_should_ignore") orelse break :try_to_use_libgit2; + + // var sig: ?*libgit2.git_signature = null; + // // var destination_z = destination.ptr[0..destination.len :0]; + // // const original_cwd = std.os.getcwd(&bun_path_buf) catch break :try_to_use_libgit2; + // // bun_path_buf[original_cwd.len] = 0; + // // std.os.chdirZ(destination_z) catch {}; + // // var origin_z = bun_path_buf[0..original_cwd.len :0]; + // // defer std.os.chdirZ(origin_z) catch {}; + + // if (libgit2_dylib.lookup(libgit2.git_libgit2_init, "git_libgit2_init")) |git_libgit2_init| { + // std.debug.assert(git_libgit2_init() > -1); + // } + + // var rc: c_int = 0; + // rc = git_repository_init(&repo, destination.ptr, 0); + // if (rc < 0) break :try_to_use_libgit2; + // var repo_ = repo orelse break :try_to_use_libgit2; + // // defer git_repository_free(repo_); + + // rc = git_signature_default(&sig, repo_); + // // this fails if they never ran git config + // // the child process will fail too, so we just skip + // if (rc < 0) { + // return false; + // } + // // defer git_signature_free(sig.?); + + // var index: ?*libgit2.git_index = null; + // var tree_id: libgit2.git_oid = undefined; + // var commit_id: libgit2.git_oid = undefined; + // var second_commit_id: libgit2.git_oid = undefined; + // var git_tree: *libgit2.git_tree = undefined; + + // rc = git_repository_index(&index, repo_); + // if (rc < 0) break :try_to_use_libgit2; + + // // // add gitignore file first + // // // technically, we should add every gitignore first but i'll leave that as a future TODO + // // if (files_list.containsAdapted(comptime std.hash.Wyhash.hash(0, "gitignore"), Archive.Context.U64Context{})) { + // // rc = git_index_add_bypath(index.?, ".gitignore"); + // // if (rc < 0) break :try_to_use_libgit2; + // // } else if (files_list.containsAdapted(comptime std.hash.Wyhash.hash(0, ".gitignore"), Archive.Context.U64Context{})) { + // // rc = git_index_add_bypath(index.?, ".gitignore"); + // // if (rc < 0) break :try_to_use_libgit2; + // // } + + // var files = files_list.values(); + + // // without git + // // [6.00ms] bun create /Users/jarred/.bun-create/react2 + + // // with git: + // // [44.00ms] bun create /Users/jarred/.bun-create/react2 + + // var strs = std.mem.zeroes(libgit2.git_strarray); + // // var star = [_]u8{ '*', 0 }; + // // var star_ptr: [*c]u8 = ☆ + // strs.strings = files.ptr; + // strs.count = files.len; + // rc = git_index_add_all(index, &strs, libgit2.GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH, null, null); + // if (rc < 0) break :try_to_use_libgit2; + + // // if (comptime verbose) { + // // for (files) |file| { + // // var ignored: c_int = 0; + // // rc = git_status_should_ignore(&ignored, repo_, file); + // // if (rc < 0) break :try_to_use_libgit2; + // // if (ignored > 0) { + // // Output.prettyErrorln("[libgit2]: ignored {s}", .{std.mem.sliceTo(file, 0)}); + // // continue; + // // } + + // // // this can fail for a variety of reasons + // // // if it does, better to get most of the files than stop + // // rc = git_index_add_bypath(index.?, file); + // // switch (rc) { + // // libgit2.GIT_OK => { + // // Output.prettyErrorln("[libgit2]: Added {s}", .{std.mem.sliceTo(file, 0)}); + // // }, + // // libgit2.GIT_ENOTFOUND => { + // // Output.prettyErrorln("[libgit2]: not found {s}", .{std.mem.sliceTo(file, 0)}); + // // }, + // // else => { + // // Output.prettyErrorln("[libgit2]: error {d} for {s}", .{ rc, std.mem.sliceTo(file, 0) }); + // // }, + // // } + // // } + // // } else { + // // for (files) |file| { + // // var ignored: c_int = 0; + // // rc = git_status_should_ignore(&ignored, repo_, file); + // // if (rc < 0) break :try_to_use_libgit2; + // // if (ignored > 0) continue; + + // // // this can fail for a variety of reasons + // // // if it does, better to get most of the files than stop + // // _ = git_index_add_bypath(index.?, file); + // // } + // // } + + // // if you don't call git_index_write, git status will look very strange. + // rc = git_index_write(index.?); + // if (rc < 0) break :try_to_use_libgit2; + + // rc = git_index_write_tree(&tree_id, index); + // if (rc < 0) break :try_to_use_libgit2; + + // rc = git_tree_lookup(&git_tree, repo_, &tree_id); + // if (rc < 0) break :try_to_use_libgit2; + // // defer git_tree_free(git_tree); + + // rc = git_commit_create_v( + // &commit_id, + // repo_, + // "HEAD", + // sig.?, + // sig.?, + // null, + // "Initial commit (via bun create)", + // git_tree, + // 0, + // ); + // if (rc < 0) break :try_to_use_libgit2; + + // if (comptime verbose) { + // Output.prettyErrorln("git backend: libgit2", .{}); + // } + + // Output.prettyError("\n", .{}); + // Output.printStartEnd(git_start, std.time.nanoTimestamp()); + // Output.prettyError(" <d>git<r>\n", .{}); + + // // success! + // return true; + // } + // } + + if (which(&bun_path_buf, PATH, destination, "git")) |git| { + const git_commands = .{ + &[_]string{ std.mem.span(git), "init", "--quiet" }, + &[_]string{ std.mem.span(git), "add", destination, "--ignore-errors" }, + &[_]string{ std.mem.span(git), "commit", "-am", "\"Initial commit (via bun create)\"", "--quiet" }, + }; + + if (comptime verbose) { + Output.prettyErrorln("git backend: {s}", .{git}); + } + + // same names, just comptime known values + + inline for (comptime std.meta.fieldNames(@TypeOf(Commands))) |command_field| { + const command: []const string = @field(git_commands, command_field); + var process = try std.ChildProcess.init(command, default_allocator); + process.cwd = destination; + process.stdin_behavior = .Inherit; + process.stdout_behavior = .Inherit; + process.stderr_behavior = .Inherit; + defer process.deinit(); + + var term = try process.spawnAndWait(); + _ = process.kill() catch undefined; + } + + Output.prettyError("\n", .{}); + Output.printStartEnd(git_start, std.time.nanoTimestamp()); + Output.prettyError(" <d>git<r>\n", .{}); + return true; + } + + return false; + } +}; |