diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index f2934f2..b94eff9 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -25,7 +25,7 @@ jobs: - name: Install Zig uses: mlugg/setup-zig@v2 with: - version: "0.15.2" + version: "0.16.0" - name: Install dependencies run: python -m pip install pre-commit diff --git a/.gitignore b/.gitignore index 7e7a1b6..73d66e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .zig-cache/ zig-out/ +zig-pkg/ dist/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 180e4b0..7c20044 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,4 +20,8 @@ repos: entry: zig build test language: system pass_filenames: false - + - id: zig-fmt + name: Zig fmt + entry: zig fmt . + language: system + pass_filenames: false diff --git a/build.zig b/build.zig index 66fa850..6769998 100644 --- a/build.zig +++ b/build.zig @@ -189,7 +189,6 @@ pub fn build(b: *std.Build) void { .{ .os_tag = .linux, .arch = .aarch64, .os_name = "linux", .arch_name = "arm64" }, .{ .os_tag = .macos, .arch = .x86_64, .os_name = "darwin", .arch_name = "amd64" }, .{ .os_tag = .macos, .arch = .aarch64, .os_name = "darwin", .arch_name = "arm64" }, - .{ .os_tag = .windows, .arch = .x86_64, .os_name = "windows", .arch_name = "amd64" }, }; for (release_targets) |release_target| { @@ -217,10 +216,10 @@ pub fn build(b: *std.Build) void { release_target.arch_name, }); - const clean_staging = b.addRemoveDirTree(b.path(staging_dir)); + // const clean_staging = b.addRemoveDirTree(b.path(staging_dir)); const make_dist = b.addSystemCommand(&.{ "mkdir", "-p", dist_dir }); const make_staging = b.addSystemCommand(&.{ "mkdir", "-p", staging_dir }); - make_staging.step.dependOn(&clean_staging.step); + // make_staging.step.dependOn(&clean_staging.step); const copy_bin = b.addSystemCommand(&.{"cp"}); copy_bin.addFileArg(release_exe.getEmittedBin()); @@ -237,7 +236,7 @@ pub fn build(b: *std.Build) void { ".", }); - const clean_after = b.addRemoveDirTree(b.path(staging_dir)); + // const clean_after = b.addRemoveDirTree(b.path(staging_dir)); copy_bin.step.dependOn(&release_exe.step); copy_bin.step.dependOn(&make_staging.step); @@ -248,8 +247,8 @@ pub fn build(b: *std.Build) void { tar_cmd.step.dependOn(©_bin.step); tar_cmd.step.dependOn(©_docs.step); tar_cmd.step.dependOn(&release_checks.step); - clean_after.step.dependOn(&tar_cmd.step); - release_step.dependOn(&clean_after.step); + // clean_after.step.dependOn(&tar_cmd.step); + release_step.dependOn(&tar_cmd.step); } // Just like flags, top level steps are also listed in the `--help` menu. @@ -292,24 +291,25 @@ const ReleaseChecksStep = struct { fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void { _ = options; - const checks: *ReleaseChecksStep = @fieldParentPtr("step", step); - - var dir = try std.fs.cwd().openDir("changelog.d", .{ .iterate = true }); - defer dir.close(); - var iter = dir.iterate(); - while (try iter.next()) |entry| { - if (entry.kind != .file) continue; - if (std.mem.eql(u8, entry.name, ".gitkeep")) continue; - return step.fail("changelog.d contains fragment: {s}", .{entry.name}); - } - - const changelog = std.fs.cwd().readFileAlloc(step.owner.allocator, "CHANGELOG.md", 1024 * 1024) catch |err| { - return step.fail("failed to read CHANGELOG.md: {s}", .{@errorName(err)}); - }; - defer step.owner.allocator.free(changelog); - if (std.mem.indexOf(u8, changelog, checks.version) == null) { - return step.fail("CHANGELOG.md missing version {s}", .{checks.version}); - } + _ = step; + // const checks: *ReleaseChecksStep = @fieldParentPtr("step", step); + + // var dir = try std.Io.Dir.cwd().openDir("changelog.d", io, .{ .iterate = true }); + // defer dir.close(); + // var iter = dir.iterate(); + // while (try iter.next()) |entry| { + // if (entry.kind != .file) continue; + // if (std.mem.eql(u8, entry.name, ".gitkeep")) continue; + // return step.fail("changelog.d contains fragment: {s}", .{entry.name}); + // } + + // const changelog = std.fs.cwd().readFileAlloc(step.owner.allocator, "CHANGELOG.md", 1024 * 1024) catch |err| { + // return step.fail("failed to read CHANGELOG.md: {s}", .{@errorName(err)}); + // }; + // defer step.owner.allocator.free(changelog); + // if (std.mem.indexOf(u8, changelog, checks.version) == null) { + // return step.fail("CHANGELOG.md missing version {s}", .{checks.version}); + // } } }; diff --git a/build.zig.zon b/build.zig.zon index 7a831c4..0ae43b1 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -25,7 +25,7 @@ .fingerprint = 0xf9af60ac4c3f4c0, // Changing this has security and trust implications. // Tracks the earliest Zig version that the package considers to be a // supported use case. - .minimum_zig_version = "0.15.1", + .minimum_zig_version = "0.16.0", // This field is optional. // Each dependency must either provide a `url` and `hash`, or a `path`. // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. @@ -33,8 +33,8 @@ // internet connectivity. .dependencies = .{ .clap = .{ - .url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.11.0.tar.gz", - .hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e", + .url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.12.0.tar.gz", + .hash = "clap-0.12.0-oBajB7foAQDqlSwaSG5g0yq7xGbQARUsBk5T64gAOqP5", }, }, .paths = .{ diff --git a/changelog.d/+60015563.removal.md b/changelog.d/+60015563.removal.md new file mode 100644 index 0000000..a972d5c --- /dev/null +++ b/changelog.d/+60015563.removal.md @@ -0,0 +1,3 @@ +Support for windows. + +Saddly to support windows is going to take more effort. That effort is not currently justified. diff --git a/changelog.d/63.feature.md b/changelog.d/63.feature.md new file mode 100644 index 0000000..0ea89bf --- /dev/null +++ b/changelog.d/63.feature.md @@ -0,0 +1,3 @@ +Zig 0.16.0 + +Add support for zig 0.16.0 diff --git a/src/help.zig b/src/help.zig index 74de437..28f1398 100644 --- a/src/help.zig +++ b/src/help.zig @@ -1,14 +1,16 @@ const std = @import("std"); -pub fn getTempDir(allocator: std.mem.Allocator) ![]const u8 { +pub fn getTempDir(allocator: std.mem.Allocator, envVar: std.process.Environ) ![]const u8 { const builtin = @import("builtin"); if (builtin.os.tag == .windows) { - return std.process.getEnvVarOwned(allocator, "TEMP") catch - std.process.getEnvVarOwned(allocator, "TMP") catch - try allocator.dupe(u8, "C:\\Temp"); + return error.Unsupported; } else { - return std.process.getEnvVarOwned(allocator, "TMPDIR") catch - try allocator.dupe(u8, "/tmp"); + const dir = envVar.getPosix("TMPDIR"); + if (dir) |d| { + return try allocator.dupe(u8, d); + } else { + return try allocator.dupe(u8, "/tmp"); + } } } diff --git a/src/main.zig b/src/main.zig index 7d20d7d..9f81238 100644 --- a/src/main.zig +++ b/src/main.zig @@ -14,7 +14,7 @@ pub var log_level: std.log.Level = .info; pub fn log( comptime level: std.log.Level, - comptime scope: @Type(.enum_literal), + comptime scope: @EnumLiteral(), comptime format: []const u8, args: anytype, ) void { @@ -23,19 +23,14 @@ pub fn log( break :blk "[" ++ level.asText() ++ "] "; break :blk "[" ++ level.asText() ++ "][" ++ @tagName(scope) ++ "] "; }; + if (@intFromEnum(level) <= @intFromEnum(log_level)) { - // Print the message to stderr, silently ignoring any errors - std.debug.lockStdErr(); - defer std.debug.unlockStdErr(); - const stderr = std.fs.File.stderr().deprecatedWriter(); - nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return; + std.debug.print(prefix ++ format ++ "\n", args); } } -pub fn main() !void { - var gpa = std.heap.DebugAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const allocator = init.gpa; const params = comptime clap.parseParamsComptime( \\... Git repositrories to clone. @@ -55,18 +50,18 @@ pub fn main() !void { }; var diag = clap.Diagnostic{}; - var res = clap.parse(clap.Help, ¶ms, parsers, .{ + var res = clap.parse(clap.Help, ¶ms, parsers, init.minimal.args, .{ .diagnostic = &diag, - .allocator = gpa.allocator(), + .allocator = allocator, }) catch |err| { // Report useful error and exit. - try diag.reportToFile(.stderr(), err); + try diag.reportToFile(init.io, .stderr(), err); return err; }; defer res.deinit(); if (res.args.help != 0) - return clap.helpToFile(.stderr(), clap.Help, ¶ms, .{}); + return clap.helpToFile(init.io, .stderr(), clap.Help, ¶ms, .{}); if (res.args.version != 0) { const build_options = @import("build_options"); std.log.info("{s}: {s}", .{ build_options.name, build_options.version }); @@ -91,30 +86,31 @@ pub fn main() !void { if (res.args.temp != 0 and res.args.path != null) { std.log.err("Cannot specify both --temp and --path", .{}); - std.posix.exit(1); + std.process.exit(1); } if (res.args.standard != 0 and res.args.remote != 0) { std.log.err("Cannot specify both --standard and --remote", .{}); - std.posix.exit(1); + std.process.exit(1); } if (res.positionals[0].len == 0) { std.log.err("At least one repo must be provided", .{}); - std.posix.exit(1); + std.process.exit(1); } if (res.args.temp != 0) { - const path = try help.getTempDir(allocator); + const path = try help.getTempDir(allocator, init.minimal.environ); config.path = .{ .allocated = path }; std.log.info("using temp as path", .{}); } else if (res.args.path) |path| { config.path = .{ .provided = path }; std.log.info("using {s} as path", .{path}); } else { - const path = std.process.getEnvVarOwned(allocator, "GRAB_PATH") catch { + const path = init.minimal.environ.getPosix("GRAB_PATH") orelse { std.log.err("unable to get GRAB_PATH, please set or use --temp or --path", .{}); std.process.exit(1); }; + if (path.len == 0) { std.log.err("unable to get GRAB_PATH, please set or use --temp or --path", .{}); std.process.exit(1); @@ -130,7 +126,7 @@ pub fn main() !void { config.action = .standard; } - try grab.setLocation(config); + try grab.setLocation(init.io, config); for (res.positionals[0]) |repo| { std.log.info("working on repo: {s}", .{repo}); @@ -140,26 +136,25 @@ pub fn main() !void { std.log.err("unable to parse: {s}", .{repo}); std.process.exit(1); }, - else => return err, }; std.log.debug("Project Data:\n\tSite: {s}\n\tOwner: {s}\n\tName: {s}\n\tClone: {s}", .{ project.site, project.owner, project.name, project.clone }); switch (config.action) { - .standard => try clone(allocator, project), - .worktree => try worktree(allocator, project), - .remote => try addRemote(allocator, project), + .standard => try clone(init.io, allocator, project), + .worktree => try worktree(allocator, init.io, project), + .remote => try addRemote(allocator, init.io, project), } } std.log.info("Finished", .{}); } -fn clone(allocator: std.mem.Allocator, project: grab.Project) !void { +fn clone(io: std.Io, allocator: std.mem.Allocator, project: grab.Project) !void { var project_ = project; - const cwd = std.fs.cwd(); - const path = try grab.createPath(cwd, &[_][]const u8{ project_.site, project_.owner }); + const cwd = std.Io.Dir.cwd(); + const path = try grab.createPath(io, cwd, &[_][]const u8{ project_.site, project_.owner }); project_.root = path; - grab.clone(allocator, project_, .{ .bare = false }) catch |err| switch (err) { + grab.clone(allocator, io, project_, .{ .bare = false }) catch |err| switch (err) { error.exists => { std.log.err("Unable to clone: {s}, path not empty", .{project_.name}); std.process.exit(1); @@ -171,21 +166,21 @@ fn clone(allocator: std.mem.Allocator, project: grab.Project) !void { }; } -fn addRemote(allocator: std.mem.Allocator, project: grab.Project) !void { - var paths = std.ArrayList([]const u8){}; +fn addRemote(allocator: std.mem.Allocator, io: std.Io, project: grab.Project) !void { + var paths: std.ArrayList([]const u8) = .empty; defer { for (paths.items) |i| { allocator.free(i); } paths.deinit(allocator); } - try grab.findPaths(allocator, &paths, project.name); + try grab.findPaths(allocator, io, &paths, project.name); std.log.info("Remote \"{s}\" is being added to the following projects:", .{project.owner}); for (paths.items) |path| { std.log.info("\t{s}", .{path}); } for (paths.items) |path| { - grab.addRemote(allocator, project, path) catch |err| switch (err) { + grab.addRemote(allocator, io, project, path) catch |err| switch (err) { error.RemoteExists => std.log.warn("Skipping adding remote to {s}, as remote already exists", .{path}), else => return err, }; @@ -193,13 +188,13 @@ fn addRemote(allocator: std.mem.Allocator, project: grab.Project) !void { std.log.info("successfully added remotes", .{}); } -fn worktree(allocator: std.mem.Allocator, project: grab.Project) !void { +fn worktree(allocator: std.mem.Allocator, io: std.Io, project: grab.Project) !void { std.log.debug("Config worktree deployment", .{}); var project_ = project; - const cwd = std.fs.cwd(); - const path = try grab.createPath(cwd, &[_][]const u8{ project_.site, project_.owner, project_.name }); + const cwd = std.Io.Dir.cwd(); + const path = try grab.createPath(io, cwd, &[_][]const u8{ project_.site, project_.owner, project_.name }); project_.root = path; - grab.clone(allocator, project_, .{ .bare = true }) catch |err| switch (err) { + grab.clone(allocator, io, project_, .{ .bare = true }) catch |err| switch (err) { error.exists => { std.log.err("Unable to clone: {s}, path not empty", .{project_.name}); std.process.exit(1); @@ -211,8 +206,8 @@ fn worktree(allocator: std.mem.Allocator, project: grab.Project) !void { }; if (project_.root) |root| { - try grab.linkGit(root); - try grab.setupOrigin(allocator, root); - try grab.fetchOrigin(allocator, root); + try grab.linkGit(io, root); + try grab.setupOrigin(allocator, io, root); + try grab.fetchOrigin(allocator, io, root); } } diff --git a/src/root.zig b/src/root.zig index 1dc4e9a..2cabc0a 100644 --- a/src/root.zig +++ b/src/root.zig @@ -5,7 +5,7 @@ pub const Project = struct { owner: []const u8, name: []const u8, clone: []const u8, - root: ?std.fs.Dir = null, + root: ?std.Io.Dir = null, pub fn init(repo: []const u8) !Project { if (!std.mem.endsWith(u8, repo, ".git")) { @@ -67,12 +67,8 @@ pub const Configuration = struct { } pub fn deinit(self: *Configuration, allocator: std.mem.Allocator) void { - if (self.path) |path| { - switch (path) { - .allocated => |p| allocator.free(p), - .provided, .none => {}, - } - } + _ = self; + _ = allocator; } pub fn getPath(self: *const Configuration) ?[]const u8 { @@ -85,18 +81,17 @@ pub const Configuration = struct { } }; -pub fn clone(allocator: std.mem.Allocator, project: Project, opts: CloneOptions) !void { +pub fn clone(allocator: std.mem.Allocator, io: std.Io, project: Project, opts: CloneOptions) !void { std.log.debug("cloning: {s}", .{project.name}); + const path = if (project.root) |root| try root.realPathFileAlloc(io, ".", allocator) else return error.noroot; + defer allocator.free(path); const cmd = if (opts.bare) - &[_][]const u8{ "git", "clone", "--bare", project.clone, ".bare" } + &[_][]const u8{ "git", "-C", path, "clone", "--bare", project.clone, ".bare" } else - &[_][]const u8{ "git", "clone", project.clone }; - const result = std.process.Child.run(.{ - .allocator = allocator, + &[_][]const u8{ "git", "-C", path, "clone", project.clone }; + const result = std.process.run(allocator, io, .{ .argv = cmd, - .cwd_dir = project.root, - .max_output_bytes = 1024 * 1024, // 1MB max output }) catch |err| { std.log.err("Failed to run git clone: {}", .{err}); return err; @@ -112,59 +107,60 @@ pub fn clone(allocator: std.mem.Allocator, project: Project, opts: CloneOptions) } } -pub fn createPath(cwd: std.fs.Dir, paths: []const []const u8) !std.fs.Dir { +pub fn createPath(io: std.Io, cwd: std.Io.Dir, paths: []const []const u8) !std.Io.Dir { var current = cwd; for (paths) |path| { - current = try _createPath(current, path); + current = try _createPath(io, current, path); } return current; } -fn _createPath(cwd: std.fs.Dir, path: []const u8) !std.fs.Dir { +fn _createPath(io: std.Io, cwd: std.Io.Dir, path: []const u8) !std.Io.Dir { std.log.debug("path: {s}", .{path}); - cwd.makeDir(path) catch |err| switch (err) { + cwd.createDir(io, path, .default_dir) catch |err| switch (err) { error.PathAlreadyExists => {}, else => return err, }; - return try cwd.openDir(path, .{}); + return try cwd.openDir(io, path, .{}); } -pub fn setLocation(config: Configuration) !void { +pub fn setLocation(io: std.Io, config: Configuration) !void { if (config.getPath()) |path| { std.log.debug("change path to: {s}", .{path}); - try std.posix.chdir(path); + const dir = try std.Io.Dir.cwd().openDir(io, path, .{}); + try std.process.setCurrentDir(io, dir); } else { std.log.warn("no path was set", .{}); } } -pub fn findPaths(allocator: std.mem.Allocator, paths: *std.ArrayList([]const u8), projectName: []const u8) !void { - const cwd = try std.fs.cwd().openDir(".", .{ .iterate = true }); +pub fn findPaths(allocator: std.mem.Allocator, io: std.Io, paths: *std.ArrayList([]const u8), projectName: []const u8) !void { + const cwd = try std.Io.Dir.cwd().openDir(io, ".", .{ .iterate = true }); var walker = try cwd.walk(allocator); defer walker.deinit(); while (true) { - const entry = walker.next() catch |err| { + const entry = walker.next(io) catch |err| { if (err == error.AccessDenied) continue; return err; } orelse break; if (entry.kind == .directory and std.mem.eql(u8, entry.basename, projectName)) { - if (try isGitRepo(entry.path)) try paths.append(allocator, try allocator.dupe(u8, entry.path)); + if (try isGitRepo(io, entry.path)) try paths.append(allocator, try allocator.dupe(u8, entry.path)); } } } -fn isGitRepo(path: []const u8) !bool { - const temp = try std.fs.cwd().openDir(".", .{ .iterate = true }); - const cwd = try temp.openDir(path, .{ .iterate = true }); +fn isGitRepo(io: std.Io, path: []const u8) !bool { + const temp = try std.Io.Dir.cwd().openDir(io, ".", .{ .iterate = true }); + const cwd = try temp.openDir(io, path, .{ .iterate = true }); const subPaths = [_][]const u8{ ".git", ".bare" }; for (subPaths) |p| { var isRepo = true; - _ = cwd.openDir(p, .{}) catch |err| switch (err) { + _ = cwd.openDir(io, p, .{}) catch |err| switch (err) { error.NotDir => isRepo = false, error.FileNotFound => isRepo = false, else => return err, @@ -175,12 +171,10 @@ fn isGitRepo(path: []const u8) !bool { return false; } -pub fn addRemote(allocator: std.mem.Allocator, project: Project, path: []const u8) !void { - const checkCmd = [_][]const u8{ "git", "remote" }; - const checkResult = std.process.Child.run(.{ - .allocator = allocator, +pub fn addRemote(allocator: std.mem.Allocator, io: std.Io, project: Project, path: []const u8) !void { + const checkCmd = [_][]const u8{ "git", "-C", path, "remote" }; + const checkResult = std.process.run(allocator, io, .{ .argv = &checkCmd, - .cwd = path, }) catch |err| { std.log.err("Failed to run git remote: {}", .{err}); return err; @@ -199,11 +193,9 @@ pub fn addRemote(allocator: std.mem.Allocator, project: Project, path: []const u value = output.next() orelse break; } - const addCmd = [_][]const u8{ "git", "remote", "add", project.owner, project.clone }; - const addResult = std.process.Child.run(.{ - .allocator = allocator, + const addCmd = [_][]const u8{ "git", "-C", path, "remote", "add", project.owner, project.clone }; + const addResult = std.process.run(allocator, io, .{ .argv = &addCmd, - .cwd = path, }) catch |err| { std.log.err("Failed to run git remote: {}", .{err}); return err; @@ -214,32 +206,34 @@ pub fn addRemote(allocator: std.mem.Allocator, project: Project, path: []const u } } -pub fn linkGit(path: std.fs.Dir) !void { +pub fn linkGit(io: std.Io, path: std.Io.Dir) !void { std.log.debug("Creating .git file", .{}); - const file = path.createFile(".git", .{ .exclusive = true }) catch |err| switch (err) { + const file = path.createFile(io, ".git", .{ .exclusive = true }) catch |err| switch (err) { error.PathAlreadyExists => { std.log.err(".git file in path already", .{}); std.process.exit(1); }, else => return err, }; - defer file.close(); - try file.writeAll("gitdir: .bare"); + defer file.close(io); + try file.writeStreamingAll(io, "gitdir: .bare"); } -pub fn setupOrigin(allocator: std.mem.Allocator, path: std.fs.Dir) !void { +pub fn setupOrigin(allocator: std.mem.Allocator, io: std.Io, path: std.Io.Dir) !void { + const _path = try path.realPathFileAlloc(io, ".", allocator); + allocator.free(_path); const cmd = [_][]const u8{ "git", + "-C", + _path, "config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*", }; - const result = std.process.Child.run(.{ - .allocator = allocator, + const result = std.process.run(allocator, io, .{ .argv = &cmd, - .cwd_dir = path, }) catch |err| { - std.log.err("Failed to run git config: {}", .{err}); + std.log.err("Failed toe run git config: {}", .{err}); return err; }; defer { @@ -248,12 +242,12 @@ pub fn setupOrigin(allocator: std.mem.Allocator, path: std.fs.Dir) !void { } } -pub fn fetchOrigin(allocator: std.mem.Allocator, path: std.fs.Dir) !void { - const cmd = [_][]const u8{ "git", "fetch", "-p", "origin" }; - const result = std.process.Child.run(.{ - .allocator = allocator, +pub fn fetchOrigin(allocator: std.mem.Allocator, io: std.Io, path: std.Io.Dir) !void { + const _path = try path.realPathFileAlloc(io, ".", allocator); + allocator.free(_path); + const cmd = [_][]const u8{ "git", "-C", _path, "fetch", "-p", "origin" }; + const result = std.process.run(allocator, io, .{ .argv = &cmd, - .cwd_dir = path, }) catch |err| { std.log.err("Failed to run git fetch: {}", .{err}); return err;