git-stack-cli 2.1.0-beta → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/js/index.js +81 -45
- package/package.json +2 -1
- package/src/app/DetectInitialPR.tsx +2 -2
- package/src/app/Store.tsx +18 -3
- package/src/core/GitReviseTodo.ts +2 -2
- package/src/core/cli.ts +6 -3
- package/src/core/get_tmp_dir.ts +12 -0
- package/src/core/github.tsx +19 -10
- package/src/index.tsx +7 -0
package/dist/js/index.js
CHANGED
|
@@ -28099,6 +28099,7 @@ var require_last = __commonJS((exports, module) => {
|
|
|
28099
28099
|
|
|
28100
28100
|
// src/index.tsx
|
|
28101
28101
|
var React55 = __toESM(require_react(), 1);
|
|
28102
|
+
import fs14 from "node:fs/promises";
|
|
28102
28103
|
|
|
28103
28104
|
// node_modules/.pnpm/ink-cjs@4.4.1_@types+react@18.2.33_react-devtools-core@4.19.1_react@18.2.0/node_modules/ink-cjs/build/render.js
|
|
28104
28105
|
import { Stream } from "node:stream";
|
|
@@ -37089,11 +37090,24 @@ var BaseStore = createStore()(immer2((set2, get) => ({
|
|
|
37089
37090
|
state.mutate.output(state, { node });
|
|
37090
37091
|
});
|
|
37091
37092
|
},
|
|
37092
|
-
error(
|
|
37093
|
-
|
|
37094
|
-
|
|
37093
|
+
error(error) {
|
|
37094
|
+
let node;
|
|
37095
|
+
if (typeof error === "string") {
|
|
37096
|
+
node = /* @__PURE__ */ React16.createElement(Text, {
|
|
37097
|
+
color: colors.red
|
|
37098
|
+
}, error);
|
|
37099
|
+
} else if (error instanceof Error) {
|
|
37100
|
+
node = /* @__PURE__ */ React16.createElement(Box_default, {
|
|
37101
|
+
flexDirection: "column"
|
|
37102
|
+
}, /* @__PURE__ */ React16.createElement(Text, {
|
|
37103
|
+
color: colors.red
|
|
37104
|
+
}, error.stack));
|
|
37105
|
+
} else {
|
|
37106
|
+
node = /* @__PURE__ */ React16.createElement(Text, {
|
|
37095
37107
|
color: colors.red
|
|
37096
|
-
},
|
|
37108
|
+
}, `Unhandled error: ${JSON.stringify(error)}`);
|
|
37109
|
+
}
|
|
37110
|
+
set2((state) => {
|
|
37097
37111
|
state.mutate.output(state, { node });
|
|
37098
37112
|
});
|
|
37099
37113
|
},
|
|
@@ -37279,9 +37293,7 @@ async function cli(unsafe_command, unsafe_options) {
|
|
|
37279
37293
|
};
|
|
37280
37294
|
state.actions.set((state2) => state2.mutate.end_pending_output(state2, id));
|
|
37281
37295
|
state.actions.debug(log.end(result));
|
|
37282
|
-
state.actions.debug(
|
|
37283
|
-
state.actions.debug(`
|
|
37284
|
-
`);
|
|
37296
|
+
state.actions.debug(log.output(result));
|
|
37285
37297
|
if (!options.ignoreExitCode && result.code !== 0) {
|
|
37286
37298
|
reject(new Error(log.error(result)));
|
|
37287
37299
|
} else {
|
|
@@ -37317,7 +37329,7 @@ cli.sync = function cli_sync(unsafe_command, unsafe_options) {
|
|
|
37317
37329
|
duration
|
|
37318
37330
|
};
|
|
37319
37331
|
state.actions.debug(log.end(result));
|
|
37320
|
-
state.actions.debug(
|
|
37332
|
+
state.actions.debug(log.output(result));
|
|
37321
37333
|
if (!options.ignoreExitCode && result.code !== 0) {
|
|
37322
37334
|
throw new Error(log.error(result));
|
|
37323
37335
|
}
|
|
@@ -37335,6 +37347,10 @@ var log = {
|
|
|
37335
37347
|
const { command, code, duration } = result;
|
|
37336
37348
|
return `[end] ${command} (exit_code=${code} duration=${duration})`;
|
|
37337
37349
|
},
|
|
37350
|
+
output(result) {
|
|
37351
|
+
return `${result.output}
|
|
37352
|
+
`;
|
|
37353
|
+
},
|
|
37338
37354
|
error(result) {
|
|
37339
37355
|
const { command, code, duration } = result;
|
|
37340
37356
|
return `${command} (exit_code=${code} duration=${duration})`;
|
|
@@ -38015,9 +38031,21 @@ var RE3 = {
|
|
|
38015
38031
|
|
|
38016
38032
|
// src/core/github.tsx
|
|
38017
38033
|
var React24 = __toESM(require_react(), 1);
|
|
38034
|
+
import crypto from "node:crypto";
|
|
38035
|
+
import fs9 from "node:fs/promises";
|
|
38036
|
+
import path6 from "node:path";
|
|
38037
|
+
|
|
38038
|
+
// src/core/get_tmp_dir.ts
|
|
38018
38039
|
import fs8 from "node:fs/promises";
|
|
38019
38040
|
import os2 from "node:os";
|
|
38020
38041
|
import path5 from "node:path";
|
|
38042
|
+
async function get_tmp_dir() {
|
|
38043
|
+
const dir = path5.join(os2.tmpdir(), "git-stack-cli");
|
|
38044
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
38045
|
+
return dir;
|
|
38046
|
+
}
|
|
38047
|
+
|
|
38048
|
+
// src/core/github.tsx
|
|
38021
38049
|
async function pr_list() {
|
|
38022
38050
|
const state = Store.getState();
|
|
38023
38051
|
const actions = state.actions;
|
|
@@ -38146,15 +38174,21 @@ async function pr_draft(args) {
|
|
|
38146
38174
|
}
|
|
38147
38175
|
var JSON_FIELDS = "--json id,number,state,baseRefName,headRefName,commits,title,body,url,isDraft";
|
|
38148
38176
|
async function gh_json(command) {
|
|
38149
|
-
|
|
38177
|
+
let hash = crypto.createHash("md5").update(command).digest("hex");
|
|
38178
|
+
let tmp_filename = safe_filename(`gh_json-${hash}`);
|
|
38179
|
+
const tmp_pr_json = path6.join(await get_tmp_dir(), `${tmp_filename}.json`);
|
|
38150
38180
|
const options = { ignoreExitCode: true };
|
|
38151
38181
|
const cli_result = await cli(`gh ${command} > ${tmp_pr_json}`, options);
|
|
38152
38182
|
if (cli_result.code !== 0) {
|
|
38153
38183
|
return new Error(cli_result.output);
|
|
38154
38184
|
}
|
|
38155
|
-
const json_str = await
|
|
38156
|
-
|
|
38157
|
-
|
|
38185
|
+
const json_str = String(await fs9.readFile(tmp_pr_json));
|
|
38186
|
+
try {
|
|
38187
|
+
const json = JSON.parse(json_str);
|
|
38188
|
+
return json;
|
|
38189
|
+
} catch (error) {
|
|
38190
|
+
return new Error(`gh_json JSON.parse: ${error}`);
|
|
38191
|
+
}
|
|
38158
38192
|
}
|
|
38159
38193
|
function handle_error(output) {
|
|
38160
38194
|
const state = Store.getState();
|
|
@@ -38166,14 +38200,15 @@ function handle_error(output) {
|
|
|
38166
38200
|
}
|
|
38167
38201
|
async function write_body_file(args) {
|
|
38168
38202
|
invariant(args.body, "args.body must exist");
|
|
38169
|
-
|
|
38170
|
-
|
|
38171
|
-
temp_filename = temp_filename.replace(RE4.non_alphanumeric_dash, "-");
|
|
38172
|
-
const temp_path = path5.join(temp_dir, temp_filename);
|
|
38203
|
+
let tmp_filename = safe_filename(`git-stack-body-${args.base}`);
|
|
38204
|
+
const temp_path = path6.join(await get_tmp_dir(), tmp_filename);
|
|
38173
38205
|
await safe_rm(temp_path);
|
|
38174
|
-
await
|
|
38206
|
+
await fs9.writeFile(temp_path, args.body);
|
|
38175
38207
|
return temp_path;
|
|
38176
38208
|
}
|
|
38209
|
+
function safe_filename(value) {
|
|
38210
|
+
return value.replace(RE4.non_alphanumeric_dash, "-");
|
|
38211
|
+
}
|
|
38177
38212
|
var RE4 = {
|
|
38178
38213
|
non_alphanumeric_dash: /[^a-zA-Z0-9_-]+/g
|
|
38179
38214
|
};
|
|
@@ -38327,9 +38362,8 @@ function lines(value) {
|
|
|
38327
38362
|
var UNASSIGNED = "unassigned";
|
|
38328
38363
|
|
|
38329
38364
|
// src/core/GitReviseTodo.ts
|
|
38330
|
-
import
|
|
38331
|
-
import
|
|
38332
|
-
import path6 from "node:path";
|
|
38365
|
+
import fs10 from "node:fs/promises";
|
|
38366
|
+
import path7 from "node:path";
|
|
38333
38367
|
function GitReviseTodo(args) {
|
|
38334
38368
|
const commit_list = [];
|
|
38335
38369
|
const group_list = args.commit_range.group_list;
|
|
@@ -38365,7 +38399,7 @@ GitReviseTodo.todo = function todo(args) {
|
|
|
38365
38399
|
return todo2;
|
|
38366
38400
|
};
|
|
38367
38401
|
GitReviseTodo.execute = async function grt_execute(args) {
|
|
38368
|
-
const tmp_git_sequence_editor_path =
|
|
38402
|
+
const tmp_git_sequence_editor_path = path7.join(await get_tmp_dir(), "git-sequence-editor.sh");
|
|
38369
38403
|
const GIT_SEQUENCE_EDITOR_SCRIPT = `#!/bin/sh
|
|
38370
38404
|
|
|
38371
38405
|
# Example
|
|
@@ -38403,8 +38437,8 @@ echo "------ END ------"
|
|
|
38403
38437
|
echo "$GIT_REVISE_TODO" > "$git_revise_todo_path"
|
|
38404
38438
|
`;
|
|
38405
38439
|
invariant(GIT_SEQUENCE_EDITOR_SCRIPT, "GIT_SEQUENCE_EDITOR_SCRIPT must exist");
|
|
38406
|
-
await
|
|
38407
|
-
await
|
|
38440
|
+
await fs10.writeFile(tmp_git_sequence_editor_path, GIT_SEQUENCE_EDITOR_SCRIPT);
|
|
38441
|
+
await fs10.chmod(tmp_git_sequence_editor_path, "755");
|
|
38408
38442
|
const git_revise_todo = GitReviseTodo(args);
|
|
38409
38443
|
const command = [
|
|
38410
38444
|
`GIT_EDITOR="${tmp_git_sequence_editor_path}"`,
|
|
@@ -38481,7 +38515,7 @@ function DetectInitialPR(props) {
|
|
|
38481
38515
|
const branch_name2 = Store.getState().branch_name;
|
|
38482
38516
|
const commit_range = Store.getState().commit_range;
|
|
38483
38517
|
invariant(branch_name2, "branch_name must exist");
|
|
38484
|
-
invariant(commit_range, "
|
|
38518
|
+
invariant(commit_range, "commit_range must exist");
|
|
38485
38519
|
try {
|
|
38486
38520
|
let has_existing_metadata = false;
|
|
38487
38521
|
for (const commit2 of commit_range.commit_list) {
|
|
@@ -38513,7 +38547,7 @@ function DetectInitialPR(props) {
|
|
|
38513
38547
|
const branch_name2 = Store.getState().branch_name;
|
|
38514
38548
|
const commit_range = import_cloneDeep.default(Store.getState().commit_range);
|
|
38515
38549
|
invariant(branch_name2, "branch_name must exist");
|
|
38516
|
-
invariant(commit_range, "
|
|
38550
|
+
invariant(commit_range, "commit_range must exist");
|
|
38517
38551
|
for (const group of commit_range.group_list) {
|
|
38518
38552
|
group.id = branch_name2;
|
|
38519
38553
|
group.title = state.pr?.title || "-";
|
|
@@ -38845,16 +38879,16 @@ var React32 = __toESM(require_react(), 1);
|
|
|
38845
38879
|
|
|
38846
38880
|
// src/commands/Rebase.tsx
|
|
38847
38881
|
var React31 = __toESM(require_react(), 1);
|
|
38848
|
-
import
|
|
38882
|
+
import fs11 from "node:fs";
|
|
38849
38883
|
|
|
38850
38884
|
// src/core/short_id.ts
|
|
38851
|
-
import
|
|
38885
|
+
import crypto2 from "node:crypto";
|
|
38852
38886
|
function short_id() {
|
|
38853
38887
|
const timestamp = Date.now();
|
|
38854
38888
|
const js_max_bits = 53;
|
|
38855
38889
|
const timestamp_bits = Math.floor(Math.log2(timestamp)) + 1;
|
|
38856
38890
|
const padding_bits = js_max_bits - timestamp_bits;
|
|
38857
|
-
const random =
|
|
38891
|
+
const random = crypto2.randomInt(0, Math.pow(2, padding_bits));
|
|
38858
38892
|
const combined = interleave_bits(timestamp, random);
|
|
38859
38893
|
return encode(combined);
|
|
38860
38894
|
}
|
|
@@ -38998,7 +39032,7 @@ Rebase.run = async function run4() {
|
|
|
38998
39032
|
cli.sync(`git clean -df`, spawn_options);
|
|
38999
39033
|
cli.sync(`git checkout ${branch_name}`, spawn_options);
|
|
39000
39034
|
cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
|
|
39001
|
-
if (
|
|
39035
|
+
if (fs11.existsSync(cwd2)) {
|
|
39002
39036
|
process.chdir(cwd2);
|
|
39003
39037
|
}
|
|
39004
39038
|
cli.sync(`pwd`, spawn_options);
|
|
@@ -39021,7 +39055,7 @@ function LocalMergeRebase() {
|
|
|
39021
39055
|
|
|
39022
39056
|
// src/app/ManualRebase.tsx
|
|
39023
39057
|
var React33 = __toESM(require_react(), 1);
|
|
39024
|
-
import
|
|
39058
|
+
import fs12 from "node:fs";
|
|
39025
39059
|
function ManualRebase() {
|
|
39026
39060
|
return /* @__PURE__ */ React33.createElement(Await, {
|
|
39027
39061
|
fallback: /* @__PURE__ */ React33.createElement(Text, {
|
|
@@ -39121,7 +39155,7 @@ async function run5() {
|
|
|
39121
39155
|
cli.sync(`git clean -df`, spawn_options);
|
|
39122
39156
|
cli.sync(`git checkout ${branch_name}`, spawn_options);
|
|
39123
39157
|
cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
|
|
39124
|
-
if (
|
|
39158
|
+
if (fs12.existsSync(cwd2)) {
|
|
39125
39159
|
process.chdir(cwd2);
|
|
39126
39160
|
}
|
|
39127
39161
|
cli.sync(`pwd`, spawn_options);
|
|
@@ -39380,8 +39414,8 @@ function PreLocalMergeRebase() {
|
|
|
39380
39414
|
|
|
39381
39415
|
// src/app/PreManualRebase.tsx
|
|
39382
39416
|
var React38 = __toESM(require_react(), 1);
|
|
39383
|
-
import
|
|
39384
|
-
import
|
|
39417
|
+
import fs13 from "node:fs/promises";
|
|
39418
|
+
import path8 from "node:path";
|
|
39385
39419
|
function PreManualRebase() {
|
|
39386
39420
|
return /* @__PURE__ */ React38.createElement(Await, {
|
|
39387
39421
|
fallback: null,
|
|
@@ -39403,7 +39437,7 @@ async function run7() {
|
|
|
39403
39437
|
for (const key of PR_TEMPLATE_KEY_LIST) {
|
|
39404
39438
|
const pr_template_fn = PR_TEMPLATE[key];
|
|
39405
39439
|
if (await safe_exists(pr_template_fn(repo_root))) {
|
|
39406
|
-
pr_template_body = await
|
|
39440
|
+
pr_template_body = await fs13.readFile(pr_template_fn(repo_root), "utf-8");
|
|
39407
39441
|
actions.output(/* @__PURE__ */ React38.createElement(FormatText, {
|
|
39408
39442
|
wrapper: /* @__PURE__ */ React38.createElement(Text, {
|
|
39409
39443
|
color: colors.yellow
|
|
@@ -39418,7 +39452,7 @@ async function run7() {
|
|
|
39418
39452
|
}
|
|
39419
39453
|
let pr_templates = [];
|
|
39420
39454
|
if (await safe_exists(PR_TEMPLATE.TemplateDir(repo_root))) {
|
|
39421
|
-
pr_templates = await
|
|
39455
|
+
pr_templates = await fs13.readdir(PR_TEMPLATE.TemplateDir(repo_root));
|
|
39422
39456
|
}
|
|
39423
39457
|
actions.set((state2) => {
|
|
39424
39458
|
state2.pr_template_body = pr_template_body;
|
|
@@ -39441,10 +39475,10 @@ async function run7() {
|
|
|
39441
39475
|
});
|
|
39442
39476
|
}
|
|
39443
39477
|
var PR_TEMPLATE = Object.freeze({
|
|
39444
|
-
Github: (root) =>
|
|
39445
|
-
Root: (root) =>
|
|
39446
|
-
Docs: (root) =>
|
|
39447
|
-
TemplateDir: (root) =>
|
|
39478
|
+
Github: (root) => path8.join(root, ".github", "pull_request_template.md"),
|
|
39479
|
+
Root: (root) => path8.join(root, "pull_request_template.md"),
|
|
39480
|
+
Docs: (root) => path8.join(root, "docs", "pull_request_template.md"),
|
|
39481
|
+
TemplateDir: (root) => path8.join(root, ".github", "PULL_REQUEST_TEMPLATE")
|
|
39448
39482
|
});
|
|
39449
39483
|
var PR_TEMPLATE_KEY_LIST = Object.keys(PR_TEMPLATE);
|
|
39450
39484
|
|
|
@@ -40426,7 +40460,7 @@ function Providers(props) {
|
|
|
40426
40460
|
|
|
40427
40461
|
// src/app/RebaseCheck.tsx
|
|
40428
40462
|
var React48 = __toESM(require_react(), 1);
|
|
40429
|
-
import
|
|
40463
|
+
import path9 from "node:path";
|
|
40430
40464
|
function reducer5(state, patch) {
|
|
40431
40465
|
return { ...state, ...patch };
|
|
40432
40466
|
}
|
|
@@ -40464,8 +40498,8 @@ function RebaseCheck(props) {
|
|
|
40464
40498
|
try {
|
|
40465
40499
|
const git_dir = (await cli(`git rev-parse --absolute-git-dir`)).stdout;
|
|
40466
40500
|
let is_rebase = false;
|
|
40467
|
-
is_rebase ||= await safe_exists(
|
|
40468
|
-
is_rebase ||= await safe_exists(
|
|
40501
|
+
is_rebase ||= await safe_exists(path9.join(git_dir, "rebase-apply"));
|
|
40502
|
+
is_rebase ||= await safe_exists(path9.join(git_dir, "rebase-merge"));
|
|
40469
40503
|
const status = is_rebase ? "prompt" : "done";
|
|
40470
40504
|
patch({ status });
|
|
40471
40505
|
} catch (err) {
|
|
@@ -42021,9 +42055,9 @@ var parser = new YargsParser({
|
|
|
42021
42055
|
format,
|
|
42022
42056
|
normalize,
|
|
42023
42057
|
resolve: resolve2,
|
|
42024
|
-
require: (
|
|
42058
|
+
require: (path10) => {
|
|
42025
42059
|
if (true) {
|
|
42026
|
-
return __require(
|
|
42060
|
+
return __require(path10);
|
|
42027
42061
|
} else
|
|
42028
42062
|
;
|
|
42029
42063
|
}
|
|
@@ -45606,7 +45640,7 @@ var yargs_default = Yargs;
|
|
|
45606
45640
|
|
|
45607
45641
|
// src/command.ts
|
|
45608
45642
|
async function command2() {
|
|
45609
|
-
return yargs_default(hideBin(process.argv)).usage("Usage: git stack [command] [options]").command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions)).command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) => yargs.positional("commit", FixupOptions.commit)).command("log [args...]", "Print an abbreviated log with numbered commits, useful for git stack fixup", (yargs) => yargs.strict(false)).command("rebase", "Update local branch via rebase with latest changes from origin master branch", (yargs) => yargs).option("verbose", GlobalOptions.verbose).wrap(123).strict().version("2.1.
|
|
45643
|
+
return yargs_default(hideBin(process.argv)).usage("Usage: git stack [command] [options]").command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions)).command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) => yargs.positional("commit", FixupOptions.commit)).command("log [args...]", "Print an abbreviated log with numbered commits, useful for git stack fixup", (yargs) => yargs.strict(false)).command("rebase", "Update local branch via rebase with latest changes from origin master branch", (yargs) => yargs).option("verbose", GlobalOptions.verbose).wrap(123).strict().version("2.1.2").showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`").help("help", "Show usage via `git stack help`").argv;
|
|
45610
45644
|
}
|
|
45611
45645
|
var GlobalOptions = {
|
|
45612
45646
|
verbose: {
|
|
@@ -45715,6 +45749,8 @@ var FixupOptions = {
|
|
|
45715
45749
|
maybe_verbose_help();
|
|
45716
45750
|
process.exit(238);
|
|
45717
45751
|
});
|
|
45752
|
+
const tmp_dir = await get_tmp_dir();
|
|
45753
|
+
await fs14.rm(tmp_dir, { recursive: true });
|
|
45718
45754
|
const ink = render_default(/* @__PURE__ */ React55.createElement(App2, null), {
|
|
45719
45755
|
exitOnCtrlC: false
|
|
45720
45756
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-stack-cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "magus",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"zustand": "^4.4.4"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
+
"@oven/bun-darwin-aarch64": "1.1.42",
|
|
55
56
|
"@types/chalk": "^2.2.0",
|
|
56
57
|
"@types/lodash": "^4.17.7",
|
|
57
58
|
"@types/luxon": "^3.4.2",
|
|
@@ -108,7 +108,7 @@ export function DetectInitialPR(props: Props) {
|
|
|
108
108
|
const commit_range = Store.getState().commit_range;
|
|
109
109
|
|
|
110
110
|
invariant(branch_name, "branch_name must exist");
|
|
111
|
-
invariant(commit_range, "
|
|
111
|
+
invariant(commit_range, "commit_range must exist");
|
|
112
112
|
|
|
113
113
|
try {
|
|
114
114
|
let has_existing_metadata = false;
|
|
@@ -149,7 +149,7 @@ export function DetectInitialPR(props: Props) {
|
|
|
149
149
|
const commit_range = cloneDeep(Store.getState().commit_range);
|
|
150
150
|
|
|
151
151
|
invariant(branch_name, "branch_name must exist");
|
|
152
|
-
invariant(commit_range, "
|
|
152
|
+
invariant(commit_range, "commit_range must exist");
|
|
153
153
|
|
|
154
154
|
for (const group of commit_range.group_list) {
|
|
155
155
|
group.id = branch_name;
|
package/src/app/Store.tsx
CHANGED
|
@@ -84,7 +84,7 @@ export type State = {
|
|
|
84
84
|
unmount(): void;
|
|
85
85
|
newline(): void;
|
|
86
86
|
json(value: pretty_json.JSONValue): void;
|
|
87
|
-
error(
|
|
87
|
+
error(error: unknown): void;
|
|
88
88
|
output(node: React.ReactNode): void;
|
|
89
89
|
debug(node: React.ReactNode, id?: string): void;
|
|
90
90
|
|
|
@@ -176,9 +176,24 @@ const BaseStore = createStore<State>()(
|
|
|
176
176
|
});
|
|
177
177
|
},
|
|
178
178
|
|
|
179
|
-
error(
|
|
179
|
+
error(error) {
|
|
180
|
+
let node: React.ReactNode;
|
|
181
|
+
|
|
182
|
+
if (typeof error === "string") {
|
|
183
|
+
node = <Ink.Text color={colors.red}>{error}</Ink.Text>;
|
|
184
|
+
} else if (error instanceof Error) {
|
|
185
|
+
node = (
|
|
186
|
+
<Ink.Box flexDirection="column">
|
|
187
|
+
<Ink.Text color={colors.red}>{error.stack}</Ink.Text>
|
|
188
|
+
</Ink.Box>
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
node = (
|
|
192
|
+
<Ink.Text color={colors.red}>{`Unhandled error: ${JSON.stringify(error)}`}</Ink.Text>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
180
196
|
set((state) => {
|
|
181
|
-
const node = <Ink.Text color={colors.red}>{message}</Ink.Text>;
|
|
182
197
|
state.mutate.output(state, { node });
|
|
183
198
|
});
|
|
184
199
|
},
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
|
|
5
4
|
import * as Metadata from "~/core/Metadata";
|
|
6
5
|
import { cli } from "~/core/cli";
|
|
6
|
+
import { get_tmp_dir } from "~/core/get_tmp_dir";
|
|
7
7
|
import { invariant } from "~/core/invariant";
|
|
8
8
|
import { safe_rm } from "~/core/safe_rm";
|
|
9
9
|
|
|
@@ -109,7 +109,7 @@ GitReviseTodo.todo = function todo(args: CommitListArgs) {
|
|
|
109
109
|
|
|
110
110
|
GitReviseTodo.execute = async function grt_execute(args: ExecuteArgs) {
|
|
111
111
|
// generate temporary directory and drop sequence editor script
|
|
112
|
-
const tmp_git_sequence_editor_path = path.join(
|
|
112
|
+
const tmp_git_sequence_editor_path = path.join(await get_tmp_dir(), "git-sequence-editor.sh");
|
|
113
113
|
|
|
114
114
|
// replaced at build time with literal contents of `scripts/git-sequence-editor.sh`
|
|
115
115
|
const GIT_SEQUENCE_EDITOR_SCRIPT = process.env.GIT_SEQUENCE_EDITOR_SCRIPT;
|
package/src/core/cli.ts
CHANGED
|
@@ -79,8 +79,7 @@ export async function cli(
|
|
|
79
79
|
|
|
80
80
|
state.actions.set((state) => state.mutate.end_pending_output(state, id));
|
|
81
81
|
state.actions.debug(log.end(result));
|
|
82
|
-
state.actions.debug(
|
|
83
|
-
state.actions.debug("\n");
|
|
82
|
+
state.actions.debug(log.output(result));
|
|
84
83
|
|
|
85
84
|
if (!options.ignoreExitCode && result.code !== 0) {
|
|
86
85
|
reject(new Error(log.error(result)));
|
|
@@ -131,7 +130,7 @@ cli.sync = function cli_sync(
|
|
|
131
130
|
};
|
|
132
131
|
|
|
133
132
|
state.actions.debug(log.end(result));
|
|
134
|
-
state.actions.debug(
|
|
133
|
+
state.actions.debug(log.output(result));
|
|
135
134
|
|
|
136
135
|
if (!options.ignoreExitCode && result.code !== 0) {
|
|
137
136
|
throw new Error(log.error(result));
|
|
@@ -154,6 +153,10 @@ const log = {
|
|
|
154
153
|
return `[end] ${command} (exit_code=${code} duration=${duration})`;
|
|
155
154
|
},
|
|
156
155
|
|
|
156
|
+
output(result: Return) {
|
|
157
|
+
return `${result.output}\n`;
|
|
158
|
+
},
|
|
159
|
+
|
|
157
160
|
error(result: Return) {
|
|
158
161
|
const { command, code, duration } = result;
|
|
159
162
|
return `${command} (exit_code=${code} duration=${duration})`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
export async function get_tmp_dir(): Promise<string> {
|
|
6
|
+
const dir = path.join(os.tmpdir(), "git-stack-cli");
|
|
7
|
+
|
|
8
|
+
// ensure tmp directory exists
|
|
9
|
+
await fs.mkdir(dir, { recursive: true });
|
|
10
|
+
|
|
11
|
+
return dir;
|
|
12
|
+
}
|
package/src/core/github.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
+
import crypto from "node:crypto";
|
|
3
4
|
import fs from "node:fs/promises";
|
|
4
|
-
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
|
|
7
7
|
import * as Ink from "ink-cjs";
|
|
@@ -10,6 +10,7 @@ import { Brackets } from "~/app/Brackets";
|
|
|
10
10
|
import { Store } from "~/app/Store";
|
|
11
11
|
import { cli } from "~/core/cli";
|
|
12
12
|
import { colors } from "~/core/colors";
|
|
13
|
+
import { get_tmp_dir } from "~/core/get_tmp_dir";
|
|
13
14
|
import { invariant } from "~/core/invariant";
|
|
14
15
|
import { safe_quote } from "~/core/safe_quote";
|
|
15
16
|
import { safe_rm } from "~/core/safe_rm";
|
|
@@ -228,7 +229,10 @@ const JSON_FIELDS = "--json id,number,state,baseRefName,headRefName,commits,titl
|
|
|
228
229
|
// consistent handle gh cli commands returning json
|
|
229
230
|
// redirect to tmp file to avoid scrollback overflow causing scrollback to be cleared
|
|
230
231
|
async function gh_json<T>(command: string): Promise<T | Error> {
|
|
231
|
-
|
|
232
|
+
// hash command for unique short string
|
|
233
|
+
let hash = crypto.createHash("md5").update(command).digest("hex");
|
|
234
|
+
let tmp_filename = safe_filename(`gh_json-${hash}`);
|
|
235
|
+
const tmp_pr_json = path.join(await get_tmp_dir(), `${tmp_filename}.json`);
|
|
232
236
|
|
|
233
237
|
const options = { ignoreExitCode: true };
|
|
234
238
|
const cli_result = await cli(`gh ${command} > ${tmp_pr_json}`, options);
|
|
@@ -238,9 +242,13 @@ async function gh_json<T>(command: string): Promise<T | Error> {
|
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
// read from file
|
|
241
|
-
const json_str = await fs.readFile(tmp_pr_json
|
|
242
|
-
|
|
243
|
-
|
|
245
|
+
const json_str = String(await fs.readFile(tmp_pr_json));
|
|
246
|
+
try {
|
|
247
|
+
const json = JSON.parse(json_str);
|
|
248
|
+
return json;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
return new Error(`gh_json JSON.parse: ${error}`);
|
|
251
|
+
}
|
|
244
252
|
}
|
|
245
253
|
|
|
246
254
|
function handle_error(output: string): never {
|
|
@@ -258,15 +266,12 @@ function handle_error(output: string): never {
|
|
|
258
266
|
async function write_body_file(args: EditPullRequestArgs) {
|
|
259
267
|
invariant(args.body, "args.body must exist");
|
|
260
268
|
|
|
261
|
-
const temp_dir = os.tmpdir();
|
|
262
|
-
|
|
263
269
|
// ensure unique filename is safe for filesystem
|
|
264
270
|
// base (group id) might contain slashes, e.g. dev/magus/gs-3cmrMBSUj
|
|
265
271
|
// the flashes would mess up the filesystem path to this file
|
|
266
|
-
let
|
|
267
|
-
temp_filename = temp_filename.replace(RE.non_alphanumeric_dash, "-");
|
|
272
|
+
let tmp_filename = safe_filename(`git-stack-body-${args.base}`);
|
|
268
273
|
|
|
269
|
-
const temp_path = path.join(
|
|
274
|
+
const temp_path = path.join(await get_tmp_dir(), tmp_filename);
|
|
270
275
|
|
|
271
276
|
await safe_rm(temp_path);
|
|
272
277
|
|
|
@@ -275,6 +280,10 @@ async function write_body_file(args: EditPullRequestArgs) {
|
|
|
275
280
|
return temp_path;
|
|
276
281
|
}
|
|
277
282
|
|
|
283
|
+
function safe_filename(value: string): string {
|
|
284
|
+
return value.replace(RE.non_alphanumeric_dash, "-");
|
|
285
|
+
}
|
|
286
|
+
|
|
278
287
|
type Commit = {
|
|
279
288
|
authoredDate: string; // "2023-10-22T23:13:35Z"
|
|
280
289
|
authors: [
|
package/src/index.tsx
CHANGED
|
@@ -4,11 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
|
|
7
|
+
import fs from "node:fs/promises";
|
|
8
|
+
|
|
7
9
|
import * as Ink from "ink-cjs";
|
|
8
10
|
|
|
9
11
|
import { App } from "~/app/App";
|
|
10
12
|
import { Store } from "~/app/Store";
|
|
11
13
|
import { command } from "~/command";
|
|
14
|
+
import { get_tmp_dir } from "~/core/get_tmp_dir";
|
|
12
15
|
import { pretty_json } from "~/core/pretty_json";
|
|
13
16
|
|
|
14
17
|
(async function main() {
|
|
@@ -33,6 +36,10 @@ import { pretty_json } from "~/core/pretty_json";
|
|
|
33
36
|
process.exit(238);
|
|
34
37
|
});
|
|
35
38
|
|
|
39
|
+
// cleanup leftover temporary files from previous run
|
|
40
|
+
const tmp_dir = await get_tmp_dir();
|
|
41
|
+
await fs.rm(tmp_dir, { recursive: true });
|
|
42
|
+
|
|
36
43
|
const ink = Ink.render(<App />, {
|
|
37
44
|
// If true, each update will be rendered as a separate output, without replacing the previous one.
|
|
38
45
|
// debug: true,
|