mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
Polish
This commit is contained in:
@@ -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 {};
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
// 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);
|
||||
};
|
||||
};
|
||||
}
|
||||
break :brk destination_dir.createFile(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);
|
||||
};
|
||||
};
|
||||
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();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user