git-stack-cli 2.5.1 → 2.5.3

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.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
package/src/app/App.tsx CHANGED
@@ -19,6 +19,7 @@ import { VerboseDebugInfo } from "~/app/VerboseDebugInfo";
19
19
  import { Fixup } from "~/commands/Fixup";
20
20
  import { Log } from "~/commands/Log";
21
21
  import { Rebase } from "~/commands/Rebase";
22
+ import { Update } from "~/commands/Update";
22
23
  import { ErrorBoundary } from "~/components/ErrorBoundary";
23
24
  import { ExitingGate } from "~/components/ExitingGate";
24
25
 
@@ -28,7 +29,7 @@ export function App() {
28
29
  const ink = Store.useState((state) => state.ink);
29
30
  const argv = Store.useState((state) => state.argv);
30
31
 
31
- if (!ink || !argv) {
32
+ if (!ink || !argv || !argv.$0) {
32
33
  return null;
33
34
  }
34
35
 
@@ -42,6 +43,9 @@ export function App() {
42
43
  // </React.Fragment>
43
44
  // );
44
45
 
46
+ const positional_list = new Set(argv["_"]);
47
+ const is_update = positional_list.has("update") || positional_list.has("upgrade");
48
+
45
49
  return (
46
50
  <Providers>
47
51
  <ErrorBoundary>
@@ -51,11 +55,12 @@ export function App() {
51
55
  <ExitingGate>
52
56
  <AutoUpdate
53
57
  name="git-stack-cli"
54
- verbose={argv.verbose || argv.update}
55
- timeoutMs={argv.update ? 30 * 1000 : 2 * 1000}
58
+ verbose={argv.verbose}
59
+ force={is_update}
60
+ timeoutMs={is_update ? 30 * 1000 : 2 * 1000}
56
61
  onOutput={actions.output}
57
62
  onDone={() => {
58
- if (argv.update) {
63
+ if (is_update) {
59
64
  actions.exit(0);
60
65
  }
61
66
  }}
@@ -84,13 +89,19 @@ function MaybeMain() {
84
89
  return <Fixup />;
85
90
  } else if (positional_list.has("log")) {
86
91
  return <Log />;
92
+ } else if (positional_list.has("update")) {
93
+ return <Update />;
87
94
  } else if (positional_list.has("rebase")) {
88
95
  return (
89
- <GatherMetadata>
90
- <LocalCommitStatus>
91
- <Rebase />
92
- </LocalCommitStatus>
93
- </GatherMetadata>
96
+ <DependencyCheck>
97
+ <DirtyCheck>
98
+ <GatherMetadata>
99
+ <LocalCommitStatus>
100
+ <Rebase />
101
+ </LocalCommitStatus>
102
+ </GatherMetadata>
103
+ </DirtyCheck>
104
+ </DependencyCheck>
94
105
  );
95
106
  }
96
107
 
@@ -99,15 +110,15 @@ function MaybeMain() {
99
110
  {!argv.verbose ? null : <GithubApiError />}
100
111
 
101
112
  <DependencyCheck>
102
- <GatherMetadata>
103
- <DirtyCheck>
113
+ <DirtyCheck>
114
+ <GatherMetadata>
104
115
  <LocalCommitStatus>
105
116
  <DetectInitialPR>
106
117
  <Main />
107
118
  </DetectInitialPR>
108
119
  </LocalCommitStatus>
109
- </DirtyCheck>
110
- </GatherMetadata>
120
+ </GatherMetadata>
121
+ </DirtyCheck>
111
122
  </DependencyCheck>
112
123
  </React.Fragment>
113
124
  );
@@ -1,18 +1,15 @@
1
1
  import * as React from "react";
2
2
 
3
- import fs from "node:fs/promises";
4
- import path from "node:path";
5
-
6
3
  import * as Ink from "ink-cjs";
7
4
 
8
5
  import { Brackets } from "~/app/Brackets";
6
+ import { Command } from "~/app/Command";
9
7
  import { FormatText } from "~/app/FormatText";
10
8
  import { YesNoPrompt } from "~/app/YesNoPrompt";
11
9
  import { cli } from "~/core/cli";
12
10
  import { colors } from "~/core/colors";
13
11
  import { fetch_json } from "~/core/fetch_json";
14
12
  import { is_finite_value } from "~/core/is_finite_value";
15
- import { read_json } from "~/core/read_json";
16
13
  import { semver_compare } from "~/core/semver_compare";
17
14
  import { sleep } from "~/core/sleep";
18
15
 
@@ -20,6 +17,7 @@ type Props = {
20
17
  name: string;
21
18
  children: React.ReactNode;
22
19
  verbose?: boolean;
20
+ force?: boolean;
23
21
  timeoutMs?: number;
24
22
  onError?: (error: Error) => void;
25
23
  onOutput?: (output: React.ReactNode) => void;
@@ -31,6 +29,7 @@ type State = {
31
29
  local_version: null | string;
32
30
  latest_version: null | string;
33
31
  status: "init" | "prompt" | "install" | "done" | "exit";
32
+ is_brew_bun_standalone: boolean;
34
33
  };
35
34
 
36
35
  function reducer(state: State, patch: Partial<State>) {
@@ -48,6 +47,7 @@ export function AutoUpdate(props: Props) {
48
47
  local_version: null,
49
48
  latest_version: null,
50
49
  status: "init",
50
+ is_brew_bun_standalone: false,
51
51
  });
52
52
 
53
53
  function handle_output(node: React.ReactNode) {
@@ -62,11 +62,18 @@ export function AutoUpdate(props: Props) {
62
62
 
63
63
  React.useEffect(() => {
64
64
  let status: State["status"] = "done";
65
- let local_version: string | null = null;
66
65
  let latest_version: string | null = null;
66
+ let is_brew_bun_standalone = false;
67
+
68
+ const local_version = process.env.CLI_VERSION;
69
+ const is_output = props_ref.current.verbose || props_ref.current.force;
67
70
 
68
71
  async function auto_update() {
69
- if (props_ref.current.verbose) {
72
+ if (!local_version) {
73
+ throw new Error("Auto update requires process.env.CLI_VERSION to be set");
74
+ }
75
+
76
+ if (is_output) {
70
77
  handle_output(<Ink.Text key="init">Checking for latest version...</Ink.Text>);
71
78
  }
72
79
 
@@ -86,22 +93,24 @@ export function AutoUpdate(props: Props) {
86
93
  throw new Error("Unable to retrieve latest version from npm");
87
94
  }
88
95
 
89
- const script_path = await fs.realpath(process.argv[1]);
90
- const script_dir = path.dirname(script_path);
96
+ const binary_path = process.argv[1];
91
97
 
92
- // dist/ts/index.js
93
- const package_json_path = path.join(script_dir, "..", "..", "package.json");
98
+ if (props_ref.current.verbose) {
99
+ handle_output(<Ink.Text dimColor>{JSON.stringify({ binary_path })}</Ink.Text>);
100
+ }
94
101
 
95
- type PackageJson = { version: string };
96
- const package_json = await read_json<PackageJson>(package_json_path);
102
+ is_brew_bun_standalone = binary_path.startsWith("/$bunfs");
97
103
 
98
- if (!package_json) {
99
- // unable to find read package.json, skip auto update
100
- throw new Error(`Unable to read package.json [${package_json_path}]`);
104
+ if (props_ref.current.verbose) {
105
+ if (is_brew_bun_standalone) {
106
+ handle_output(
107
+ <Ink.Text dimColor>brew install detected (compiled bun standalone)</Ink.Text>,
108
+ );
109
+ } else {
110
+ handle_output(<Ink.Text dimColor>npm install detected</Ink.Text>);
111
+ }
101
112
  }
102
113
 
103
- local_version = package_json.version;
104
-
105
114
  if (props_ref.current.verbose) {
106
115
  handle_output(
107
116
  <FormatText
@@ -119,6 +128,13 @@ export function AutoUpdate(props: Props) {
119
128
  const semver_result = semver_compare(latest_version, local_version);
120
129
 
121
130
  if (semver_result === 0) {
131
+ if (is_output) {
132
+ handle_output(
133
+ <Ink.Text>
134
+ ✅ Everything up to date. <Brackets>{latest_version}</Brackets>
135
+ </Ink.Text>,
136
+ );
137
+ }
122
138
  return;
123
139
  }
124
140
 
@@ -137,10 +153,10 @@ export function AutoUpdate(props: Props) {
137
153
 
138
154
  auto_update()
139
155
  .then(() => {
140
- patch({ status, local_version, latest_version });
156
+ patch({ status, local_version, latest_version, is_brew_bun_standalone });
141
157
  })
142
158
  .catch((error) => {
143
- patch({ status, error, local_version, latest_version });
159
+ patch({ status, error, local_version, latest_version, is_brew_bun_standalone });
144
160
  onError(error);
145
161
 
146
162
  if (props_ref.current.verbose) {
@@ -161,13 +177,36 @@ export function AutoUpdate(props: Props) {
161
177
  case "init":
162
178
  return null;
163
179
 
164
- case "prompt":
180
+ case "prompt": {
181
+ let install_command = "";
182
+ if (state.is_brew_bun_standalone) {
183
+ install_command = `npm install -g ${props.name}@latest`;
184
+ } else {
185
+ install_command = "brew upgrade magus/git-stack/git-stack";
186
+ }
187
+
165
188
  return (
166
189
  <YesNoPrompt
167
190
  message={
168
- <Ink.Text color={colors.yellow}>
169
- New version available, would you like to update?
170
- </Ink.Text>
191
+ <Ink.Box flexDirection="column">
192
+ <Ink.Text color={colors.yellow}>
193
+ <FormatText
194
+ wrapper={<Ink.Text />}
195
+ message="New version available {latest_version}, would you like to update?"
196
+ values={{
197
+ latest_version: <Brackets>{state.latest_version}</Brackets>,
198
+ }}
199
+ />
200
+ ,
201
+ </Ink.Text>
202
+ <Ink.Text> </Ink.Text>
203
+ <Command>{install_command}</Command>
204
+ <Ink.Text> </Ink.Text>
205
+ <FormatText
206
+ wrapper={<Ink.Text color={colors.yellow} />}
207
+ message="Would you like to run the above command to update?"
208
+ />
209
+ </Ink.Box>
171
210
  }
172
211
  onYes={async () => {
173
212
  handle_output(
@@ -184,7 +223,7 @@ export function AutoUpdate(props: Props) {
184
223
 
185
224
  patch({ status: "install" });
186
225
 
187
- await cli(`npm install -g ${props.name}@latest`);
226
+ await cli(install_command);
188
227
 
189
228
  patch({ status: "exit" });
190
229
 
@@ -195,6 +234,7 @@ export function AutoUpdate(props: Props) {
195
234
  }}
196
235
  />
197
236
  );
237
+ }
198
238
 
199
239
  case "install":
200
240
  return null;
package/src/command.ts CHANGED
@@ -30,6 +30,12 @@ export async function command() {
30
30
  (yargs) => yargs,
31
31
  )
32
32
 
33
+ .command(
34
+ ["update", "upgrade"],
35
+ "Check and install the latest version of git stack",
36
+ (yargs) => yargs,
37
+ )
38
+
33
39
  .option("verbose", GlobalOptions.verbose)
34
40
 
35
41
  // yargs default wraps to 80 columns
@@ -86,13 +92,6 @@ const DefaultOptions = {
86
92
  ].join("\n"),
87
93
  },
88
94
 
89
- "update": {
90
- type: "boolean",
91
- alias: ["u", "upgrade"],
92
- default: false,
93
- description: "Check and install the latest version",
94
- },
95
-
96
95
  "branch": {
97
96
  type: "string",
98
97
  alias: ["b"],
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+
3
+ import { Await } from "~/app/Await";
4
+ import { Store } from "~/app/Store";
5
+
6
+ export function Update() {
7
+ return <Await fallback={null} function={run} />;
8
+ }
9
+
10
+ async function run() {
11
+ const state = Store.getState();
12
+ const actions = state.actions;
13
+
14
+ actions.exit(0);
15
+ }
@@ -67,10 +67,10 @@ test("git-revise-todo handles double quotes in commit message", () => {
67
67
  [
68
68
  //force line break
69
69
  "++ pick f143d03c723c",
70
- '[new] invalid \\"by me\\" quotes',
70
+ '[new] invalid "by me" quotes',
71
71
  "",
72
72
  "git-stack-id: 6Ak-qn+5Z",
73
- 'git-stack-title: [new] invalid \\"by me\\" quotes',
73
+ 'git-stack-title: [new] invalid "by me" quotes',
74
74
  ].join("\n"),
75
75
  );
76
76
  });
@@ -129,10 +129,10 @@ test("write handles double quotes", () => {
129
129
  expect(Metadata.write(body, metadata)).toEqual(
130
130
  [
131
131
  // force line break
132
- 'Revert \\"[abc / 123] subject (#1234)\\"',
132
+ 'Revert "[abc / 123] subject (#1234)"',
133
133
  "",
134
134
  "git-stack-id: abc123",
135
- 'git-stack-title: Revert \\"[abc / 123] subject (#1234)\\"',
135
+ 'git-stack-title: Revert "[abc / 123] subject (#1234)"',
136
136
  ].join("\n"),
137
137
  );
138
138
  });