git-stack-cli 2.7.4 → 2.7.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "2.7.4",
3
+ "version": "2.7.6",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  "tsconfig.json"
23
23
  ],
24
24
  "scripts": {
25
- "dev": "pnpm run build --watch",
25
+ "dev": "pnpm run build --dev --watch",
26
26
  "build": "bun run scripts/bun-build.ts",
27
27
  "compile": "bun run scripts/bun-compile.ts",
28
28
  "release:npm": "bun run scripts/release-npm.ts",
@@ -2,7 +2,10 @@ import * as fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import * as util from "util";
4
4
 
5
+ import type { BuildConfig } from "bun";
6
+
5
7
  import * as file from "~/core/file";
8
+ import { get_define } from "~/core/get_define";
6
9
  import { get_local_iso } from "~/core/get_local_iso";
7
10
  import { spawn } from "~/core/spawn";
8
11
 
@@ -13,6 +16,10 @@ const parsed_args = util.parseArgs({
13
16
  type: "boolean",
14
17
  default: false,
15
18
  },
19
+ dev: {
20
+ type: "boolean",
21
+ default: false,
22
+ },
16
23
  verbose: {
17
24
  type: "boolean",
18
25
  default: false,
@@ -24,6 +31,7 @@ const parsed_args = util.parseArgs({
24
31
 
25
32
  const WATCH = parsed_args.values.watch;
26
33
  const VERBOSE = parsed_args.values.verbose;
34
+ const DEV = parsed_args.values.dev;
27
35
 
28
36
  function log(...args: any[]) {
29
37
  const timestamp = get_local_iso(new Date());
@@ -38,38 +46,25 @@ if (VERBOSE) {
38
46
 
39
47
  const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
40
48
 
41
- async function get_define() {
42
- const PACKAGE_JSON = await file.read_json(path.join(REPO_ROOT, "package.json"));
43
- const GIT_SEQUENCE_EDITOR_SCRIPT_PATH = path.join(REPO_ROOT, "scripts", "git-sequence-editor.sh");
44
- const UNSAFE_GIT_SEQUENCE_EDITOR_SCRIPT = await file.read_text(GIT_SEQUENCE_EDITOR_SCRIPT_PATH);
45
- const GIT_SEQUENCE_EDITOR_SCRIPT = UNSAFE_GIT_SEQUENCE_EDITOR_SCRIPT.replace(/`/g, "\\`");
49
+ const define = await get_define();
46
50
 
47
- const define = {
48
- "process.env.NODE_ENV": JSON.stringify("production"),
49
- "process.env.CLI_VERSION": JSON.stringify(String(PACKAGE_JSON.version)),
50
- "process.env.GIT_SEQUENCE_EDITOR_SCRIPT": JSON.stringify(GIT_SEQUENCE_EDITOR_SCRIPT),
51
- };
51
+ const BUILD_CONFIG = {
52
+ entrypoints: ["./src/index.tsx"],
53
+ outdir: "./dist/js",
54
+ target: "node",
55
+ env: "inline",
56
+ format: "esm",
57
+ sourcemap: "inline",
58
+ define,
59
+ minify: !DEV,
60
+ } satisfies BuildConfig;
52
61
 
53
- return define;
54
- }
62
+ log({ BUILD_CONFIG });
55
63
 
56
64
  async function run_build() {
57
65
  const start = Date.now();
58
66
 
59
- const define = await get_define();
60
-
61
- if (VERBOSE) {
62
- log({ define });
63
- }
64
-
65
- const result = await Bun.build({
66
- entrypoints: ["./src/index.tsx"],
67
- outdir: "./dist/js",
68
- target: "node",
69
- env: "inline",
70
- format: "esm",
71
- define,
72
- });
67
+ const result = await Bun.build(BUILD_CONFIG);
73
68
 
74
69
  const duration_ms = Date.now() - start;
75
70
 
@@ -2,6 +2,7 @@ import path from "node:path";
2
2
  import * as util from "util";
3
3
 
4
4
  import * as file from "~/core/file";
5
+ import { get_define } from "~/core/get_define";
5
6
  import { spawn } from "~/core/spawn";
6
7
 
7
8
  const parsed_args = util.parseArgs({
@@ -30,12 +31,12 @@ if (VERBOSE) {
30
31
 
31
32
  const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
32
33
  const DIST_DIR = path.join(REPO_ROOT, "dist");
33
- const INPUT_JS = path.join(DIST_DIR, "js", "index.js");
34
+ const INPUT_JS = path.join(REPO_ROOT, "/src/index.tsx");
34
35
 
35
36
  if (!(await file.exists(INPUT_JS))) {
36
37
  console.error(`❌ Missing ${path.relative(REPO_ROOT, INPUT_JS)}`);
37
38
  console.debug("Run `pnpm run build` first to generate the input file.");
38
- process.exit(12);
39
+ process.exit(6);
39
40
  }
40
41
 
41
42
  if (TARGET) {
@@ -58,11 +59,23 @@ async function compile_target(args: CompileTargetArgs) {
58
59
 
59
60
  const start = Date.now();
60
61
 
62
+ // https://bun.com/docs/bundler/executables#build-time-constants
63
+ const defines: Array<string> = [];
64
+ for (const [key, value] of Object.entries(await get_define())) {
65
+ // --define BUILD_VERSION='"1.2.3"'
66
+ defines.push("--define", `${key}=${value}`);
67
+ }
68
+
61
69
  // pnpm bun build --compile --target=bun-darwin-arm64 ./dist/js/index.js --outfile git-stack-bun-darwin-arm64
70
+ // https://bun.com/docs/bundler/executables
62
71
  const bun_compile = await spawn.sync([
63
72
  "bun",
64
73
  "build",
65
74
  "--compile",
75
+ "--minify",
76
+ "--sourcemap",
77
+ // "--bytecode",
78
+ ...defines,
66
79
  `--target=${args.target}`,
67
80
  INPUT_JS,
68
81
  `--outfile=${outfile}`,
@@ -0,0 +1,21 @@
1
+ import path from "node:path";
2
+
3
+ import * as file from "~/core/file";
4
+ import { spawn } from "~/core/spawn";
5
+
6
+ const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
7
+
8
+ export async function get_define() {
9
+ const PACKAGE_JSON = await file.read_json(path.join(REPO_ROOT, "package.json"));
10
+ const GIT_SEQUENCE_EDITOR_SCRIPT_PATH = path.join(REPO_ROOT, "scripts", "git-sequence-editor.sh");
11
+ const UNSAFE_GIT_SEQUENCE_EDITOR_SCRIPT = await file.read_text(GIT_SEQUENCE_EDITOR_SCRIPT_PATH);
12
+ const GIT_SEQUENCE_EDITOR_SCRIPT = UNSAFE_GIT_SEQUENCE_EDITOR_SCRIPT.replace(/`/g, "\\`");
13
+
14
+ const define = {
15
+ "process.env.NODE_ENV": JSON.stringify("production"),
16
+ "process.env.CLI_VERSION": JSON.stringify(String(PACKAGE_JSON.version)),
17
+ "process.env.GIT_SEQUENCE_EDITOR_SCRIPT": JSON.stringify(GIT_SEQUENCE_EDITOR_SCRIPT),
18
+ };
19
+
20
+ return define;
21
+ }
@@ -4,5 +4,5 @@
4
4
  if (!process.env.GS_RELEASE_NPM) {
5
5
  console.error("Must publish using `pnpm run release:npm`");
6
6
  console.error();
7
- process.exit(10);
7
+ process.exit(5);
8
8
  }
package/src/app/App.tsx CHANGED
@@ -95,6 +95,8 @@ function MaybeMain() {
95
95
  return <Update />;
96
96
  } else if (positional_list.has("config")) {
97
97
  return <Config />;
98
+ } else if (positional_list.has("api")) {
99
+ return <GithubApiError exit />;
98
100
  } else if (positional_list.has("rebase")) {
99
101
  return (
100
102
  <DependencyCheck>
package/src/app/Exit.tsx CHANGED
@@ -2,6 +2,7 @@ import * as React from "react";
2
2
 
3
3
  import * as Ink from "ink-cjs";
4
4
 
5
+ import { FormatText } from "~/app/FormatText";
5
6
  import { Store } from "~/app/Store";
6
7
  import { cli } from "~/core/cli";
7
8
  import { colors } from "~/core/colors";
@@ -37,6 +38,14 @@ Exit.handle_exit = async function handle_exit(props: Props) {
37
38
  exit_code = await state.abort_handler();
38
39
  }
39
40
 
41
+ if (!state.argv.verbose && props.code > 0) {
42
+ actions.output(
43
+ <Ink.Text color={colors.gray}>
44
+ <FormatText message="Try again with `--verbose` to see more information." />
45
+ </Ink.Text>,
46
+ );
47
+ }
48
+
40
49
  // restore git stash if necessary
41
50
  if (state.is_dirty_check_stash) {
42
51
  await cli("git stash pop");
@@ -10,11 +10,15 @@ import { cli } from "~/core/cli";
10
10
  import { colors } from "~/core/colors";
11
11
  import * as date from "~/core/date";
12
12
 
13
- export function GithubApiError() {
14
- return <Await fallback={null} function={run} />;
13
+ type Props = {
14
+ exit?: boolean;
15
+ };
16
+
17
+ export function GithubApiError(props: Props) {
18
+ return <Await fallback={null} function={() => run(props)} />;
15
19
  }
16
20
 
17
- async function run() {
21
+ async function run(props: Props) {
18
22
  const actions = Store.getState().actions;
19
23
 
20
24
  const res = await cli(`gh api https://api.github.com/rate_limit`);
@@ -42,7 +46,7 @@ async function run() {
42
46
  }
43
47
 
44
48
  actions.output(
45
- <Ink.Text dimColor>
49
+ <Ink.Text>
46
50
  <Ink.Text>{"Github "}</Ink.Text>
47
51
 
48
52
  <Brackets>graphql</Brackets>
@@ -71,4 +75,8 @@ async function run() {
71
75
  </Parens>
72
76
  </Ink.Text>,
73
77
  );
78
+
79
+ if (props.exit) {
80
+ actions.exit(0);
81
+ }
74
82
  }
@@ -12,6 +12,8 @@ export function HandleCtrlCSigint() {
12
12
 
13
13
  Ink.useInput((input, key) => {
14
14
  handle_input().catch((err) => {
15
+ // eslint-disable-next-line no-console
16
+ console.error("🚨 HandleCtrlCSigint catch");
15
17
  // eslint-disable-next-line no-console
16
18
  console.error(err);
17
19
  });
@@ -27,10 +29,17 @@ export function HandleCtrlCSigint() {
27
29
  );
28
30
 
29
31
  await sleep(1);
30
- actions.exit(235);
32
+
33
+ try {
34
+ actions.exit(HandleCtrlCSigint.ExitCode);
35
+ } catch {
36
+ // ignore intentional throw from actions.exit
37
+ }
31
38
  }
32
39
  }
33
40
  });
34
41
 
35
42
  return null;
36
43
  }
44
+
45
+ HandleCtrlCSigint.ExitCode = 235;
package/src/app/Store.tsx CHANGED
@@ -141,6 +141,8 @@ const BaseStore = createStore<State>()(
141
141
 
142
142
  actions: {
143
143
  exit(code, args) {
144
+ const clear = args?.clear ?? true;
145
+
144
146
  set((state) => {
145
147
  if (args?.quiet ?? code === 0) {
146
148
  state.exit_mode = "quiet";
@@ -148,11 +150,13 @@ const BaseStore = createStore<State>()(
148
150
  state.exit_mode = "normal";
149
151
  }
150
152
 
151
- let clear = args?.clear ?? true;
152
-
153
153
  const node = <Exit clear={clear} code={code} />;
154
154
  state.mutate.output(state, { node });
155
155
  });
156
+
157
+ if (code > 0) {
158
+ throw new Error(`exit(${JSON.stringify({ code, clear })})`);
159
+ }
156
160
  },
157
161
 
158
162
  clear() {
@@ -135,7 +135,7 @@ async function run() {
135
135
  actions.error("Try again with `--verbose` to see more information.");
136
136
  }
137
137
 
138
- actions.exit(18);
138
+ actions.exit(15);
139
139
  }
140
140
 
141
141
  function get_push_group_list() {
package/src/command.ts CHANGED
@@ -60,7 +60,14 @@ export async function command(argv: string[], options: CommandOptions = {}) {
60
60
  .command(
61
61
  "config",
62
62
  "Generate a one-time configuration json based on the passed arguments",
63
- (yargs) => yargs.options(DefaultOptions),
63
+ (yargs) => yargs,
64
+ )
65
+
66
+ .command(
67
+ //
68
+ "api",
69
+ "Check Github API quota and rate limits",
70
+ (yargs) => yargs,
64
71
  )
65
72
 
66
73
  .option("verbose", GlobalOptions.verbose)
@@ -69,7 +69,7 @@ function parse_env_config() {
69
69
  console.error(`ERROR GIT_STACK_CONFIG=${GIT_STACK_CONFIG}`);
70
70
  // eslint-disable-next-line no-console
71
71
  console.error("ERROR GIT_STACK_CONFIG environment variable is not valid JSON");
72
- process.exit(18);
72
+ process.exit(6);
73
73
  }
74
74
  }
75
75
 
@@ -75,7 +75,7 @@ Rebase.run = async function run(props: Props) {
75
75
  actions.error(err.message);
76
76
  }
77
77
 
78
- actions.exit(20);
78
+ actions.exit(8);
79
79
  }
80
80
 
81
81
  actions.debug("start CommitMetadata.range");
@@ -38,7 +38,7 @@ export class ErrorBoundary extends React.Component<Props, State> {
38
38
  // remove first line of component_stack
39
39
  component_stack = component_stack.split("\n").slice(1).join("\n");
40
40
  this.setState({ component_stack }, async () => {
41
- await Exit.handle_exit({ code: 30, clear: true });
41
+ await Exit.handle_exit({ code: 5, clear: false });
42
42
  });
43
43
  }
44
44
  }
@@ -154,7 +154,7 @@ GitReviseTodo.execute = async function grt_execute(args: ExecuteArgs) {
154
154
  actions.error("🚨 git revise failed to sign commit");
155
155
  actions.error("💡 Try again with `--no-revise-sign`?");
156
156
  actions.error("\n\n");
157
- actions.exit(21);
157
+ actions.exit(18);
158
158
  }
159
159
  }
160
160
  }
package/src/core/cache.ts CHANGED
@@ -22,17 +22,15 @@ export function cache<T, E>(cacheable: Cacheable<T>) {
22
22
  // cacheable is a function to allow deferred reads
23
23
  // this will call cacheable to kickoff async promise
24
24
  if (!suspender) {
25
- suspender = Promise.resolve().then(() => {
26
- cacheable()
27
- .then((res: T) => {
28
- status = "success";
29
- response = res;
30
- })
31
- .catch((err: E) => {
32
- status = "error";
33
- response = err;
34
- });
35
- });
25
+ suspender = cacheable()
26
+ .then((res: T) => {
27
+ status = "success";
28
+ response = res;
29
+ })
30
+ .catch((err: E) => {
31
+ status = "error";
32
+ response = err;
33
+ });
36
34
  }
37
35
 
38
36
  switch (status) {
package/src/core/git.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Store } from "~/app/Store";
1
2
  import * as Metadata from "~/core/Metadata";
2
3
  import { cli } from "~/core/cli";
3
4
 
@@ -20,6 +21,15 @@ export async function get_commits(dot_range: string) {
20
21
 
21
22
  const [sha, full_message] = record.split(SEP.field);
22
23
 
24
+ // ensure sha is a hex string, otherwise we should throw an error
25
+ if (!RE.git_sha.test(sha)) {
26
+ const actions = Store.getState().actions;
27
+ const sep_values = JSON.stringify(Object.values(SEP));
28
+ const message = `unable to parse git commits, maybe commit message contained ${sep_values}`;
29
+ actions.error(message);
30
+ actions.exit(19);
31
+ }
32
+
23
33
  const metadata = Metadata.read(full_message);
24
34
  const branch_id = metadata.id;
25
35
  const subject_line = metadata.subject || "";
@@ -53,3 +63,7 @@ const SEP = {
53
63
  };
54
64
 
55
65
  const FORMAT = `%H${SEP.field}%B${SEP.record}`;
66
+
67
+ const RE = {
68
+ git_sha: /^[0-9a-fA-F]{40}$/,
69
+ };
package/src/index.tsx CHANGED
@@ -74,9 +74,11 @@ import { pretty_json } from "~/core/pretty_json";
74
74
  }
75
75
  }
76
76
  } catch (err) {
77
+ console.error("🚨 main catch");
77
78
  console.error(err);
78
79
  process.exit(236);
79
80
  }
80
81
  })().catch((err) => {
82
+ console.error("🚨 index catch");
81
83
  console.error(err);
82
84
  });