diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fdb0b2c..d393d93 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,4 +25,4 @@ jobs: - name: Check tests run: deno task test - name: Check release - run: deno task release:check + run: deno task release diff --git a/deno.json b/deno.json index c8a7cf7..469af8b 100644 --- a/deno.json +++ b/deno.json @@ -5,16 +5,9 @@ "fmt": "zig fmt . && deno fmt", "fmt:check": "zig fmt . --check && deno fmt --check", "build": "zig build -Dtarget=x86_64-windows-msvc -Doptimize=ReleaseSmall", - "release": "deno task build && deno run --allow-run -RWE release.ts", - "release:check": "deno task release --dry-run", + "release": "deno task build && zig run ./scripts/release.zig", "check:hash": "certutil -hashfile dist/rb-x86_64-pc-windows-msvc.zip SHA256" }, - "imports": { - "@david/dax": "jsr:@david/dax@^0.43.0", - "@std/crypto": "jsr:@std/crypto@^1.0.3", - "@std/encoding": "jsr:@std/encoding@^1.0.10", - "zzon": "npm:zzon@1.7.0" - }, "fmt": { "proseWrap": "preserve" } diff --git a/deno.lock b/deno.lock deleted file mode 100644 index 6656e76..0000000 --- a/deno.lock +++ /dev/null @@ -1,80 +0,0 @@ -{ - "version": "5", - "specifiers": { - "jsr:@david/dax@0.43": "0.43.0", - "jsr:@david/path@0.2": "0.2.0", - "jsr:@david/which@~0.4.1": "0.4.1", - "jsr:@std/bytes@^1.0.5": "1.0.5", - "jsr:@std/crypto@^1.0.3": "1.0.4", - "jsr:@std/encoding@^1.0.10": "1.0.10", - "jsr:@std/fmt@1": "1.0.6", - "jsr:@std/fs@1": "1.0.16", - "jsr:@std/io@0.225": "0.225.2", - "jsr:@std/path@1": "1.0.8", - "jsr:@std/path@^1.0.8": "1.0.8", - "npm:zzon@1.7.0": "1.7.0" - }, - "jsr": { - "@david/dax@0.43.0": { - "integrity": "72068d3b9eb00c08d70d0d1e2db8872402377279a0e6c88df172112eee1357f1", - "dependencies": [ - "jsr:@david/path", - "jsr:@david/which", - "jsr:@std/fmt", - "jsr:@std/fs", - "jsr:@std/io", - "jsr:@std/path@1" - ] - }, - "@david/path@0.2.0": { - "integrity": "f2d7aa7f02ce5a55e27c09f9f1381794acb09d328f8d3c8a2e3ab3ffc294dccd", - "dependencies": [ - "jsr:@std/fs", - "jsr:@std/path@1" - ] - }, - "@david/which@0.4.1": { - "integrity": "896a682b111f92ab866cc70c5b4afab2f5899d2f9bde31ed00203b9c250f225e" - }, - "@std/bytes@1.0.5": { - "integrity": "4465dd739d7963d964c809202ebea6d5c6b8e3829ef25c6a224290fbb8a1021e" - }, - "@std/crypto@1.0.4": { - "integrity": "cee245c453bd5366207f4d8aa25ea3e9c86cecad2be3fefcaa6cb17203d79340" - }, - "@std/encoding@1.0.10": { - "integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1" - }, - "@std/fmt@1.0.6": { - "integrity": "a2c56a69a2369876ddb3ad6a500bb6501b5bad47bb3ea16bfb0c18974d2661fc" - }, - "@std/fs@1.0.16": { - "integrity": "81878f62b6eeda0bf546197fc3daa5327c132fee1273f6113f940784a468b036", - "dependencies": [ - "jsr:@std/path@^1.0.8" - ] - }, - "@std/io@0.225.2": { - "integrity": "3c740cd4ee4c082e6cfc86458f47e2ab7cb353dc6234d5e9b1f91a2de5f4d6c7", - "dependencies": [ - "jsr:@std/bytes" - ] - }, - "@std/path@1.0.8": { - "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be" - } - }, - "npm": { - "zzon@1.7.0": { - "integrity": "sha512-M4ZuGKWaXY4ezkT2IBKcsIom5rHdSTMtozd9VAL0JmjwDSIPI70y8sCiI2wzXaucmP2k3Hc2CCPu4QHia0cKCg==" - } - }, - "workspace": { - "dependencies": [ - "jsr:@david/dax@0.43", - "jsr:@std/crypto@^1.0.3", - "jsr:@std/encoding@^1.0.10", - "npm:zzon@1.7.0" - ] - } -} diff --git a/release.ts b/release.ts deleted file mode 100644 index 7e156a7..0000000 --- a/release.ts +++ /dev/null @@ -1,49 +0,0 @@ -import $ from "@david/dax"; -import { crypto } from "@std/crypto"; -import { encodeHex } from "@std/encoding/hex"; -import { ZON } from "zzon"; - -// Update version.zon -const buildZigZon = ZON.parse(Deno.readTextFileSync("build.zig.zon")); -const versionZon = { - version: buildZigZon.version, -}; -await Deno.writeTextFile("src/version.zon", ZON.stringify(versionZon)); -await $`zig fmt src/version.zon`; - -// create dist directory -await Deno.mkdir("dist", { recursive: true }); -// zip the binary -await $`powershell Compress-Archive -Path zig-out/bin/rb.exe -DestinationPath dist/rb-x86_64-pc-windows-msvc.zip -Force`; - -const data = await Deno.readFile("dist/rb-x86_64-pc-windows-msvc.zip"); -const hash = encodeHex(await crypto.subtle.digest("SHA-256", data)); - -const scoopTemplate = { - version: buildZigZon.version, - homepage: "https://github.com/ryuapp/rb", - license: "MIT", - architecture: { - "64bit": { - url: - `https://github.com/ryuapp/rb/releases/download/v${buildZigZon.version}/rb-x86_64-pc-windows-msvc.zip`, - hash: hash, - }, - }, - bin: "rb.exe", - checkver: "github", - autoupdate: { - architecture: { - "64bit": { - url: - "https://github.com/ryuapp/rb/releases/download/v$version/rb-x86_64-pc-windows-msvc.zip", - }, - }, - }, -}; - -// update scoop config -Deno.writeFileSync( - "rb.json", - new TextEncoder().encode(JSON.stringify(scoopTemplate, null, 2) + "\n"), -); diff --git a/scripts/release.zig b/scripts/release.zig new file mode 100644 index 0000000..083389b --- /dev/null +++ b/scripts/release.zig @@ -0,0 +1,124 @@ +const std = @import("std"); +const fs = std.fs; +const process = std.process; +const io = std.io; +const json = std.json; + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + // Read build.zig.zon + const build_zon_content = try fs.cwd().readFileAlloc(allocator, "build.zig.zon", 1024 * 1024); + defer allocator.free(build_zon_content); + + // Parse version from build.zig.zon + const version = try parseVersion(allocator, build_zon_content); + defer allocator.free(version); + + // Write version.zon + try writeVersionZon(allocator, version); + + // Format version.zon + try formatVersionZon(allocator); + + // Create dist directory + try fs.cwd().makePath("dist"); + + // Compress the binary + try compressBinary(allocator); + + // Read the zip file and calculate hash + const zip_path = "dist/rb-x86_64-pc-windows-msvc.zip"; + const zip_data = try fs.cwd().readFileAlloc(allocator, zip_path, 100 * 1024 * 1024); + defer allocator.free(zip_data); + + // Calculate SHA-256 hash + var hash: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(zip_data, &hash, .{}); + + // Convert hash to hex string + var hex_hash: [std.crypto.hash.sha2.Sha256.digest_length * 2]u8 = undefined; + _ = std.fmt.bufPrint(&hex_hash, "{}", .{std.fmt.fmtSliceHexLower(&hash)}) catch unreachable; + + // Generate scoop manifest + try generateScoopManifest(allocator, version, &hex_hash); + + std.debug.print("✅ Release preparation completed successfully\n", .{}); +} + +fn parseVersion(allocator: std.mem.Allocator, content: []const u8) ![]u8 { + // Find .version = "x.x.x" pattern + const version_start = std.mem.indexOf(u8, content, ".version = \"") orelse return error.VersionNotFound; + const quote_start = version_start + 12; + const quote_end = std.mem.indexOfPos(u8, content, quote_start, "\"") orelse return error.VersionNotFound; + + const version = content[quote_start..quote_end]; + return allocator.dupe(u8, version); +} + +fn writeVersionZon(allocator: std.mem.Allocator, version: []const u8) !void { + const version_content = try std.fmt.allocPrint(allocator, ".{{ .version = \"{s}\" }}\n", .{version}); + defer allocator.free(version_content); + + const file = try fs.cwd().createFile("src/version.zon", .{}); + defer file.close(); + try file.writeAll(version_content); +} + +fn formatVersionZon(allocator: std.mem.Allocator) !void { + const argv = [_][]const u8{ "zig", "fmt", "src/version.zon" }; + var child = std.process.Child.init(&argv, allocator); + _ = try child.spawnAndWait(); +} + +fn compressBinary(allocator: std.mem.Allocator) !void { + const argv = [_][]const u8{ + "powershell", + "Compress-Archive", + "-Path", + "zig-out/bin/rb.exe", + "-DestinationPath", + "dist/rb-x86_64-pc-windows-msvc.zip", + "-Force", + }; + var child = std.process.Child.init(&argv, allocator); + _ = try child.spawnAndWait(); +} + +fn generateScoopManifest(allocator: std.mem.Allocator, version: []const u8, hash: []const u8) !void { + const url = try std.fmt.allocPrint(allocator, "https://github.com/ryuapp/rb/releases/download/v{s}/rb-x86_64-pc-windows-msvc.zip", .{version}); + defer allocator.free(url); + + const manifest = .{ + .version = version, + .homepage = "https://github.com/ryuapp/rb", + .license = "MIT", + .architecture = .{ + .@"64bit" = .{ + .url = url, + .hash = hash, + }, + }, + .bin = "rb.exe", + .checkver = "github", + .autoupdate = .{ + .architecture = .{ + .@"64bit" = .{ + .url = "https://github.com/ryuapp/rb/releases/download/v$version/rb-x86_64-pc-windows-msvc.zip", + }, + }, + }, + }; + + var buffer = std.ArrayList(u8).init(allocator); + defer buffer.deinit(); + + try json.stringify(manifest, .{ .whitespace = .indent_2 }, buffer.writer()); + try buffer.append('\n'); + + const file = try fs.cwd().createFile("rb.json", .{}); + defer file.close(); + try file.writeAll(buffer.items); +}