@coderpp/yunxiao-cli 0.1.0 → 0.1.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/README.md +51 -2
- package/dist/src/cli.js +60 -14
- package/package.json +13 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ npx @coderpp/yunxiao-cli mr list --state opened
|
|
|
28
28
|
也可以指定版本:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
npx @coderpp/yunxiao-cli@0.1.
|
|
31
|
+
npx @coderpp/yunxiao-cli@0.1.4 mr list --output json
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
包内的可执行命令名仍然是 `yunxiao`,全局安装后可这样使用:
|
|
@@ -38,6 +38,37 @@ npm install -g @coderpp/yunxiao-cli
|
|
|
38
38
|
yunxiao mr list --state opened
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
## CI/CD
|
|
42
|
+
|
|
43
|
+
仓库包含两个 GitHub Actions 工作流:
|
|
44
|
+
|
|
45
|
+
- `CI`:在 pull request 和 `main` 分支 push 时运行 `npm ci`、`npm test`、`npm run pack:dry-run`。
|
|
46
|
+
- `Publish`:在推送 `v*.*.*` tag 或手动触发时发布 npm 包。
|
|
47
|
+
|
|
48
|
+
自动发布使用 npm Trusted Publishing,不需要在 GitHub 保存长期 npm token。npm 包设置中需要配置 Trusted Publisher:
|
|
49
|
+
|
|
50
|
+
- Provider:GitHub Actions
|
|
51
|
+
- Organization or user:`coderpp`
|
|
52
|
+
- Repository:`yunxiao-cli`
|
|
53
|
+
- Workflow filename:`publish.yml`
|
|
54
|
+
- Environment name:留空
|
|
55
|
+
|
|
56
|
+
Trusted Publishing 要求发布环境使用 npm CLI 11.5.1 或更高版本,workflow 的发布 job 使用 Node.js 24。
|
|
57
|
+
|
|
58
|
+
发布新版本的流程:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm version patch
|
|
62
|
+
git push origin main --follow-tags
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
如果手动创建 tag,需要确保 tag 和 `package.json` 版本一致,例如:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm version 0.1.1
|
|
69
|
+
git push origin main v0.1.1
|
|
70
|
+
```
|
|
71
|
+
|
|
41
72
|
## 配置
|
|
42
73
|
|
|
43
74
|
不要把个人访问令牌写入代码或提交到仓库。建议通过环境变量提供:
|
|
@@ -66,7 +97,7 @@ node dist/src/index.js mr list --state opened --project-ids 2813489 --per-page 2
|
|
|
66
97
|
输出 JSON:
|
|
67
98
|
|
|
68
99
|
```bash
|
|
69
|
-
node dist/src/index.js mr list --state opened --json
|
|
100
|
+
node dist/src/index.js mr list --state opened --output json
|
|
70
101
|
```
|
|
71
102
|
|
|
72
103
|
评审通过指定合并请求:
|
|
@@ -101,6 +132,24 @@ node dist/src/index.js mr tree 2813489 12 \
|
|
|
101
132
|
node dist/src/index.js mr patches 2813489 12
|
|
102
133
|
```
|
|
103
134
|
|
|
135
|
+
## 输出格式
|
|
136
|
+
|
|
137
|
+
所有会输出 API 结果的命令都支持 `--output`:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
node dist/src/index.js mr list --output table
|
|
141
|
+
node dist/src/index.js mr list --output json
|
|
142
|
+
node dist/src/index.js mr review 2813489 12 --output table
|
|
143
|
+
node dist/src/index.js mr patches 2813489 12 --output json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
支持的格式:
|
|
147
|
+
|
|
148
|
+
- `table`:普通表格文本,默认值。
|
|
149
|
+
- `json`:格式化 JSON。
|
|
150
|
+
|
|
151
|
+
`--json` 仍然可用,等价于 `--output json`。
|
|
152
|
+
|
|
104
153
|
## 参数补充
|
|
105
154
|
|
|
106
155
|
`repositoryId` 可以是代码库 ID,也可以是仓库完整路径,工具会自动做 URL 编码。
|
package/dist/src/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { YunxiaoClient } from "./client.js";
|
|
2
2
|
const mergeTypes = new Set(["ff-only", "no-fast-forward", "squash", "rebase"]);
|
|
3
|
+
const outputFormats = new Set(["table", "json"]);
|
|
3
4
|
export async function runCli(args = process.argv.slice(2), env = process.env, io = {}) {
|
|
4
5
|
const stdout = io.stdout ?? ((text) => process.stdout.write(text));
|
|
5
6
|
const stderr = io.stderr ?? ((text) => process.stderr.write(text));
|
|
@@ -13,11 +14,12 @@ export async function runCli(args = process.argv.slice(2), env = process.env, io
|
|
|
13
14
|
if (scope !== "mr" || !command) {
|
|
14
15
|
throw new Error(`Unknown command: ${parsed.positionals.join(" ")}`);
|
|
15
16
|
}
|
|
17
|
+
const outputFormat = outputFormatFromFlags(parsed.flags);
|
|
16
18
|
const client = createClient(parsed.flags, env, io.fetcher);
|
|
17
19
|
let result;
|
|
18
20
|
if (command === "list") {
|
|
19
21
|
result = await client.listChangeRequests(listOptions(parsed.flags));
|
|
20
|
-
printResult(result,
|
|
22
|
+
printResult(result, outputFormat, stdout);
|
|
21
23
|
return 0;
|
|
22
24
|
}
|
|
23
25
|
if (!repositoryId || !localIdRaw) {
|
|
@@ -53,7 +55,7 @@ export async function runCli(args = process.argv.slice(2), env = process.env, io
|
|
|
53
55
|
default:
|
|
54
56
|
throw new Error(`Unknown mr command: ${command}`);
|
|
55
57
|
}
|
|
56
|
-
printResult(result,
|
|
58
|
+
printResult(result, outputFormat, stdout);
|
|
57
59
|
return 0;
|
|
58
60
|
}
|
|
59
61
|
catch (error) {
|
|
@@ -126,8 +128,19 @@ function requireConfirmation(flags) {
|
|
|
126
128
|
throw new Error("This command changes repository state. Re-run with --yes after you have reviewed the merge request.");
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
|
-
function
|
|
130
|
-
|
|
131
|
+
function outputFormatFromFlags(flags) {
|
|
132
|
+
const requestedOutput = optionalString(flags.output);
|
|
133
|
+
if (flags.json && requestedOutput && requestedOutput !== "json") {
|
|
134
|
+
throw new Error("--json cannot be combined with --output table.");
|
|
135
|
+
}
|
|
136
|
+
const output = flags.json ? "json" : requestedOutput ?? "table";
|
|
137
|
+
if (!outputFormats.has(output)) {
|
|
138
|
+
throw new Error(`Invalid --output: ${output}. Supported formats: table, json.`);
|
|
139
|
+
}
|
|
140
|
+
return output;
|
|
141
|
+
}
|
|
142
|
+
function printResult(result, outputFormat, stdout) {
|
|
143
|
+
if (outputFormat === "json") {
|
|
131
144
|
stdout(`${JSON.stringify(result, null, 2)}\n`);
|
|
132
145
|
return;
|
|
133
146
|
}
|
|
@@ -135,22 +148,50 @@ function printResult(result, asJson, stdout) {
|
|
|
135
148
|
stdout(formatTable(result));
|
|
136
149
|
return;
|
|
137
150
|
}
|
|
138
|
-
|
|
151
|
+
if (isRecord(result)) {
|
|
152
|
+
stdout(formatKeyValueTable(result));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (result === null || result === undefined) {
|
|
156
|
+
stdout("No result.\n");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
stdout(`${String(result)}\n`);
|
|
139
160
|
}
|
|
140
161
|
function formatTable(rows) {
|
|
141
162
|
if (rows.length === 0) {
|
|
142
163
|
return "No records found.\n";
|
|
143
164
|
}
|
|
144
165
|
const normalizedRows = rows.map((row) => (isRecord(row) ? row : { value: row }));
|
|
145
|
-
const
|
|
146
|
-
const
|
|
166
|
+
const preferredKeys = ["localId", "projectId", "title", "state", "sourceBranch", "targetBranch", "detailUrl"].filter((key) => normalizedRows.some((row) => row[key] !== undefined));
|
|
167
|
+
const keys = preferredKeys.length > 0 ? preferredKeys : uniqueKeys(normalizedRows);
|
|
168
|
+
const widths = keys.map((key) => Math.max(key.length, ...normalizedRows.map((row) => formatCellValue(row[key]).length)));
|
|
147
169
|
const line = (values) => values.map((value, index) => value.padEnd(widths[index])).join(" ");
|
|
148
170
|
return [
|
|
149
171
|
line(keys),
|
|
150
172
|
line(keys.map((_, index) => "-".repeat(widths[index]))),
|
|
151
|
-
...normalizedRows.map((row) => line(keys.map((key) =>
|
|
173
|
+
...normalizedRows.map((row) => line(keys.map((key) => formatCellValue(row[key]))))
|
|
152
174
|
].join("\n") + "\n";
|
|
153
175
|
}
|
|
176
|
+
function uniqueKeys(rows) {
|
|
177
|
+
return Array.from(new Set(rows.flatMap((row) => Object.keys(row))));
|
|
178
|
+
}
|
|
179
|
+
function formatKeyValueTable(record) {
|
|
180
|
+
const rows = Object.entries(record).map(([key, value]) => ({
|
|
181
|
+
key,
|
|
182
|
+
value: formatCellValue(value)
|
|
183
|
+
}));
|
|
184
|
+
return formatTable(rows);
|
|
185
|
+
}
|
|
186
|
+
function formatCellValue(value) {
|
|
187
|
+
if (value === null || value === undefined) {
|
|
188
|
+
return "";
|
|
189
|
+
}
|
|
190
|
+
if (typeof value === "object") {
|
|
191
|
+
return JSON.stringify(value);
|
|
192
|
+
}
|
|
193
|
+
return String(value);
|
|
194
|
+
}
|
|
154
195
|
function parseLocalId(value) {
|
|
155
196
|
const localId = Number(value);
|
|
156
197
|
if (!Number.isInteger(localId) || localId <= 0) {
|
|
@@ -188,12 +229,17 @@ function helpText() {
|
|
|
188
229
|
return `yunxiao - Yunxiao Codeup merge request helper
|
|
189
230
|
|
|
190
231
|
Usage:
|
|
191
|
-
yunxiao mr list [--state opened] [--project-ids 2813489] [--json]
|
|
192
|
-
yunxiao mr review <repositoryId> <localId> [--comment "LGTM"]
|
|
193
|
-
yunxiao mr merge <repositoryId> <localId> --yes [--merge-type no-fast-forward]
|
|
194
|
-
yunxiao mr approve-and-merge <repositoryId> <localId> --yes [--merge-type no-fast-forward]
|
|
195
|
-
yunxiao mr tree <repositoryId> <localId> --from-patch-set-id <id> --to-patch-set-id <id>
|
|
196
|
-
yunxiao mr patches <repositoryId> <localId>
|
|
232
|
+
yunxiao mr list [--state opened] [--project-ids 2813489] [--output table|json]
|
|
233
|
+
yunxiao mr review <repositoryId> <localId> [--comment "LGTM"] [--output table|json]
|
|
234
|
+
yunxiao mr merge <repositoryId> <localId> --yes [--merge-type no-fast-forward] [--output table|json]
|
|
235
|
+
yunxiao mr approve-and-merge <repositoryId> <localId> --yes [--merge-type no-fast-forward] [--output table|json]
|
|
236
|
+
yunxiao mr tree <repositoryId> <localId> --from-patch-set-id <id> --to-patch-set-id <id> [--output table|json]
|
|
237
|
+
yunxiao mr patches <repositoryId> <localId> [--output table|json]
|
|
238
|
+
|
|
239
|
+
Output:
|
|
240
|
+
--output table Plain text table output, default
|
|
241
|
+
--output json Pretty JSON output
|
|
242
|
+
--json Alias for --output json
|
|
197
243
|
|
|
198
244
|
Config:
|
|
199
245
|
YUNXIAO_TOKEN Personal access token
|
package/package.json
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderpp/yunxiao-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A CLI for Yunxiao Codeup merge request review and merge workflows.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/coderpp/yunxiao-cli.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/coderpp/yunxiao-cli/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/coderpp/yunxiao-cli#readme",
|
|
6
14
|
"bin": {
|
|
7
15
|
"yunxiao": "dist/src/index.js"
|
|
8
16
|
},
|
|
9
17
|
"scripts": {
|
|
10
18
|
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"pack:dry-run": "npm pack --dry-run",
|
|
11
20
|
"prepack": "npm run build",
|
|
12
21
|
"test": "tsc -p tsconfig.json && node --test dist/test/*.test.js"
|
|
13
22
|
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
14
26
|
"devDependencies": {
|
|
15
27
|
"@types/node": "^20.12.12",
|
|
16
28
|
"typescript": "^5.4.5"
|