git-stack-cli 2.9.8 → 2.10.0
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 +69 -68
- package/package.json +1 -1
- package/scripts/release-brew.ts +4 -0
- package/src/app/AutoUpdate.tsx +21 -9
- package/src/app/SyncGithub.tsx +30 -14
- package/src/command.ts +26 -15
- package/src/commands/Fixup.tsx +43 -23
- package/src/commands/Log.tsx +1 -1
- package/src/core/get_timeout_fn.ts +1 -1
- package/src/core/github.tsx +19 -1
package/package.json
CHANGED
package/scripts/release-brew.ts
CHANGED
|
@@ -92,6 +92,10 @@ core = core.replace(re_token("tarball_sha256"), tarball_asset.sha256);
|
|
|
92
92
|
|
|
93
93
|
await file.write_text(path.join("Formula", "git-stack.core.rb"), core);
|
|
94
94
|
|
|
95
|
+
// write latest.json containing latest version
|
|
96
|
+
const latest_json = JSON.stringify({ version });
|
|
97
|
+
await file.write_text(path.join("latest.json"), latest_json);
|
|
98
|
+
|
|
95
99
|
// commit homebrew repo changes
|
|
96
100
|
process.chdir(HOMEBREW_DIR);
|
|
97
101
|
await spawn.sync(`git add .`);
|
package/src/app/AutoUpdate.tsx
CHANGED
|
@@ -161,20 +161,32 @@ export function AutoUpdate(props: Props) {
|
|
|
161
161
|
if (state.latest_version !== null) return;
|
|
162
162
|
|
|
163
163
|
const local_version = process.env.CLI_VERSION;
|
|
164
|
-
const latest_version = await get_latest_version();
|
|
165
164
|
const is_brew_bun_standalone = get_is_brew_bun_standalone();
|
|
166
|
-
patch({ local_version, latest_version, is_brew_bun_standalone });
|
|
167
|
-
}
|
|
168
165
|
|
|
169
|
-
async function get_latest_version() {
|
|
170
166
|
const timeout_ms = is_finite_value(props.timeoutMs) ? props.timeoutMs : 2 * 1000;
|
|
171
167
|
const timeout = get_timeout_fn(timeout_ms, "AutoUpdate timeout");
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
168
|
+
const latest_version = await timeout(get_latest_version(is_brew_bun_standalone));
|
|
169
|
+
|
|
170
|
+
patch({ local_version, latest_version, is_brew_bun_standalone });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function get_latest_version(is_brew_bun_standalone: boolean) {
|
|
174
|
+
if (is_brew_bun_standalone) {
|
|
175
|
+
// prettier-ignore
|
|
176
|
+
const brew_json = await fetch_json("https://raw.githubusercontent.com/magus/homebrew-git-stack/refs/heads/master/latest.json");
|
|
177
|
+
const maybe_version = brew_json.version;
|
|
178
|
+
if (typeof maybe_version === "string") {
|
|
179
|
+
return maybe_version;
|
|
180
|
+
}
|
|
181
|
+
throw new Error("Unable to retrieve latest version from brew");
|
|
182
|
+
} else {
|
|
183
|
+
const npm_json = await fetch_json(`https://registry.npmjs.org/${props.name}`);
|
|
184
|
+
const maybe_version = npm_json?.["dist-tags"]?.latest;
|
|
185
|
+
if (typeof maybe_version === "string") {
|
|
186
|
+
return maybe_version;
|
|
187
|
+
}
|
|
188
|
+
throw new Error("Unable to retrieve latest version from npm");
|
|
176
189
|
}
|
|
177
|
-
throw new Error("Unable to retrieve latest version from npm");
|
|
178
190
|
}
|
|
179
191
|
|
|
180
192
|
function get_is_brew_bun_standalone() {
|
package/src/app/SyncGithub.tsx
CHANGED
|
@@ -28,6 +28,7 @@ async function run() {
|
|
|
28
28
|
const master_branch = state.master_branch;
|
|
29
29
|
const repo_path = state.repo_path;
|
|
30
30
|
const sync_github = state.sync_github;
|
|
31
|
+
const labels = argv.label ?? [];
|
|
31
32
|
|
|
32
33
|
invariant(branch_name, "branch_name must exist");
|
|
33
34
|
invariant(commit_map, "commit_map must exist");
|
|
@@ -140,17 +141,17 @@ async function run() {
|
|
|
140
141
|
const pr_url_list = all_pr_groups.map((g) => pr_url_by_group_id[g.id]);
|
|
141
142
|
|
|
142
143
|
// update PR body for all pr groups (not just push_group_list)
|
|
143
|
-
const
|
|
144
|
+
const update_pr_tasks = [];
|
|
144
145
|
for (let i = 0; i < all_pr_groups.length; i++) {
|
|
145
146
|
const group = all_pr_groups[i];
|
|
146
147
|
|
|
147
148
|
const selected_url = pr_url_by_group_id[group.id];
|
|
148
149
|
|
|
149
|
-
const task =
|
|
150
|
-
|
|
150
|
+
const task = update_pr({ group, selected_url, pr_url_list, labels });
|
|
151
|
+
update_pr_tasks.push(task);
|
|
151
152
|
}
|
|
152
153
|
|
|
153
|
-
await Promise.all(
|
|
154
|
+
await Promise.all(update_pr_tasks);
|
|
154
155
|
|
|
155
156
|
actions.unregister_abort_handler();
|
|
156
157
|
|
|
@@ -262,6 +263,7 @@ async function run() {
|
|
|
262
263
|
title: group.title,
|
|
263
264
|
body: DEFAULT_PR_BODY,
|
|
264
265
|
draft: argv.draft,
|
|
266
|
+
labels,
|
|
265
267
|
});
|
|
266
268
|
|
|
267
269
|
if (!pr_url) {
|
|
@@ -273,10 +275,11 @@ async function run() {
|
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
277
|
|
|
276
|
-
async function
|
|
278
|
+
async function update_pr(args: {
|
|
277
279
|
group: CommitMetadataGroup;
|
|
278
280
|
selected_url: string;
|
|
279
281
|
pr_url_list: Array<string>;
|
|
282
|
+
labels: Array<string>;
|
|
280
283
|
}) {
|
|
281
284
|
const { group, selected_url, pr_url_list } = args;
|
|
282
285
|
|
|
@@ -292,17 +295,30 @@ async function run() {
|
|
|
292
295
|
|
|
293
296
|
const debug_meta = `${group.id} ${selected_url}`;
|
|
294
297
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
} else {
|
|
298
|
-
actions.debug(`Update body ${debug_meta}`);
|
|
298
|
+
const body_changed = update_body !== body;
|
|
299
|
+
const needs_labels = args.labels.length > 0;
|
|
299
300
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
body: update_body,
|
|
304
|
-
});
|
|
301
|
+
if (!body_changed && !needs_labels) {
|
|
302
|
+
actions.debug(`Skipping update ${debug_meta}`);
|
|
303
|
+
return;
|
|
305
304
|
}
|
|
305
|
+
|
|
306
|
+
actions.debug(`Update PR ${debug_meta}`);
|
|
307
|
+
|
|
308
|
+
const edit_args: Parameters<typeof github.pr_edit>[0] = {
|
|
309
|
+
branch: group.id,
|
|
310
|
+
base: group.base,
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
if (body_changed) {
|
|
314
|
+
edit_args.body = update_body;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (needs_labels) {
|
|
318
|
+
edit_args.add_labels = args.labels;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
await github.pr_edit(edit_args);
|
|
306
322
|
}
|
|
307
323
|
|
|
308
324
|
function is_pr_master_base(group: CommitMetadataGroup) {
|
package/src/command.ts
CHANGED
|
@@ -18,7 +18,7 @@ export async function command(argv: string[], options: CommandOptions = {}) {
|
|
|
18
18
|
builder = builder.parserConfiguration(options.parserConfiguration);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const parsed =
|
|
21
|
+
const parsed = builder
|
|
22
22
|
.scriptName("git stack")
|
|
23
23
|
.usage("Usage: git stack [command] [options]")
|
|
24
24
|
|
|
@@ -122,6 +122,24 @@ const DefaultOptions = {
|
|
|
122
122
|
description: "Sync commit ranges to Github, disable with --no-sync",
|
|
123
123
|
},
|
|
124
124
|
|
|
125
|
+
"label": {
|
|
126
|
+
type: "array",
|
|
127
|
+
alias: ["labels"],
|
|
128
|
+
coerce: (label_input: Array<string | number>) => label_input.map((v) => String(v)),
|
|
129
|
+
description: [
|
|
130
|
+
// force line break
|
|
131
|
+
"Apply labels to all PRs in the stack (repeatable)",
|
|
132
|
+
"Example: --label backend --label needs-review",
|
|
133
|
+
].join("\n"),
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
"draft": {
|
|
137
|
+
type: "boolean",
|
|
138
|
+
alias: ["d"],
|
|
139
|
+
default: false,
|
|
140
|
+
description: "Open all PRs as drafts",
|
|
141
|
+
},
|
|
142
|
+
|
|
125
143
|
"verify": {
|
|
126
144
|
type: "boolean",
|
|
127
145
|
default: true,
|
|
@@ -142,19 +160,6 @@ const DefaultOptions = {
|
|
|
142
160
|
].join("\n"),
|
|
143
161
|
},
|
|
144
162
|
|
|
145
|
-
"draft": {
|
|
146
|
-
type: "boolean",
|
|
147
|
-
alias: ["d"],
|
|
148
|
-
default: false,
|
|
149
|
-
description: "Open all PRs as drafts",
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
"revise-sign": {
|
|
153
|
-
type: "boolean",
|
|
154
|
-
default: true,
|
|
155
|
-
description: "Disable GPG signing for git revise with --no-revise-sign",
|
|
156
|
-
},
|
|
157
|
-
|
|
158
163
|
"template": {
|
|
159
164
|
type: "boolean",
|
|
160
165
|
default: true,
|
|
@@ -164,12 +169,18 @@ const DefaultOptions = {
|
|
|
164
169
|
"Disable with --no-template",
|
|
165
170
|
].join("\n"),
|
|
166
171
|
},
|
|
172
|
+
|
|
173
|
+
"revise-sign": {
|
|
174
|
+
type: "boolean",
|
|
175
|
+
default: true,
|
|
176
|
+
description: "Disable GPG signing for git revise with --no-revise-sign",
|
|
177
|
+
},
|
|
167
178
|
} satisfies YargsOptions;
|
|
168
179
|
|
|
169
180
|
const FixupOptions = {
|
|
170
181
|
commit: {
|
|
171
182
|
type: "number",
|
|
172
|
-
default:
|
|
183
|
+
default: 0,
|
|
173
184
|
description: [
|
|
174
185
|
"Relative number of commit to amend staged changes.",
|
|
175
186
|
"Most recent is 1, next is 2, etc.",
|
package/src/commands/Fixup.tsx
CHANGED
|
@@ -3,11 +3,13 @@ import * as React from "react";
|
|
|
3
3
|
import * as Ink from "ink-cjs";
|
|
4
4
|
|
|
5
5
|
import { Await } from "~/app/Await";
|
|
6
|
+
import { Command } from "~/app/Command";
|
|
6
7
|
import { FormatText } from "~/app/FormatText";
|
|
7
8
|
import { Parens } from "~/app/Parens";
|
|
8
9
|
import { Store } from "~/app/Store";
|
|
9
10
|
import { cli } from "~/core/cli";
|
|
10
11
|
import { colors } from "~/core/colors";
|
|
12
|
+
import { is_finite_value } from "~/core/is_finite_value";
|
|
11
13
|
|
|
12
14
|
export function Fixup() {
|
|
13
15
|
return (
|
|
@@ -25,27 +27,48 @@ async function run() {
|
|
|
25
27
|
|
|
26
28
|
const relative_number = argv.commit;
|
|
27
29
|
|
|
28
|
-
if (!relative_number) {
|
|
30
|
+
if (!is_finite_value(relative_number)) {
|
|
29
31
|
actions.output(
|
|
30
|
-
<Ink.
|
|
32
|
+
<Ink.Box flexDirection="column">
|
|
33
|
+
<Ink.Text color={colors.red}>❗️ Usage: git fixup {"<relative-commit-number>"}</Ink.Text>
|
|
34
|
+
<Ink.Text color={colors.gray}>
|
|
35
|
+
Automates the process of adding staged changes to a previous commit.
|
|
36
|
+
</Ink.Text>
|
|
37
|
+
<FormatText
|
|
38
|
+
wrapper={<Ink.Text color={colors.gray} />}
|
|
39
|
+
message="You can use {git_stack_log} to get the relative commit number."
|
|
40
|
+
values={{ git_stack_log: <Command>git stack log</Command> }}
|
|
41
|
+
/>
|
|
42
|
+
<Ink.Box height={1} />
|
|
43
|
+
<FormatText
|
|
44
|
+
message=" {prompt} git stack log"
|
|
45
|
+
values={{ prompt: <Ink.Text color={colors.green}>❯</Ink.Text> }}
|
|
46
|
+
/>
|
|
47
|
+
<FormatText
|
|
48
|
+
message=" 0 * {sha} 18 hours ago noah homebrew-git-stack 2.9.9"
|
|
49
|
+
values={{ sha: <Ink.Text color={colors.green}>e329794</Ink.Text> }}
|
|
50
|
+
/>
|
|
51
|
+
<FormatText
|
|
52
|
+
message=" 1 * {sha} 18 hours ago noah 2.9.9"
|
|
53
|
+
values={{ sha: <Ink.Text color={colors.green}>c7e4065</Ink.Text> }}
|
|
54
|
+
/>
|
|
55
|
+
<FormatText
|
|
56
|
+
message=" 2 * {sha} 18 hours ago noah command: --label + github add labels"
|
|
57
|
+
values={{ sha: <Ink.Text color={colors.green}>f82ac73</Ink.Text> }}
|
|
58
|
+
/>
|
|
59
|
+
<Ink.Box height={1} />
|
|
60
|
+
<FormatText
|
|
61
|
+
wrapper={<Ink.Text color={colors.gray} />}
|
|
62
|
+
message="To target {sha} above, use {command}"
|
|
63
|
+
values={{
|
|
64
|
+
sha: <Ink.Text color={colors.green}>838e878</Ink.Text>,
|
|
65
|
+
command: <Command>git stack fixup 2</Command>,
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
</Ink.Box>,
|
|
31
69
|
);
|
|
70
|
+
|
|
32
71
|
actions.output("");
|
|
33
|
-
actions.output("This script automates the process of adding staged changes as a fixup commit");
|
|
34
|
-
actions.output(
|
|
35
|
-
"and the subsequent git rebase to flatten the commits based on relative commit number",
|
|
36
|
-
);
|
|
37
|
-
actions.output("You can use a `git log` like below to get the relative commit number");
|
|
38
|
-
actions.output("");
|
|
39
|
-
actions.output(" ❯ git stack log");
|
|
40
|
-
actions.output(
|
|
41
|
-
" 1\te329794d5f881cbf0fc3f26d2108cf6f3fdebabe enable drop_error_subtask test param",
|
|
42
|
-
);
|
|
43
|
-
actions.output(
|
|
44
|
-
" 2\t57f43b596e5c6b97bc47e2a591f82ccc81651156 test drop_error_subtask baseline",
|
|
45
|
-
);
|
|
46
|
-
actions.output(" 3\t838e878d483c6a2d5393063fc59baf2407225c6d ErrorSubtask test baseline");
|
|
47
|
-
actions.output("");
|
|
48
|
-
actions.output("To target `838e87` above, you would call `fixup 3`");
|
|
49
72
|
|
|
50
73
|
actions.exit(0);
|
|
51
74
|
}
|
|
@@ -64,11 +87,8 @@ async function run() {
|
|
|
64
87
|
// );
|
|
65
88
|
}
|
|
66
89
|
|
|
67
|
-
// Calculate commit SHA based on the relative commit number
|
|
68
|
-
const adjusted_number = Number(relative_number) - 1;
|
|
69
|
-
|
|
70
90
|
// get the commit SHA of the target commit
|
|
71
|
-
const commit_sha = (await cli(`git rev-parse HEAD~${
|
|
91
|
+
const commit_sha = (await cli(`git rev-parse HEAD~${relative_number}`)).stdout;
|
|
72
92
|
|
|
73
93
|
actions.output(
|
|
74
94
|
<FormatText
|
|
@@ -104,7 +124,7 @@ async function run() {
|
|
|
104
124
|
|
|
105
125
|
try {
|
|
106
126
|
// rebase target needs to account for new commit created above
|
|
107
|
-
const rebase_target = Number(relative_number) +
|
|
127
|
+
const rebase_target = Number(relative_number) + 2;
|
|
108
128
|
|
|
109
129
|
await cli(`git rebase -i --autosquash HEAD~${rebase_target}`, {
|
|
110
130
|
env: {
|
package/src/commands/Log.tsx
CHANGED
|
@@ -65,7 +65,7 @@ async function run(args: Args) {
|
|
|
65
65
|
const command = [
|
|
66
66
|
`git log --pretty=format:"${format}" -n20 --graph --color ${rest_args}`,
|
|
67
67
|
`cut -c 1-"${truncation_width}"`,
|
|
68
|
-
`nl -w3 -s' '`,
|
|
68
|
+
`nl -v0 -w3 -s' '`,
|
|
69
69
|
].join(" | ");
|
|
70
70
|
|
|
71
71
|
const result = await cli(command);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function get_timeout_fn(ms: number, message: string) {
|
|
2
|
-
return function timeout<T>(promise: Promise<T>) {
|
|
2
|
+
return async function timeout<T>(promise: Promise<T>) {
|
|
3
3
|
let id: ReturnType<typeof setTimeout>;
|
|
4
4
|
|
|
5
5
|
const timeout = new Promise<never>((_resolve, reject) => {
|
package/src/core/github.tsx
CHANGED
|
@@ -121,6 +121,7 @@ type CreatePullRequestArgs = {
|
|
|
121
121
|
title: string;
|
|
122
122
|
body: string;
|
|
123
123
|
draft: boolean;
|
|
124
|
+
labels?: Array<string>;
|
|
124
125
|
};
|
|
125
126
|
|
|
126
127
|
export async function pr_create(args: CreatePullRequestArgs) {
|
|
@@ -147,6 +148,13 @@ export async function pr_create(args: CreatePullRequestArgs) {
|
|
|
147
148
|
command_parts.push("--draft");
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
if (args.labels && args.labels.length > 0) {
|
|
152
|
+
for (const label of args.labels) {
|
|
153
|
+
if (!label) continue;
|
|
154
|
+
command_parts.push(`--label="${safe_quote(label)}"`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
150
158
|
const cli_result = await cli(command_parts);
|
|
151
159
|
|
|
152
160
|
if (cli_result.code !== 0) {
|
|
@@ -161,14 +169,17 @@ type EditPullRequestArgs = {
|
|
|
161
169
|
branch: string;
|
|
162
170
|
base?: string;
|
|
163
171
|
body?: string;
|
|
172
|
+
add_labels?: Array<string>;
|
|
164
173
|
};
|
|
165
174
|
|
|
166
175
|
export async function pr_edit(args: EditPullRequestArgs) {
|
|
176
|
+
// https://cli.github.com/manual/gh_pr_edit
|
|
177
|
+
|
|
167
178
|
// const state = Store.getState();
|
|
168
179
|
// const actions = state.actions;
|
|
169
180
|
// actions.debug(`github.pr_edit ${JSON.stringify(args)}`);
|
|
170
181
|
|
|
171
|
-
if (!args.base && !args.body) {
|
|
182
|
+
if (!args.base && !args.body && !(args.add_labels && args.add_labels.length > 0)) {
|
|
172
183
|
return;
|
|
173
184
|
}
|
|
174
185
|
|
|
@@ -186,6 +197,13 @@ export async function pr_edit(args: EditPullRequestArgs) {
|
|
|
186
197
|
command_parts.push(`--body-file="${body_file}"`);
|
|
187
198
|
}
|
|
188
199
|
|
|
200
|
+
if (args.add_labels && args.add_labels.length > 0) {
|
|
201
|
+
for (const label of args.add_labels) {
|
|
202
|
+
if (!label) continue;
|
|
203
|
+
command_parts.push(`--add-label="${safe_quote(label)}"`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
189
207
|
const cli_result = await cli(command_parts);
|
|
190
208
|
|
|
191
209
|
if (cli_result.code !== 0) {
|