Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zig code: auto git push, find suitable target branch and open merge url in browser #340

Open
xxleyi opened this issue Mar 25, 2023 · 0 comments

Comments

@xxleyi
Copy link
Owner

xxleyi commented Mar 25, 2023

only test in macOS.
if want make open url work in windows, need read this: https://superuser.com/questions/36728/can-i-launch-urls-from-command-line-in-windows

// v0.10.1

const std = @import("std");
const ChildProcess = std.ChildProcess;

fn ask_user(test_list: []const u8) ![]const u8 {
    const stdin = std.io.getStdIn().reader();
    const stdout = std.io.getStdOut().writer();
    try stdout.print("巧了,我现在有点拿不准你想用哪个目标分支,但我猜是下面当中的一个:\n", .{});
    var i: usize = 0;
    var splits = std.mem.split(u8, test_list, ", ");
    while (splits.next()) |line| {
        try stdout.print("{d}): {s}\n", .{ i, line });
        i += 1;
    }

    var buf: [10]u8 = undefined;
    var choose: i64 = -1;
    var reinput: bool = false;
    while (choose < 0 or choose >= i) {
        if (reinput) {
            try stdout.writeAll("\x1b[1A"); // move cursor up one line
        }
        reinput = true;
        try stdout.writeAll("\x1b[2K"); // erase current line
        try stdout.print("请输入 \")\" 前的数字,然后回车做出选择: ", .{});

        if (try stdin.readUntilDelimiterOrEof(buf[0..], '\n')) |user_input| {
            if (user_input.len > 0) {
                choose = std.fmt.parseInt(i64, user_input, 10) catch {
                    continue;
                };
            }
        }
    }
    splits = std.mem.split(u8, test_list, ", ");
    i = 0;
    while (splits.next()) |line| {
        if (i == choose) {
            return line;
        }
        i += 1;
    }
    unreachable;
}

pub fn main() anyerror!void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

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

    const user_custom_prefix = if (args.len > 1) args[1] else "";

    const remote_result = try ChildProcess.exec(.{
        .allocator = arena,
        .argv = &[_][]const u8{
            "git",
            "config",
            "--get",
            "remote.origin.url",
        },
    });
    var remote_trimmed = std.mem.trim(u8, remote_result.stdout, " \t\r\n");
    if (remote_trimmed.len == 0) {
        std.debug.print("未发现 git repo\n", .{});
        return;
    }
    remote_trimmed = remote_trimmed[19..(remote_trimmed.len - 4)];

    const remoteUrl = try std.fmt.bufPrint(try arena.alloc(u8, 23 + remote_trimmed.len), "https://code.byted.org/{s}", .{remote_trimmed});

    // std.debug.print("{s}\n", .{remoteUrl});

    const current_branch_result = try ChildProcess.exec(.{
        .allocator = arena,
        .argv = &[_][]const u8{
            "git",
            "branch",
            "--show-current",
        },
    });
    var current_branch = std.mem.trim(u8, current_branch_result.stdout, " \t\r\n");
    if (current_branch.len == 0) {
        std.debug.print("未发现 git repo\n", .{});
        return;
    }
    // std.debug.print("{s}\n", .{current_branch});

    const commits_result = try ChildProcess.exec(.{
        .allocator = arena,
        .argv = &[_][]const u8{ "git", "log", "--decorate", "--first-parent", "-n", "100" },
    });
    var splits = std.mem.split(u8, commits_result.stdout, "\n");
    const merge_url_template = "{s}/merge_requests/new?utf8=✓&merge_request[source_branch]={s}&merge_request[target_branch]={s}\n";
    while (splits.next()) |chunk| {
        if (std.mem.startsWith(u8, chunk, "commit ") and std.mem.count(u8, chunk, "(") > 0) {
            const branches = chunk[49..(chunk.len - 1)];
            var branches_splits = std.mem.split(u8, branches, ", ");
            var target_branch: []u8 = "";
            while (branches_splits.next()) |b_chunk| {
                const one_branch = if (std.mem.startsWith(u8, b_chunk, "origin/")) b_chunk[7..] else b_chunk;
                if (std.mem.count(u8, target_branch, one_branch) > 0) {
                    continue;
                }
                if ((user_custom_prefix.len > 0 and std.mem.startsWith(u8, one_branch, user_custom_prefix)) or
                    (user_custom_prefix.len == 0 and (std.mem.startsWith(u8, one_branch, "release") or
                    std.mem.startsWith(u8, one_branch, "master") or
                    std.mem.startsWith(u8, one_branch, "main"))))
                {
                    if (target_branch.len == 0) {
                        target_branch = try std.fmt.bufPrint(try arena.alloc(u8, target_branch.len + 2 + b_chunk.len), "{s}", .{one_branch});
                    } else {
                        target_branch = try std.fmt.bufPrint(try arena.alloc(u8, target_branch.len + 2 + b_chunk.len), "{s}, {s}", .{ target_branch, one_branch });
                    }
                }
            }
            if (target_branch.len > 0) {
                // target_branch = try std.fmt.bufPrint(try arena.alloc(u8, target_branch.len + 20), "{s}, {s}", .{ target_branch, "just-test" });
                if (std.mem.count(u8, target_branch, ", ") > 0) {
                    const user_choose = try ask_user(target_branch);
                    target_branch = try std.fmt.bufPrint(try arena.alloc(u8, user_choose.len), "{s}", .{user_choose});
                }

                const merge_url = try std.fmt.bufPrint(try arena.alloc(u8, merge_url_template.len + remoteUrl.len + current_branch.len + target_branch.len), merge_url_template, .{ remoteUrl, current_branch, target_branch });

                // 自动将当前分支 push 到对应的同名远程分支
                _ = try ChildProcess.exec(.{
                    .allocator = arena,
                    .argv = &[_][]const u8{ "git", "push", "--set-upstream", "origin", current_branch },
                });
                _ = try ChildProcess.exec(.{
                    .allocator = arena,
                    .argv = &[_][]const u8{
                        "open",
                        merge_url,
                    },
                });
                return;
            }
        }
    }
    if (user_custom_prefix.len > 0) {
        std.debug.print("使用 {s} 作为前缀,未匹配到合适的目标分支,请检查前缀拼写\n", .{args[1]});
        return;
    }
    std.debug.print("使用默认的 release, master, main 作为前缀,未匹配到合适的目标分支,可手动传入自定义前缀\n", .{});
}

test "use clear console output" {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("\nHello, world!\n", .{});
    try stdout.writeAll("\x1b[1A"); // move cursor up one line
    try stdout.writeAll("\x1b[2K"); // erase current line
    try stdout.print("Goodbye, world!\n", .{});
}
@xxleyi xxleyi changed the title zig code: try find valid merge target branch zig code: auto git push, find suitable target branch and open merge url in browser Mar 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant