@perrylink/dsh-github 0.6.3 → 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.
- package/README.es.md +1 -1
- package/README.hi.md +1 -1
- package/README.md +2 -1
- package/README.pt.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +342 -0
- package/lib/index.d.ts +22 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +29 -1
- package/lib/index.js.map +1 -1
- package/lib/types/client.d.ts +13 -0
- package/package.json +23 -7
- package/scripts/action-patch.mjs +152 -152
- package/scripts/action-post.mjs +61 -61
- package/scripts/local-test.mjs +259 -259
package/scripts/action-patch.mjs
CHANGED
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
// Generates the dsh headless profile overlay and the task text for the
|
|
2
|
-
// composite action. Zero-dependency Node: every input arrives through the
|
|
3
|
-
// runner's INPUT_* environment variables (composite-action inputs), and every
|
|
4
|
-
// value is emitted as a JSON string (a valid YAML scalar) so arbitrary user
|
|
5
|
-
// input can never break the YAML structure.
|
|
6
|
-
//
|
|
7
|
-
// Writes to $INPUT_OUTPUT_DIR:
|
|
8
|
-
// dsh-github-ci.cordis.yml — the --patch overlay (Minimal persona + role
|
|
9
|
-
// line, model selection, sandbox, the minimal bash/editor toolset, and
|
|
10
|
-
// the dsh-github row with the full ci.* configuration).
|
|
11
|
-
// task.txt — the headless task for the selected mode.
|
|
12
|
-
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
13
|
-
import { join } from 'node:path'
|
|
14
|
-
|
|
15
|
-
// Local-machine protection: the composite action resolves every path from the
|
|
16
|
-
// runner environment (RUNNER_TEMP for the output directory, GITHUB_WORKSPACE
|
|
17
|
-
// for the sandbox root). Outside a GitHub Actions runner those variables do
|
|
18
|
-
// not exist, and every fallback would silently write the profile overlay and
|
|
19
|
-
// task into the current working directory while a locally spawned `dsh`
|
|
20
|
-
// inherits the developer's real DSH_HOME. Refuse to run instead of guessing.
|
|
21
|
-
const runnerTemp = (process.env.RUNNER_TEMP ?? '').trim()
|
|
22
|
-
const workspace = (process.env.GITHUB_WORKSPACE ?? '').trim()
|
|
23
|
-
if (runnerTemp === '' || workspace === '') {
|
|
24
|
-
console.error('dsh-github: refusing to run outside a GitHub Actions runner — both RUNNER_TEMP and GITHUB_WORKSPACE must be set.')
|
|
25
|
-
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.')
|
|
26
|
-
process.exit(1)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const env = (name) => process.env[name] ?? ''
|
|
30
|
-
const boolOf = (value, fallback) => value.trim() === '' ? fallback : value.trim() === 'true' || value.trim() === '1'
|
|
31
|
-
const listOf = (value) => value.split(',').map(item => item.trim()).filter(item => item.length > 0)
|
|
32
|
-
const y = (value) => JSON.stringify(value)
|
|
33
|
-
|
|
34
|
-
const task = env('INPUT_TASK').trim() || 'review'
|
|
35
|
-
const pr = env('INPUT_PR').trim()
|
|
36
|
-
const ownerRepo = env('INPUT_OWNER_REPO').trim()
|
|
37
|
-
const model = env('INPUT_MODEL').trim() || 'deepseek-v4-flash'
|
|
38
|
-
const engine = env('INPUT_ENGINE').trim() === 'model' ? 'model' : 'static'
|
|
39
|
-
const checkName = env('INPUT_CHECK_NAME').trim() || 'dsh-github-review'
|
|
40
|
-
const blocking = boolOf(env('INPUT_BLOCKING'), true)
|
|
41
|
-
const failOn = env('INPUT_FAIL_ON').trim() === 'warning' ? 'warning' : 'error'
|
|
42
|
-
const labelFilters = listOf(env('INPUT_LABEL_FILTERS'))
|
|
43
|
-
const pathFilters = listOf(env('INPUT_PATH_FILTERS'))
|
|
44
|
-
const maxDiffChars = Number(env('INPUT_MAX_DIFF_CHARS')) > 0 ? Math.floor(Number(env('INPUT_MAX_DIFF_CHARS'))) : 8000
|
|
45
|
-
const postComments = boolOf(env('INPUT_POST_COMMENTS'), true)
|
|
46
|
-
const postCheck = boolOf(env('INPUT_POST_CHECK'), true)
|
|
47
|
-
const requestTimeoutMs = Number(env('INPUT_REQUEST_TIMEOUT_MS')) > 0 ? Math.floor(Number(env('INPUT_REQUEST_TIMEOUT_MS'))) : 30000
|
|
48
|
-
const outputDir = env('INPUT_OUTPUT_DIR').trim() || join(process.env.RUNNER_TEMP ?? '.', 'dsh-github')
|
|
49
|
-
const customTask = env('INPUT_TASK_PROMPT').trim()
|
|
50
|
-
|
|
51
|
-
if ((task === 'review' || task === 'fix-ci') && pr === '') {
|
|
52
|
-
console.error('dsh-github: the `pr` input is required for the review and fix-ci tasks (defaults to github.event.pull_request.number on pull_request events)')
|
|
53
|
-
process.exit(1)
|
|
54
|
-
}
|
|
55
|
-
if (ownerRepo === '') {
|
|
56
|
-
console.error('dsh-github: the `owner-repo` input is required (defaults to github.repository)')
|
|
57
|
-
process.exit(1)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
mkdirSync(outputDir, { recursive: true })
|
|
61
|
-
|
|
62
|
-
const callJson = (extra) => JSON.stringify({ task: extra.task, pr, ownerRepo, ...extra })
|
|
63
|
-
|
|
64
|
-
let taskText
|
|
65
|
-
if (customTask !== '') {
|
|
66
|
-
taskText = customTask
|
|
67
|
-
} else if (task === 'review' && engine === 'static') {
|
|
68
|
-
taskText = `You are the dsh-github CI reviewer for this repository. Call the ci_run tool once with ${callJson({ task: 'review' })} and then reply with only the verdict and the summary line from its result.`
|
|
69
|
-
} else if (task === 'review') {
|
|
70
|
-
taskText = `You are a careful software engineer reviewing a GitHub pull request. First call ci_run with ${callJson({ task: 'analyze' })}. Review the diff yourself: correctness, security, style, and maintainability. Then call ci_run with ${callJson({ task: 'publish' })} plus a "body" holding your review (Markdown, verdict first, findings grouped by file) and a "findings" array for line-anchored problems you found. Finish by replying with only the verdict and the summary line from the publish result.`
|
|
71
|
-
} else if (task === 'fix-ci') {
|
|
72
|
-
taskText = `You are the dsh-github CI engineer for this repository. Investigate the CI failures of PR #${pr} in ${ownerRepo}: read the failing checks with gh_review (fields "ci"), inspect the code with gh_file and gh_repo, and use the bash tool only to read logs or run read-only diagnostics. Do NOT modify files. Write a fix plan as your final answer and call ci_run with ${callJson({ task: 'publish' })} plus a "body" holding that plan so it is posted as a review comment.`
|
|
73
|
-
} else {
|
|
74
|
-
taskText = `You are the dsh-github CI reporter for this repository. Generate a concise, factual report about ${ownerRepo}${pr !== '' ? ` PR #${pr}` : ''} using the read tools (gh_repo, gh_file, gh_review, gh_search). Write the report as your final answer and, if a pull request is being analyzed, call ci_run with ${callJson({ task: 'publish' })} plus a "body" holding the report so it is posted as a review comment.`
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const personaText = 'You are a helpful software engineer assistant.\nYou are the dsh-github CI assistant for this repository.'
|
|
78
|
-
|
|
79
|
-
const overlay = `# dsh-github CI overlay — generated by the composite action; every value is
|
|
80
|
-
# emitted by scripts/action-patch.mjs from the action inputs. Do not edit.
|
|
81
|
-
# Rows that already exist in dsh-base are replaced by id; new rows ride an
|
|
82
|
-
# insert block (non-insert patches only replace existing rows).
|
|
83
|
-
- id: persona
|
|
84
|
-
name: '@deepseek-ai/dsh-persona'
|
|
85
|
-
config:
|
|
86
|
-
text: ${y(personaText)}
|
|
87
|
-
complete: true
|
|
88
|
-
includeRuntimeContext: false
|
|
89
|
-
|
|
90
|
-
- id: agent-default-model
|
|
91
|
-
name: '@deepseek-ai/dsh-agent-default-model'
|
|
92
|
-
config:
|
|
93
|
-
provider: deepseek-official
|
|
94
|
-
model: ${y(model)}
|
|
95
|
-
|
|
96
|
-
- id: llm-deepseek
|
|
97
|
-
name: '@deepseek-ai/dsh-llm-deepseek'
|
|
98
|
-
config:
|
|
99
|
-
apiKeyEnv: DEEPSEEK_API_KEY
|
|
100
|
-
|
|
101
|
-
- id: sandbox-policy
|
|
102
|
-
name: '@deepseek-ai/dsh-sandbox-policy'
|
|
103
|
-
config:
|
|
104
|
-
mode: workspace-write
|
|
105
|
-
workspaceRoot: !!js process.env.GITHUB_WORKSPACE ?? process.cwd()
|
|
106
|
-
|
|
107
|
-
# The minimal toolset (persistent bash + editor), matching the official
|
|
108
|
-
# minimal preset: enough to investigate CI failures and generate reports,
|
|
109
|
-
# bounded by the workspace-write sandbox above.
|
|
110
|
-
- insert:
|
|
111
|
-
- id: terminal
|
|
112
|
-
name: '@deepseek-ai/dsh-terminal'
|
|
113
|
-
|
|
114
|
-
- id: terminal-bash
|
|
115
|
-
name: '@deepseek-ai/dsh-terminal-bash'
|
|
116
|
-
config:
|
|
117
|
-
timeoutMs: 300000
|
|
118
|
-
|
|
119
|
-
- id: persistent-bash
|
|
120
|
-
name: '@deepseek-ai/dsh-tool-bash-persistent'
|
|
121
|
-
config:
|
|
122
|
-
timeoutMs: 300000
|
|
123
|
-
|
|
124
|
-
- id: str-replace-editor
|
|
125
|
-
name: '@deepseek-ai/dsh-tool-str-replace-editor'
|
|
126
|
-
config:
|
|
127
|
-
maxOutputChars: 16000
|
|
128
|
-
|
|
129
|
-
- id: dsh-github
|
|
130
|
-
name: '@perrylink/dsh-github'
|
|
131
|
-
config:
|
|
132
|
-
tokenSource: env
|
|
133
|
-
tokenRef: DSH_GITHUB_TOKEN
|
|
134
|
-
maxDiffChars: ${maxDiffChars}
|
|
135
|
-
requestTimeoutMs: ${requestTimeoutMs}
|
|
136
|
-
ci:
|
|
137
|
-
enabled: true
|
|
138
|
-
engine: ${y(engine)}
|
|
139
|
-
autoApprove: ['ci.run']
|
|
140
|
-
checkName: ${y(checkName)}
|
|
141
|
-
blocking: ${blocking}
|
|
142
|
-
failOn: ${y(failOn)}
|
|
143
|
-
pollIntervalMs: 0
|
|
144
|
-
labelFilters: ${y(labelFilters)}
|
|
145
|
-
pathFilters: ${y(pathFilters)}
|
|
146
|
-
postComments: ${postComments}
|
|
147
|
-
reportDir: ${y(outputDir)}
|
|
148
|
-
`
|
|
149
|
-
|
|
150
|
-
writeFileSync(join(outputDir, 'dsh-github-ci.cordis.yml'), overlay)
|
|
151
|
-
writeFileSync(join(outputDir, 'task.txt'), taskText + '\n')
|
|
152
|
-
console.log(`dsh-github: overlay and task written to ${outputDir} (task: ${task}, engine: ${engine})`)
|
|
1
|
+
// Generates the dsh headless profile overlay and the task text for the
|
|
2
|
+
// composite action. Zero-dependency Node: every input arrives through the
|
|
3
|
+
// runner's INPUT_* environment variables (composite-action inputs), and every
|
|
4
|
+
// value is emitted as a JSON string (a valid YAML scalar) so arbitrary user
|
|
5
|
+
// input can never break the YAML structure.
|
|
6
|
+
//
|
|
7
|
+
// Writes to $INPUT_OUTPUT_DIR:
|
|
8
|
+
// dsh-github-ci.cordis.yml — the --patch overlay (Minimal persona + role
|
|
9
|
+
// line, model selection, sandbox, the minimal bash/editor toolset, and
|
|
10
|
+
// the dsh-github row with the full ci.* configuration).
|
|
11
|
+
// task.txt — the headless task for the selected mode.
|
|
12
|
+
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
13
|
+
import { join } from 'node:path'
|
|
14
|
+
|
|
15
|
+
// Local-machine protection: the composite action resolves every path from the
|
|
16
|
+
// runner environment (RUNNER_TEMP for the output directory, GITHUB_WORKSPACE
|
|
17
|
+
// for the sandbox root). Outside a GitHub Actions runner those variables do
|
|
18
|
+
// not exist, and every fallback would silently write the profile overlay and
|
|
19
|
+
// task into the current working directory while a locally spawned `dsh`
|
|
20
|
+
// inherits the developer's real DSH_HOME. Refuse to run instead of guessing.
|
|
21
|
+
const runnerTemp = (process.env.RUNNER_TEMP ?? '').trim()
|
|
22
|
+
const workspace = (process.env.GITHUB_WORKSPACE ?? '').trim()
|
|
23
|
+
if (runnerTemp === '' || workspace === '') {
|
|
24
|
+
console.error('dsh-github: refusing to run outside a GitHub Actions runner — both RUNNER_TEMP and GITHUB_WORKSPACE must be set.')
|
|
25
|
+
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.')
|
|
26
|
+
process.exit(1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const env = (name) => process.env[name] ?? ''
|
|
30
|
+
const boolOf = (value, fallback) => value.trim() === '' ? fallback : value.trim() === 'true' || value.trim() === '1'
|
|
31
|
+
const listOf = (value) => value.split(',').map(item => item.trim()).filter(item => item.length > 0)
|
|
32
|
+
const y = (value) => JSON.stringify(value)
|
|
33
|
+
|
|
34
|
+
const task = env('INPUT_TASK').trim() || 'review'
|
|
35
|
+
const pr = env('INPUT_PR').trim()
|
|
36
|
+
const ownerRepo = env('INPUT_OWNER_REPO').trim()
|
|
37
|
+
const model = env('INPUT_MODEL').trim() || 'deepseek-v4-flash'
|
|
38
|
+
const engine = env('INPUT_ENGINE').trim() === 'model' ? 'model' : 'static'
|
|
39
|
+
const checkName = env('INPUT_CHECK_NAME').trim() || 'dsh-github-review'
|
|
40
|
+
const blocking = boolOf(env('INPUT_BLOCKING'), true)
|
|
41
|
+
const failOn = env('INPUT_FAIL_ON').trim() === 'warning' ? 'warning' : 'error'
|
|
42
|
+
const labelFilters = listOf(env('INPUT_LABEL_FILTERS'))
|
|
43
|
+
const pathFilters = listOf(env('INPUT_PATH_FILTERS'))
|
|
44
|
+
const maxDiffChars = Number(env('INPUT_MAX_DIFF_CHARS')) > 0 ? Math.floor(Number(env('INPUT_MAX_DIFF_CHARS'))) : 8000
|
|
45
|
+
const postComments = boolOf(env('INPUT_POST_COMMENTS'), true)
|
|
46
|
+
const postCheck = boolOf(env('INPUT_POST_CHECK'), true)
|
|
47
|
+
const requestTimeoutMs = Number(env('INPUT_REQUEST_TIMEOUT_MS')) > 0 ? Math.floor(Number(env('INPUT_REQUEST_TIMEOUT_MS'))) : 30000
|
|
48
|
+
const outputDir = env('INPUT_OUTPUT_DIR').trim() || join(process.env.RUNNER_TEMP ?? '.', 'dsh-github')
|
|
49
|
+
const customTask = env('INPUT_TASK_PROMPT').trim()
|
|
50
|
+
|
|
51
|
+
if ((task === 'review' || task === 'fix-ci') && pr === '') {
|
|
52
|
+
console.error('dsh-github: the `pr` input is required for the review and fix-ci tasks (defaults to github.event.pull_request.number on pull_request events)')
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
if (ownerRepo === '') {
|
|
56
|
+
console.error('dsh-github: the `owner-repo` input is required (defaults to github.repository)')
|
|
57
|
+
process.exit(1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
mkdirSync(outputDir, { recursive: true })
|
|
61
|
+
|
|
62
|
+
const callJson = (extra) => JSON.stringify({ task: extra.task, pr, ownerRepo, ...extra })
|
|
63
|
+
|
|
64
|
+
let taskText
|
|
65
|
+
if (customTask !== '') {
|
|
66
|
+
taskText = customTask
|
|
67
|
+
} else if (task === 'review' && engine === 'static') {
|
|
68
|
+
taskText = `You are the dsh-github CI reviewer for this repository. Call the ci_run tool once with ${callJson({ task: 'review' })} and then reply with only the verdict and the summary line from its result.`
|
|
69
|
+
} else if (task === 'review') {
|
|
70
|
+
taskText = `You are a careful software engineer reviewing a GitHub pull request. First call ci_run with ${callJson({ task: 'analyze' })}. Review the diff yourself: correctness, security, style, and maintainability. Then call ci_run with ${callJson({ task: 'publish' })} plus a "body" holding your review (Markdown, verdict first, findings grouped by file) and a "findings" array for line-anchored problems you found. Finish by replying with only the verdict and the summary line from the publish result.`
|
|
71
|
+
} else if (task === 'fix-ci') {
|
|
72
|
+
taskText = `You are the dsh-github CI engineer for this repository. Investigate the CI failures of PR #${pr} in ${ownerRepo}: read the failing checks with gh_review (fields "ci"), inspect the code with gh_file and gh_repo, and use the bash tool only to read logs or run read-only diagnostics. Do NOT modify files. Write a fix plan as your final answer and call ci_run with ${callJson({ task: 'publish' })} plus a "body" holding that plan so it is posted as a review comment.`
|
|
73
|
+
} else {
|
|
74
|
+
taskText = `You are the dsh-github CI reporter for this repository. Generate a concise, factual report about ${ownerRepo}${pr !== '' ? ` PR #${pr}` : ''} using the read tools (gh_repo, gh_file, gh_review, gh_search). Write the report as your final answer and, if a pull request is being analyzed, call ci_run with ${callJson({ task: 'publish' })} plus a "body" holding the report so it is posted as a review comment.`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const personaText = 'You are a helpful software engineer assistant.\nYou are the dsh-github CI assistant for this repository.'
|
|
78
|
+
|
|
79
|
+
const overlay = `# dsh-github CI overlay — generated by the composite action; every value is
|
|
80
|
+
# emitted by scripts/action-patch.mjs from the action inputs. Do not edit.
|
|
81
|
+
# Rows that already exist in dsh-base are replaced by id; new rows ride an
|
|
82
|
+
# insert block (non-insert patches only replace existing rows).
|
|
83
|
+
- id: persona
|
|
84
|
+
name: '@deepseek-ai/dsh-persona'
|
|
85
|
+
config:
|
|
86
|
+
text: ${y(personaText)}
|
|
87
|
+
complete: true
|
|
88
|
+
includeRuntimeContext: false
|
|
89
|
+
|
|
90
|
+
- id: agent-default-model
|
|
91
|
+
name: '@deepseek-ai/dsh-agent-default-model'
|
|
92
|
+
config:
|
|
93
|
+
provider: deepseek-official
|
|
94
|
+
model: ${y(model)}
|
|
95
|
+
|
|
96
|
+
- id: llm-deepseek
|
|
97
|
+
name: '@deepseek-ai/dsh-llm-deepseek'
|
|
98
|
+
config:
|
|
99
|
+
apiKeyEnv: DEEPSEEK_API_KEY
|
|
100
|
+
|
|
101
|
+
- id: sandbox-policy
|
|
102
|
+
name: '@deepseek-ai/dsh-sandbox-policy'
|
|
103
|
+
config:
|
|
104
|
+
mode: workspace-write
|
|
105
|
+
workspaceRoot: !!js process.env.GITHUB_WORKSPACE ?? process.cwd()
|
|
106
|
+
|
|
107
|
+
# The minimal toolset (persistent bash + editor), matching the official
|
|
108
|
+
# minimal preset: enough to investigate CI failures and generate reports,
|
|
109
|
+
# bounded by the workspace-write sandbox above.
|
|
110
|
+
- insert:
|
|
111
|
+
- id: terminal
|
|
112
|
+
name: '@deepseek-ai/dsh-terminal'
|
|
113
|
+
|
|
114
|
+
- id: terminal-bash
|
|
115
|
+
name: '@deepseek-ai/dsh-terminal-bash'
|
|
116
|
+
config:
|
|
117
|
+
timeoutMs: 300000
|
|
118
|
+
|
|
119
|
+
- id: persistent-bash
|
|
120
|
+
name: '@deepseek-ai/dsh-tool-bash-persistent'
|
|
121
|
+
config:
|
|
122
|
+
timeoutMs: 300000
|
|
123
|
+
|
|
124
|
+
- id: str-replace-editor
|
|
125
|
+
name: '@deepseek-ai/dsh-tool-str-replace-editor'
|
|
126
|
+
config:
|
|
127
|
+
maxOutputChars: 16000
|
|
128
|
+
|
|
129
|
+
- id: dsh-github
|
|
130
|
+
name: '@perrylink/dsh-github'
|
|
131
|
+
config:
|
|
132
|
+
tokenSource: env
|
|
133
|
+
tokenRef: DSH_GITHUB_TOKEN
|
|
134
|
+
maxDiffChars: ${maxDiffChars}
|
|
135
|
+
requestTimeoutMs: ${requestTimeoutMs}
|
|
136
|
+
ci:
|
|
137
|
+
enabled: true
|
|
138
|
+
engine: ${y(engine)}
|
|
139
|
+
autoApprove: ['ci.run']
|
|
140
|
+
checkName: ${y(checkName)}
|
|
141
|
+
blocking: ${blocking}
|
|
142
|
+
failOn: ${y(failOn)}
|
|
143
|
+
pollIntervalMs: 0
|
|
144
|
+
labelFilters: ${y(labelFilters)}
|
|
145
|
+
pathFilters: ${y(pathFilters)}
|
|
146
|
+
postComments: ${postComments}
|
|
147
|
+
reportDir: ${y(outputDir)}
|
|
148
|
+
`
|
|
149
|
+
|
|
150
|
+
writeFileSync(join(outputDir, 'dsh-github-ci.cordis.yml'), overlay)
|
|
151
|
+
writeFileSync(join(outputDir, 'task.txt'), taskText + '\n')
|
|
152
|
+
console.log(`dsh-github: overlay and task written to ${outputDir} (task: ${task}, engine: ${engine})`)
|
package/scripts/action-post.mjs
CHANGED
|
@@ -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
|
+
}
|