Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 2 additions & 25 deletions src/Packages.zig
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ test "parse.fuzz" {
try std.testing.fuzz({}, fuzz.fnFromParseAndWrite(parseAndWrite), .{});
}

pub fn sort(pkgs: *Packages) void {
fn sort(pkgs: *Packages) void {
pkgs.by_name.sort(struct {
keys: []const []const u8,

Expand Down Expand Up @@ -467,30 +467,6 @@ test sort {
},
},
}, .{}) == null);
try expectWrite(&pkgs,
\\[btest.info]
\\version = 0.2.0
\\
\\[btest.update]
\\version = https://github.com/test/test
\\
\\[btest.linux_x86_64]
\\url = test_url
\\hash = test_hash
\\
\\[atest.info]
\\version = 0.2.0
\\
\\[atest.update]
\\version = https://github.com/test/test
\\
\\[atest.linux_x86_64]
\\url = test_url
\\hash = test_hash
\\
);

pkgs.sort();
try expectWrite(&pkgs,
\\[atest.info]
\\version = 0.2.0
Expand Down Expand Up @@ -571,6 +547,7 @@ pub fn update(
} else {
entry.key_ptr.* = pkg.name;
entry.value_ptr.* = pkg.pkg;
pkgs.sort();
return null;
}
}
Expand Down
98 changes: 86 additions & 12 deletions src/git.zig
Original file line number Diff line number Diff line change
@@ -1,21 +1,95 @@
pub fn commitFile(io: std.Io, dir: std.Io.Dir, file: []const u8, msg: []const u8) !void {
var child = try std.process.spawn(io, .{
.argv = &.{ "git", "commit", "-i", file, "-m", msg },
.stdin = .ignore,
.stdout = .pipe,
.stderr = .pipe,
return switch (try runCommand(io, dir, &.{ "git", "commit", "-i", file, "-m", msg })) {
0 => {},
else => error.GitCommitFailed,
};
}

pub fn hasDiffForFile(io: std.Io, dir: std.Io.Dir, file: []const u8) !bool {
const code = try runCommand(io, dir, &.{ "git", "diff", "--quiet", "--", file });
return switch (code) {
0 => false,
1 => true,
else => error.GitDiffFailed,
};
}

pub fn currentBranch(gpa: std.mem.Allocator, io: std.Io, dir: std.Io.Dir) ![]u8 {
const res = try std.process.run(gpa, io, .{
.argv = &.{ "git", "branch", "--show-current" },
.cwd = .{ .dir = dir },
});
const failed = switch (try child.wait(io)) {
defer gpa.free(res.stdout);
defer gpa.free(res.stderr);

switch (res.term) {
.exited => |code| switch (code) {
0 => false, // successful commit
1 => false, // nothing to commit commit
else => true,
0 => {},
else => return error.GitCurrentBranchFailed,
},
else => true,
else => return error.ProcessExitedAbnormally,
}

const trimmed = std.mem.trim(u8, res.stdout, " \r\n\t");
if (trimmed.len == 0)
return error.GitCurrentBranchFailed;

return gpa.dupe(u8, trimmed);
}

pub fn createBranch(io: std.Io, dir: std.Io.Dir, branch: []const u8) !void {
const code = try runCommand(io, dir, &.{ "git", "switch", "-c", branch });
if (code != 0)
return error.GitCreateBranchFailed;
}

pub fn switchBranch(io: std.Io, dir: std.Io.Dir, branch: []const u8) !void {
const code = try runCommand(io, dir, &.{ "git", "switch", branch });
if (code != 0)
return error.GitSwitchBranchFailed;
}

pub fn push(io: std.Io, dir: std.Io.Dir) !void {
const code = try runCommand(io, dir, &.{ "git", "push" });
if (code != 0)
return error.GitPushFailed;
}

pub const PullOptions = struct {
prune: bool = false,
};

pub fn pull(io: std.Io, dir: std.Io.Dir, options: PullOptions) !void {
const code = try runCommand(io, dir, &.{ "git", "pull", if (options.prune) "--prune" else "--no-prune" });
if (code != 0)
return error.GitPullFailed;
}

pub const PrOptions = struct {
base: []const u8 = "main",
};

pub fn createPullRequest(io: std.Io, dir: std.Io.Dir, options: PrOptions) !void {
const code = try runCommand(io, dir, &.{
"gh", "pr", "create", "--fill", "--base", options.base,
});
if (code != 0)
return error.GhPrCreateFailed;
}

fn runCommand(io: std.Io, dir: std.Io.Dir, argv: []const []const u8) !u8 {
var child = try std.process.spawn(io, .{
.argv = argv,
.stdin = .ignore,
.stdout = .ignore,
.stderr = .ignore,
.cwd = .{ .dir = dir },
});

return switch (try child.wait(io)) {
.exited => |code| code,
else => error.ProcessExitedAbnormally,
};
if (failed)
return error.GitCommitFailed;
}

pub const MessageOptions = struct {
Expand Down
53 changes: 45 additions & 8 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,9 @@ const pkgs_update_usage =
\\ -f, --pkgs-file <path>
\\ Path to pkgs.ini (default: ./pkgs.ini)
\\
\\ -p, --pull-request
\\ Create branch, commit, push and open a PR for each updated pkg
\\
\\ -h, --help
\\ Display this message
\\
Expand All @@ -556,6 +559,10 @@ fn pkgsUpdateCommand(prog: *Program) !void {
options.commit = true;
if (prog.args.flag(&.{ "-d", "--update-description" }))
options.update_description = true;
if (prog.args.flag(&.{ "-p", "--pull-request" })) {
options.pr = true;
options.commit = true;
}
if (prog.args.flag(&.{ "-h", "--help" }))
return prog.stdoutWriteAllLocked(pkgs_update_usage);
if (prog.args.positional()) |url|
Expand Down Expand Up @@ -649,6 +656,7 @@ fn pkgsAddCommand(prog: *Program) !void {
const PackagesAddOptions = struct {
pkgs_ini_path: []const u8 = "./pkgs.ini",
commit: bool = false,
pr: bool = false,
update_description: bool,
};

Expand All @@ -670,19 +678,28 @@ fn pkgsAdd(prog: *Program, add_pkg: AddPackage, options: PackagesAddOptions) !vo

fn pkgsAddInner(prog: *Program, add_pkg: AddPackage, options: PackagesAddOptions) !void {
const io = prog.init.io;

var arena_allocator = std.heap.ArenaAllocator.init(prog.init.gpa);
const arena = arena_allocator.allocator();
defer arena_allocator.deinit();

var http_client = std.http.Client{
.io = prog.init.io,
.allocator = prog.init.gpa,
};
defer http_client.deinit();

const cwd = std.Io.Dir.cwd();
const pkgs_ini_dir_path = std.fs.path.dirname(options.pkgs_ini_path) orelse ".";
const pkgs_ini_base_name = std.fs.path.basename(options.pkgs_ini_path);

var pkgs_ini_dir, const pkgs_ini_file = try fs.openDirAndFile(io, cwd, options.pkgs_ini_path, .{
.file = .{ .mode = .read_write },
});
var pkgs_ini_dir = try cwd.openDir(io, pkgs_ini_dir_path, .{});
defer pkgs_ini_dir.close(io);

// Ensure repo is up to date
try git.pull(prog.init.io, pkgs_ini_dir, .{ .prune = true });

const pkgs_ini_file = try pkgs_ini_dir.openFile(io, pkgs_ini_base_name, .{ .mode = .read_write });
defer pkgs_ini_file.close(io);

var pkgs = try Packages.parseFile(prog.init.io, prog.init.gpa, pkgs_ini_file);
Expand All @@ -696,21 +713,41 @@ fn pkgsAddInner(prog: *Program, add_pkg: AddPackage, options: PackagesAddOptions
.gpa = prog.init.gpa,
.arena = pkgs.arena(),
.http_client = &http_client,
.progress = progress,
.name = add_pkg.name,
.version_uri = add_pkg.version,
.download_uri = add_pkg.download,
.target = .{ .os = builtin.os.tag, .arch = builtin.target.cpu.arch },
});
const old_pkg = try pkgs.update(pkg, .{ .description = options.update_description });

pkgs.sort();
try pkgs.writeToFile(io, pkgs_ini_file);
try pkgs_ini_file.sync(io);
if (options.commit) {
const msg = try git.createCommitMessage(prog.init.arena.allocator(), pkg, old_pkg, .{
.description = options.update_description,

if (!options.commit and !options.pr)
return;
if (!try git.hasDiffForFile(io, pkgs_ini_dir, pkgs_ini_base_name))
return;

const msg = try git.createCommitMessage(arena, pkg, old_pkg, .{
.description = options.update_description,
});

if (options.pr) {
const base_branch = try git.currentBranch(arena, io, pkgs_ini_dir);
const pr_branch = try std.fmt.allocPrint(arena, "{s}-{s}", .{
pkg.name, pkg.pkg.info.version,
});
try git.commitFile(prog.init.io, pkgs_ini_dir, pkgs_ini_base_name, msg);

try git.createBranch(io, pkgs_ini_dir, pr_branch);
errdefer git.switchBranch(io, pkgs_ini_dir, base_branch) catch {};

try git.commitFile(io, pkgs_ini_dir, pkgs_ini_base_name, msg);
try git.push(io, pkgs_ini_dir);
try git.createPullRequest(io, pkgs_ini_dir, .{ .base = base_branch });
try git.switchBranch(io, pkgs_ini_dir, base_branch);
} else {
try git.commitFile(io, pkgs_ini_dir, pkgs_ini_base_name, msg);
}
}

Expand Down
Loading