@coderpp/yunxiao-cli 0.1.11 → 0.2.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/README.md +162 -2
- package/dist/src/arguments.d.ts +11 -0
- package/dist/src/arguments.js +271 -0
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +276 -180
- package/dist/src/client.d.ts +17 -7
- package/dist/src/client.js +94 -141
- package/dist/src/config.d.ts +4 -0
- package/dist/src/config.js +106 -0
- package/dist/src/errors.d.ts +14 -0
- package/dist/src/errors.js +42 -0
- package/dist/src/mr.d.ts +13 -0
- package/dist/src/mr.js +202 -0
- package/dist/src/pagination.d.ts +10 -0
- package/dist/src/pagination.js +35 -0
- package/dist/src/pipeline.d.ts +3 -0
- package/dist/src/pipeline.js +50 -0
- package/dist/src/release.d.ts +2 -0
- package/dist/src/release.js +203 -0
- package/dist/src/transport.d.ts +16 -0
- package/dist/src/transport.js +119 -0
- package/dist/src/workitem-content.d.ts +1 -0
- package/dist/src/workitem-content.js +39 -18
- package/package.json +2 -2
package/dist/src/cli.js
CHANGED
|
@@ -1,41 +1,195 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { parseArgs, validateArgs, loadFileFlags, commandHelp, } from "./arguments.js";
|
|
3
|
+
import { resolveConfig, configCommand } from "./config.js";
|
|
4
|
+
import { CliError, errorInfo, redact } from "./errors.js";
|
|
5
|
+
import { paginate } from "./pagination.js";
|
|
6
|
+
import { runExtendedMr } from "./mr.js";
|
|
7
|
+
import { runExtendedPipeline } from "./pipeline.js";
|
|
8
|
+
import { readWorkitemContent, downloadWorkitemImages, downloadWorkitemAttachments, parseWorkitemUrl, } from "./workitem-content.js";
|
|
9
|
+
import { YunxiaoClient, } from "./client.js";
|
|
3
10
|
const mergeTypes = new Set(["ff-only", "no-fast-forward", "squash", "rebase"]);
|
|
4
|
-
const outputFormats = new Set(["table", "json"]);
|
|
5
|
-
const booleanFlags = new Set(["download-attachments", "all-pages", "create-only", "help", "json", "remove-source-branch", "yes"]);
|
|
11
|
+
const outputFormats = new Set(["table", "json", "text"]);
|
|
6
12
|
const workitemStateNames = new Map([
|
|
7
13
|
["todo", ["TODO", "待处理", "待办"]],
|
|
8
14
|
["opened", ["TODO", "待处理", "待办"]],
|
|
9
15
|
["doing", ["DOING", "IN_PROGRESS", "PROCESSING", "进行中", "处理中"]],
|
|
10
16
|
["in-progress", ["DOING", "IN_PROGRESS", "PROCESSING", "进行中", "处理中"]],
|
|
11
17
|
["done", ["DONE", "COMPLETED", "CLOSED", "已完成", "完成", "已关闭"]],
|
|
12
|
-
["completed", ["DONE", "COMPLETED", "CLOSED", "已完成", "完成", "已关闭"]]
|
|
18
|
+
["completed", ["DONE", "COMPLETED", "CLOSED", "已完成", "完成", "已关闭"]],
|
|
13
19
|
]);
|
|
14
20
|
export async function runCli(args = process.argv.slice(2), env = process.env, io = {}) {
|
|
15
21
|
const stdout = io.stdout ?? ((text) => process.stdout.write(text));
|
|
16
22
|
const stderr = io.stderr ?? ((text) => process.stderr.write(text));
|
|
23
|
+
let parsed;
|
|
24
|
+
let effectiveEnv = env;
|
|
17
25
|
try {
|
|
18
|
-
|
|
19
|
-
if (parsed.
|
|
20
|
-
stdout(
|
|
26
|
+
parsed = parseArgs(args);
|
|
27
|
+
if (parsed.flags.version === true) {
|
|
28
|
+
stdout(JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version + "\n");
|
|
21
29
|
return 0;
|
|
22
30
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
if (parsed.positionals.length === 0 || parsed.flags.help === true) {
|
|
32
|
+
stdout(parsed.positionals.length
|
|
33
|
+
? commandHelp(parsed.positionals)
|
|
34
|
+
: "yunxiao - Yunxiao DevOps command helper\n\n" + commandHelp([]));
|
|
35
|
+
return 0;
|
|
26
36
|
}
|
|
37
|
+
validateArgs(parsed);
|
|
38
|
+
await loadFileFlags(parsed.flags, io.stdin);
|
|
39
|
+
const [scope, command] = parsed.positionals;
|
|
27
40
|
const outputFormat = outputFormatFromFlags(parsed.flags);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
if (scope === "config") {
|
|
42
|
+
printResult(await configCommand(command, parsed.positionals[2], parsed.flags, env), outputFormat, stdout);
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
effectiveEnv = await resolveConfig(parsed.flags, env);
|
|
46
|
+
if (scope === "doctor" && parsed.flags.remote !== true) {
|
|
47
|
+
const checks = {
|
|
48
|
+
tokenConfigured: !!(parsed.flags.token ?? effectiveEnv.YUNXIAO_TOKEN),
|
|
49
|
+
domain: parsed.flags.domain ??
|
|
50
|
+
effectiveEnv.YUNXIAO_DOMAIN ??
|
|
51
|
+
"openapi-rdc.aliyuncs.com",
|
|
52
|
+
edition: parsed.flags.edition ??
|
|
53
|
+
effectiveEnv.YUNXIAO_EDITION ??
|
|
54
|
+
((parsed.flags["organization-id"] ??
|
|
55
|
+
effectiveEnv.YUNXIAO_ORGANIZATION_ID)
|
|
56
|
+
? "center"
|
|
57
|
+
: "region"),
|
|
58
|
+
};
|
|
59
|
+
createClient(parsed.flags, effectiveEnv, io.fetcher);
|
|
60
|
+
printResult({ ...checks, valid: true, remoteChecked: false }, outputFormat, stdout);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
const client = createClient(parsed.flags, effectiveEnv, io.fetcher);
|
|
64
|
+
let result;
|
|
65
|
+
if (scope === "auth")
|
|
66
|
+
result = await client.getCurrentUser();
|
|
67
|
+
else if (scope === "doctor")
|
|
68
|
+
result = {
|
|
69
|
+
valid: true,
|
|
70
|
+
remoteChecked: true,
|
|
71
|
+
user: await client.getCurrentUser(),
|
|
72
|
+
writePermissions: "not verified",
|
|
73
|
+
};
|
|
74
|
+
else
|
|
75
|
+
result = await runCommand(scope, command, parsed, client, effectiveEnv, stderr);
|
|
76
|
+
if (outputFormat === "text") {
|
|
77
|
+
const r = result;
|
|
78
|
+
if (scope === "pipeline") {
|
|
79
|
+
stdout(String(r.content ?? ""));
|
|
80
|
+
if (r.more)
|
|
81
|
+
stderr("Server reports more log content; this is a single snapshot.\n");
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
stdout(r.files
|
|
85
|
+
.map((f) => `${f.complete ? "" : `# INCOMPLETE: ${f.reason}\n`}${f.diff}`)
|
|
86
|
+
.join("\n"));
|
|
87
|
+
if (!r.complete)
|
|
88
|
+
stderr("Diff is incomplete; inspect JSON metadata.\n");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else
|
|
92
|
+
printResult(result, scope === "workitem" &&
|
|
93
|
+
["content", "images", "attachments"].includes(command)
|
|
94
|
+
? "json"
|
|
95
|
+
: outputFormat, stdout);
|
|
31
96
|
return 0;
|
|
32
97
|
}
|
|
33
98
|
catch (error) {
|
|
34
|
-
|
|
35
|
-
|
|
99
|
+
const info = errorInfo(error);
|
|
100
|
+
if (parsed?.positionals[0] === "mr" &&
|
|
101
|
+
parsed.positionals[1] === "release" &&
|
|
102
|
+
info.details &&
|
|
103
|
+
typeof info.details === "object") {
|
|
104
|
+
const details = info.details;
|
|
105
|
+
if (Array.isArray(details.resumeArgs)) {
|
|
106
|
+
const contextArgs = [
|
|
107
|
+
"config",
|
|
108
|
+
"profile",
|
|
109
|
+
"domain",
|
|
110
|
+
"organization-id",
|
|
111
|
+
"edition",
|
|
112
|
+
].flatMap((key) => typeof parsed.flags[key] === "string"
|
|
113
|
+
? [`--${key}`, String(parsed.flags[key])]
|
|
114
|
+
: []);
|
|
115
|
+
info.details = {
|
|
116
|
+
...details,
|
|
117
|
+
resumeArgs: [...details.resumeArgs, ...contextArgs],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const secrets = [
|
|
122
|
+
String(parsed?.flags.token ?? ""),
|
|
123
|
+
effectiveEnv.YUNXIAO_TOKEN ?? "",
|
|
124
|
+
];
|
|
125
|
+
const json = parsed?.flags["error-format"] === "json" ||
|
|
126
|
+
args.includes("--error-format=json") ||
|
|
127
|
+
args.some((v, i) => v === "--error-format" && args[i + 1] === "json");
|
|
128
|
+
stderr(redact(json
|
|
129
|
+
? JSON.stringify({
|
|
130
|
+
error: {
|
|
131
|
+
code: info.code,
|
|
132
|
+
message: info.message,
|
|
133
|
+
requestId: info.requestId,
|
|
134
|
+
details: info.details,
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
: info.message +
|
|
138
|
+
(info.details ? "\n" + JSON.stringify(info.details) : ""), secrets) + "\n");
|
|
139
|
+
return info.exitCode;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function runCommand(scope, command, parsed, client, env, warn) {
|
|
143
|
+
const f = parsed.flags;
|
|
144
|
+
const pageOptions = {
|
|
145
|
+
page: optionalNumber(f.page),
|
|
146
|
+
perPage: optionalNumber(f["per-page"]) ?? 20,
|
|
147
|
+
allPages: hasFlag(f, "all-pages"),
|
|
148
|
+
warn,
|
|
149
|
+
};
|
|
150
|
+
if (scope === "mr" &&
|
|
151
|
+
[
|
|
152
|
+
"get",
|
|
153
|
+
"diff",
|
|
154
|
+
"comments",
|
|
155
|
+
"comment",
|
|
156
|
+
"create",
|
|
157
|
+
"update",
|
|
158
|
+
"close",
|
|
159
|
+
].includes(command))
|
|
160
|
+
return runExtendedMr(command, parsed.positionals, f, client);
|
|
161
|
+
if (scope === "pipeline" &&
|
|
162
|
+
["jobs", "logs", "start", "cancel"].includes(command))
|
|
163
|
+
return runExtendedPipeline(command, parsed.positionals, f, client);
|
|
164
|
+
if (scope === "mr" && command === "list")
|
|
165
|
+
return paginate((page, perPage) => client.listChangeRequests({ ...listOptions(f), page, perPage }), pageOptions);
|
|
166
|
+
if (scope === "project" && command === "list")
|
|
167
|
+
return paginate((page, perPage) => client.searchProjects({ ...projectSearchOptions(f), page, perPage }), pageOptions);
|
|
168
|
+
if (scope === "member" && command === "list")
|
|
169
|
+
return paginate((page, perPage) => client.listMembers({ page, perPage }), pageOptions);
|
|
170
|
+
if (scope === "pipeline" && command === "runs")
|
|
171
|
+
return paginate((page, perPage) => client.listPipelineRuns(parsed.positionals[2], {
|
|
172
|
+
...pipelineRunsOptions(f),
|
|
173
|
+
page,
|
|
174
|
+
perPage,
|
|
175
|
+
}), pageOptions);
|
|
176
|
+
if (scope === "pipeline" && command === "list")
|
|
177
|
+
return paginate((page, perPage) => client.listPipelines({
|
|
178
|
+
page,
|
|
179
|
+
perPage,
|
|
180
|
+
pipelineName: optionalString(f.name),
|
|
181
|
+
statusList: optionalString(f.status),
|
|
182
|
+
createStartTime: optionalNumber(f["create-start-time"]),
|
|
183
|
+
createEndTime: optionalNumber(f["create-end-time"]),
|
|
184
|
+
executeStartTime: optionalNumber(f["start-time"]),
|
|
185
|
+
executeEndTime: optionalNumber(f["end-time"]),
|
|
186
|
+
}), pageOptions);
|
|
187
|
+
if (scope === "workitem" && ["list", "mine"].includes(command)) {
|
|
188
|
+
const flags = command === "mine"
|
|
189
|
+
? { ...f, "assigned-to": (await client.getCurrentUser()).id }
|
|
190
|
+
: f;
|
|
191
|
+
return searchWorkitems(client, flags, env, warn);
|
|
36
192
|
}
|
|
37
|
-
}
|
|
38
|
-
async function runCommand(scope, command, parsed, client, env) {
|
|
39
193
|
switch (scope) {
|
|
40
194
|
case "mr":
|
|
41
195
|
return runMergeRequestCommand(command, parsed, client);
|
|
@@ -50,7 +204,7 @@ async function runCommand(scope, command, parsed, client, env) {
|
|
|
50
204
|
case "wi":
|
|
51
205
|
return runWorkitemCommand(command, parsed, client, env);
|
|
52
206
|
default:
|
|
53
|
-
throw new
|
|
207
|
+
throw new CliError("INVALID_ARGUMENT", `Unknown command scope: ${scope}`);
|
|
54
208
|
}
|
|
55
209
|
}
|
|
56
210
|
async function runMergeRequestCommand(command, parsed, client) {
|
|
@@ -59,7 +213,7 @@ async function runMergeRequestCommand(command, parsed, client) {
|
|
|
59
213
|
return client.listChangeRequests(listOptions(parsed.flags));
|
|
60
214
|
}
|
|
61
215
|
if (!repositoryId || !localIdRaw) {
|
|
62
|
-
throw new
|
|
216
|
+
throw new CliError("INVALID_ARGUMENT", `Command mr ${command} requires <repositoryId> and <localId>.`);
|
|
63
217
|
}
|
|
64
218
|
if (command === "release") {
|
|
65
219
|
requireConfirmation(parsed.flags);
|
|
@@ -68,16 +222,18 @@ async function runMergeRequestCommand(command, parsed, client) {
|
|
|
68
222
|
sourceBranch: localIdRaw,
|
|
69
223
|
targetBranch: requiredPositional(parsed.positionals[4], "targetBranch"),
|
|
70
224
|
createOnly: hasFlag(parsed.flags, "create-only"),
|
|
225
|
+
resume: optionalNumber(parsed.flags.resume),
|
|
71
226
|
title: optionalString(parsed.flags.title),
|
|
72
227
|
description: optionalString(parsed.flags.description),
|
|
73
|
-
mergeType: mergeOptions(parsed.flags).mergeType
|
|
228
|
+
mergeType: mergeOptions(parsed.flags).mergeType,
|
|
74
229
|
});
|
|
75
230
|
}
|
|
76
231
|
const localId = parseLocalId(localIdRaw);
|
|
77
232
|
switch (command) {
|
|
78
233
|
case "review":
|
|
79
234
|
return client.reviewChangeRequest(repositoryId, localId, {
|
|
80
|
-
reviewComment: optionalString(parsed.flags.comment)
|
|
235
|
+
reviewComment: optionalString(parsed.flags.comment),
|
|
236
|
+
reviewOpinion: parsed.flags.opinion === "not-pass" ? "NOT_PASS" : "PASS",
|
|
81
237
|
});
|
|
82
238
|
case "merge":
|
|
83
239
|
requireConfirmation(parsed.flags);
|
|
@@ -86,17 +242,17 @@ async function runMergeRequestCommand(command, parsed, client) {
|
|
|
86
242
|
requireConfirmation(parsed.flags);
|
|
87
243
|
return client.approveAndMerge(repositoryId, localId, {
|
|
88
244
|
reviewComment: optionalString(parsed.flags.comment),
|
|
89
|
-
...mergeOptions(parsed.flags)
|
|
245
|
+
...mergeOptions(parsed.flags),
|
|
90
246
|
});
|
|
91
247
|
case "tree":
|
|
92
248
|
return client.getChangeRequestTree(repositoryId, localId, {
|
|
93
249
|
fromPatchSetId: requiredFlag(parsed.flags, "from-patch-set-id"),
|
|
94
|
-
toPatchSetId: requiredFlag(parsed.flags, "to-patch-set-id")
|
|
250
|
+
toPatchSetId: requiredFlag(parsed.flags, "to-patch-set-id"),
|
|
95
251
|
});
|
|
96
252
|
case "patches":
|
|
97
253
|
return client.listChangeRequestPatchSets(repositoryId, localId);
|
|
98
254
|
default:
|
|
99
|
-
throw new
|
|
255
|
+
throw new CliError("INVALID_ARGUMENT", `Unknown mr command: ${command}`);
|
|
100
256
|
}
|
|
101
257
|
}
|
|
102
258
|
async function runProjectCommand(command, parsed, client) {
|
|
@@ -110,7 +266,7 @@ async function runProjectCommand(command, parsed, client) {
|
|
|
110
266
|
case "members":
|
|
111
267
|
return client.listProjectMembers(requiredPositional(projectId, "projectId"), {
|
|
112
268
|
name: optionalString(parsed.flags.name),
|
|
113
|
-
roleId: optionalString(parsed.flags["role-id"])
|
|
269
|
+
roleId: optionalString(parsed.flags["role-id"]),
|
|
114
270
|
});
|
|
115
271
|
case "member-add":
|
|
116
272
|
case "add-member":
|
|
@@ -118,7 +274,7 @@ async function runProjectCommand(command, parsed, client) {
|
|
|
118
274
|
return client.createProjectMember(requiredPositional(projectId, "projectId"), {
|
|
119
275
|
operatorId: optionalString(parsed.flags["operator-id"]),
|
|
120
276
|
roleId: optionalString(parsed.flags["role-id"]) ?? "project.participant",
|
|
121
|
-
userIds: requiredCsvFlag(parsed.flags, "user-ids")
|
|
277
|
+
userIds: requiredCsvFlag(parsed.flags, "user-ids"),
|
|
122
278
|
});
|
|
123
279
|
case "member-remove":
|
|
124
280
|
case "remove-member":
|
|
@@ -126,10 +282,10 @@ async function runProjectCommand(command, parsed, client) {
|
|
|
126
282
|
return client.deleteProjectMember(requiredPositional(projectId, "projectId"), {
|
|
127
283
|
operatorId: optionalString(parsed.flags["operator-id"]),
|
|
128
284
|
roleId: optionalString(parsed.flags["role-id"]) ?? "project.participant",
|
|
129
|
-
userIds: requiredCsvFlag(parsed.flags, "user-ids")
|
|
285
|
+
userIds: requiredCsvFlag(parsed.flags, "user-ids"),
|
|
130
286
|
});
|
|
131
287
|
default:
|
|
132
|
-
throw new
|
|
288
|
+
throw new CliError("INVALID_ARGUMENT", `Unknown project command: ${command}`);
|
|
133
289
|
}
|
|
134
290
|
}
|
|
135
291
|
async function runMemberCommand(command, parsed, client) {
|
|
@@ -137,16 +293,18 @@ async function runMemberCommand(command, parsed, client) {
|
|
|
137
293
|
case "list":
|
|
138
294
|
return client.listMembers({
|
|
139
295
|
page: optionalNumber(parsed.flags.page),
|
|
140
|
-
perPage: optionalNumber(parsed.flags["per-page"])
|
|
296
|
+
perPage: optionalNumber(parsed.flags["per-page"]),
|
|
141
297
|
});
|
|
142
298
|
case "get":
|
|
143
299
|
return client.getMember(requiredPositional(parsed.positionals[2], "memberId"));
|
|
144
300
|
default:
|
|
145
|
-
throw new
|
|
301
|
+
throw new CliError("INVALID_ARGUMENT", `Unknown member command: ${command}`);
|
|
146
302
|
}
|
|
147
303
|
}
|
|
148
304
|
async function runPipelineCommand(command, parsed, client) {
|
|
149
|
-
if (command === "failed-runs" ||
|
|
305
|
+
if (command === "failed-runs" ||
|
|
306
|
+
command === "failed" ||
|
|
307
|
+
command === "latest-failed-runs") {
|
|
150
308
|
return client.listLatestFailedPipelineRuns(latestFailedPipelineRunsOptions(parsed.flags));
|
|
151
309
|
}
|
|
152
310
|
const pipelineId = requiredPositional(parsed.positionals[2], "pipelineId");
|
|
@@ -165,7 +323,7 @@ async function runPipelineCommand(command, parsed, client) {
|
|
|
165
323
|
requireConfirmation(parsed.flags);
|
|
166
324
|
return client.retryPipelineJobRun(pipelineId, requiredPositional(pipelineRunId, "pipelineRunId"), requiredPositional(jobId, "jobId"));
|
|
167
325
|
default:
|
|
168
|
-
throw new
|
|
326
|
+
throw new CliError("INVALID_ARGUMENT", `Unknown pipeline command: ${command}`);
|
|
169
327
|
}
|
|
170
328
|
}
|
|
171
329
|
async function runWorkitemCommand(command, parsed, client, env) {
|
|
@@ -182,19 +340,23 @@ async function runWorkitemCommand(command, parsed, client, env) {
|
|
|
182
340
|
const link = requiredPositional(workitemId, "workitemUrl");
|
|
183
341
|
const { projectId, workitemId: id } = parseWorkitemUrl(link);
|
|
184
342
|
if (!workitemProjectIds(parsed.flags, env).includes(projectId))
|
|
185
|
-
throw new
|
|
343
|
+
throw new CliError("INVALID_ARGUMENT", "Link project is outside the configured project scope.");
|
|
186
344
|
return readWorkitemContent(client, link, {
|
|
187
345
|
dir: optionalString(parsed.flags.dir) ?? `workitem-${id}-content`,
|
|
188
|
-
|
|
346
|
+
timeoutMs: optionalNumber(parsed.flags["timeout-ms"]),
|
|
347
|
+
downloadAttachments: hasFlag(parsed.flags, "download-attachments"),
|
|
189
348
|
});
|
|
190
349
|
}
|
|
191
350
|
case "images":
|
|
192
351
|
return downloadWorkitemImages(client, requiredPositional(workitemId, "workitemId"), {
|
|
193
|
-
|
|
352
|
+
timeoutMs: optionalNumber(parsed.flags["timeout-ms"]),
|
|
353
|
+
dir: optionalString(parsed.flags.dir) ?? `workitem-${workitemId}-images`,
|
|
194
354
|
});
|
|
195
355
|
case "attachments":
|
|
196
356
|
return downloadWorkitemAttachments(client, requiredPositional(workitemId, "workitemId"), {
|
|
197
|
-
|
|
357
|
+
timeoutMs: optionalNumber(parsed.flags["timeout-ms"]),
|
|
358
|
+
dir: optionalString(parsed.flags.dir) ??
|
|
359
|
+
`workitem-${workitemId}-attachments`,
|
|
198
360
|
});
|
|
199
361
|
case "get":
|
|
200
362
|
return client.getWorkitem(requiredPositional(workitemId, "workitemId"));
|
|
@@ -211,57 +373,28 @@ async function runWorkitemCommand(command, parsed, client, env) {
|
|
|
211
373
|
return client.createWorkitemComment(requiredPositional(workitemId, "workitemId"), {
|
|
212
374
|
content: requiredFlag(parsed.flags, "content"),
|
|
213
375
|
operatorId: optionalString(parsed.flags["operator-id"]),
|
|
214
|
-
parentId: optionalString(parsed.flags["parent-id"])
|
|
376
|
+
parentId: optionalString(parsed.flags["parent-id"]),
|
|
215
377
|
});
|
|
216
378
|
case "timeline":
|
|
217
379
|
case "history":
|
|
218
380
|
return client.getWorkitemTimeline(requiredPositional(workitemId, "workitemId"));
|
|
219
381
|
default:
|
|
220
|
-
throw new
|
|
382
|
+
throw new CliError("INVALID_ARGUMENT", `Unknown workitem command: ${command}`);
|
|
221
383
|
}
|
|
222
384
|
}
|
|
223
385
|
function createClient(flags, env, fetcher) {
|
|
224
386
|
return new YunxiaoClient({
|
|
225
387
|
token: stringFlag(flags.token) ?? env.YUNXIAO_TOKEN ?? "",
|
|
226
|
-
domain: stringFlag(flags.domain) ??
|
|
388
|
+
domain: stringFlag(flags.domain) ??
|
|
389
|
+
env.YUNXIAO_DOMAIN ??
|
|
390
|
+
"openapi-rdc.aliyuncs.com",
|
|
227
391
|
organizationId: stringFlag(flags["organization-id"]) ?? env.YUNXIAO_ORGANIZATION_ID,
|
|
228
392
|
edition: (stringFlag(flags.edition) ?? env.YUNXIAO_EDITION),
|
|
229
|
-
fetcher
|
|
393
|
+
fetcher,
|
|
394
|
+
timeoutMs: optionalNumber(flags["timeout-ms"]),
|
|
395
|
+
retries: flags.retries === undefined ? undefined : Number(flags.retries),
|
|
230
396
|
});
|
|
231
397
|
}
|
|
232
|
-
function parseArgs(args) {
|
|
233
|
-
const positionals = [];
|
|
234
|
-
const flags = {};
|
|
235
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
236
|
-
const arg = args[index];
|
|
237
|
-
if (!arg.startsWith("--")) {
|
|
238
|
-
positionals.push(arg);
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
const [rawName, inlineValue] = arg.slice(2).split("=", 2);
|
|
242
|
-
if (inlineValue !== undefined) {
|
|
243
|
-
setFlag(flags, rawName, inlineValue);
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
const next = args[index + 1];
|
|
247
|
-
if (next && !next.startsWith("--") && !booleanFlags.has(rawName)) {
|
|
248
|
-
setFlag(flags, rawName, next);
|
|
249
|
-
index += 1;
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
setFlag(flags, rawName, true);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return { positionals, flags };
|
|
256
|
-
}
|
|
257
|
-
function setFlag(flags, name, value) {
|
|
258
|
-
const existing = flags[name];
|
|
259
|
-
if (existing === undefined) {
|
|
260
|
-
flags[name] = value;
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
flags[name] = [...flagValues(existing), ...flagValues(value)];
|
|
264
|
-
}
|
|
265
398
|
function listOptions(flags) {
|
|
266
399
|
return {
|
|
267
400
|
page: optionalNumber(flags.page),
|
|
@@ -274,17 +407,18 @@ function listOptions(flags) {
|
|
|
274
407
|
orderBy: optionalString(flags["order-by"]),
|
|
275
408
|
sort: optionalString(flags.sort),
|
|
276
409
|
createdBefore: optionalString(flags["created-before"]),
|
|
277
|
-
createdAfter: optionalString(flags["created-after"])
|
|
410
|
+
createdAfter: optionalString(flags["created-after"]),
|
|
278
411
|
};
|
|
279
412
|
}
|
|
280
413
|
function projectSearchOptions(flags) {
|
|
281
414
|
return {
|
|
282
|
-
conditions: optionalString(flags.conditions) ??
|
|
415
|
+
conditions: optionalString(flags.conditions) ??
|
|
416
|
+
projectNameConditions(optionalString(flags.name)),
|
|
283
417
|
extraConditions: optionalString(flags["extra-conditions"]),
|
|
284
418
|
orderBy: optionalString(flags["order-by"]) ?? "gmtCreate",
|
|
285
419
|
page: optionalNumber(flags.page) ?? 1,
|
|
286
420
|
perPage: optionalNumber(flags["per-page"]) ?? 20,
|
|
287
|
-
sort: optionalString(flags.sort) ?? "desc"
|
|
421
|
+
sort: optionalString(flags.sort) ?? "desc",
|
|
288
422
|
};
|
|
289
423
|
}
|
|
290
424
|
function pipelineRunsOptions(flags) {
|
|
@@ -294,7 +428,7 @@ function pipelineRunsOptions(flags) {
|
|
|
294
428
|
startTime: optionalNumber(flags["start-time"]),
|
|
295
429
|
endTime: optionalNumber(flags["end-time"]) ?? optionalNumber(flags["end-tme"]),
|
|
296
430
|
status: optionalString(flags.status),
|
|
297
|
-
triggerMode: optionalNumber(flags["trigger-mode"])
|
|
431
|
+
triggerMode: optionalNumber(flags["trigger-mode"]),
|
|
298
432
|
};
|
|
299
433
|
}
|
|
300
434
|
function latestFailedPipelineRunsOptions(flags) {
|
|
@@ -302,38 +436,34 @@ function latestFailedPipelineRunsOptions(flags) {
|
|
|
302
436
|
hours: optionalNumber(flags.hours) ?? 24,
|
|
303
437
|
startTime: optionalNumber(flags["start-time"]),
|
|
304
438
|
endTime: optionalNumber(flags["end-time"]) ?? optionalNumber(flags["end-tme"]),
|
|
305
|
-
perPage: optionalNumber(flags["per-page"]) ?? 30
|
|
439
|
+
perPage: optionalNumber(flags["per-page"]) ?? 30,
|
|
306
440
|
};
|
|
307
441
|
}
|
|
308
|
-
async function searchWorkitems(client, flags, env) {
|
|
442
|
+
async function searchWorkitems(client, flags, env, warn) {
|
|
309
443
|
const projectIds = workitemProjectIds(flags, env);
|
|
310
444
|
const perPage = optionalNumber(flags["per-page"]) ?? 20;
|
|
311
445
|
const firstPage = optionalNumber(flags.page) ?? 1;
|
|
312
446
|
const results = [];
|
|
313
447
|
for (const projectId of projectIds) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const response = await client.searchWorkitems(workitemSearchOptions(flags, projectId, page, perPage, env));
|
|
317
|
-
const rows = Array.isArray(response) ? response : [response];
|
|
318
|
-
results.push(...rows.map((row) => addQueryProjectId(row, projectId)));
|
|
319
|
-
if (!hasFlag(flags, "all-pages") || !Array.isArray(response) || response.length < perPage) {
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
page += 1;
|
|
323
|
-
}
|
|
448
|
+
const rows = await paginate((page, size) => client.searchWorkitems(workitemSearchOptions(flags, projectId, page, size, env)), { page: firstPage, perPage, allPages: hasFlag(flags, "all-pages"), warn });
|
|
449
|
+
results.push(...rows.map((row) => addQueryProjectId(row, projectId)));
|
|
324
450
|
}
|
|
451
|
+
if (flags.state && !hasFlag(flags, "all-pages"))
|
|
452
|
+
warn?.("State filtering applies only to fetched pages; use --all-pages for complete results.\n");
|
|
325
453
|
return filterWorkitems(results, flags);
|
|
326
454
|
}
|
|
327
455
|
function workitemSearchOptions(flags, projectId, page, perPage, env) {
|
|
328
456
|
return {
|
|
329
|
-
category: optionalString(flags.category) ??
|
|
457
|
+
category: optionalString(flags.category) ??
|
|
458
|
+
env.YUNXIAO_WORKITEM_CATEGORIES ??
|
|
459
|
+
"Req,Task,Bug",
|
|
330
460
|
conditions: workitemConditions(flags),
|
|
331
461
|
orderBy: optionalString(flags["order-by"]) ?? "gmtCreate",
|
|
332
462
|
page,
|
|
333
463
|
perPage,
|
|
334
464
|
sort: optionalString(flags.sort) ?? "desc",
|
|
335
465
|
spaceId: projectId,
|
|
336
|
-
spaceType: optionalString(flags["space-type"]) ?? "Project"
|
|
466
|
+
spaceType: optionalString(flags["space-type"]) ?? "Project",
|
|
337
467
|
};
|
|
338
468
|
}
|
|
339
469
|
function workitemConditions(flags) {
|
|
@@ -345,8 +475,9 @@ function workitemConditions(flags) {
|
|
|
345
475
|
const subject = optionalString(flags.subject);
|
|
346
476
|
const from = optionalString(flags.from) ?? optionalString(flags["created-after"]);
|
|
347
477
|
const to = optionalString(flags.to) ?? optionalString(flags["created-before"]);
|
|
348
|
-
if (rawConditions &&
|
|
349
|
-
|
|
478
|
+
if (rawConditions &&
|
|
479
|
+
(assignedTo || creator || statusIds || subject || from || to)) {
|
|
480
|
+
throw new CliError("INVALID_ARGUMENT", "--conditions cannot be combined with shortcut workitem filters.");
|
|
350
481
|
}
|
|
351
482
|
if (rawConditions) {
|
|
352
483
|
return rawConditions;
|
|
@@ -364,7 +495,7 @@ function workitemConditions(flags) {
|
|
|
364
495
|
value: csvValues(statusIds),
|
|
365
496
|
toValue: null,
|
|
366
497
|
className: "status",
|
|
367
|
-
format: "list"
|
|
498
|
+
format: "list",
|
|
368
499
|
});
|
|
369
500
|
}
|
|
370
501
|
if (subject) {
|
|
@@ -374,7 +505,7 @@ function workitemConditions(flags) {
|
|
|
374
505
|
value: [subject],
|
|
375
506
|
toValue: null,
|
|
376
507
|
className: "string",
|
|
377
|
-
format: "input"
|
|
508
|
+
format: "input",
|
|
378
509
|
});
|
|
379
510
|
}
|
|
380
511
|
if (from || to) {
|
|
@@ -384,10 +515,12 @@ function workitemConditions(flags) {
|
|
|
384
515
|
value: from ? [from] : [],
|
|
385
516
|
toValue: to ?? null,
|
|
386
517
|
className: "dateTime",
|
|
387
|
-
format: "input"
|
|
518
|
+
format: "input",
|
|
388
519
|
});
|
|
389
520
|
}
|
|
390
|
-
return filters.length > 0
|
|
521
|
+
return filters.length > 0
|
|
522
|
+
? JSON.stringify({ conditionGroups: [filters] })
|
|
523
|
+
: undefined;
|
|
391
524
|
}
|
|
392
525
|
function userCondition(fieldIdentifier, value) {
|
|
393
526
|
return {
|
|
@@ -396,12 +529,12 @@ function userCondition(fieldIdentifier, value) {
|
|
|
396
529
|
value,
|
|
397
530
|
toValue: null,
|
|
398
531
|
className: "user",
|
|
399
|
-
format: "list"
|
|
532
|
+
format: "list",
|
|
400
533
|
};
|
|
401
534
|
}
|
|
402
535
|
function workitemUpdateBody(flags) {
|
|
403
536
|
const body = {
|
|
404
|
-
...jsonObjectFlag(flags.fields)
|
|
537
|
+
...jsonObjectFlag(flags.fields),
|
|
405
538
|
};
|
|
406
539
|
for (const field of flagStrings(flags.field)) {
|
|
407
540
|
const [key, value] = parseKeyValue(field, "--field");
|
|
@@ -415,39 +548,40 @@ function workitemUpdateBody(flags) {
|
|
|
415
548
|
if (participants) {
|
|
416
549
|
body.participants = csvValues(participants);
|
|
417
550
|
}
|
|
418
|
-
const dueDate = optionalString(flags["due-date"]) ??
|
|
551
|
+
const dueDate = optionalString(flags["due-date"]) ??
|
|
552
|
+
optionalString(flags["plan-finish-at"]);
|
|
419
553
|
if (dueDate) {
|
|
420
554
|
body[optionalString(flags["due-field"]) ?? "dueDate"] = dueDate;
|
|
421
555
|
}
|
|
422
556
|
if (Object.keys(body).length === 0) {
|
|
423
|
-
throw new
|
|
557
|
+
throw new CliError("INVALID_ARGUMENT", "No workitem fields to update. Use --assigned-to, --participants, --due-date, --fields, or --field.");
|
|
424
558
|
}
|
|
425
559
|
return body;
|
|
426
560
|
}
|
|
427
561
|
function mergeOptions(flags) {
|
|
428
562
|
const mergeType = optionalString(flags["merge-type"]) ?? "no-fast-forward";
|
|
429
563
|
if (!mergeTypes.has(mergeType)) {
|
|
430
|
-
throw new
|
|
564
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid --merge-type: ${mergeType}`);
|
|
431
565
|
}
|
|
432
566
|
return {
|
|
433
567
|
mergeMessage: optionalString(flags.message),
|
|
434
568
|
mergeType: mergeType,
|
|
435
|
-
removeSourceBranch: hasFlag(flags, "remove-source-branch")
|
|
569
|
+
removeSourceBranch: hasFlag(flags, "remove-source-branch"),
|
|
436
570
|
};
|
|
437
571
|
}
|
|
438
572
|
function requireConfirmation(flags) {
|
|
439
573
|
if (!hasFlag(flags, "yes")) {
|
|
440
|
-
throw new
|
|
574
|
+
throw new CliError("INVALID_ARGUMENT", "This command changes state. Re-run with --yes after you have reviewed the operation.");
|
|
441
575
|
}
|
|
442
576
|
}
|
|
443
577
|
function outputFormatFromFlags(flags) {
|
|
444
578
|
const requestedOutput = optionalString(flags.output);
|
|
445
579
|
if (hasFlag(flags, "json") && requestedOutput && requestedOutput !== "json") {
|
|
446
|
-
throw new
|
|
580
|
+
throw new CliError("INVALID_ARGUMENT", "--json cannot be combined with a non-JSON --output.");
|
|
447
581
|
}
|
|
448
|
-
const output = hasFlag(flags, "json") ? "json" : requestedOutput ?? "table";
|
|
582
|
+
const output = hasFlag(flags, "json") ? "json" : (requestedOutput ?? "table");
|
|
449
583
|
if (!outputFormats.has(output)) {
|
|
450
|
-
throw new
|
|
584
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid --output: ${output}. Supported formats: table, json.`);
|
|
451
585
|
}
|
|
452
586
|
return output;
|
|
453
587
|
}
|
|
@@ -474,7 +608,7 @@ function formatTable(rows) {
|
|
|
474
608
|
if (rows.length === 0) {
|
|
475
609
|
return "No records found.\n";
|
|
476
610
|
}
|
|
477
|
-
const normalizedRows = rows.map((row) =>
|
|
611
|
+
const normalizedRows = rows.map((row) => isRecord(row) ? row : { value: row });
|
|
478
612
|
const preferredKeys = [
|
|
479
613
|
"pipelineName",
|
|
480
614
|
"pipelineRunId",
|
|
@@ -506,16 +640,16 @@ function formatTable(rows) {
|
|
|
506
640
|
"email",
|
|
507
641
|
"sourceBranch",
|
|
508
642
|
"targetBranch",
|
|
509
|
-
"detailUrl"
|
|
643
|
+
"detailUrl",
|
|
510
644
|
].filter((key) => normalizedRows.some((row) => row[key] !== undefined));
|
|
511
645
|
const keys = preferredKeys.length > 0 ? preferredKeys : uniqueKeys(normalizedRows);
|
|
512
646
|
const widths = keys.map((key) => Math.max(key.length, ...normalizedRows.map((row) => formatCellValue(row[key]).length)));
|
|
513
647
|
const line = (values) => values.map((value, index) => value.padEnd(widths[index])).join(" ");
|
|
514
|
-
return [
|
|
648
|
+
return ([
|
|
515
649
|
line(keys),
|
|
516
650
|
line(keys.map((_, index) => "-".repeat(widths[index]))),
|
|
517
|
-
...normalizedRows.map((row) => line(keys.map((key) => formatCellValue(row[key]))))
|
|
518
|
-
].join("\n") + "\n";
|
|
651
|
+
...normalizedRows.map((row) => line(keys.map((key) => formatCellValue(row[key])))),
|
|
652
|
+
].join("\n") + "\n");
|
|
519
653
|
}
|
|
520
654
|
function uniqueKeys(rows) {
|
|
521
655
|
return Array.from(new Set(rows.flatMap((row) => Object.keys(row))));
|
|
@@ -523,7 +657,7 @@ function uniqueKeys(rows) {
|
|
|
523
657
|
function formatKeyValueTable(record) {
|
|
524
658
|
const rows = Object.entries(record).map(([key, value]) => ({
|
|
525
659
|
key,
|
|
526
|
-
value: formatCellValue(value)
|
|
660
|
+
value: formatCellValue(value),
|
|
527
661
|
}));
|
|
528
662
|
return formatTable(rows);
|
|
529
663
|
}
|
|
@@ -535,7 +669,11 @@ function formatCellValue(value) {
|
|
|
535
669
|
return value.map(formatCellValue).join(",");
|
|
536
670
|
}
|
|
537
671
|
if (isRecord(value)) {
|
|
538
|
-
const displayValue = value.displayName ??
|
|
672
|
+
const displayValue = value.displayName ??
|
|
673
|
+
value.name ??
|
|
674
|
+
value.userName ??
|
|
675
|
+
value.title ??
|
|
676
|
+
value.id;
|
|
539
677
|
if (displayValue !== undefined) {
|
|
540
678
|
return String(displayValue);
|
|
541
679
|
}
|
|
@@ -545,21 +683,21 @@ function formatCellValue(value) {
|
|
|
545
683
|
}
|
|
546
684
|
function parseLocalId(value) {
|
|
547
685
|
const localId = Number(value);
|
|
548
|
-
if (!Number.
|
|
549
|
-
throw new
|
|
686
|
+
if (!Number.isSafeInteger(localId) || localId <= 0) {
|
|
687
|
+
throw new CliError("INVALID_ARGUMENT", `<localId> must be a positive integer: ${value}`);
|
|
550
688
|
}
|
|
551
689
|
return localId;
|
|
552
690
|
}
|
|
553
691
|
function requiredPositional(value, name) {
|
|
554
692
|
if (!value) {
|
|
555
|
-
throw new
|
|
693
|
+
throw new CliError("INVALID_ARGUMENT", `Missing required positional argument <${name}>.`);
|
|
556
694
|
}
|
|
557
695
|
return value;
|
|
558
696
|
}
|
|
559
697
|
function requiredFlag(flags, name) {
|
|
560
698
|
const value = optionalString(flags[name]);
|
|
561
699
|
if (!value) {
|
|
562
|
-
throw new
|
|
700
|
+
throw new CliError("INVALID_ARGUMENT", `Missing required flag --${name}.`);
|
|
563
701
|
}
|
|
564
702
|
return value;
|
|
565
703
|
}
|
|
@@ -579,16 +717,18 @@ function projectNameConditions(name) {
|
|
|
579
717
|
format: "input",
|
|
580
718
|
operator: "CONTAINS",
|
|
581
719
|
toValue: null,
|
|
582
|
-
value: [name]
|
|
583
|
-
}
|
|
584
|
-
]
|
|
585
|
-
]
|
|
720
|
+
value: [name],
|
|
721
|
+
},
|
|
722
|
+
],
|
|
723
|
+
],
|
|
586
724
|
});
|
|
587
725
|
}
|
|
588
726
|
function workitemProjectIds(flags, env) {
|
|
589
|
-
const value = optionalString(flags["project-ids"]) ??
|
|
727
|
+
const value = optionalString(flags["project-ids"]) ??
|
|
728
|
+
optionalString(flags["project-id"]) ??
|
|
729
|
+
env.YUNXIAO_PROJECT_IDS;
|
|
590
730
|
if (!value) {
|
|
591
|
-
throw new
|
|
731
|
+
throw new CliError("INVALID_ARGUMENT", "Missing workitem project scope. Set --project-id, --project-ids, or YUNXIAO_PROJECT_IDS.");
|
|
592
732
|
}
|
|
593
733
|
return csvValues(value);
|
|
594
734
|
}
|
|
@@ -606,18 +746,20 @@ function filterWorkitems(rows, flags) {
|
|
|
606
746
|
if (statusNameEn && statusText(row.status, "nameEn") !== statusNameEn) {
|
|
607
747
|
return false;
|
|
608
748
|
}
|
|
609
|
-
if (statusName &&
|
|
749
|
+
if (statusName &&
|
|
750
|
+
statusText(row.status, "name") !== statusName &&
|
|
751
|
+
statusText(row.status, "displayName") !== statusName) {
|
|
610
752
|
return false;
|
|
611
753
|
}
|
|
612
754
|
if (state && state !== "all") {
|
|
613
755
|
const expected = workitemStateNames.get(state);
|
|
614
756
|
if (!expected) {
|
|
615
|
-
throw new
|
|
757
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid --state: ${state}. Supported values: todo, doing, in-progress, done, completed, all.`);
|
|
616
758
|
}
|
|
617
759
|
const actualValues = [
|
|
618
760
|
statusText(row.status, "nameEn"),
|
|
619
761
|
statusText(row.status, "name"),
|
|
620
|
-
statusText(row.status, "displayName")
|
|
762
|
+
statusText(row.status, "displayName"),
|
|
621
763
|
].filter((value) => Boolean(value));
|
|
622
764
|
return actualValues.some((actual) => expected.includes(actual));
|
|
623
765
|
}
|
|
@@ -638,14 +780,14 @@ function jsonObjectFlag(value) {
|
|
|
638
780
|
}
|
|
639
781
|
const parsed = JSON.parse(raw);
|
|
640
782
|
if (!isRecord(parsed) || Array.isArray(parsed)) {
|
|
641
|
-
throw new
|
|
783
|
+
throw new CliError("INVALID_ARGUMENT", "--fields must be a JSON object.");
|
|
642
784
|
}
|
|
643
785
|
return parsed;
|
|
644
786
|
}
|
|
645
787
|
function parseKeyValue(value, flagName) {
|
|
646
788
|
const separatorIndex = value.indexOf("=");
|
|
647
789
|
if (separatorIndex <= 0) {
|
|
648
|
-
throw new
|
|
790
|
+
throw new CliError("INVALID_ARGUMENT", `${flagName} expects key=value, got: ${value}`);
|
|
649
791
|
}
|
|
650
792
|
return [value.slice(0, separatorIndex), value.slice(separatorIndex + 1)];
|
|
651
793
|
}
|
|
@@ -685,7 +827,9 @@ function hasFlag(flags, name) {
|
|
|
685
827
|
}
|
|
686
828
|
function optionalString(value) {
|
|
687
829
|
if (Array.isArray(value)) {
|
|
688
|
-
return [...value]
|
|
830
|
+
return [...value]
|
|
831
|
+
.reverse()
|
|
832
|
+
.find((item) => typeof item === "string");
|
|
689
833
|
}
|
|
690
834
|
return typeof value === "string" ? value : undefined;
|
|
691
835
|
}
|
|
@@ -699,58 +843,10 @@ function optionalNumber(value) {
|
|
|
699
843
|
}
|
|
700
844
|
const number = Number(stringValue);
|
|
701
845
|
if (!Number.isInteger(number) || number <= 0) {
|
|
702
|
-
throw new
|
|
846
|
+
throw new CliError("INVALID_ARGUMENT", `Expected a positive integer, got: ${stringValue}`);
|
|
703
847
|
}
|
|
704
848
|
return number;
|
|
705
849
|
}
|
|
706
850
|
function isRecord(value) {
|
|
707
851
|
return typeof value === "object" && value !== null;
|
|
708
852
|
}
|
|
709
|
-
function helpText() {
|
|
710
|
-
return `yunxiao - Yunxiao DevOps command helper
|
|
711
|
-
|
|
712
|
-
Usage:
|
|
713
|
-
yunxiao mr list [--state opened] [--project-ids 2813489] [--output table|json]
|
|
714
|
-
yunxiao mr review <repositoryId> <localId> [--comment "LGTM"] [--output table|json]
|
|
715
|
-
yunxiao mr merge <repositoryId> <localId> --yes [--merge-type no-fast-forward] [--output table|json]
|
|
716
|
-
yunxiao mr approve-and-merge <repositoryId> <localId> --yes [--merge-type no-fast-forward] [--output table|json]
|
|
717
|
-
yunxiao mr release <repositoryName> <sourceBranch> <targetBranch> --yes [--create-only] [--title "Release"] [--output table|json]
|
|
718
|
-
yunxiao mr tree <repositoryId> <localId> --from-patch-set-id <id> --to-patch-set-id <id> [--output table|json]
|
|
719
|
-
yunxiao mr patches <repositoryId> <localId> [--output table|json]
|
|
720
|
-
yunxiao project list [--name keyword] [--output table|json]
|
|
721
|
-
yunxiao project get <projectId> [--output table|json]
|
|
722
|
-
yunxiao project members <projectId> [--name keyword] [--role-id project.admin] [--output table|json]
|
|
723
|
-
yunxiao project member-add <projectId> --user-ids user1,user2 [--role-id project.participant] --yes
|
|
724
|
-
yunxiao project member-remove <projectId> --user-ids user1,user2 [--role-id project.participant] --yes
|
|
725
|
-
yunxiao member list [--page 1] [--per-page 100] [--output table|json]
|
|
726
|
-
yunxiao member get <memberId> [--output table|json]
|
|
727
|
-
yunxiao pipeline failed-runs [--hours 24] [--output table|json]
|
|
728
|
-
yunxiao pipeline runs <pipelineId> [--status FAIL] [--start-time 1729178040000] [--end-time 1729181640000] [--output table|json]
|
|
729
|
-
yunxiao pipeline run <pipelineId> <pipelineRunId> [--output table|json]
|
|
730
|
-
yunxiao pipeline retry-job <pipelineId> <pipelineRunId> <jobId> --yes [--output table|json]
|
|
731
|
-
yunxiao workitem list --project-ids p1,p2 [--assigned-to userId] [--created-by userId] [--state todo|doing|done] [--from "2026-04-01 00:00:00"] [--to "2026-04-30 23:59:59"]
|
|
732
|
-
yunxiao workitem mine --project-ids p1,p2 [--state todo|doing|done] [--output table|json]
|
|
733
|
-
yunxiao workitem get <workitemId> [--output table|json]
|
|
734
|
-
yunxiao workitem content <workitemUrl> [--dir directory] [--download-attachments]
|
|
735
|
-
yunxiao workitem images <workitemId> [--dir directory]
|
|
736
|
-
yunxiao workitem attachments <workitemId> [--dir directory]
|
|
737
|
-
yunxiao workitem update <workitemId> --yes [--assigned-to userId] [--participants user1,user2] [--due-date "2026-05-01 18:00:00"] [--field key=value]
|
|
738
|
-
yunxiao workitem activities <workitemId> [--output table|json]
|
|
739
|
-
yunxiao workitem comments <workitemId> [--output table|json]
|
|
740
|
-
yunxiao workitem comment <workitemId> --content "comment" --yes [--output table|json]
|
|
741
|
-
yunxiao workitem timeline <workitemId> [--output table|json]
|
|
742
|
-
|
|
743
|
-
Output:
|
|
744
|
-
--output table Plain text table output, default
|
|
745
|
-
--output json Pretty JSON output
|
|
746
|
-
--json Alias for --output json
|
|
747
|
-
|
|
748
|
-
Config:
|
|
749
|
-
YUNXIAO_TOKEN Personal access token
|
|
750
|
-
YUNXIAO_DOMAIN API domain, default openapi-rdc.aliyuncs.com
|
|
751
|
-
YUNXIAO_ORGANIZATION_ID Required for center edition
|
|
752
|
-
YUNXIAO_EDITION center or region
|
|
753
|
-
YUNXIAO_PROJECT_IDS Default comma-separated project IDs for workitem commands
|
|
754
|
-
YUNXIAO_WORKITEM_CATEGORIES Default workitem categories, default Req,Task,Bug
|
|
755
|
-
`;
|
|
756
|
-
}
|