dsh-pr-release-workspace 0.1.0 → 0.1.1
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/lib/client.js +10 -1
- package/lib/host/exec.js +13 -0
- package/lib/host/pull.js +29 -7
- package/locale/en.json +1 -0
- package/locale/zh.json +1 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -77,6 +77,7 @@ window.__ModuleLoader__.load({
|
|
|
77
77
|
'detail.noDiff': 'No changed files were returned.',
|
|
78
78
|
'detail.binary': 'Binary file — no text diff.',
|
|
79
79
|
'detail.diffCapped': 'This pull request is larger than one diff; files were read individually.',
|
|
80
|
+
'detail.diffIncomplete': 'This pull request changes more files than this panel will page through; the list below is incomplete.',
|
|
80
81
|
'detail.diffFailed': 'The diff could not be loaded.',
|
|
81
82
|
'detail.noComments': 'No review comments yet.',
|
|
82
83
|
'detail.reviewComment': 'on line {line}',
|
|
@@ -186,6 +187,7 @@ window.__ModuleLoader__.load({
|
|
|
186
187
|
'detail.noDiff': '没有返回变更文件。',
|
|
187
188
|
'detail.binary': '二进制文件,无文本差异。',
|
|
188
189
|
'detail.diffCapped': '这个 PR 的改动超过单份 diff 的限额,已按文件逐个读取。',
|
|
190
|
+
'detail.diffIncomplete': '这个 PR 的改动文件数超过本面板的分页上限,下面的列表并不完整。',
|
|
189
191
|
'detail.diffFailed': 'diff 加载失败。',
|
|
190
192
|
'detail.noComments': '还没有评审评论。',
|
|
191
193
|
'detail.reviewComment': '第 {line} 行',
|
|
@@ -1859,7 +1861,14 @@ window.__ModuleLoader__.load({
|
|
|
1859
1861
|
'div',
|
|
1860
1862
|
{ style: { display: 'flex', flexDirection: 'column', gap: '2px' } },
|
|
1861
1863
|
value.truncated === true
|
|
1862
|
-
? h(
|
|
1864
|
+
? h(
|
|
1865
|
+
'p',
|
|
1866
|
+
{ className: 'prw-note', style: { margin: '0 0 8px' } },
|
|
1867
|
+
// `truncated` says GitHub would not render one diff; `complete`
|
|
1868
|
+
// says whether the per-file read reached the end. They are
|
|
1869
|
+
// different claims and a partial list must not look whole.
|
|
1870
|
+
value.complete === false ? t('detail.diffIncomplete') : t('detail.diffCapped'),
|
|
1871
|
+
)
|
|
1863
1872
|
: null,
|
|
1864
1873
|
value.files.map(function (file, index) {
|
|
1865
1874
|
return h(FileRow, {
|
package/lib/host/exec.js
CHANGED
|
@@ -67,6 +67,19 @@ export function classifyGhFailure(error, stderr) {
|
|
|
67
67
|
const rate = matchRateLimit(text)
|
|
68
68
|
if (rate !== null) return rate
|
|
69
69
|
|
|
70
|
+
// `gh pr diff` refuses any PR over 300 changed files with
|
|
71
|
+
// `HTTP 406: … the diff exceeded the maximum number of files (300)` plus
|
|
72
|
+
// `PullRequest.diff too_large`. This has its own code because the caller can
|
|
73
|
+
// recover from it — the per-file endpoint still answers — and because the
|
|
74
|
+
// generic `unknown` message would erase the only thing that identifies it.
|
|
75
|
+
if (/too_large|exceeded the maximum number of files|diff exceeded the maximum/i.test(text)) {
|
|
76
|
+
return {
|
|
77
|
+
code: 'diff-too-large',
|
|
78
|
+
message: 'This pull request changes more files than GitHub will return as a single diff.',
|
|
79
|
+
retryAfterSeconds: null,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
70
83
|
if (/Resource not accessible by integration|must have .* access|insufficient.*scope/i.test(text)) {
|
|
71
84
|
return {
|
|
72
85
|
code: 'forbidden',
|
package/lib/host/pull.js
CHANGED
|
@@ -452,13 +452,18 @@ export async function readPullDiff(target, options = {}) {
|
|
|
452
452
|
// hunks. Falling back keeps a big PR readable instead of turning the detail
|
|
453
453
|
// view into an error page.
|
|
454
454
|
if (!isDiffTooLarge(error)) throw error
|
|
455
|
-
const files = await readFilePatches(target, options)
|
|
455
|
+
const { files, complete } = await readFilePatches(target, options)
|
|
456
456
|
return {
|
|
457
457
|
files,
|
|
458
|
+
// `truncated` means "GitHub would not render this as one diff", which is
|
|
459
|
+
// exactly the condition that got us here.
|
|
458
460
|
truncated: true,
|
|
461
|
+
complete,
|
|
459
462
|
bytes: 0,
|
|
460
463
|
source: 'rest-file-patches',
|
|
461
|
-
note:
|
|
464
|
+
note: complete
|
|
465
|
+
? 'This pull request has more changed files than GitHub will render as one diff; it was reassembled from per-file patches.'
|
|
466
|
+
: 'This pull request has more changed files than GitHub will render as one diff, and more than this panel will page through; the list below is incomplete.',
|
|
462
467
|
}
|
|
463
468
|
}
|
|
464
469
|
}
|
|
@@ -466,10 +471,17 @@ export async function readPullDiff(target, options = {}) {
|
|
|
466
471
|
/**
|
|
467
472
|
* Was this failure the "too many files" refusal rather than a real error?
|
|
468
473
|
*
|
|
474
|
+
* `runGh` classifies a failure and hands back a content-free message, so the
|
|
475
|
+
* original stderr — the only place `HTTP 406` and `too_large` appear — is gone
|
|
476
|
+
* by the time we get here. The classifier therefore assigns its own code, and
|
|
477
|
+
* this checks that code. The message pattern stays as a second line of defence
|
|
478
|
+
* for errors that did not come from `runGh`.
|
|
479
|
+
*
|
|
469
480
|
* @param {unknown} error
|
|
470
481
|
* @returns {boolean}
|
|
471
482
|
*/
|
|
472
483
|
function isDiffTooLarge(error) {
|
|
484
|
+
if (error?.code === 'diff-too-large') return true
|
|
473
485
|
const message = String(error?.message ?? '')
|
|
474
486
|
return /406|too_large|exceeded the maximum number of files|too large/i.test(message)
|
|
475
487
|
}
|
|
@@ -485,19 +497,26 @@ function isDiffTooLarge(error) {
|
|
|
485
497
|
*
|
|
486
498
|
* @param {{ owner: string, repo: string, number: number }} target
|
|
487
499
|
* @param {{ signal?: AbortSignal }} [options]
|
|
488
|
-
* @returns {Promise<{ path: string, oldText: string | null, newText: string | null }[]>}
|
|
500
|
+
* @returns {Promise<{ files: { path: string, oldText: string | null, newText: string | null }[], complete: boolean }>}
|
|
489
501
|
*/
|
|
490
502
|
async function readFilePatches(target, options) {
|
|
491
503
|
/** @type {{ path: string, oldText: string | null, newText: string | null }[]} */
|
|
492
504
|
const out = []
|
|
493
505
|
// `per_page` is capped at 100 by the API regardless of what is asked for.
|
|
494
|
-
|
|
506
|
+
// Twenty pages is 2000 files: past that the read is not a diff any more, and
|
|
507
|
+
// the caller is told the list is incomplete rather than being handed a slice
|
|
508
|
+
// that looks whole.
|
|
509
|
+
const maxPages = 20
|
|
510
|
+
let complete = false
|
|
495
511
|
for (let page = 1; page <= maxPages; page += 1) {
|
|
496
512
|
const items = await ghApiJson(
|
|
497
513
|
`/repos/${encodeURIComponent(target.owner)}/${encodeURIComponent(target.repo)}/pulls/${target.number}/files`,
|
|
498
514
|
{ query: { per_page: '100', page: String(page) }, signal: options.signal, timeoutMs: 30_000 },
|
|
499
515
|
)
|
|
500
|
-
if (!Array.isArray(items) || items.length === 0)
|
|
516
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
517
|
+
complete = true
|
|
518
|
+
break
|
|
519
|
+
}
|
|
501
520
|
for (const item of items) {
|
|
502
521
|
if (typeof item?.filename !== 'string') continue
|
|
503
522
|
const status = typeof item?.status === 'string' ? item.status : 'modified'
|
|
@@ -513,9 +532,12 @@ async function readFilePatches(target, options) {
|
|
|
513
532
|
newText: status === 'removed' ? null : newText,
|
|
514
533
|
})
|
|
515
534
|
}
|
|
516
|
-
if (items.length < 100)
|
|
535
|
+
if (items.length < 100) {
|
|
536
|
+
complete = true
|
|
537
|
+
break
|
|
538
|
+
}
|
|
517
539
|
}
|
|
518
|
-
return out
|
|
540
|
+
return { files: out, complete }
|
|
519
541
|
}
|
|
520
542
|
|
|
521
543
|
/**
|
package/locale/en.json
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"detail.conflicts": "Conflicts",
|
|
20
20
|
"detail.diffCapped": "This pull request is larger than one diff; files were read individually.",
|
|
21
21
|
"detail.diffFailed": "The diff could not be loaded.",
|
|
22
|
+
"detail.diffIncomplete": "This pull request changes more files than this panel will page through; the list below is incomplete.",
|
|
22
23
|
"detail.drafts": "My notes",
|
|
23
24
|
"detail.files": "Files",
|
|
24
25
|
"detail.head": "from",
|
package/locale/zh.json
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"detail.conflicts": "有冲突",
|
|
20
20
|
"detail.diffCapped": "这个 PR 的改动超过单份 diff 的限额,已按文件逐个读取。",
|
|
21
21
|
"detail.diffFailed": "diff 加载失败。",
|
|
22
|
+
"detail.diffIncomplete": "这个 PR 的改动文件数超过本面板的分页上限,下面的列表并不完整。",
|
|
22
23
|
"detail.drafts": "我的批注",
|
|
23
24
|
"detail.files": "文件",
|
|
24
25
|
"detail.head": "来自",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-pr-release-workspace",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "PR and release workspace for DSH: grouped pull requests, file diffs, review comments, CI checks, and an evidence-backed release chain from merged PR to the live market index.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|