Skip to content

Commit

Permalink
std.progress cli (#62)
Browse files Browse the repository at this point in the history
* wip: add minisign

* fix: minisign impl

* wip: minisign

* wip: add minisign

* wip: minisign impl

* remove logs

* remove logs

* remove logs

* chore: update README.md

* wip: add std.progress

* feat: improved std.progress implementation

* fix: install master version

* chore: zig fmt
  • Loading branch information
hendriknielaender authored Nov 27, 2024
1 parent 96f8e23 commit 71e90c5
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 81 deletions.
4 changes: 3 additions & 1 deletion src/alias.zig
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ fn verify_zig_version(expected_version: []const u8) !void {
const actual_version = try util_data.get_current_version(allocator, false);
defer allocator.free(actual_version);

if (!std.mem.eql(u8, expected_version, actual_version)) {
if (std.mem.eql(u8, expected_version, "master")) {
std.debug.print("Now using Zig version {s}\n", .{actual_version});
} else if (!std.mem.eql(u8, expected_version, actual_version)) {
std.debug.print("Expected Zig version {s}, but currently using {s}. Please check.\n", .{ expected_version, actual_version });
} else {
std.debug.print("Now using Zig version {s}\n", .{expected_version});
Expand Down
12 changes: 5 additions & 7 deletions src/command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const command_opts = [_]CommandOption{
};

/// Parse and handle commands
pub fn handle_command(params: []const []const u8) !void {
pub fn handle_command(params: []const []const u8, root_node: std.Progress.Node) !void {
const command: CommandData = blk: {
if (params.len < 2) break :blk CommandData{};

Expand Down Expand Up @@ -90,7 +90,7 @@ pub fn handle_command(params: []const []const u8) !void {

switch (command.cmd) {
.List => try handle_list(command.param),
.Install => try install_version(command.subcmd, command.param),
.Install => try install_version(command.subcmd, command.param, root_node),
.Use => try use_version(command.subcmd, command.param),
.Remove => try remove_version(command.subcmd, command.param),
.Version => try get_version(),
Expand Down Expand Up @@ -182,7 +182,7 @@ fn handle_list(param: ?[]const u8) !void {
}
}

fn install_version(subcmd: ?[]const u8, param: ?[]const u8) !void {
fn install_version(subcmd: ?[]const u8, param: ?[]const u8, root_node: std.Progress.Node) !void {
if (subcmd) |scmd| {
var is_zls: bool = undefined;

Expand All @@ -200,12 +200,10 @@ fn install_version(subcmd: ?[]const u8, param: ?[]const u8) !void {
return;
};

try install.install(version, is_zls);
try install.install(version, is_zls, root_node);
} else if (param) |version| {
// set zig version
try install.install(version, false);
// set zls version
//try install.install(version, true);
try install.install(version, false, root_node);
} else {
std.debug.print("Please specify a version to install: 'install zig/zls <version>' or 'install <version>'.\n", .{});
}
Expand Down
120 changes: 81 additions & 39 deletions src/install.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const util_extract = @import("util/extract.zig");
const util_tool = @import("util/tool.zig");
const util_http = @import("util/http.zig");
const util_minisign = @import("util/minisign.zig");
const Progress = std.Progress;

const Version = struct {
name: []const u8,
Expand All @@ -19,16 +20,16 @@ const Version = struct {
};

/// try install specified version
pub fn install(version: []const u8, is_zls: bool) !void {
pub fn install(version: []const u8, is_zls: bool, root_node: Progress.Node) !void {
if (is_zls) {
try install_zls(version);
} else {
try install_zig(version);
try install_zig(version, root_node);
}
}

/// Try to install the specified version of zig
fn install_zig(version: []const u8) !void {
fn install_zig(version: []const u8, root_node: Progress.Node) !void {
var allocator = util_data.get_allocator();

const platform_str = try util_arch.platform_str(.{
Expand All @@ -42,85 +43,99 @@ fn install_zig(version: []const u8) !void {

const arena_allocator = arena.allocator();

// Get version path
var items_done: usize = 0;

// Step 1: Get version path
const version_path = try util_data.get_zvm_zig_version(arena_allocator);
// Get extract path
items_done += 1;
root_node.setCompletedItems(items_done);

// Step 2: Get extract path
const extract_path = try std.fs.path.join(arena_allocator, &.{ version_path, version });
items_done += 1;
root_node.setCompletedItems(items_done);

// Get version data
// Step 3: Get version data
const version_data: meta.Zig.VersionData = blk: {
const res = try util_http.http_get(arena_allocator, config.zig_url);
var zig_meta = try meta.Zig.init(res, arena_allocator);
const tmp_val = try zig_meta.get_version_data(version, platform_str, arena_allocator);
break :blk tmp_val orelse return error.UnsupportedVersion;
};
items_done += 1;
root_node.setCompletedItems(items_done);

if (util_tool.does_path_exist(extract_path)) {
try alias.set_version(version, false);
root_node.end();
return;
}

// Step 4: Download the tarball
const file_name = std.fs.path.basename(version_data.tarball);

const parsed_uri = std.Uri.parse(version_data.tarball) catch unreachable;

// Download the tarball
const tarball_file = try util_http.download(parsed_uri, file_name, version_data.shasum, version_data.size);
defer tarball_file.close();
// Create a child progress node for the download
const download_node = root_node.start("download zig", version_data.size);
const tarball_file = try util_http.download(parsed_uri, file_name, version_data.shasum, version_data.size, download_node);
// defer tarball_file.close();
download_node.end();
items_done += 1;

root_node.setCompletedItems(items_done);

// Derive signature URI by appending ".minisig" to the tarball URL
// Step 5: Download the signature file
var signature_uri_buffer: [1024]u8 = undefined;
const signature_uri_buf = try std.fmt.bufPrint(
&signature_uri_buffer,
"{s}.minisig",
.{version_data.tarball}, // Use the original tarball URL
.{version_data.tarball},
);

const signature_uri = try std.Uri.parse(signature_uri_buffer[0..signature_uri_buf.len]);

// Define signature file name
const signature_file_name = try std.mem.concat(
arena_allocator,
u8,
&.{ file_name, ".minisig" },
);

// Download the signature file
const minisig_file = try util_http.download(signature_uri, signature_file_name, null, null);
// Create a child progress node for the signature download
const sig_download_node = root_node.start("verifying file signature", 0);
const minisig_file = try util_http.download(signature_uri, signature_file_name, null, null, sig_download_node);
defer minisig_file.close();
// sig_download_node.end();
items_done += 1;
root_node.setCompletedItems(items_done);

// Get paths to the tarball and signature files
// Step 6: Perform Minisign Verification
const zvm_store_path = try util_data.get_zvm_path_segment(allocator, "store");
defer allocator.free(zvm_store_path);
const tarball_path = try std.fs.path.join(arena_allocator, &.{ zvm_store_path, file_name });
const sig_path = try std.fs.path.join(arena_allocator, &.{ zvm_store_path, signature_file_name });

// Perform Minisign Verification
try util_minisign.verify(
&allocator,
sig_path,
config.ZIG_MINISIGN_PUBLIC_KEY,
tarball_path,
);
items_done += 1;
root_node.setCompletedItems(items_done);

// Proceed with extraction after successful verification
const extract_node = root_node.start("extracting zig", 0);
try util_tool.try_create_path(extract_path);
const extract_dir = try std.fs.openDirAbsolute(extract_path, .{});
try util_extract.extract(extract_dir, tarball_file, if (builtin.os.tag == .windows) .zip else .tarxz, false, extract_node);
extract_node.end();
items_done += 1;
root_node.setCompletedItems(items_done);

try util_extract.extract(extract_dir, tarball_file, if (builtin.os.tag == .windows) .zip else .tarxz, false);

//TODO: not needed (macOS) still needed for unix and windows?
// const sub_path = try std.fs.path.join(arena_allocator, &.{
// extract_path, try std.mem.concat(
// arena_allocator,
// u8,
// &.{},
// ),
// });
//
//try util_tool.copy_dir(sub_path, extract_path);

// Set the version alias
try alias.set_version(version, false);

root_node.end();
}

/// Try to install the specified version of zls
Expand Down Expand Up @@ -150,41 +165,68 @@ fn install_zls(version: []const u8) !void {

const arena_allocator = arena.allocator();

// get version path
// Determine total steps
const total_steps = 4;

// Initialize progress root node
const root_node = std.Progress.start(.{
.root_name = "Installing ZLS",
.estimated_total_items = total_steps,
});

var items_done: usize = 0;

// Step 1: Get version path
const version_path = try util_data.get_zvm_zls_version(arena_allocator);
// get extract path
items_done += 1;
root_node.setCompletedItems(items_done);

// Step 2: Get extract path
const extract_path = try std.fs.path.join(arena_allocator, &.{ version_path, true_version });
items_done += 1;
root_node.setCompletedItems(items_done);

if (util_tool.does_path_exist(extract_path)) {
try alias.set_version(true_version, true);
root_node.end();
return;
}

// get version data
// Step 3: Get version data
const version_data: meta.Zls.VersionData = blk: {
const res = try util_http.http_get(arena_allocator, config.zls_url);
var zls_meta = try meta.Zls.init(res, arena_allocator);
const tmp_val = try zls_meta.get_version_data(true_version, reverse_platform_str, arena_allocator);
break :blk tmp_val orelse return error.UnsupportedVersion;
};
items_done += 1;
root_node.setCompletedItems(items_done);

// Step 4: Download the tarball
const file_name = try std.mem.concat(
arena_allocator,
u8,
&.{ "zls-", reverse_platform_str, "-", true_version, ".", config.archive_ext },
);

const parsed_uri = std.Uri.parse(version_data.tarball) catch unreachable;
const new_file = try util_http.download(parsed_uri, file_name, null, version_data.size);

// Create a child progress node for the download
const download_node = root_node.start("Downloading ZLS tarball", version_data.size);
const new_file = try util_http.download(parsed_uri, file_name, null, version_data.size, download_node);
defer new_file.close();
download_node.end();
items_done += 1;
root_node.setCompletedItems(items_done);

// Proceed with extraction
const extract_node = root_node.start("Extracting ZLS tarball", 0);
try util_tool.try_create_path(extract_path);

const extract_dir = try std.fs.openDirAbsolute(extract_path, .{});
try util_extract.extract(extract_dir, new_file, if (builtin.os.tag == .windows)
.zip
else
.tarxz, true);
try util_extract.extract(extract_dir, new_file, if (builtin.os.tag == .windows) .zip else .tarxz, true, extract_node);
extract_node.end();
items_done += 1;
root_node.setCompletedItems(items_done);

try alias.set_version(true_version, true);
}
Expand Down
8 changes: 7 additions & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ pub fn main() !void {
// this will detect the memory whether leak
defer if (gpa.deinit() == .leak) @panic("memory leaked!");

// Initialize the root progress node
const root_node = std.Progress.start(.{
.root_name = "zig installation",
.estimated_total_items = 6,
});

// init some useful data
try util_data.data_init(gpa.allocator());
// deinit some data
Expand All @@ -23,5 +29,5 @@ pub fn main() !void {
try command.handle_alias(args);

// parse the args and handle command
try command.handle_command(args);
try command.handle_command(args, root_node);
}
25 changes: 18 additions & 7 deletions src/util/extract.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,46 @@ const tool = @import("tool.zig");
const xz = std.compress.xz;
const tar = std.tar;

/// extract file to out_dir
/// Extract file to out_dir
pub fn extract(
out_dir: std.fs.Dir,
file: std.fs.File,
file_type: enum { tarxz, zip },
is_zls: bool,
root_node: std.Progress.Node,
) !void {
switch (file_type) {
.zip => try extract_zip_dir(out_dir, file),
.tarxz => try extract_tarxz_to_dir(out_dir, file, is_zls),
.zip => try extract_zip_dir(out_dir, file, root_node),
.tarxz => try extract_tarxz_to_dir(out_dir, file, is_zls, root_node),
}
}

/// extract tar.xz to dir
fn extract_tarxz_to_dir(out_dir: std.fs.Dir, file: std.fs.File, is_zls: bool) !void {
/// Extract tar.xz to dir
fn extract_tarxz_to_dir(
out_dir: std.fs.Dir,
file: std.fs.File,
is_zls: bool,
root_node: std.Progress.Node,
) !void {
var buffered_reader = std.io.bufferedReader(file.reader());

var decompressed = try xz.decompress(data.get_allocator(), buffered_reader.reader());
defer decompressed.deinit();

// Start extraction with an indeterminate progress indicator
root_node.setEstimatedTotalItems(0);

try tar.pipeToFileSystem(
out_dir,
decompressed.reader(),
.{ .mode_mode = .executable_bit_only, .strip_components = if (is_zls) 0 else 1 },
);

root_node.setCompletedItems(1);
}

/// extract zip to directory
fn extract_zip_dir(out_dir: std.fs.Dir, file: std.fs.File) !void {
/// Extract zip to directory
fn extract_zip_dir(out_dir: std.fs.Dir, file: std.fs.File, _: std.Progress.Node) !void {
var arena = std.heap.ArenaAllocator.init(data.get_allocator());
defer arena.deinit();

Expand Down
Loading

0 comments on commit 71e90c5

Please sign in to comment.