git-stack-cli 2.8.0 → 2.8.2
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 +6 -0
- package/dist/js/index.js +69 -69
- package/package.json +1 -1
- package/scripts/release-npm.ts +3 -3
- package/src/app/GatherMetadata.test.tsx +33 -0
- package/src/app/GatherMetadata.tsx +12 -1
- package/src/app/PreManualRebase.tsx +39 -16
- package/src/app/SyncGithub.tsx +5 -4
- package/src/core/CommitMetadata.ts +53 -20
- package/src/core/github.tsx +4 -0
package/package.json
CHANGED
package/scripts/release-npm.ts
CHANGED
|
@@ -6,6 +6,9 @@ import { spawn } from "~/core/spawn";
|
|
|
6
6
|
|
|
7
7
|
process.env.NODE_ENV = "production";
|
|
8
8
|
|
|
9
|
+
// ensure npm is authenticated
|
|
10
|
+
await spawn(`npm whoami`);
|
|
11
|
+
|
|
9
12
|
// get paths relative to this script
|
|
10
13
|
const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
|
|
11
14
|
const DIST_DIR = path.join(REPO_ROOT, "dist");
|
|
@@ -54,9 +57,6 @@ for (const filepath of package_json.files) {
|
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
// ensure npm is authenticated
|
|
58
|
-
await spawn(`npm whoami`);
|
|
59
|
-
|
|
60
60
|
process.chdir(REPO_ROOT);
|
|
61
61
|
|
|
62
62
|
await spawn.sync(`git commit -a -m ${version}`);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { test, expect } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import * as GatherMetadata from "~/app/GatherMetadata";
|
|
4
|
+
|
|
5
|
+
test("invalid origin", () => {
|
|
6
|
+
const origin_url = "";
|
|
7
|
+
const repo_path = GatherMetadata.get_repo_path(origin_url);
|
|
8
|
+
expect(repo_path).toBe(null);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("https .git", () => {
|
|
12
|
+
const origin_url = "https://github.com/magus/git-multi-diff-playground.git";
|
|
13
|
+
const repo_path = GatherMetadata.get_repo_path(origin_url);
|
|
14
|
+
expect(repo_path).toBe("magus/git-multi-diff-playground");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("https without .git", () => {
|
|
18
|
+
const origin_url = "https://github.com/magus/git-multi-diff-playground";
|
|
19
|
+
const repo_path = GatherMetadata.get_repo_path(origin_url);
|
|
20
|
+
expect(repo_path).toBe("magus/git-multi-diff-playground");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("git@ .git", () => {
|
|
24
|
+
const origin_url = "git@github.com:magus/git-multi-diff-playground.git";
|
|
25
|
+
const repo_path = GatherMetadata.get_repo_path(origin_url);
|
|
26
|
+
expect(repo_path).toBe("magus/git-multi-diff-playground");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("git@ without .git", () => {
|
|
30
|
+
const origin_url = "git@github.com:magus/git-multi-diff-playground";
|
|
31
|
+
const repo_path = GatherMetadata.get_repo_path(origin_url);
|
|
32
|
+
expect(repo_path).toBe("magus/git-multi-diff-playground");
|
|
33
|
+
});
|
|
@@ -97,10 +97,21 @@ async function run() {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
export function get_repo_path(origin_url: string): null | string {
|
|
101
|
+
try {
|
|
102
|
+
return match_group(origin_url, RE.repo_path, "repo_path");
|
|
103
|
+
} catch {
|
|
104
|
+
// pass
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
100
109
|
const RE = {
|
|
101
110
|
// git@github.com:magus/git-multi-diff-playground.git
|
|
102
111
|
// https://github.com/magus/git-multi-diff-playground.git
|
|
103
|
-
|
|
112
|
+
// the .git suffix is optional for remote urls, and may not always be provided
|
|
113
|
+
// https://regex101.com/r/pICG7G/1
|
|
114
|
+
repo_path: /(?<repo_path>[^:^/]+\/[^/]+?)(\.git)?$/,
|
|
104
115
|
};
|
|
105
116
|
|
|
106
117
|
const BRANCH = {
|
|
@@ -39,10 +39,11 @@ async function run() {
|
|
|
39
39
|
// ./pull_request_template.md
|
|
40
40
|
// ./docs/pull_request_template.md
|
|
41
41
|
for (const key of PR_TEMPLATE_KEY_LIST) {
|
|
42
|
-
const pr_template_fn = PR_TEMPLATE[key
|
|
42
|
+
const pr_template_fn = PR_TEMPLATE[key];
|
|
43
|
+
const pr_template_file = pr_template_fn(repo_root);
|
|
43
44
|
|
|
44
|
-
if (await safe_exists(
|
|
45
|
-
pr_template_body = await fs.readFile(
|
|
45
|
+
if (await safe_exists(pr_template_file)) {
|
|
46
|
+
pr_template_body = await fs.readFile(pr_template_file, "utf-8");
|
|
46
47
|
|
|
47
48
|
actions.output(
|
|
48
49
|
<FormatText
|
|
@@ -58,10 +59,23 @@ async function run() {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
// ./.github/PULL_REQUEST_TEMPLATE/*.md
|
|
62
62
|
let pr_templates: Array<string> = [];
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
let pr_dir: string = "";
|
|
64
|
+
|
|
65
|
+
// ./.github/PULL_REQUEST_TEMPLATE/*.md
|
|
66
|
+
pr_dir = PR_TEMPLATE.DirGithub(repo_root);
|
|
67
|
+
if (await safe_exists(pr_dir)) {
|
|
68
|
+
for (const filename of await fs.readdir(pr_dir)) {
|
|
69
|
+
pr_templates.push(path.join(pr_dir, filename));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ./docs/PULL_REQUEST_TEMPLATE/*.md
|
|
74
|
+
pr_dir = PR_TEMPLATE.DirDocs(repo_root);
|
|
75
|
+
if (await safe_exists(pr_dir)) {
|
|
76
|
+
for (const filename of await fs.readdir(pr_dir)) {
|
|
77
|
+
pr_templates.push(path.join(pr_dir, filename));
|
|
78
|
+
}
|
|
65
79
|
}
|
|
66
80
|
|
|
67
81
|
// check if repo has multiple pr templates
|
|
@@ -71,14 +85,20 @@ async function run() {
|
|
|
71
85
|
|
|
72
86
|
if (pr_templates.length > 0) {
|
|
73
87
|
actions.output(
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
<Ink.Box flexDirection="column">
|
|
89
|
+
{pr_templates.map((filepath) => {
|
|
90
|
+
const relpath = path.relative(repo_root, filepath);
|
|
91
|
+
return <Ink.Text key={filepath}>- {relpath}</Ink.Text>;
|
|
92
|
+
})}
|
|
93
|
+
|
|
94
|
+
<FormatText
|
|
95
|
+
wrapper={<Ink.Text color={colors.yellow} />}
|
|
96
|
+
message="{count} queryable templates found, but not supported."
|
|
97
|
+
values={{
|
|
98
|
+
count: <Ink.Text color={colors.blue}>{pr_templates.length}</Ink.Text>,
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
</Ink.Box>,
|
|
82
102
|
);
|
|
83
103
|
}
|
|
84
104
|
|
|
@@ -91,8 +111,11 @@ const PR_TEMPLATE = Object.freeze({
|
|
|
91
111
|
Github: (root: string) => path.join(root, ".github", "pull_request_template.md"),
|
|
92
112
|
Root: (root: string) => path.join(root, "pull_request_template.md"),
|
|
93
113
|
Docs: (root: string) => path.join(root, "docs", "pull_request_template.md"),
|
|
94
|
-
|
|
114
|
+
|
|
115
|
+
DirDocs: (root: string) => path.join(root, "docs", "PULL_REQUEST_TEMPLATE/"),
|
|
116
|
+
DirGithub: (root: string) => path.join(root, ".github", "PULL_REQUEST_TEMPLATE/"),
|
|
95
117
|
});
|
|
96
118
|
|
|
97
119
|
// prettier-ignore
|
|
98
|
-
|
|
120
|
+
//
|
|
121
|
+
const PR_TEMPLATE_KEY_LIST = ["Github", "Root", "Docs"] satisfies Array<keyof typeof PR_TEMPLATE>;
|
package/src/app/SyncGithub.tsx
CHANGED
|
@@ -75,12 +75,15 @@ async function run() {
|
|
|
75
75
|
|
|
76
76
|
const git_push_target_list: Array<string> = [];
|
|
77
77
|
|
|
78
|
-
for (
|
|
78
|
+
for (let i = 0; i < push_group_list.length; i++) {
|
|
79
|
+
const group = push_group_list[i];
|
|
79
80
|
const last_commit = last(group.commits);
|
|
80
81
|
invariant(last_commit, "last_commit must exist");
|
|
81
82
|
|
|
82
83
|
// push group in isolation if master_base is set
|
|
83
|
-
|
|
84
|
+
// for the first group (i > 0) we can skip this
|
|
85
|
+
// since it'll be based off master anyway
|
|
86
|
+
if (group.master_base && i > 0) {
|
|
84
87
|
await push_master_group(group);
|
|
85
88
|
continue;
|
|
86
89
|
}
|
|
@@ -237,8 +240,6 @@ async function run() {
|
|
|
237
240
|
if (!is_master_base(group)) {
|
|
238
241
|
// ensure base matches pr in github
|
|
239
242
|
await github.pr_edit({ branch: group.id, base: group.base });
|
|
240
|
-
} else {
|
|
241
|
-
await github.pr_edit({ branch: group.id });
|
|
242
243
|
}
|
|
243
244
|
} else {
|
|
244
245
|
// create pr in github
|
|
@@ -121,6 +121,7 @@ export async function range(commit_group_map?: CommitGroupMap) {
|
|
|
121
121
|
|
|
122
122
|
for (let i = 0; i < group_value_list.length; i++) {
|
|
123
123
|
const group = group_value_list[i];
|
|
124
|
+
const previous_group: undefined | CommitGroup = group_value_list[i - 1];
|
|
124
125
|
|
|
125
126
|
if (group.id !== UNASSIGNED) {
|
|
126
127
|
let pr_result = pr_lookup[group.id];
|
|
@@ -164,29 +165,61 @@ export async function range(commit_group_map?: CommitGroupMap) {
|
|
|
164
165
|
|
|
165
166
|
if (!group.pr) {
|
|
166
167
|
group.dirty = true;
|
|
167
|
-
} else if (group.pr.commits.length !== group.commits.length) {
|
|
168
|
-
group.dirty = true;
|
|
169
|
-
} else if (group.pr.baseRefName !== group.base) {
|
|
170
|
-
group.dirty = true;
|
|
171
|
-
} else if (group.master_base) {
|
|
172
|
-
group.pr.number;
|
|
173
|
-
// master_base groups cannot be compared by commit sha
|
|
174
|
-
// instead compare the literal diff local against origin
|
|
175
|
-
// gh pr diff --color=never 110
|
|
176
|
-
// git --no-pager diff --color=never 00c8fe0~1..00c8fe0 | pbcopy
|
|
177
|
-
const diff_github = await github.pr_diff(group.pr.number);
|
|
178
|
-
const diff_local = await git.get_diff(group.commits);
|
|
179
|
-
if (diff_github !== diff_local) {
|
|
180
|
-
group.dirty = true;
|
|
181
|
-
}
|
|
182
168
|
} else {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
169
|
+
if (group.pr.baseRefName !== group.base) {
|
|
170
|
+
group.dirty = true;
|
|
171
|
+
} else if (group.master_base && i > 0) {
|
|
172
|
+
// special case
|
|
173
|
+
// master_base groups cannot be compared by commit sha
|
|
174
|
+
// instead compare the literal diff local against origin
|
|
175
|
+
// gh pr diff --color=never 110
|
|
176
|
+
// git --no-pager diff --color=never 00c8fe0~1..00c8fe0
|
|
177
|
+
const diff_github = await github.pr_diff(group.pr.number);
|
|
178
|
+
const diff_local = await git.get_diff(group.commits);
|
|
179
|
+
if (diff_github !== diff_local) {
|
|
180
|
+
group.dirty = true;
|
|
181
|
+
}
|
|
182
|
+
} else if (!group.master_base && previous_group && previous_group.master_base) {
|
|
183
|
+
// special case
|
|
184
|
+
// boundary between normal commits and master commits
|
|
185
|
+
|
|
186
|
+
// collect all previous groups for sha comparison
|
|
187
|
+
const all_commits: Array<git.Commit> = [];
|
|
188
|
+
const previous_groups = group_value_list.slice(0, i);
|
|
189
|
+
for (const g of previous_groups) {
|
|
190
|
+
for (const c of g.commits) {
|
|
191
|
+
all_commits.push(c);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
for (const c of group.commits) {
|
|
195
|
+
all_commits.push(c);
|
|
196
|
+
}
|
|
187
197
|
|
|
188
|
-
|
|
198
|
+
// compare all commits against pr commits
|
|
199
|
+
if (group.pr.commits.length !== all_commits.length) {
|
|
189
200
|
group.dirty = true;
|
|
201
|
+
} else {
|
|
202
|
+
for (let i = 0; i < group.pr.commits.length; i++) {
|
|
203
|
+
const pr_commit = group.pr.commits[i];
|
|
204
|
+
const local_commit = all_commits[i];
|
|
205
|
+
|
|
206
|
+
if (pr_commit.oid !== local_commit.sha) {
|
|
207
|
+
group.dirty = true;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} else if (group.pr.commits.length !== group.commits.length) {
|
|
212
|
+
group.dirty = true;
|
|
213
|
+
} else {
|
|
214
|
+
// if we still haven't marked this dirty, check each commit
|
|
215
|
+
// comapre literal commit shas in group
|
|
216
|
+
for (let i = 0; i < group.pr.commits.length; i++) {
|
|
217
|
+
const pr_commit = group.pr.commits[i];
|
|
218
|
+
const local_commit = group.commits[i];
|
|
219
|
+
|
|
220
|
+
if (pr_commit.oid !== local_commit.sha) {
|
|
221
|
+
group.dirty = true;
|
|
222
|
+
}
|
|
190
223
|
}
|
|
191
224
|
}
|
|
192
225
|
}
|
package/src/core/github.tsx
CHANGED
|
@@ -171,6 +171,10 @@ export async function pr_edit(args: EditPullRequestArgs) {
|
|
|
171
171
|
// const actions = state.actions;
|
|
172
172
|
// actions.debug(`github.pr_edit ${JSON.stringify(args)}`);
|
|
173
173
|
|
|
174
|
+
if (!args.base && !args.body) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
174
178
|
const command_parts = [`gh pr edit ${args.branch}`];
|
|
175
179
|
|
|
176
180
|
if (args.base) {
|