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