From f1f4e22b5a7f862463b3c82a9d468d14e1716d0c Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 24 May 2025 09:39:56 -0600 Subject: [PATCH] update URLs to use the new ARCH-OS ordering --- .github/workflows/artifact.yml | 4 +- build.zig | 5 +- zigup.zig | 111 ++++++++++++++++++++++++++++++--- 3 files changed, 108 insertions(+), 12 deletions(-) diff --git a/.github/workflows/artifact.yml b/.github/workflows/artifact.yml index bd14029..1bceaaf 100644 --- a/.github/workflows/artifact.yml +++ b/.github/workflows/artifact.yml @@ -10,9 +10,7 @@ jobs: runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 - - uses: mlugg/setup-zig@v1 - with: - version: 0.14.0 + - uses: mlugg/setup-zig@v2 - run: | zig build ci --summary all - if: ${{ matrix.os == 'ubuntu-latest' }} diff --git a/build.zig b/build.zig index 130f137..3eb96e7 100644 --- a/build.zig +++ b/build.zig @@ -318,9 +318,10 @@ fn addTests( .check = .{ .expect_stderr_match = "error: compiler '0.7.0' is not installed\n" }, }); + const non_existent_version = "0.0.99"; tests.addWithClean(.{ .name = "test-bad-version", - .argv = &.{"THIS_ZIG_VERSION_DOES_NOT_EXIT"}, + .argv = &.{non_existent_version}, .checks = &.{ .{ .expect_stderr_match = "error: could not download '" }, }, @@ -331,7 +332,7 @@ fn addTests( // it should be more permanent if (false) tests.addWithClean(.{ .name = "test-dev-version", - .argv = &.{"0.14.0-dev.2465+70de2f3a7"}, + .argv = &.{"0.15.0-dev.621+a63f7875f"}, .check = .{ .expect_stdout_exact = "" }, }); diff --git a/zigup.zig b/zigup.zig index d7f719a..13add1b 100644 --- a/zigup.zig +++ b/zigup.zig @@ -22,8 +22,8 @@ const os = switch (builtin.os.tag) { .windows => "windows", else => @compileError("Unsupported OS"), }; -const url_platform = os ++ "-" ++ arch; -const json_platform = arch ++ "-" ++ os; +const os_arch = os ++ "-" ++ arch; +const arch_os = arch ++ "-" ++ os; const archive_ext = if (builtin.os.tag == .windows) "zip" else "tar.xz"; var global_override_appdata: ?[]const u8 = null; // only used for testing @@ -605,7 +605,7 @@ fn fetchCompiler( optional_download_index = try fetchDownloadIndex(allocator, index_url); const master = optional_download_index.?.json.value.object.get(version_arg).?; const compiler_version = master.object.get("version").?.string; - const master_linux = master.object.get(json_platform).?; + const master_linux = master.object.get(arch_os).?; const master_linux_tarball = master_linux.object.get("tarball").?.string; break :blk VersionUrl{ .version = compiler_version, .url = master_linux_tarball }; }; @@ -1142,15 +1142,112 @@ fn createExeLink(link_target: []const u8, path_link: []const u8) !void { try file.writer().writeAll(win32exelink.content[win32exelink.exe_offset + link_target.len ..]); } -const VersionKind = enum { release, dev }; +const VersionKind = union(enum) { release: Release, dev }; fn determineVersionKind(version: []const u8) VersionKind { - return if (std.mem.indexOfAny(u8, version, "-+")) |_| .dev else .release; + const v = SemanticVersion.parse(version) orelse std.debug.panic( + "invalid version '{s}'", + .{version}, + ); + if (v.pre != null or v.build != null) return .dev; + return .{ .release = .{ .major = v.major, .minor = v.minor, .patch = v.patch } }; } +const Release = struct { + major: usize, + minor: usize, + patch: usize, + pub fn order(a: Release, b: Release) std.math.Order { + if (a.major != b.major) return std.math.order(a.major, b.major); + if (a.minor != b.minor) return std.math.order(a.minor, b.minor); + return std.math.order(a.patch, b.patch); + } +}; + +// The Zig release where the OS-ARCH in the url was swapped to ARCH-OS +const arch_os_swap_release: Release = .{ .major = 0, .minor = 14, .patch = 1 }; + +const SemanticVersion = struct { + const max_pre = 50; + const max_build = 50; + const max_string = 50 + max_pre + max_build; + + major: usize, + minor: usize, + patch: usize, + pre: ?std.BoundedArray(u8, max_pre), + build: ?std.BoundedArray(u8, max_build), + + pub fn array(self: *const SemanticVersion) std.BoundedArray(u8, max_string) { + var result: std.BoundedArray(u8, max_string) = undefined; + const roundtrip = std.fmt.bufPrint(&result.buffer, "{}", .{self}) catch unreachable; + result.len = roundtrip.len; + return result; + } + + pub fn parse(s: []const u8) ?SemanticVersion { + const parsed = std.SemanticVersion.parse(s) catch |e| switch (e) { + error.Overflow, error.InvalidVersion => return null, + }; + std.debug.assert(s.len <= max_string); + + var result: SemanticVersion = .{ + .major = parsed.major, + .minor = parsed.minor, + .patch = parsed.patch, + .pre = if (parsed.pre) |pre| std.BoundedArray(u8, max_pre).init(pre.len) catch |e| switch (e) { + error.Overflow => std.debug.panic("semantic version pre '{s}' is too long (max is {})", .{ pre, max_pre }), + } else null, + .build = if (parsed.build) |build| std.BoundedArray(u8, max_build).init(build.len) catch |e| switch (e) { + error.Overflow => std.debug.panic("semantic version build '{s}' is too long (max is {})", .{ build, max_build }), + } else null, + }; + if (parsed.pre) |pre| @memcpy(result.pre.?.slice(), pre); + if (parsed.build) |build| @memcpy(result.build.?.slice(), build); + + { + // sanity check, ensure format gives us the same string back we just parsed + const roundtrip = result.array(); + if (!std.mem.eql(u8, roundtrip.slice(), s)) std.debug.panic( + "codebug parse/format version mismatch:\nparsed: '{s}'\nformat: '{s}'\n", + .{ s, roundtrip.slice() }, + ); + } + + return result; + } + pub fn ref(self: *const SemanticVersion) std.SemanticVersion { + return .{ + .major = self.major, + .minor = self.minor, + .patch = self.patch, + .pre = if (self.pre) |*pre| pre.slice() else null, + .build = if (self.build) |*build| build.slice() else null, + }; + } + pub fn format( + self: SemanticVersion, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + try self.ref().format(fmt, options, writer); + } +}; + fn getDefaultUrl(allocator: Allocator, compiler_version: []const u8) ![]const u8 { return switch (determineVersionKind(compiler_version)) { - .dev => try std.fmt.allocPrint(allocator, "https://ziglang.org/builds/zig-" ++ url_platform ++ "-{0s}." ++ archive_ext, .{compiler_version}), - .release => try std.fmt.allocPrint(allocator, "https://ziglang.org/download/{s}/zig-" ++ url_platform ++ "-{0s}." ++ archive_ext, .{compiler_version}), + .dev => try std.fmt.allocPrint(allocator, "https://ziglang.org/builds/zig-" ++ arch_os ++ "-{0s}." ++ archive_ext, .{compiler_version}), + .release => |release| try std.fmt.allocPrint( + allocator, + "https://ziglang.org/download/{s}/zig-{1s}-{0s}." ++ archive_ext, + .{ + compiler_version, + switch (release.order(arch_os_swap_release)) { + .lt => os_arch, + .gt, .eq => arch_os, + }, + }, + ), }; }