git-stack-cli 2.7.3 → 2.7.5

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.3",
3
+ "version": "2.7.5",
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>
@@ -218,7 +218,7 @@ export function AutoUpdate(props: Props) {
218
218
 
219
219
  switch (semver_result) {
220
220
  case 0: {
221
- info(
221
+ info_quiet(
222
222
  <Ink.Text>
223
223
  ✅ Everything up to date. <Brackets>{latest_version}</Brackets>
224
224
  </Ink.Text>,
@@ -248,7 +248,7 @@ export function AutoUpdate(props: Props) {
248
248
  }
249
249
 
250
250
  case -1: {
251
- info(
251
+ info_quiet(
252
252
  <FormatText
253
253
  message="⚠️ Local version {local_version} is newer than latest version {latest_version}"
254
254
  values={{
@@ -269,6 +269,10 @@ export function AutoUpdate(props: Props) {
269
269
  }
270
270
 
271
271
  function info(node: React.ReactNode) {
272
+ handle_output(node);
273
+ }
274
+
275
+ function info_quiet(node: React.ReactNode) {
272
276
  if (props_ref.current.verbose || props_ref.current.force) {
273
277
  handle_output(node);
274
278
  }
@@ -280,7 +284,7 @@ export function AutoUpdate(props: Props) {
280
284
  }
281
285
  }
282
286
  function abort(error: Error) {
283
- info(
287
+ info_quiet(
284
288
  <Ink.Text key="error" color={colors.red}>
285
289
  {error.message}
286
290
  </Ink.Text>,
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) {
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
  });