@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
|
@@ -5,8 +5,16 @@ import MarkdownIt from "markdown-it";
|
|
|
5
5
|
export function parseWorkitemUrl(link) {
|
|
6
6
|
const u = new URL(link);
|
|
7
7
|
const parts = u.pathname.replace(/\/$/, "").split("/");
|
|
8
|
-
if (u.protocol !== "https:" ||
|
|
9
|
-
|
|
8
|
+
if (u.protocol !== "https:" ||
|
|
9
|
+
u.host !== "devops.aliyun.com" ||
|
|
10
|
+
u.username ||
|
|
11
|
+
u.password ||
|
|
12
|
+
parts.length !== 6 ||
|
|
13
|
+
parts[1] !== "projex" ||
|
|
14
|
+
parts[2] !== "project" ||
|
|
15
|
+
!parts[3] ||
|
|
16
|
+
!parts[4] ||
|
|
17
|
+
!parts[5]) {
|
|
10
18
|
throw new Error("Expected a Yunxiao workitem URL: https://devops.aliyun.com/projex/project/<projectId>/<type>/<workitemId>");
|
|
11
19
|
}
|
|
12
20
|
return { projectId: parts[3], workitemId: parts[5] };
|
|
@@ -21,7 +29,9 @@ function records(value) {
|
|
|
21
29
|
throw new Error("Expected an API array.");
|
|
22
30
|
return value.map(record);
|
|
23
31
|
}
|
|
24
|
-
function str(value) {
|
|
32
|
+
function str(value) {
|
|
33
|
+
return typeof value === "string" ? value : "";
|
|
34
|
+
}
|
|
25
35
|
function markup(content, format) {
|
|
26
36
|
const text = str(content);
|
|
27
37
|
if (format === "MARKDOWN")
|
|
@@ -41,7 +51,8 @@ function imageExtension(head) {
|
|
|
41
51
|
return ".jpg";
|
|
42
52
|
if (/^GIF8[79]a/.test(head.toString("ascii", 0, 6)))
|
|
43
53
|
return ".gif";
|
|
44
|
-
if (head.toString("ascii", 0, 4) === "RIFF" &&
|
|
54
|
+
if (head.toString("ascii", 0, 4) === "RIFF" &&
|
|
55
|
+
head.toString("ascii", 8, 12) === "WEBP")
|
|
45
56
|
return ".webp";
|
|
46
57
|
if (head.toString("ascii", 0, 2) === "BM")
|
|
47
58
|
return ".bmp";
|
|
@@ -49,10 +60,10 @@ function imageExtension(head) {
|
|
|
49
60
|
return ".ico";
|
|
50
61
|
throw new Error("Download is not a supported image (PNG/JPEG/GIF/WebP/BMP/ICO).");
|
|
51
62
|
}
|
|
52
|
-
async function download(url, target, image, fetcher) {
|
|
63
|
+
async function download(url, target, image, fetcher, timeoutMs = 30000) {
|
|
53
64
|
let response;
|
|
54
65
|
try {
|
|
55
|
-
response = await fetcher(url, { signal: AbortSignal.timeout(
|
|
66
|
+
response = await fetcher(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
56
67
|
}
|
|
57
68
|
catch {
|
|
58
69
|
throw new Error("File download failed (network error or timeout).");
|
|
@@ -105,12 +116,13 @@ async function download(url, target, image, fetcher) {
|
|
|
105
116
|
}
|
|
106
117
|
}
|
|
107
118
|
}
|
|
108
|
-
async function localize(client, id, content, format, source, dir, cache, images, fetcher) {
|
|
119
|
+
async function localize(client, id, content, format, source, dir, cache, images, fetcher, timeoutMs) {
|
|
109
120
|
const tree = parseFragment(markup(content, format));
|
|
110
121
|
async function visit(node) {
|
|
111
122
|
if ("tagName" in node && node.tagName === "img") {
|
|
112
|
-
const src = node.attrs.find(a => a.name === "src");
|
|
113
|
-
const fileId = src &&
|
|
123
|
+
const src = node.attrs.find((a) => a.name === "src");
|
|
124
|
+
const fileId = src &&
|
|
125
|
+
new URL(src.value, "https://devops.aliyun.com").searchParams.get("fileIdentifier");
|
|
114
126
|
if (!src || !fileId)
|
|
115
127
|
throw new Error(`${source}: only Yunxiao images with fileIdentifier are supported.`);
|
|
116
128
|
let local = cache.get(fileId);
|
|
@@ -118,11 +130,11 @@ async function localize(client, id, content, format, source, dir, cache, images,
|
|
|
118
130
|
const file = record(await client.getWorkitemFile(id, fileId));
|
|
119
131
|
if (!str(file.url))
|
|
120
132
|
throw new Error(`Missing download URL for image ${fileId}.`);
|
|
121
|
-
local = await download(str(file.url), path.resolve(dir, String(cache.size + 1).padStart(2, "0")), true, fetcher);
|
|
133
|
+
local = await download(str(file.url), path.resolve(dir, String(cache.size + 1).padStart(2, "0")), true, fetcher, timeoutMs);
|
|
122
134
|
cache.set(fileId, local);
|
|
123
135
|
}
|
|
124
136
|
src.value = local;
|
|
125
|
-
if (!images.some(i => i.fileId === fileId && i.source === source))
|
|
137
|
+
if (!images.some((i) => i.fileId === fileId && i.source === source))
|
|
126
138
|
images.push({ fileId, path: local, source });
|
|
127
139
|
}
|
|
128
140
|
if ("childNodes" in node)
|
|
@@ -144,7 +156,7 @@ async function attachments(client, id, options) {
|
|
|
144
156
|
let name = path.posix.basename(str(file.fileName).replaceAll("\\", "/"));
|
|
145
157
|
if (!name || name === "." || name === "..")
|
|
146
158
|
name = "attachment";
|
|
147
|
-
file.path = await download(url, path.resolve(options.dir, `${String(index + 1).padStart(2, "0")}-${name}`), false, options.fetcher ?? fetch);
|
|
159
|
+
file.path = await download(url, path.resolve(options.dir, `${String(index + 1).padStart(2, "0")}-${name}`), false, options.fetcher ?? fetch, options.timeoutMs);
|
|
148
160
|
}
|
|
149
161
|
return files;
|
|
150
162
|
}
|
|
@@ -161,11 +173,20 @@ export async function readWorkitemContent(client, link, options) {
|
|
|
161
173
|
const cache = new Map();
|
|
162
174
|
const dir = path.join(options.dir, "images");
|
|
163
175
|
const fetcher = options.fetcher ?? fetch;
|
|
164
|
-
const descriptionHtml = await localize(client, id, workitem.description, workitem.formatType, "description", dir, cache, images, fetcher);
|
|
176
|
+
const descriptionHtml = await localize(client, id, workitem.description, workitem.formatType, "description", dir, cache, images, fetcher, options.timeoutMs);
|
|
165
177
|
for (const c of comments)
|
|
166
|
-
c.contentHtml = await localize(client, id, c.content, c.contentFormat, `comment:${c.id}`, dir, cache, images, fetcher);
|
|
167
|
-
return {
|
|
168
|
-
|
|
178
|
+
c.contentHtml = await localize(client, id, c.content, c.contentFormat, `comment:${c.id}`, dir, cache, images, fetcher, options.timeoutMs);
|
|
179
|
+
return {
|
|
180
|
+
url: link,
|
|
181
|
+
workitem,
|
|
182
|
+
descriptionHtml,
|
|
183
|
+
comments,
|
|
184
|
+
images,
|
|
185
|
+
attachments: await attachments(client, id, {
|
|
186
|
+
...options,
|
|
187
|
+
dir: path.join(options.dir, "attachments"),
|
|
188
|
+
}),
|
|
189
|
+
};
|
|
169
190
|
}
|
|
170
191
|
export async function downloadWorkitemImages(client, id, options) {
|
|
171
192
|
const workitem = record(await client.getWorkitem(id));
|
|
@@ -173,8 +194,8 @@ export async function downloadWorkitemImages(client, id, options) {
|
|
|
173
194
|
const images = [];
|
|
174
195
|
const cache = new Map();
|
|
175
196
|
const fetcher = options.fetcher ?? fetch;
|
|
176
|
-
await localize(client, id, workitem.description, workitem.formatType, "description", options.dir, cache, images, fetcher);
|
|
197
|
+
await localize(client, id, workitem.description, workitem.formatType, "description", options.dir, cache, images, fetcher, options.timeoutMs);
|
|
177
198
|
for (const c of comments)
|
|
178
|
-
await localize(client, id, c.content, c.contentFormat, `comment:${c.id}`, options.dir, cache, images, fetcher);
|
|
199
|
+
await localize(client, id, c.content, c.contentFormat, `comment:${c.id}`, options.dir, cache, images, fetcher, options.timeoutMs);
|
|
179
200
|
return images;
|
|
180
201
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderpp/yunxiao-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A CLI for Yunxiao merge
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A CLI for Yunxiao merge requests, reviews, releases, pipelines, project members, and workitems.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|