git-stack-cli
Advanced tools
| 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 }; | ||
| } |
| export {}; |
| // prettier-ignore | ||
| export const METADATA = { | ||
| "username": "magus", | ||
| "repo_path": "magus/git-multi-diff-playground", | ||
| "head": "1a50c5fe3cd129547c5c34a54d1611ec06ab213e", | ||
@@ -4,0 +6,0 @@ "merge_base": "9528176b12abf81c779bc5244afc7d760f6fa422", |
@@ -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/, | ||
| }; |
+2
-0
@@ -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); |
+20
-18
@@ -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"))))))); | ||
| } |
+46
-6
| 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); | ||
| } |
+2
-1
@@ -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 @@ }; |
+58
-11
| 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); | ||
| } |
+1
-1
| { | ||
| "name": "git-stack-cli", | ||
| "version": "0.2.1", | ||
| "version": "0.3.0", | ||
| "description": "", | ||
@@ -5,0 +5,0 @@ "author": "magus", |
+4
-2
@@ -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 |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
175383
4.34%73
8.96%4251
4.5%73
2.82%3
50%