git-stack-cli 1.14.0 → 1.15.1
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/README.md +2 -4
- package/dist/cjs/index.cjs +310 -180
- package/package.json +2 -1
- package/scripts/link.ts +14 -0
- package/src/app/App.tsx +41 -30
- package/src/app/AutoUpdate.tsx +9 -24
- package/src/app/CherryPickCheck.tsx +1 -2
- package/src/app/Debug.tsx +5 -6
- package/src/app/DependencyCheck.tsx +6 -6
- package/src/app/DetectInitialPR.tsx +2 -8
- package/src/app/DirtyCheck.tsx +51 -26
- package/src/app/Exit.tsx +41 -8
- package/src/app/FormatText.tsx +1 -5
- package/src/app/GatherMetadata.tsx +6 -13
- package/src/app/GithubApiError.tsx +1 -1
- package/src/app/HandleCtrlCSigint.tsx +36 -0
- package/src/app/LocalCommitStatus.tsx +1 -3
- package/src/app/LogTimestamp.tsx +1 -5
- package/src/app/ManualRebase.tsx +15 -37
- package/src/app/MultiSelect.tsx +2 -2
- package/src/app/PostRebaseStatus.tsx +2 -0
- package/src/app/PreManualRebase.tsx +3 -5
- package/src/app/RebaseCheck.tsx +1 -2
- package/src/app/SelectCommitRanges.tsx +6 -10
- package/src/app/Status.tsx +1 -1
- package/src/app/StatusTable.tsx +1 -4
- package/src/app/Store.tsx +29 -3
- package/src/app/SyncGithub.tsx +15 -45
- package/src/app/Table.tsx +4 -14
- package/src/app/TextInput.tsx +2 -7
- package/src/app/VerboseDebugInfo.tsx +1 -5
- package/src/app/YesNoPrompt.tsx +42 -31
- package/src/command.ts +8 -17
- package/src/commands/Fixup.tsx +17 -24
- package/src/commands/Log.tsx +3 -7
- package/src/commands/Rebase.tsx +18 -38
- package/src/components/ErrorBoundary.tsx +79 -0
- package/src/components/ExitingGate.tsx +27 -0
- package/src/core/CommitMetadata.ts +1 -1
- package/src/core/GitReviseTodo.test.ts +3 -3
- package/src/core/GitReviseTodo.ts +6 -8
- package/src/core/Metadata.test.ts +4 -4
- package/src/core/StackSummaryTable.ts +3 -3
- package/src/core/chalk.ts +1 -5
- package/src/core/cli.ts +2 -2
- package/src/core/github.tsx +15 -14
- package/src/core/pretty_json.ts +7 -0
- package/src/github/gh.auth_status.test.ts +2 -6
- package/src/index.tsx +42 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-stack-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "magus",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"dev": "npm run build -- --watch",
|
|
25
25
|
"build": "rollup -c rollup.config.js",
|
|
26
26
|
"build:standalone": "GIT_STACK_STANDALONE=true bun run scripts/build-standalone.ts",
|
|
27
|
+
"link": "bun run scripts/link.ts",
|
|
27
28
|
"release:npm": "bun run scripts/release-npm.ts",
|
|
28
29
|
"release:github": "bun run scripts/release-github.ts",
|
|
29
30
|
"release:brew": "bun run scripts/release-brew.ts",
|
package/scripts/link.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { spawn } from "~/core/spawn";
|
|
4
|
+
|
|
5
|
+
const SCRIPT_DIR = import.meta.dir;
|
|
6
|
+
const PROJECT_DIR = path.join(SCRIPT_DIR, "..");
|
|
7
|
+
|
|
8
|
+
process.chdir(PROJECT_DIR);
|
|
9
|
+
|
|
10
|
+
await spawn.sync("npm unlink git-stack-cli");
|
|
11
|
+
await spawn.sync("npm link");
|
|
12
|
+
|
|
13
|
+
console.debug();
|
|
14
|
+
console.debug("✅", "linked");
|
package/src/app/App.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { DetectInitialPR } from "~/app/DetectInitialPR";
|
|
|
8
8
|
import { DirtyCheck } from "~/app/DirtyCheck";
|
|
9
9
|
import { GatherMetadata } from "~/app/GatherMetadata";
|
|
10
10
|
import { GithubApiError } from "~/app/GithubApiError";
|
|
11
|
+
import { HandleCtrlCSigint } from "~/app/HandleCtrlCSigint";
|
|
11
12
|
import { LocalCommitStatus } from "~/app/LocalCommitStatus";
|
|
12
13
|
import { Main } from "~/app/Main";
|
|
13
14
|
import { Output } from "~/app/Output";
|
|
@@ -18,6 +19,8 @@ import { VerboseDebugInfo } from "~/app/VerboseDebugInfo";
|
|
|
18
19
|
import { Fixup } from "~/commands/Fixup";
|
|
19
20
|
import { Log } from "~/commands/Log";
|
|
20
21
|
import { Rebase } from "~/commands/Rebase";
|
|
22
|
+
import { ErrorBoundary } from "~/components/ErrorBoundary";
|
|
23
|
+
import { ExitingGate } from "~/components/ExitingGate";
|
|
21
24
|
|
|
22
25
|
export function App() {
|
|
23
26
|
const actions = Store.useActions();
|
|
@@ -41,30 +44,36 @@ export function App() {
|
|
|
41
44
|
|
|
42
45
|
return (
|
|
43
46
|
<Providers>
|
|
44
|
-
<
|
|
45
|
-
|
|
47
|
+
<ErrorBoundary>
|
|
48
|
+
<Debug />
|
|
49
|
+
<Output />
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
51
|
+
<ExitingGate>
|
|
52
|
+
<AutoUpdate
|
|
53
|
+
name="git-stack-cli"
|
|
54
|
+
verbose={argv.verbose || argv.update}
|
|
55
|
+
timeoutMs={argv.update ? 30 * 1000 : 2 * 1000}
|
|
56
|
+
onOutput={actions.output}
|
|
57
|
+
onDone={() => {
|
|
58
|
+
if (argv.update) {
|
|
59
|
+
actions.exit(0);
|
|
60
|
+
}
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
<VerboseDebugInfo>
|
|
64
|
+
<DependencyCheck>
|
|
65
|
+
<RebaseCheck>
|
|
66
|
+
<CherryPickCheck>
|
|
67
|
+
<MaybeMain />
|
|
68
|
+
</CherryPickCheck>
|
|
69
|
+
</RebaseCheck>
|
|
70
|
+
</DependencyCheck>
|
|
71
|
+
</VerboseDebugInfo>
|
|
72
|
+
</AutoUpdate>
|
|
73
|
+
|
|
74
|
+
<HandleCtrlCSigint />
|
|
75
|
+
</ExitingGate>
|
|
76
|
+
</ErrorBoundary>
|
|
68
77
|
</Providers>
|
|
69
78
|
);
|
|
70
79
|
}
|
|
@@ -88,16 +97,18 @@ function MaybeMain() {
|
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
return (
|
|
91
|
-
<
|
|
100
|
+
<React.Fragment>
|
|
92
101
|
{!argv.verbose ? null : <GithubApiError />}
|
|
93
102
|
|
|
94
103
|
<GatherMetadata>
|
|
95
|
-
<
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
<DirtyCheck>
|
|
105
|
+
<LocalCommitStatus>
|
|
106
|
+
<DetectInitialPR>
|
|
107
|
+
<Main />
|
|
108
|
+
</DetectInitialPR>
|
|
109
|
+
</LocalCommitStatus>
|
|
110
|
+
</DirtyCheck>
|
|
100
111
|
</GatherMetadata>
|
|
101
|
-
</
|
|
112
|
+
</React.Fragment>
|
|
102
113
|
);
|
|
103
114
|
}
|
package/src/app/AutoUpdate.tsx
CHANGED
|
@@ -67,14 +67,10 @@ export function AutoUpdate(props: Props) {
|
|
|
67
67
|
|
|
68
68
|
async function auto_update() {
|
|
69
69
|
if (props_ref.current.verbose) {
|
|
70
|
-
handle_output(
|
|
71
|
-
<Ink.Text key="init">Checking for latest version...</Ink.Text>
|
|
72
|
-
);
|
|
70
|
+
handle_output(<Ink.Text key="init">Checking for latest version...</Ink.Text>);
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
const timeout_ms = is_finite_value(props.timeoutMs)
|
|
76
|
-
? props.timeoutMs
|
|
77
|
-
: 2 * 1000;
|
|
73
|
+
const timeout_ms = is_finite_value(props.timeoutMs) ? props.timeoutMs : 2 * 1000;
|
|
78
74
|
|
|
79
75
|
const npm_json = await Promise.race([
|
|
80
76
|
fetch_json(`https://registry.npmjs.org/${props.name}`),
|
|
@@ -94,12 +90,7 @@ export function AutoUpdate(props: Props) {
|
|
|
94
90
|
const script_dir = path.dirname(script_path);
|
|
95
91
|
|
|
96
92
|
// dist/ts/index.js
|
|
97
|
-
const package_json_path = path.join(
|
|
98
|
-
script_dir,
|
|
99
|
-
"..",
|
|
100
|
-
"..",
|
|
101
|
-
"package.json"
|
|
102
|
-
);
|
|
93
|
+
const package_json_path = path.join(script_dir, "..", "..", "package.json");
|
|
103
94
|
|
|
104
95
|
type PackageJson = { version: string };
|
|
105
96
|
const package_json = await read_json<PackageJson>(package_json_path);
|
|
@@ -121,7 +112,7 @@ export function AutoUpdate(props: Props) {
|
|
|
121
112
|
latest_version: <Brackets>{latest_version}</Brackets>,
|
|
122
113
|
local_version: <Brackets>{local_version}</Brackets>,
|
|
123
114
|
}}
|
|
124
|
-
|
|
115
|
+
/>,
|
|
125
116
|
);
|
|
126
117
|
}
|
|
127
118
|
|
|
@@ -134,7 +125,7 @@ export function AutoUpdate(props: Props) {
|
|
|
134
125
|
if (semver_result === -1) {
|
|
135
126
|
// latest version is less than or equal to local version, skip auto update
|
|
136
127
|
throw new Error(
|
|
137
|
-
`latest version < local_version, skipping auto update [${latest_version} < ${local_version}]
|
|
128
|
+
`latest version < local_version, skipping auto update [${latest_version} < ${local_version}]`,
|
|
138
129
|
);
|
|
139
130
|
}
|
|
140
131
|
|
|
@@ -156,7 +147,7 @@ export function AutoUpdate(props: Props) {
|
|
|
156
147
|
handle_output(
|
|
157
148
|
<Ink.Text key="error" color={colors.red}>
|
|
158
149
|
{error?.message}
|
|
159
|
-
</Ink.Text
|
|
150
|
+
</Ink.Text>,
|
|
160
151
|
);
|
|
161
152
|
}
|
|
162
153
|
})
|
|
@@ -185,16 +176,10 @@ export function AutoUpdate(props: Props) {
|
|
|
185
176
|
wrapper={<Ink.Text />}
|
|
186
177
|
message="Installing {name}@{version}..."
|
|
187
178
|
values={{
|
|
188
|
-
name:
|
|
189
|
-
|
|
190
|
-
),
|
|
191
|
-
version: (
|
|
192
|
-
<Ink.Text color={colors.blue}>
|
|
193
|
-
{state.latest_version}
|
|
194
|
-
</Ink.Text>
|
|
195
|
-
),
|
|
179
|
+
name: <Ink.Text color={colors.yellow}>{props.name}</Ink.Text>,
|
|
180
|
+
version: <Ink.Text color={colors.blue}>{state.latest_version}</Ink.Text>,
|
|
196
181
|
}}
|
|
197
|
-
|
|
182
|
+
/>,
|
|
198
183
|
);
|
|
199
184
|
|
|
200
185
|
patch({ status: "install" });
|
|
@@ -40,8 +40,7 @@ export function CherryPickCheck(props: Props) {
|
|
|
40
40
|
<YesNoPrompt
|
|
41
41
|
message={
|
|
42
42
|
<Ink.Text color={colors.yellow}>
|
|
43
|
-
<Command>git cherry-pick</Command> detected, would you like to
|
|
44
|
-
abort it?
|
|
43
|
+
<Command>git cherry-pick</Command> detected, would you like to abort it?
|
|
45
44
|
</Ink.Text>
|
|
46
45
|
}
|
|
47
46
|
onYes={async () => {
|
package/src/app/Debug.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import * as Ink from "ink-cjs";
|
|
|
8
8
|
import { Store } from "~/app/Store";
|
|
9
9
|
import { colors } from "~/core/colors";
|
|
10
10
|
import * as json from "~/core/json";
|
|
11
|
+
import { pretty_json } from "~/core/pretty_json";
|
|
11
12
|
import { safe_rm } from "~/core/safe_rm";
|
|
12
13
|
|
|
13
14
|
export function Debug() {
|
|
@@ -19,12 +20,10 @@ export function Debug() {
|
|
|
19
20
|
React.useEffect(
|
|
20
21
|
function debugMessageOnce() {
|
|
21
22
|
if (debug) {
|
|
22
|
-
actions.output(
|
|
23
|
-
<Ink.Text color={colors.yellow}>Debug mode enabled</Ink.Text>
|
|
24
|
-
);
|
|
23
|
+
actions.output(<Ink.Text color={colors.yellow}>Debug mode enabled</Ink.Text>);
|
|
25
24
|
}
|
|
26
25
|
},
|
|
27
|
-
[argv]
|
|
26
|
+
[argv],
|
|
28
27
|
);
|
|
29
28
|
|
|
30
29
|
React.useEffect(
|
|
@@ -41,11 +40,11 @@ export function Debug() {
|
|
|
41
40
|
await safe_rm(output_file);
|
|
42
41
|
|
|
43
42
|
const serialized = json.serialize(state);
|
|
44
|
-
const content =
|
|
43
|
+
const content = pretty_json(serialized);
|
|
45
44
|
await fs.writeFile(output_file, content);
|
|
46
45
|
}
|
|
47
46
|
},
|
|
48
|
-
[argv, state]
|
|
47
|
+
[argv, state],
|
|
49
48
|
);
|
|
50
49
|
|
|
51
50
|
return null;
|
|
@@ -55,7 +55,7 @@ function CheckGit(props: Props) {
|
|
|
55
55
|
actions.output(
|
|
56
56
|
<Ink.Text color={colors.yellow}>
|
|
57
57
|
<Command>git</Command> must be installed.
|
|
58
|
-
</Ink.Text
|
|
58
|
+
</Ink.Text>,
|
|
59
59
|
);
|
|
60
60
|
|
|
61
61
|
actions.exit(2);
|
|
@@ -86,7 +86,7 @@ function CheckGithubCli(props: Props) {
|
|
|
86
86
|
actions.output(
|
|
87
87
|
<Ink.Text color={colors.yellow}>
|
|
88
88
|
<Command>gh</Command> must be installed.
|
|
89
|
-
</Ink.Text
|
|
89
|
+
</Ink.Text>,
|
|
90
90
|
);
|
|
91
91
|
|
|
92
92
|
actions.output(
|
|
@@ -98,7 +98,7 @@ function CheckGithubCli(props: Props) {
|
|
|
98
98
|
|
|
99
99
|
command: <Command>gh</Command>,
|
|
100
100
|
}}
|
|
101
|
-
|
|
101
|
+
/>,
|
|
102
102
|
);
|
|
103
103
|
|
|
104
104
|
actions.exit(3);
|
|
@@ -150,7 +150,7 @@ function CheckGithubCliAuth(props: Props) {
|
|
|
150
150
|
|
|
151
151
|
command: <Command>gh auth login</Command>,
|
|
152
152
|
}}
|
|
153
|
-
|
|
153
|
+
/>,
|
|
154
154
|
);
|
|
155
155
|
|
|
156
156
|
actions.exit(4);
|
|
@@ -181,7 +181,7 @@ function CheckGitRevise(props: Props) {
|
|
|
181
181
|
actions.output(
|
|
182
182
|
<Ink.Text color={colors.yellow}>
|
|
183
183
|
<Command>git revise</Command> must be installed.
|
|
184
|
-
</Ink.Text
|
|
184
|
+
</Ink.Text>,
|
|
185
185
|
);
|
|
186
186
|
|
|
187
187
|
actions.output(
|
|
@@ -199,7 +199,7 @@ function CheckGitRevise(props: Props) {
|
|
|
199
199
|
</Parens>
|
|
200
200
|
),
|
|
201
201
|
}}
|
|
202
|
-
|
|
202
|
+
/>,
|
|
203
203
|
);
|
|
204
204
|
|
|
205
205
|
actions.exit(10);
|
|
@@ -97,11 +97,7 @@ export function DetectInitialPR(props: Props) {
|
|
|
97
97
|
return (
|
|
98
98
|
<Await
|
|
99
99
|
function={run}
|
|
100
|
-
fallback={
|
|
101
|
-
<Ink.Text color={colors.yellow}>
|
|
102
|
-
Checking for existing PR on Github…
|
|
103
|
-
</Ink.Text>
|
|
104
|
-
}
|
|
100
|
+
fallback={<Ink.Text color={colors.yellow}>Checking for existing PR on Github…</Ink.Text>}
|
|
105
101
|
/>
|
|
106
102
|
);
|
|
107
103
|
}
|
|
@@ -167,9 +163,7 @@ export function DetectInitialPR(props: Props) {
|
|
|
167
163
|
// get latest merge_base relative to local master
|
|
168
164
|
const rebase_group_index = 0;
|
|
169
165
|
|
|
170
|
-
const rebase_merge_base = (
|
|
171
|
-
await cli(`git merge-base HEAD ${master_branch}`)
|
|
172
|
-
).stdout;
|
|
166
|
+
const rebase_merge_base = (await cli(`git merge-base HEAD ${master_branch}`)).stdout;
|
|
173
167
|
|
|
174
168
|
await GitReviseTodo.execute({
|
|
175
169
|
rebase_group_index,
|
package/src/app/DirtyCheck.tsx
CHANGED
|
@@ -15,7 +15,7 @@ type Props = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
type State = {
|
|
18
|
-
status: "init" | "prompt" | "done";
|
|
18
|
+
status: "init" | "prompt" | "stash" | "done";
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
function reducer(state: State, patch: Partial<State>) {
|
|
@@ -33,36 +33,58 @@ export function DirtyCheck(props: Props) {
|
|
|
33
33
|
case "done":
|
|
34
34
|
return props.children;
|
|
35
35
|
|
|
36
|
+
case "stash":
|
|
37
|
+
return (
|
|
38
|
+
<FormatText
|
|
39
|
+
wrapper={<Ink.Text color={colors.yellow} />}
|
|
40
|
+
message="📦 Stashing uncommitted changes…"
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
|
|
36
44
|
case "prompt":
|
|
37
45
|
return (
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
<Ink.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
<Ink.Box flexDirection="column">
|
|
47
|
+
<FormatText
|
|
48
|
+
wrapper={<Ink.Text color={colors.yellow} />}
|
|
49
|
+
message="⚠️ Uncommitted changes detected. {git_stack} needs a clean working tree."
|
|
50
|
+
values={{
|
|
51
|
+
git: <Command>git</Command>,
|
|
52
|
+
git_stack: <Command>git stack</Command>,
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
|
|
56
|
+
<YesNoPrompt
|
|
57
|
+
message={
|
|
49
58
|
<FormatText
|
|
50
59
|
wrapper={<Ink.Text color={colors.yellow} />}
|
|
51
|
-
message="
|
|
60
|
+
message="{git_stash} changes to proceed?"
|
|
52
61
|
values={{
|
|
53
|
-
|
|
54
|
-
git_stack: <Command>git stack</Command>,
|
|
62
|
+
git_stash: <Command>git stash</Command>,
|
|
55
63
|
}}
|
|
56
64
|
/>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
}
|
|
66
|
+
onYes={async () => {
|
|
67
|
+
patch({ status: "stash" });
|
|
68
|
+
|
|
69
|
+
await cli("git stash --include-untracked");
|
|
70
|
+
|
|
71
|
+
actions.output(
|
|
72
|
+
<Ink.Text color={colors.yellow}>
|
|
73
|
+
<FormatText message="📦 Changes saved to stash" />
|
|
74
|
+
</Ink.Text>,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
actions.set((state) => {
|
|
78
|
+
state.is_dirty_check_stash = true;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
patch({ status: "done" });
|
|
82
|
+
}}
|
|
83
|
+
onNo={async () => {
|
|
84
|
+
actions.exit(0);
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
</Ink.Box>
|
|
66
88
|
);
|
|
67
89
|
|
|
68
90
|
default:
|
|
@@ -84,8 +106,11 @@ export function DirtyCheck(props: Props) {
|
|
|
84
106
|
try {
|
|
85
107
|
const git_dirty = (await cli(`git status --porcelain`)).stdout;
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
|
|
109
|
+
if (!git_dirty) {
|
|
110
|
+
patch({ status: "done" });
|
|
111
|
+
} else {
|
|
112
|
+
patch({ status: "prompt" });
|
|
113
|
+
}
|
|
89
114
|
} catch (err) {
|
|
90
115
|
actions.error("Must be run from within a git repository.");
|
|
91
116
|
|
package/src/app/Exit.tsx
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
+
import * as Ink from "ink-cjs";
|
|
4
|
+
|
|
3
5
|
import { Store } from "~/app/Store";
|
|
6
|
+
import { cli } from "~/core/cli";
|
|
7
|
+
import { colors } from "~/core/colors";
|
|
8
|
+
import { sleep } from "~/core/sleep";
|
|
4
9
|
|
|
5
10
|
type Props = {
|
|
6
11
|
clear: boolean;
|
|
@@ -8,17 +13,45 @@ type Props = {
|
|
|
8
13
|
};
|
|
9
14
|
|
|
10
15
|
export function Exit(props: Props) {
|
|
11
|
-
const actions = Store.useActions();
|
|
12
|
-
|
|
13
16
|
React.useEffect(() => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
// immediately handle exit on mount
|
|
18
|
+
handle_exit().catch((err) => {
|
|
19
|
+
// eslint-disable-next-line no-console
|
|
20
|
+
console.error(err);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
async function handle_exit() {
|
|
24
|
+
const state = Store.getState();
|
|
25
|
+
const actions = state.actions;
|
|
26
|
+
|
|
27
|
+
actions.debug(`[Exit] handle_exit ${JSON.stringify(props)}`);
|
|
28
|
+
|
|
29
|
+
let exit_code = props.code;
|
|
17
30
|
|
|
18
|
-
|
|
31
|
+
// run abort_handler if it exists
|
|
32
|
+
if (state.abort_handler) {
|
|
33
|
+
exit_code = await state.abort_handler();
|
|
34
|
+
}
|
|
19
35
|
|
|
20
|
-
|
|
21
|
-
|
|
36
|
+
// restore git stash if necessary
|
|
37
|
+
if (state.is_dirty_check_stash) {
|
|
38
|
+
await cli("git stash pop");
|
|
39
|
+
actions.output(<Ink.Text color={colors.green}>✅ Changes restored from stash</Ink.Text>);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ensure output has a chance to render
|
|
43
|
+
await sleep(1);
|
|
44
|
+
|
|
45
|
+
// finally handle the actual app and process exit
|
|
46
|
+
if (props.clear) {
|
|
47
|
+
actions.clear();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
actions.unmount();
|
|
51
|
+
|
|
52
|
+
process.exitCode = exit_code;
|
|
53
|
+
process.exit();
|
|
54
|
+
}
|
|
22
55
|
}, [props.clear, props.code]);
|
|
23
56
|
|
|
24
57
|
return null;
|
package/src/app/FormatText.tsx
CHANGED
|
@@ -13,11 +13,7 @@ export function FormatText(props: Props) {
|
|
|
13
13
|
const wrapper = (props.wrapper as React.ReactElement) || <Ink.Text />;
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
|
-
<FormattedMessage
|
|
17
|
-
id="FormatText"
|
|
18
|
-
defaultMessage={props.message}
|
|
19
|
-
values={props.values}
|
|
20
|
-
>
|
|
16
|
+
<FormattedMessage id="FormatText" defaultMessage={props.message} values={props.values}>
|
|
21
17
|
{(chunks) => {
|
|
22
18
|
return React.cloneElement(wrapper, {}, chunks);
|
|
23
19
|
}}
|
|
@@ -15,9 +15,7 @@ type Props = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export function GatherMetadata(props: Props) {
|
|
18
|
-
const fallback =
|
|
19
|
-
<Ink.Text color={colors.yellow}>Gathering local git information…</Ink.Text>
|
|
20
|
-
);
|
|
18
|
+
const fallback = <Ink.Text color={colors.yellow}>Gathering local git information…</Ink.Text>;
|
|
21
19
|
|
|
22
20
|
return (
|
|
23
21
|
<Await fallback={fallback} function={run}>
|
|
@@ -41,14 +39,12 @@ async function run() {
|
|
|
41
39
|
values={{
|
|
42
40
|
branch: <Brackets>{argv.branch}</Brackets>,
|
|
43
41
|
}}
|
|
44
|
-
|
|
42
|
+
/>,
|
|
45
43
|
);
|
|
46
44
|
|
|
47
45
|
master_branch = argv.branch;
|
|
48
46
|
} else {
|
|
49
|
-
const detect_master = await cli(
|
|
50
|
-
`git branch --list "${BRANCH.master}" --color=never`
|
|
51
|
-
);
|
|
47
|
+
const detect_master = await cli(`git branch --list "${BRANCH.master}" --color=never`);
|
|
52
48
|
|
|
53
49
|
if (detect_master.stdout !== "") {
|
|
54
50
|
master_branch = BRANCH.master;
|
|
@@ -60,7 +56,7 @@ async function run() {
|
|
|
60
56
|
master: <Brackets>{BRANCH.master}</Brackets>,
|
|
61
57
|
main: <Brackets>{BRANCH.main}</Brackets>,
|
|
62
58
|
}}
|
|
63
|
-
|
|
59
|
+
/>,
|
|
64
60
|
);
|
|
65
61
|
|
|
66
62
|
master_branch = BRANCH.main;
|
|
@@ -84,15 +80,12 @@ async function run() {
|
|
|
84
80
|
}
|
|
85
81
|
|
|
86
82
|
const head = (await cli("git rev-parse HEAD")).stdout;
|
|
87
|
-
const merge_base = (await cli(`git merge-base HEAD ${master_branch}`))
|
|
88
|
-
.stdout;
|
|
83
|
+
const merge_base = (await cli(`git merge-base HEAD ${master_branch}`)).stdout;
|
|
89
84
|
|
|
90
85
|
// handle when there are no detected changes
|
|
91
86
|
if (head === merge_base) {
|
|
92
87
|
actions.newline();
|
|
93
|
-
actions.output(
|
|
94
|
-
<Ink.Text color={colors.gray}>No changes detected.</Ink.Text>
|
|
95
|
-
);
|
|
88
|
+
actions.output(<Ink.Text color={colors.gray}>No changes detected.</Ink.Text>);
|
|
96
89
|
actions.exit(0);
|
|
97
90
|
return;
|
|
98
91
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import * as Ink from "ink-cjs";
|
|
4
|
+
|
|
5
|
+
import { FormatText } from "~/app/FormatText";
|
|
6
|
+
import { Store } from "~/app/Store";
|
|
7
|
+
import { colors } from "~/core/colors";
|
|
8
|
+
import { sleep } from "~/core/sleep";
|
|
9
|
+
|
|
10
|
+
export function HandleCtrlCSigint() {
|
|
11
|
+
const actions = Store.useActions();
|
|
12
|
+
|
|
13
|
+
Ink.useInput((input, key) => {
|
|
14
|
+
handle_input().catch((err) => {
|
|
15
|
+
// eslint-disable-next-line no-console
|
|
16
|
+
console.error(err);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
async function handle_input() {
|
|
20
|
+
if (input === "c" && key.ctrl) {
|
|
21
|
+
actions.clear();
|
|
22
|
+
|
|
23
|
+
actions.output(
|
|
24
|
+
<Ink.Text color={colors.red}>
|
|
25
|
+
<FormatText message="🚨 Ctrl+C detected" />
|
|
26
|
+
</Ink.Text>,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
await sleep(1);
|
|
30
|
+
actions.exit(235);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
@@ -15,9 +15,7 @@ type Props = {
|
|
|
15
15
|
export function LocalCommitStatus(props: Props) {
|
|
16
16
|
const argv = Store.useState((state) => state.argv);
|
|
17
17
|
|
|
18
|
-
const fallback =
|
|
19
|
-
<Ink.Text color={colors.yellow}>Fetching PR status from Github…</Ink.Text>
|
|
20
|
-
);
|
|
18
|
+
const fallback = <Ink.Text color={colors.yellow}>Fetching PR status from Github…</Ink.Text>;
|
|
21
19
|
|
|
22
20
|
if (argv["mock-metadata"]) {
|
|
23
21
|
return (
|
package/src/app/LogTimestamp.tsx
CHANGED
|
@@ -4,9 +4,5 @@ import * as Ink from "ink-cjs";
|
|
|
4
4
|
import { DateTime } from "luxon";
|
|
5
5
|
|
|
6
6
|
export function LogTimestamp() {
|
|
7
|
-
return (
|
|
8
|
-
<Ink.Text dimColor>
|
|
9
|
-
{DateTime.now().toFormat("[yyyy-MM-dd HH:mm:ss.SSS] ")}
|
|
10
|
-
</Ink.Text>
|
|
11
|
-
);
|
|
7
|
+
return <Ink.Text dimColor>{DateTime.now().toFormat("[yyyy-MM-dd HH:mm:ss.SSS] ")}</Ink.Text>;
|
|
12
8
|
}
|