From 2d05496121e8f4e0107a72c4fbb42abd311625af Mon Sep 17 00:00:00 2001 From: pfg Date: Mon, 1 Sep 2025 23:52:05 -0700 Subject: [PATCH 1/4] fix zls --- src/main.zig | 68 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/src/main.zig b/src/main.zig index 0884370..587b523 100644 --- a/src/main.zig +++ b/src/main.zig @@ -338,13 +338,6 @@ pub fn main() !void { const version_specifier: VersionSpecifier, const is_init = blk: { if (maybe_command) |command| { - if (std.mem.startsWith(u8, command, "-") and !std.mem.eql(u8, command, "-h") and !std.mem.eql(u8, command, "--help")) { - try std.io.getStdErr().writer().print( - "error: expected a command but got '{s}'\n", - .{command}, - ); - std.process.exit(0xff); - } if (build_options.exe == .zig and (std.mem.eql(u8, command, "init") or std.mem.eql(u8, command, "init-exe") or std.mem.eql(u8, command, "init-lib"))) { const is_help = blk_is_help: { var index: usize = cmdline_offset + 1; @@ -878,11 +871,23 @@ fn getVersionUrl( app_data_path: []const u8, semantic_version: SemanticVersion, ) !DownloadUrl { - if (build_options.exe == .zls) return DownloadUrl.initOfficial(std.fmt.allocPrint( - arena, - "https://builds.zigtools.org/zls-{s}-{}.{s}", - .{ os_arch, semantic_version, archive_ext }, - ) catch |e| oom(e)); + if (build_options.exe == .zls) { + const info_url = try std.fmt.allocPrint(arena, "https://releases.zigtools.org/v1/zls/select-version?compatibility=only-runtime&zig_version={}", .{semantic_version}); + const info_uri = try std.Uri.parse(info_url); + const info_basename = try std.fmt.allocPrint(arena, "download-index-zls-{}.json", .{semantic_version}); + const index_path = try std.fs.path.join(arena, &.{ app_data_path, info_basename }); + + try fetchFile(arena, info_url, info_uri, index_path); + const index_content = blk: { + // since we just downloaded the file, this should always succeed now + const file = try std.fs.cwd().openFile(index_path, .{}); + defer file.close(); + break :blk try file.readToEndAlloc(arena, std.math.maxInt(usize)); + }; + defer arena.free(index_content); + + return extractUrlFromZlsDownloadIndex(arena, index_path, index_content); + } if (!isMachVersion(semantic_version)) return makeOfficialUrl(arena, semantic_version); @@ -942,6 +947,45 @@ fn extractMasterVersion( ); } +fn extractUrlFromZlsDownloadIndex( + allocator: std.mem.Allocator, + index_filepath: []const u8, + download_index: []const u8, +) DownloadUrl { + const root = std.json.parseFromSlice(std.json.Value, allocator, download_index, .{ + .allocate = .alloc_if_needed, + }) catch |e| std.debug.panic( + "failed to parse download index '{s}' as JSON with {s}\n{s}", + .{ index_filepath, @errorName(e), download_index }, + ); + defer root.deinit(); + if (root.value != .object) std.debug.panic( + "zls index root value is not an object\n{s}", + .{download_index}, + ); + const arch_os_obj = root.value.object.get(arch_os) orelse std.debug.panic( + "zls index does not contain an entry for arch-os '{s}'\n{s}", + .{ arch_os, download_index }, + ); + const fetch_url = arch_os_obj.object.get("tarball") orelse std.debug.panic( + "zls index arch-os '{s}' is missing the 'tarball' property\n{s}", + .{ arch_os, download_index }, + ); + if (fetch_url != .string) std.debug.panic( + "zls index arch-os '{s}' tarball property is not a string\n{s}", + .{ arch_os, download_index }, + ); + const shasum_url = arch_os_obj.object.get("shasum") orelse std.debug.panic( + "zls index arch-os '{s}' is missing the 'shasum' property\n{s}", + .{ arch_os, download_index }, + ); + _ = shasum_url; // TODO: verify shasum + return .{ + .fetch = allocator.dupe(u8, fetch_url.string) catch |e| oom(e), + .official = allocator.dupe(u8, fetch_url.string) catch |e| oom(e), + }; +} + fn extractUrlFromMachDownloadIndex( allocator: std.mem.Allocator, semantic_version: SemanticVersion, From bd3923a2905f27ebf3ee88e477fa58140ac40767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Laferri=C3=A8re?= Date: Fri, 17 Oct 2025 19:27:06 -0400 Subject: [PATCH 2/4] Fix test failures --- build.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 78105f4..a84155e 100644 --- a/build.zig +++ b/build.zig @@ -186,7 +186,9 @@ fn addTests( const run = b.addRunArtifact(anyzig); run.setName("anyzig -no-command"); run.addArg("-no-command"); - run.expectStdErrEqual("error: expected a command but got '-no-command'\n"); + // The app should exit with an error code and at least mention what you passed in. + run.addCheck(.{ .expect_term = std.process.Child.Term{ .Exited = 1 } }); + run.addCheck(.{ .expect_stderr_match = "-no-command" }); test_step.dependOn(&run.step); } From 42a4f865998319bce5ed810db21c05f1f0613865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Laferri=C3=A8re?= Date: Fri, 17 Oct 2025 21:09:24 -0400 Subject: [PATCH 3/4] Forward the exit code when it is originating from the child process --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 587b523..18ab451 100644 --- a/src/main.zig +++ b/src/main.zig @@ -472,7 +472,7 @@ pub fn main() !void { try child.spawn(); const result = try child.wait(); switch (result) { - .Exited => |code| if (code != 0) std.process.exit(0xff), + .Exited => |code| if (code != 0) std.process.exit(code), else => std.process.exit(0xff), } } From 0bf0cf102c2cc833ba51acfadae0b07fcc1d9750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Laferri=C3=A8re?= Date: Fri, 24 Oct 2025 20:19:50 -0400 Subject: [PATCH 4/4] Revert all changes to forward the subprocess exit code --- build.zig | 4 +--- src/main.zig | 9 ++++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index a84155e..78105f4 100644 --- a/build.zig +++ b/build.zig @@ -186,9 +186,7 @@ fn addTests( const run = b.addRunArtifact(anyzig); run.setName("anyzig -no-command"); run.addArg("-no-command"); - // The app should exit with an error code and at least mention what you passed in. - run.addCheck(.{ .expect_term = std.process.Child.Term{ .Exited = 1 } }); - run.addCheck(.{ .expect_stderr_match = "-no-command" }); + run.expectStdErrEqual("error: expected a command but got '-no-command'\n"); test_step.dependOn(&run.step); } diff --git a/src/main.zig b/src/main.zig index 18ab451..28ab261 100644 --- a/src/main.zig +++ b/src/main.zig @@ -338,6 +338,13 @@ pub fn main() !void { const version_specifier: VersionSpecifier, const is_init = blk: { if (maybe_command) |command| { + if (std.mem.startsWith(u8, command, "-") and !std.mem.eql(u8, command, "-h") and !std.mem.eql(u8, command, "--help")) { + try std.io.getStdErr().writer().print( + "error: expected a command but got '{s}'\n", + .{command}, + ); + std.process.exit(0xff); + } if (build_options.exe == .zig and (std.mem.eql(u8, command, "init") or std.mem.eql(u8, command, "init-exe") or std.mem.eql(u8, command, "init-lib"))) { const is_help = blk_is_help: { var index: usize = cmdline_offset + 1; @@ -472,7 +479,7 @@ pub fn main() !void { try child.spawn(); const result = try child.wait(); switch (result) { - .Exited => |code| if (code != 0) std.process.exit(code), + .Exited => |code| if (code != 0) std.process.exit(0xff), else => std.process.exit(0xff), } }