@perrylink/dsh-github 0.6.2 → 0.6.4

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.
@@ -1,61 +1,61 @@
1
- // Post-process the CI run for the composite action: publish the step outputs
2
- // from dsh-github-ci-result.json and enforce the blocking gate.
3
- //
4
- // Exit codes:
5
- // 0 — report exists and the gate passed (or the verdict is non-blocking).
6
- // 1 — report missing (the headless run never produced one), or
7
- // blocking=true and the verdict is "needs-changes".
8
- // The status check itself (published by the pipeline) is what gates branch
9
- // protection; this exit code fails the CI job for blocking=false-style
10
- // workflow-level enforcement and for downstream status reporting.
11
- import { readFileSync, appendFileSync } from 'node:fs'
12
- import { join } from 'node:path'
13
-
14
- // Local-machine protection: the post step reads the CI result from the output
15
- // directory and publishes step outputs through GITHUB_OUTPUT, both of which
16
- // only exist on a GitHub Actions runner. Outside a runner the fallback would
17
- // read an arbitrary `./dsh-github` directory next to the caller's cwd and
18
- // report phantom verdicts. Refuse to run instead of guessing.
19
- const runnerTemp = (process.env.RUNNER_TEMP ?? '').trim()
20
- const workspace = (process.env.GITHUB_WORKSPACE ?? '').trim()
21
- if (runnerTemp === '' || workspace === '') {
22
- console.error('dsh-github: refusing to run outside a GitHub Actions runner — both RUNNER_TEMP and GITHUB_WORKSPACE must be set.')
23
- console.error('dsh-github: to exercise the action locally, use `node scripts/local-test.mjs`; it pins DSH_HOME, DSH_PROFILE_DIR, and the output directory under the system temp directory.')
24
- process.exit(1)
25
- }
26
-
27
- const outputDir = process.env.INPUT_OUTPUT_DIR?.trim() || join(process.env.RUNNER_TEMP ?? '.', 'dsh-github')
28
- const resultPath = join(outputDir, 'dsh-github-ci-result.json')
29
- const blocking = (process.env.INPUT_BLOCKING ?? 'true').trim() === 'true'
30
- const outputFile = process.env.GITHUB_OUTPUT ?? ''
31
-
32
- let result
33
- try {
34
- result = JSON.parse(readFileSync(resultPath, 'utf8'))
35
- } catch (error) {
36
- console.error(`dsh-github: no CI result at ${resultPath} — the headless run did not produce a review (${error instanceof Error ? error.message : String(error)}). Check the dsh stderr log next to it.`)
37
- if (outputFile !== '') appendFileSync(outputFile, 'verdict=error\n')
38
- process.exit(1)
39
- }
40
-
41
- const markdownPath = join(outputDir, 'dsh-github-ci-summary.md')
42
- const verdict = typeof result.verdict === 'string' ? result.verdict : 'error'
43
- if (outputFile !== '') {
44
- appendFileSync(outputFile,
45
- `verdict=${verdict}\n`
46
- + `report-json=${resultPath}\n`
47
- + `report-markdown=${markdownPath}\n`
48
- + `${result.checkRun?.url !== undefined ? `check-url=${result.checkRun.url}\n` : ''}`)
49
- }
50
-
51
- console.log(`dsh-github: verdict ${verdict} — PR #${result.pr} in ${result.repo}${result.alreadyReviewed ? ' (already reviewed at this head commit)' : ''}`)
52
- console.log(`dsh-github: ${result.summary}`)
53
-
54
- if (blocking && verdict === 'needs-changes') {
55
- console.error(`dsh-github: blocking gate failed — the review verdict is "needs-changes". See ${markdownPath}`)
56
- process.exit(1)
57
- }
58
- if (verdict === 'error') {
59
- console.error('dsh-github: the pipeline reported an error verdict')
60
- process.exit(1)
61
- }
1
+ // Post-process the CI run for the composite action: publish the step outputs
2
+ // from dsh-github-ci-result.json and enforce the blocking gate.
3
+ //
4
+ // Exit codes:
5
+ // 0 — report exists and the gate passed (or the verdict is non-blocking).
6
+ // 1 — report missing (the headless run never produced one), or
7
+ // blocking=true and the verdict is "needs-changes".
8
+ // The status check itself (published by the pipeline) is what gates branch
9
+ // protection; this exit code fails the CI job for blocking=false-style
10
+ // workflow-level enforcement and for downstream status reporting.
11
+ import { readFileSync, appendFileSync } from 'node:fs'
12
+ import { join } from 'node:path'
13
+
14
+ // Local-machine protection: the post step reads the CI result from the output
15
+ // directory and publishes step outputs through GITHUB_OUTPUT, both of which
16
+ // only exist on a GitHub Actions runner. Outside a runner the fallback would
17
+ // read an arbitrary `./dsh-github` directory next to the caller's cwd and
18
+ // report phantom verdicts. Refuse to run instead of guessing.
19
+ const runnerTemp = (process.env.RUNNER_TEMP ?? '').trim()
20
+ const workspace = (process.env.GITHUB_WORKSPACE ?? '').trim()
21
+ if (runnerTemp === '' || workspace === '') {
22
+ console.error('dsh-github: refusing to run outside a GitHub Actions runner — both RUNNER_TEMP and GITHUB_WORKSPACE must be set.')
23
+ console.error('dsh-github: to exercise the action locally, use `node scripts/local-test.mjs`; it pins DSH_HOME, DSH_PROFILE_DIR, and the output directory under the system temp directory.')
24
+ process.exit(1)
25
+ }
26
+
27
+ const outputDir = process.env.INPUT_OUTPUT_DIR?.trim() || join(process.env.RUNNER_TEMP ?? '.', 'dsh-github')
28
+ const resultPath = join(outputDir, 'dsh-github-ci-result.json')
29
+ const blocking = (process.env.INPUT_BLOCKING ?? 'true').trim() === 'true'
30
+ const outputFile = process.env.GITHUB_OUTPUT ?? ''
31
+
32
+ let result
33
+ try {
34
+ result = JSON.parse(readFileSync(resultPath, 'utf8'))
35
+ } catch (error) {
36
+ console.error(`dsh-github: no CI result at ${resultPath} — the headless run did not produce a review (${error instanceof Error ? error.message : String(error)}). Check the dsh stderr log next to it.`)
37
+ if (outputFile !== '') appendFileSync(outputFile, 'verdict=error\n')
38
+ process.exit(1)
39
+ }
40
+
41
+ const markdownPath = join(outputDir, 'dsh-github-ci-summary.md')
42
+ const verdict = typeof result.verdict === 'string' ? result.verdict : 'error'
43
+ if (outputFile !== '') {
44
+ appendFileSync(outputFile,
45
+ `verdict=${verdict}\n`
46
+ + `report-json=${resultPath}\n`
47
+ + `report-markdown=${markdownPath}\n`
48
+ + `${result.checkRun?.url !== undefined ? `check-url=${result.checkRun.url}\n` : ''}`)
49
+ }
50
+
51
+ console.log(`dsh-github: verdict ${verdict} — PR #${result.pr} in ${result.repo}${result.alreadyReviewed ? ' (already reviewed at this head commit)' : ''}`)
52
+ console.log(`dsh-github: ${result.summary}`)
53
+
54
+ if (blocking && verdict === 'needs-changes') {
55
+ console.error(`dsh-github: blocking gate failed — the review verdict is "needs-changes". See ${markdownPath}`)
56
+ process.exit(1)
57
+ }
58
+ if (verdict === 'error') {
59
+ console.error('dsh-github: the pipeline reported an error verdict')
60
+ process.exit(1)
61
+ }
@@ -0,0 +1,48 @@
1
+ // scripts/check-endpoints.mjs — M3 外部端点存活探测(Claude Code check-mcp-urls
2
+ // 模式):401/403/405/5xx = 存活(未带凭据的预期响应),仅 404/410/DNS/TLS/超时
3
+ // = 失败。GitHub API 为插件声明的固定公共端点。
4
+ import { request } from 'node:https'
5
+
6
+ const ENDPOINTS = [
7
+ { name: 'GitHub API', url: 'https://api.github.com' },
8
+ { name: 'GitHub web', url: 'https://github.com' },
9
+ ]
10
+
11
+ const TIMEOUT_MS = Number(process.env.TIMEOUT_MS ?? 15000)
12
+
13
+ function probe(url) {
14
+ return new Promise((resolve) => {
15
+ const controller = new AbortController()
16
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)
17
+ const req = request(url, { method: 'GET', headers: { 'user-agent': 'dsh-endpoint-liveness/1.0' }, signal: controller.signal }, (res) => {
18
+ res.resume()
19
+ clearTimeout(timer)
20
+ resolve({ status: res.statusCode })
21
+ })
22
+ req.on('error', (error) => {
23
+ clearTimeout(timer)
24
+ const message = String(error?.message ?? error)
25
+ if (controller.signal.aborted) return resolve({ status: null, error: 'timeout' })
26
+ if (message.includes('ENOTFOUND')) return resolve({ status: null, error: 'DNS' })
27
+ if (/certificate|TLS|SSL|EPROTO/u.test(message)) return resolve({ status: null, error: 'TLS' })
28
+ resolve({ status: null, error: message })
29
+ })
30
+ req.end()
31
+ })
32
+ }
33
+
34
+ const failures = []
35
+ for (const endpoint of ENDPOINTS) {
36
+ const result = await probe(endpoint.url)
37
+ const status = result.status
38
+ const alive = status === 200 || (status !== null && status >= 400 && status !== 404 && status !== 410)
39
+ const verdict = alive ? 'ALIVE' : 'FAIL'
40
+ console.log(`${verdict} ${String(status ?? result.error)} ${endpoint.name} ${endpoint.url}`)
41
+ if (!alive) failures.push(`${endpoint.name}: ${String(status ?? result.error)}`)
42
+ }
43
+
44
+ if (failures.length > 0) {
45
+ console.error(`\nendpoint liveness failed:\n- ${failures.join('\n- ')}`)
46
+ process.exit(1)
47
+ }
48
+ console.log(`\nendpoint liveness passed: ${ENDPOINTS.length} endpoint(s) alive`)
@@ -0,0 +1,39 @@
1
+ // fix-dts.mjs — rewrite `.ts` relative specifiers to `.js` in emitted
2
+ // declarations (lib/**/*.d.ts). TypeScript 5.9's
3
+ // `rewriteRelativeImportExtensions` rewrites the JS emit but NOT the
4
+ // declaration emit (verified empirically), so declaration consumers under
5
+ // NodeNext resolution would fail on `.ts` specifiers. Node builtins only:
6
+ // this runs inside the git-install `prepare` with production deps alone.
7
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
8
+ import { dirname, join } from 'node:path'
9
+ import { fileURLToPath } from 'node:url'
10
+
11
+ const root = join(dirname(fileURLToPath(import.meta.url)), '..', 'lib')
12
+ if (!existsSync(root)) {
13
+ console.log('fix-dts: no lib to rewrite')
14
+ process.exit(0)
15
+ }
16
+
17
+ let files = 0
18
+ let rewrites = 0
19
+ const rewrite = (dir) => {
20
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
21
+ const full = join(dir, entry.name)
22
+ if (entry.isDirectory()) {
23
+ rewrite(full)
24
+ } else if (entry.name.endsWith('.d.ts')) {
25
+ const text = readFileSync(full, 'utf8')
26
+ const next = text.replace(
27
+ /((?:from\s+|import\()['"])(\.\.?\/[^'"]+?)(\.ts|\.tsx)(['"])/gu,
28
+ (match, prefix, specifier, ext, quote) => `${prefix}${specifier}.js${quote}`,
29
+ )
30
+ if (next !== text) {
31
+ writeFileSync(full, next)
32
+ files += 1
33
+ rewrites += (text.match(/(?:from\s+|import\()['"]\.\.?\/[^'"]+?\.tsx?['"]/gu) ?? []).length
34
+ }
35
+ }
36
+ }
37
+ }
38
+ rewrite(root)
39
+ console.log(`fix-dts: ${files} file(s), ${rewrites} specifier(s) rewritten to .js`)