🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

git-stack-cli

Package Overview
Dependencies
Maintainers
1
Versions
127
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

git-stack-cli - npm Package Compare versions

Comparing version
0.2.1
to
0.3.0
+10
dist/app/Brackets copy.js
import * as React from "react";
import * as Ink from "ink";
export function Brackets(props) {
const color = "#f97316";
const text_color = "#06b6d4";
return (React.createElement(Ink.Text, { color: text_color },
React.createElement(Ink.Text, { color: color }, "["),
props.children,
React.createElement(Ink.Text, { color: color }, "]")));
}
import * as React from "react";
import * as Ink from "ink";
export function Command(props) {
const text_color = "#f97316";
return (React.createElement(Ink.Text, { bold: true, color: text_color }, props.children));
}
import * as React from "react";
import * as Ink from "ink";
export function Url(props) {
const text_color = "#38bdf8";
return React.createElement(Ink.Text, { color: text_color }, props.children);
}
import * as React from "react";
import * as Ink from "ink";
export function Url(props) {
const text_color = "#38bdf8";
return (React.createElement(Ink.Text, { bold: true, color: text_color }, props.children));
}
export function env() {
const DEV = process.env["DEV"];
return { DEV };
}
+2
-0
// prettier-ignore
export const METADATA = {
"username": "magus",
"repo_path": "magus/git-multi-diff-playground",
"head": "1a50c5fe3cd129547c5c34a54d1611ec06ab213e",

@@ -4,0 +6,0 @@ "merge_base": "9528176b12abf81c779bc5244afc7d760f6fa422",

+1
-1

@@ -5,3 +5,3 @@ import * as React from "react";

const color = "#f97316";
const text_color = "#06b6d4";
const text_color = "#38bdf8";
return (React.createElement(Ink.Text, { color: text_color },

@@ -8,0 +8,0 @@ React.createElement(Ink.Text, { color: color }, "["),

@@ -11,11 +11,12 @@ import * as React from "react";

const state = Store.useState((state) => state);
const debug = Store.useState((state) => state.argv?.debug);
const argv = Store.useState((state) => state.argv);
React.useEffect(function debugMessageOnce() {
if (debug) {
actions.output(React.createElement(Ink.Text, { color: "yellow" }, "Debug mode enabled"));
actions.debug(React.createElement(Ink.Text, { color: "yellow" }, "Debug mode enabled"));
if (argv?.verbose) {
actions.debug(React.createElement(Ink.Text, { dimColor: true }, JSON.stringify(argv, null, 2)));
}
}, [debug]);
}, [argv]);
React.useEffect(function syncStateJson() {
invariant(state.cwd, "state.cwd must exist");
if (!debug) {
if (!argv?.["write-state-json"]) {
return;

@@ -30,4 +31,4 @@ }

fs.writeFileSync(output_file, content);
}, [debug, state]);
}, [argv, state]);
return null;
}

@@ -5,49 +5,66 @@ import * as React from "react";

import { is_command_available } from "../core/is_command_available.js";
import { match_group } from "../core/match_group.js";
import { Await } from "./Await.js";
import { Command } from "./Command.js";
import { Parens } from "./Parens.js";
import { Store } from "./Store.js";
import { Url } from "./Url.js";
export function DependencyCheck(props) {
const actions = Store.useActions();
return (React.createElement(Await, { fallback: React.createElement(Ink.Box, null,
React.createElement(Ink.Text, null, "Checking git install...")), function: async () => {
return (React.createElement(Await, { fallback: React.createElement(Ink.Text, { color: "yellow" },
"Checking ",
React.createElement(Command, null, "git"),
" install..."), function: async () => {
if (is_command_available("git")) {
return;
}
actions.output(React.createElement(Ink.Text, null,
React.createElement(Ink.Text, { color: "yellow" }, "git"),
actions.output(React.createElement(Ink.Text, { color: "yellow" },
React.createElement(Command, null, "git"),
" must be installed."));
actions.exit(2);
} },
React.createElement(Await, { fallback: React.createElement(Ink.Box, null,
React.createElement(Ink.Text, null, "Checking gh install...")), function: async () => {
React.createElement(Await, { fallback: React.createElement(Ink.Text, { color: "yellow" },
React.createElement(Ink.Text, null,
"Checking ",
React.createElement(Command, null, "gh"),
" install...")), function: async () => {
if (is_command_available("gh")) {
return;
}
actions.output(React.createElement(Ink.Text, null,
React.createElement(Ink.Text, { color: "yellow" }, "gh"),
actions.output(React.createElement(Ink.Text, { color: "yellow" },
React.createElement(Command, null, "gh"),
" must be installed."));
actions.output(React.createElement(Ink.Box, { flexDirection: "row", gap: 1 },
React.createElement(Ink.Text, null, "Visit"),
React.createElement(Ink.Text, { color: "#38bdf8" }, "https://cli.github.com"),
React.createElement(Ink.Text, null, "to install the github cli"),
React.createElement(Ink.Text, null,
"(",
React.createElement(Ink.Text, { color: "yellow" }, "gh"),
")")));
actions.output(React.createElement(Ink.Text, { color: "yellow" },
React.createElement(Ink.Text, null, "Visit "),
React.createElement(Url, null, "https://cli.github.com"),
React.createElement(Ink.Text, null, " to install the github cli "),
React.createElement(Parens, null,
React.createElement(Command, null, "gh"))));
actions.exit(3);
} },
React.createElement(Await, { fallback: React.createElement(Ink.Box, null,
React.createElement(Ink.Text, null, "Checking gh auth status...")), function: async () => {
const gh_auth_status_cli = await cli(`gh auth status`, {
React.createElement(Await, { fallback: React.createElement(Ink.Text, { color: "yellow" },
React.createElement(Ink.Text, null,
"Checking ",
React.createElement(Command, null, "gh auth status"),
"...")), function: async () => {
const auth_output = await cli(`gh auth status`, {
ignoreExitCode: true,
});
if (gh_auth_status_cli.code === 0) {
if (auth_output.code === 0) {
const username = match_group(auth_output.stdout, RE.auth_username, "username");
actions.set((state) => {
state.username = username;
});
return;
}
actions.output(React.createElement(Ink.Box, { flexDirection: "row", gap: 1 },
React.createElement(Ink.Text, { color: "yellow" }, "gh"),
React.createElement(Ink.Text, null, "requires login, please run"),
React.createElement(Ink.Text, null,
React.createElement(Ink.Text, { color: "yellow" }, "gh auth login"))));
actions.output(React.createElement(Ink.Text, { color: "yellow" },
React.createElement(Command, null, "gh"),
React.createElement(Ink.Text, null, " requires login, please run "),
React.createElement(Command, null, "gh auth login")));
actions.exit(4);
} }, props.children))));
}
const RE = {
// Logged in to github.com as magus
auth_username: /Logged in to github.com as (?<username>[^\s]+)/,
};

@@ -7,2 +7,3 @@ import * as React from "react";

import * as json from "../core/json.js";
import { match_group } from "../core/match_group.js";
import { Await } from "./Await.js";

@@ -38,9 +39,16 @@ import { Store } from "./Store.js";

}
// git@github.com:magus/git-multi-diff-playground.git
// https://github.com/magus/git-multi-diff-playground.git
const origin_url = (await cli(`git config --get remote.origin.url`)).stdout;
const repo_path = match_group(origin_url, RE.repo_path, "repo_path");
const branch_name = (await cli("git rev-parse --abbrev-ref HEAD")).stdout;
Store.setState((state) => {
state.repo_path = repo_path;
state.head = head;
state.merge_base = merge_base;
state.branch_name = branch_name;
});
try {
const commit_range = await CommitMetadata.range();
Store.setState((state) => {
state.head = head;
state.merge_base = merge_base;
state.branch_name = branch_name;
state.commit_range = commit_range;

@@ -57,1 +65,6 @@ state.step = "status";

}
const RE = {
// git@github.com:magus/git-multi-diff-playground.git
// https://github.com/magus/git-multi-diff-playground.git
repo_path: /(?<repo_path>[^:^/]+\/[^/]+)\.git/,
};

@@ -25,2 +25,4 @@ import * as React from "react";

return React.createElement(ManualRebase, null);
case "manual-rebase-no-sync":
return React.createElement(ManualRebase, { skipSync: true });
case "post-rebase-status":

@@ -27,0 +29,0 @@ return React.createElement(PostRebaseStatus, null);

@@ -12,6 +12,6 @@ import * as React from "react";

import { Store } from "./Store.js";
export function ManualRebase() {
return (React.createElement(Await, { fallback: React.createElement(Ink.Text, { color: "yellow" }, "Rebasing commits..."), function: run }));
export function ManualRebase(props) {
return (React.createElement(Await, { fallback: React.createElement(Ink.Text, { color: "yellow" }, "Rebasing commits..."), function: () => run(props) }));
}
async function run() {
async function run(props) {
const state = Store.getState();

@@ -65,18 +65,20 @@ const actions = state.actions;

"..."));
// push to origin since github requires commit shas to line up perfectly
await cli(`git push -f origin HEAD:${group.id}`);
if (group.pr) {
// ensure base matches pr in github
await github.pr_base(group.id, group.base);
if (!props.skipSync) {
// push to origin since github requires commit shas to line up perfectly
await cli(`git push -f origin HEAD:${group.id}`);
if (group.pr) {
// ensure base matches pr in github
await github.pr_base(group.id, group.base);
}
else {
// delete local group branch if leftover
await cli(`git branch -D ${group.id}`, { ignoreExitCode: true });
// move to temporary branch for creating pr
await cli(`git checkout -b ${group.id}`);
// create pr in github
await github.pr_create(group.id, group.base);
// move back to temp branch
await cli(`git checkout ${temp_branch_name}`);
}
}
else {
// delete local group branch if leftover
await cli(`git branch -D ${group.id}`, { ignoreExitCode: true });
// move to temporary branch for creating pr
await cli(`git checkout -b ${group.id}`);
// create pr in github
await github.pr_create(group.id, group.base);
// move back to temp branch
await cli(`git checkout ${temp_branch_name}`);
}
}

@@ -83,0 +85,0 @@ // after all commits have been cherry-picked and amended

import * as React from "react";
import * as Ink from "ink";
export function Parens(props) {
const color = "#06b6d4";
const color = "#38bdf8";
return (React.createElement(Ink.Text, null,

@@ -6,0 +6,0 @@ React.createElement(Ink.Text, { color: color }, "("),

@@ -62,3 +62,3 @@ import * as React from "react";

const inputLower = input.toLowerCase();
if (unassigned_count === 0 && inputLower === "s") {
if (unassigned_count === 0 && (inputLower === "r" || inputLower === "s")) {
actions.set((state) => {

@@ -71,3 +71,9 @@ state.commit_map = {};

}
state.step = "manual-rebase";
switch (inputLower) {
case "s":
state.step = "manual-rebase";
break;
case "r":
state.step = "manual-rebase-no-sync";
}
});

@@ -159,10 +165,19 @@ return;

"reate"),
" a new group")))) : (React.createElement(Ink.Text, null,
"🎉 Done! Press ",
React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, "s"),
" to ",
React.createElement(Ink.Text, { bold: true, color: "#22c55e" },
React.createElement(Parens, null, "s"),
"ync"),
" the commits to Github")),
" a new group")))) : (React.createElement(React.Fragment, null,
React.createElement(Ink.Text, null,
"🎉 Done! Press ",
React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, "s"),
" to ",
React.createElement(Ink.Text, { bold: true, color: "#22c55e" },
React.createElement(Parens, null, "s"),
"ync"),
" the commits to Github"),
React.createElement(Ink.Text, { color: "gray" },
React.createElement(Ink.Text, null, "Press "),
React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, "r"),
" to locally ",
React.createElement(Ink.Text, { bold: true, color: "#22c55e" },
React.createElement(Parens, null, "r"),
"ebase"),
" only"))),
React.createElement(Ink.Box, null,

@@ -169,0 +184,0 @@ React.createElement(Ink.Text, { color: "gray" },

@@ -18,3 +18,3 @@ import * as React from "react";

};
if (!group.pr) {
if (group.id === commit_range.UNASSIGNED) {
row.icon = "⭑";

@@ -35,5 +35,11 @@ row.status = "NEW";

}
row.title = group.pr.title;
row.count = `${group.pr.commits.length}/${group.commits.length}`;
row.url = group.pr.url;
if (group.pr) {
row.title = group.pr.title;
row.count = `${group.pr.commits.length}/${group.commits.length}`;
row.url = group.pr.url;
}
else {
row.title = group.id;
row.count = `0/${group.commits.length}`;
}
}

@@ -40,0 +46,0 @@ row_list.push(row);

@@ -10,2 +10,4 @@ import * as React from "react";

cwd: null,
username: null,
repo_path: null,
head: null,

@@ -12,0 +14,0 @@ merge_base: null,

@@ -5,4 +5,6 @@ import * as React from "react";

export function YesNoPrompt(props) {
const [answer, set_answer] = React.useState("");
Ink.useInput((input) => {
const inputLower = input.toLowerCase();
set_answer(inputLower);
switch (inputLower) {

@@ -21,5 +23,5 @@ case "n":

React.createElement(Ink.Text, { color: "gray" },
React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, "Y"),
"/",
React.createElement(Ink.Text, { color: "#ef4444" }, "n"))))));
answer && answer !== "y" ? null : (React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, "Y")),
answer ? null : React.createElement(Ink.Text, null, "/"),
answer && answer !== "n" ? null : (React.createElement(Ink.Text, { color: "#ef4444" }, "n")))))));
}
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
export async function command() {
return yargs(hideBin(process.argv))
const debug_argv = await yargs(hideBin(process.argv))
.option("debug", {
type: "boolean",
description: "Enable debug mode with more options for debugging",
})
.help(false).argv;
if (!debug_argv.debug) {
return NormalMode();
}
return DebugMode();
}
function NormalMode() {
return (yargs(hideBin(process.argv))
.option("force", {
type: "boolean",
description: "force",
description: "Force sync even if no changes are detected",
})
.option("check", {
type: "boolean",
description: "check",
description: "Print status table without syncing",
})
.option("debug", {
type: "boolean",
description: "debug",
description: "Enable debug mode with more options for debugging",
})
// disallow unknown options
.strict()
.help().argv);
}
function DebugMode() {
return (yargs(hideBin(process.argv))
.option("force", {
type: "boolean",
description: "Force sync even if no changes are detected",
})
.option("check", {
type: "boolean",
description: "Print status table without syncing",
})
.option("debug", {
type: "boolean",
description: "Enable debug mode with more options for debugging",
})
.option("verbose", {
type: "boolean",
description: "Log extra information during execution",
})
.option("write-state-json", {
type: "boolean",
description: "Write state to local json file for debugging",
})
.option("mock-metadata", {
type: "boolean",
description: "mock-metadata",
description: "Mock local store metadata for testing",
})
.help().argv;
// disallow unknown options
.strict()
.help().argv);
}

@@ -23,2 +23,3 @@ import * as child from "node:child_process";

const result = {
command,
code: code || 0,

@@ -44,3 +45,3 @@ stdout: stdout.trimEnd(),

const code = spawn_return.status || 0;
return { code, stdout, stderr, output };
return { command, code, stdout, stderr, output };
};

@@ -5,2 +5,5 @@ import * as Metadata from "./Metadata.js";

export async function range(commit_map) {
// gather all open prs in repo first
// cheaper query to populate cache
await github.pr_list();
const commit_list = await get_commit_list();

@@ -15,5 +18,4 @@ let invalid = false;

}
const pr = commit.pr;
if (!pr) {
// console.debug("INVALID", "MISSING PR", commit.message);
if (!id) {
// console.debug("INVALID", "MISSING ID", commit.message);
invalid = true;

@@ -38,3 +40,3 @@ }

id,
pr,
pr: null,
base: null,

@@ -53,2 +55,8 @@ dirty: false,

const group = group_value_list[i];
if (group.id !== UNASSIGNED) {
const pr_result = await github.pr_status(group.id);
if (pr_result && pr_result.state === "OPEN") {
group.pr = pr_result;
}
}
// console.debug("group", group.pr?.title.substring(0, 40));

@@ -124,9 +132,2 @@ // console.debug(" ", "id", group.id);

const message = display_message(raw_message);
let pr = null;
if (branch_id) {
const pr_result = await github.pr_status(branch_id);
if (pr_result && pr_result.state === "OPEN") {
pr = pr_result;
}
}
return {

@@ -136,3 +137,2 @@ sha,

raw_message,
pr,
branch_id,

@@ -139,0 +139,0 @@ };

import * as React from "react";
import * as Ink from "ink";
import { Brackets } from "../app/Brackets.js";
import { Store } from "../app/Store.js";
import { cli } from "./cli.js";
export async function pr_status(branch) {
import { invariant } from "./invariant.js";
// prettier-ignore
const JSON_FIELDS = "--json number,state,baseRefName,headRefName,commits,title,url";
export async function pr_list() {
const state = Store.getState();
const actions = state.actions;
const result = await cli(`gh pr view ${branch} --json number,state,baseRefName,headRefName,commits,title,url`, {
const username = state.username;
const repo_path = state.repo_path;
invariant(username, "username must exist");
invariant(repo_path, "repo_path must exist");
const cli_result = await cli(`gh pr list --repo ${repo_path} --author ${username} --state open ${JSON_FIELDS}`, {
ignoreExitCode: true,
});
if (result.code !== 0) {
actions.output(React.createElement(Ink.Text, { color: "#ef4444" }, result.output));
actions.set((state) => {
state.step = "github-api-error";
});
throw new Error("Unable to fetch PR status");
if (cli_result.code !== 0) {
handle_error(cli_result.output);
}
const result_pr_list = JSON.parse(cli_result.stdout);
actions.debug(React.createElement(Ink.Text, { dimColor: true },
React.createElement(Ink.Text, null, "Github cache "),
React.createElement(Ink.Text, { bold: true, color: "yellow" }, result_pr_list.length),
React.createElement(Ink.Text, null, " open PRs from "),
React.createElement(Brackets, null, repo_path),
React.createElement(Ink.Text, null, " authored by "),
React.createElement(Brackets, null, username)));
actions.set((state) => {
for (const pr of result_pr_list) {
state.pr[pr.headRefName] = pr;
}
});
return result_pr_list;
}
export async function pr_status(branch) {
const state = Store.getState();
const actions = state.actions;
const username = state.username;
const repo_path = state.repo_path;
invariant(username, "username must exist");
invariant(repo_path, "repo_path must exist");
const cache = state.pr[branch];

@@ -34,3 +60,10 @@ if (cache) {

React.createElement(Ink.Text, { dimColor: true }, branch)));
const pr = JSON.parse(result.stdout);
const cli_result = await cli(`gh pr view ${branch} --repo ${repo_path} ${JSON_FIELDS}`, {
ignoreExitCode: true,
});
if (cli_result.code !== 0) {
// handle_error(cli_result.output);
return null;
}
const pr = JSON.parse(cli_result.stdout);
actions.set((state) => {

@@ -42,6 +75,20 @@ state.pr[pr.headRefName] = pr;

export async function pr_create(branch, base) {
await cli(`gh pr create --fill --head ${branch} --base ${base}`);
const cli_result = await cli(`gh pr create --fill --head ${branch} --base ${base}`);
if (cli_result.code !== 0) {
handle_error(cli_result.output);
}
}
export async function pr_base(branch, base) {
await cli(`gh pr edit ${branch} --base ${base}`);
const cli_result = await cli(`gh pr edit ${branch} --base ${base}`);
if (cli_result.code !== 0) {
handle_error(cli_result.output);
}
}
function handle_error(output) {
const state = Store.getState();
const actions = state.actions;
actions.set((state) => {
state.step = "github-api-error";
});
throw new Error(output);
}
{
"name": "git-stack-cli",
"version": "0.2.1",
"version": "0.3.0",
"description": "",

@@ -5,0 +5,0 @@ "author": "magus",

@@ -18,4 +18,4 @@ # git-stack-cli

git stack
git multi-diff
git stack --debug
git multi-diff --debug
```

@@ -26,2 +26,4 @@

- point value for querying all pr for user vs individual pr status (current)?
- select commit ranges

@@ -28,0 +30,0 @@ - capture PR title when creating new group