@weotro/dx 0.1.18 → 0.1.21
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 +51 -0
- package/bin/dx.js +10 -1
- package/lib/cli/dx-cli.js +29 -3
- package/lib/cli/help-model.js +13 -1
- package/lib/exec.js +8 -2
- package/package.json +1 -1
- package/skills/gh-dependabot-cleanup/SKILL.md +18 -6
- package/skills/git-release/SKILL.md +3 -1
package/README.md
CHANGED
|
@@ -103,6 +103,57 @@ DX_CONFIG_DIR=/path/to/your-repo/dx/config dx status
|
|
|
103
103
|
|
|
104
104
|
### 1) dx/config/commands.json
|
|
105
105
|
|
|
106
|
+
#### 自定义命令参数透传
|
|
107
|
+
|
|
108
|
+
在选中的可执行叶节点声明 `allowPassthrough: true`,然后用第一个独立的 `--`
|
|
109
|
+
分开 DX 选项和子命令参数:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
dx release plan --prod -- --brands zh --targets backend --ref v0.2.205
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
最小 `commands.json`(脚本由目标项目提供):
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"release": {
|
|
120
|
+
"plan": {
|
|
121
|
+
"production": {
|
|
122
|
+
"command": "node scripts/release/cli.mjs plan --environment production",
|
|
123
|
+
"allowPassthrough": true
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
字段契约:
|
|
131
|
+
|
|
132
|
+
| 字段 | 类型 / 默认值 | 作用 |
|
|
133
|
+
| --- | --- | --- |
|
|
134
|
+
| `allowPassthrough` | boolean / `false` | 只有严格的 `true` 开启透传;必须放在环境选择后的 `command` 叶节点,父节点不继承 |
|
|
135
|
+
| `command` | string | 现有 Bash 命令;用户参数逐项转义后追加在末尾,固定参数在前 |
|
|
136
|
+
|
|
137
|
+
适用于通用配置处理器执行的自定义命令,包括 `release plan/run` 这类内置命令族的扩展 action,
|
|
138
|
+
以及任意非内置顶层命令。`concurrent`、`sequential`、`internal` 配置暂不支持透传,
|
|
139
|
+
即使声明 `allowPassthrough: true` 也会拒绝非空参数;不会向多个子任务广播。
|
|
140
|
+
内置 `test/db/env` 等仍使用各自已有的参数契约,此字段不会覆盖内置处理器。
|
|
141
|
+
|
|
142
|
+
迁移时,为确实需要接收参数的叶节点增加该字段,并将业务参数移到 `--` 后。
|
|
143
|
+
未开启或不支持透传的配置收到非空子参数时会非零退出,不再静默丢弃后执行默认命令。
|
|
144
|
+
没有子参数或仅有末尾 `--` 时保留旧行为;省略分隔符的未知选项继续报错。
|
|
145
|
+
|
|
146
|
+
第一个 `--` 后的顺序、重复项、空字符串、特殊字符和第二个 `--` 均保留。
|
|
147
|
+
其中的 `--prod`、`-Y`、`--help`、`--version`、`--config-dir` 不影响 DX 自身。
|
|
148
|
+
危险操作仍须通过原确认流程;子命令退出失败时 DX 非零退出。
|
|
149
|
+
DX 不校验业务参数含义,拼错的 key、品牌、目标、ref 由子脚本严格校验。
|
|
150
|
+
|
|
151
|
+
建议 `command` 只调用一个脚本;包含管道、多个语句或末尾注释的 shell 文本应先移入脚本,
|
|
152
|
+
确保追加参数由明确的入口接收。不要在命令中自行拼接用户输入。
|
|
153
|
+
|
|
154
|
+
回归验证:`pnpm test --runInBand test/configured-passthrough.test.js`,测试使用真实
|
|
155
|
+
`bin/dx.js` 和临时 probe,校验实际子进程 argv、拒绝时未启动子进程及失败退出。
|
|
156
|
+
|
|
106
157
|
这是核心文件,定义了 dx 各命令要执行的 shell 命令,也承载帮助信息配置。
|
|
107
158
|
|
|
108
159
|
它支持:
|
package/bin/dx.js
CHANGED
|
@@ -8,7 +8,9 @@ function parseConfigDir(argv) {
|
|
|
8
8
|
const envValue = process.env.DX_CONFIG_DIR
|
|
9
9
|
if (envValue) return String(envValue)
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const raw = Array.isArray(argv) ? argv : []
|
|
12
|
+
const separator = raw.indexOf('--')
|
|
13
|
+
const args = separator === -1 ? raw : raw.slice(0, separator)
|
|
12
14
|
const idx = args.indexOf('--config-dir')
|
|
13
15
|
if (idx !== -1 && idx + 1 < args.length) return String(args[idx + 1])
|
|
14
16
|
for (const token of args) {
|
|
@@ -22,7 +24,14 @@ function stripConfigDirArgs(argv) {
|
|
|
22
24
|
const out = []
|
|
23
25
|
for (let i = 0; i < args.length; i++) {
|
|
24
26
|
const token = args[i]
|
|
27
|
+
if (token === '--') {
|
|
28
|
+
out.push(...args.slice(i))
|
|
29
|
+
break
|
|
30
|
+
}
|
|
25
31
|
if (token === '--config-dir') {
|
|
32
|
+
if (args[i + 1] === undefined || args[i + 1] === '--') {
|
|
33
|
+
throw new Error('--config-dir 需要在 -- 分隔符前提供配置目录')
|
|
34
|
+
}
|
|
26
35
|
i++
|
|
27
36
|
continue
|
|
28
37
|
}
|
package/lib/cli/dx-cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
resolveTargetRequiredVars,
|
|
12
12
|
} from '../env-policy.js'
|
|
13
13
|
import { FLAG_DEFINITIONS, parseFlags } from './flags.js'
|
|
14
|
-
import { getCleanArgs, getCleanArgsWithConsumedValues } from './args.js'
|
|
14
|
+
import { getCleanArgs, getCleanArgsWithConsumedValues, getPassthroughArgs } from './args.js'
|
|
15
15
|
import { showHelp, showCommandHelp } from './help.js'
|
|
16
16
|
import { buildStrictHelpValidationContext, validateHelpConfig } from './help-schema.js'
|
|
17
17
|
import { appendNxVerboseFlag } from './nx-command.js'
|
|
@@ -189,12 +189,14 @@ class DxCli {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
registerConfiguredCommandHandlers() {
|
|
192
|
+
this.configuredCommandNames = new Set()
|
|
192
193
|
for (const [commandName, config] of Object.entries(this.commands || {})) {
|
|
193
194
|
if (commandName === 'help') continue
|
|
194
195
|
if (this.commandHandlers[commandName]) continue
|
|
195
196
|
if (!this.isExecutableConfigTree(config)) continue
|
|
196
197
|
|
|
197
198
|
this.commandHandlers[commandName] = args => this.handleConfiguredCommand(commandName, args)
|
|
199
|
+
this.configuredCommandNames.add(commandName)
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
|
|
@@ -220,6 +222,9 @@ class DxCli {
|
|
|
220
222
|
return
|
|
221
223
|
}
|
|
222
224
|
|
|
225
|
+
const passthroughArgs = getPassthroughArgs(this.args)
|
|
226
|
+
this.validateConfiguredPassthrough(config, passthroughArgs)
|
|
227
|
+
|
|
223
228
|
if (config.dangerous) {
|
|
224
229
|
const operation = config.description || [commandName, ...args].join(' ')
|
|
225
230
|
const confirmed = await confirmManager.confirmDangerous(operation, environment, this.flags.Y)
|
|
@@ -234,7 +239,17 @@ class DxCli {
|
|
|
234
239
|
} else if (config.sequential && Array.isArray(config.commands)) {
|
|
235
240
|
await this.handleSequentialCommands(config.commands, environment)
|
|
236
241
|
} else {
|
|
237
|
-
await this.executeCommand(config)
|
|
242
|
+
await this.executeCommand(config, undefined, passthroughArgs)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
validateConfiguredPassthrough(config, args) {
|
|
247
|
+
if (args.length === 0) return
|
|
248
|
+
if (config?.internal || config?.concurrent || config?.sequential || !config?.command) {
|
|
249
|
+
throw new Error('此命令类型不支持 -- 参数透传;仅普通 command 叶节点支持,请移除额外参数或改用独立脚本命令。')
|
|
250
|
+
}
|
|
251
|
+
if (config.allowPassthrough !== true) {
|
|
252
|
+
throw new Error('此命令未启用参数透传;请在选中的 command 叶节点设置 allowPassthrough: true,并使用 dx <命令路径> [环境标志] -- <子命令参数>。')
|
|
238
253
|
}
|
|
239
254
|
}
|
|
240
255
|
|
|
@@ -389,6 +404,14 @@ class DxCli {
|
|
|
389
404
|
|
|
390
405
|
this.validateInputs()
|
|
391
406
|
|
|
407
|
+
// Reject unsupported custom argv before dependency installation or startup hooks.
|
|
408
|
+
if (this.configuredCommandNames.has(this.command)) {
|
|
409
|
+
const consumed = this.validateFlags(this.command, this.getAllowedFlags(this.command))
|
|
410
|
+
const [, ...args] = getCleanArgsWithConsumedValues(this.args, consumed)
|
|
411
|
+
const config = this.resolveConfiguredCommand(this.command, args, this.determineEnvironment())
|
|
412
|
+
this.validateConfiguredPassthrough(config, getPassthroughArgs(this.args))
|
|
413
|
+
}
|
|
414
|
+
|
|
392
415
|
// Fail fast for unknown commands before dependency/env startup checks.
|
|
393
416
|
if (this.command && !this.commandHandlers[this.command]) {
|
|
394
417
|
logger.error(`未知命令: ${this.command}`)
|
|
@@ -810,6 +833,7 @@ class DxCli {
|
|
|
810
833
|
|
|
811
834
|
reportUnknownFlag(command, flag, allowedFlags) {
|
|
812
835
|
logger.error(`检测到未识别的选项: ${flag}`)
|
|
836
|
+
logger.info('自定义命令参数请放在 -- 后,并在 command 叶节点设置 allowPassthrough: true。')
|
|
813
837
|
const supported = Array.from(allowedFlags.keys())
|
|
814
838
|
if (supported.length > 0) {
|
|
815
839
|
logger.info(`支持的选项: ${supported.join(', ')}`)
|
|
@@ -937,7 +961,8 @@ class DxCli {
|
|
|
937
961
|
}
|
|
938
962
|
|
|
939
963
|
// 执行单个命令
|
|
940
|
-
async executeCommand(config, overrideFlags) {
|
|
964
|
+
async executeCommand(config, overrideFlags, passthroughArgs = []) {
|
|
965
|
+
this.validateConfiguredPassthrough(config, passthroughArgs)
|
|
941
966
|
if (!config) {
|
|
942
967
|
logger.error('无效的命令配置')
|
|
943
968
|
return
|
|
@@ -1022,6 +1047,7 @@ class DxCli {
|
|
|
1022
1047
|
env: config.env || {},
|
|
1023
1048
|
skipEnvValidation: Boolean(config.skipEnvValidation),
|
|
1024
1049
|
forcePortCleanup: Boolean(config.forcePortCleanup),
|
|
1050
|
+
passthroughArgs,
|
|
1025
1051
|
}
|
|
1026
1052
|
|
|
1027
1053
|
await execManager.executeCommand(rawCommand, options)
|
package/lib/cli/help-model.js
CHANGED
|
@@ -84,19 +84,31 @@ export function getGlobalHelpModel(commands = {}, context = {}) {
|
|
|
84
84
|
export function getCommandHelpModel(commands = {}, commandName, context = {}) {
|
|
85
85
|
const commandConfig = commands?.[commandName] ?? {}
|
|
86
86
|
const commandHelp = getCommandHelpConfig(commands, commandName)
|
|
87
|
+
const notes = [...normalizeArray(commandHelp?.notes)]
|
|
88
|
+
if (hasPassthroughCommand(commandConfig)) {
|
|
89
|
+
notes.push(`自定义 command 叶节点设置 allowPassthrough: true 后可透传参数:dx ${commandName} <命令路径> [环境标志] -- <子命令参数>;未开启或组合/internal 命令收到额外参数会报错。`)
|
|
90
|
+
}
|
|
87
91
|
|
|
88
92
|
return {
|
|
89
93
|
name: commandName,
|
|
90
94
|
summary: resolveSummary(commandConfig, commandHelp) || BUILTIN_SUMMARIES[commandName] || '',
|
|
91
95
|
usage: resolveUsage(commandName, commandHelp, commandConfig),
|
|
92
96
|
args: normalizeArray(commandHelp?.args),
|
|
93
|
-
notes
|
|
97
|
+
notes,
|
|
94
98
|
examples: normalizeArray(commandHelp?.examples),
|
|
95
99
|
options: normalizeArray(commandHelp?.options),
|
|
96
100
|
targets: resolveVisibleTargets(commands, commandName, commandConfig, context),
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
function hasPassthroughCommand(node) {
|
|
105
|
+
if (!isPlainObject(node)) return false
|
|
106
|
+
if (node.command || node.internal || node.concurrent || node.sequential) {
|
|
107
|
+
return Boolean(node.command && node.allowPassthrough === true && !node.internal && !node.concurrent && !node.sequential)
|
|
108
|
+
}
|
|
109
|
+
return Object.entries(node).some(([key, value]) => key !== 'help' && hasPassthroughCommand(value))
|
|
110
|
+
}
|
|
111
|
+
|
|
100
112
|
export function resolveSummary(node = {}, help = null) {
|
|
101
113
|
return help?.summary || node?.help?.summary || node?.description || ''
|
|
102
114
|
}
|
package/lib/exec.js
CHANGED
|
@@ -66,6 +66,7 @@ export class ExecManager {
|
|
|
66
66
|
ports = [],
|
|
67
67
|
skipEnvValidation = false,
|
|
68
68
|
forcePortCleanup = false,
|
|
69
|
+
passthroughArgs = [],
|
|
69
70
|
} = options
|
|
70
71
|
|
|
71
72
|
// 在执行前同步环境,确保 Nx/构建工具拿到规范的 NODE_ENV
|
|
@@ -186,7 +187,12 @@ export class ExecManager {
|
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
|
|
190
|
+
// Quote for the bash executor, after command classification so child data
|
|
191
|
+
// cannot change environment selection or trigger port-cleanup heuristics.
|
|
192
|
+
const executionCommand = passthroughArgs.length > 0
|
|
193
|
+
? `${fullCommand} ${passthroughArgs.map(arg => `'${arg.replace(/'/g, "'\\''")}'`).join(' ')}`
|
|
194
|
+
: fullCommand
|
|
195
|
+
logger.command(executionCommand)
|
|
190
196
|
|
|
191
197
|
// 执行命令(可能重试)
|
|
192
198
|
let lastError = null
|
|
@@ -253,7 +259,7 @@ export class ExecManager {
|
|
|
253
259
|
}
|
|
254
260
|
}
|
|
255
261
|
|
|
256
|
-
const result = await this.spawnCommand(
|
|
262
|
+
const result = await this.spawnCommand(executionCommand, {
|
|
257
263
|
cwd: cwd || process.cwd(),
|
|
258
264
|
stdio,
|
|
259
265
|
env: sanitizeChildEnv({
|
package/package.json
CHANGED
|
@@ -25,8 +25,9 @@ Use this skill to complete a Dependabot remediation loop with minimal manual inp
|
|
|
25
25
|
5. Apply dependency changes (prefer overrides/resolutions for transitive alerts).
|
|
26
26
|
6. Refresh lockfile.
|
|
27
27
|
7. Run required project verification commands.
|
|
28
|
-
8. For
|
|
29
|
-
9.
|
|
28
|
+
8. For authorized issue and PR delivery, find and reuse a matching tracking issue or create one with the alert scope, remediation plan, and verification results. Commit, push, and open a focused PR linked to that issue. A local remediation request finishes with verified local changes and drafts for any requested delivery that still lacks authorization.
|
|
29
|
+
9. If the user requests merging, continue through the merge workflow below using the existing authorization; opening a PR alone does not complete that request.
|
|
30
|
+
10. Report the issue, PR, merge outcome, and residual risk using the final reply requirements below.
|
|
30
31
|
|
|
31
32
|
## Default Commands
|
|
32
33
|
```bash
|
|
@@ -51,7 +52,18 @@ pnpm install --lockfile-only
|
|
|
51
52
|
|
|
52
53
|
## PR Requirements
|
|
53
54
|
Include these sections:
|
|
54
|
-
1.
|
|
55
|
-
2.
|
|
56
|
-
3.
|
|
57
|
-
4.
|
|
55
|
+
1. Tracking issue: issue number and URL; use `Closes #<issue>` only when the PR completes the issue's entire scope. Otherwise use `Refs #<issue>` and keep the remaining work tracked.
|
|
56
|
+
2. Fixed alerts: alert id, package, target version
|
|
57
|
+
3. Remaining alerts: alert id, reason (for example, no upstream patch)
|
|
58
|
+
4. Verification: exact commands run and outcomes
|
|
59
|
+
5. Risk note: what is deferred and why
|
|
60
|
+
|
|
61
|
+
## Merge Workflow
|
|
62
|
+
- Before merging, verify the PR's current head, required checks, review requirements, and mergeability. Follow the repository's merge strategy and branch protection; do not bypass required checks or reviews.
|
|
63
|
+
- When merge is authorized and requirements pass, merge with `gh pr merge`, binding the operation to the verified head via `--match-head-commit`. If the head changes, verify the new head before retrying.
|
|
64
|
+
- If checks or reviews block merging, resolve actionable failures within scope and recheck. Report any remaining blocker precisely. Enabling auto-merge or entering a merge queue is pending, not a completed merge.
|
|
65
|
+
- After merging, verify the PR's `MERGED` state and merge commit, check the tracking issue's state, and fetch Dependabot alerts again. Report still-open alerts as observed; distinguish scan results that have not yet updated from confirmed remediation failures.
|
|
66
|
+
|
|
67
|
+
## Final Reply Requirements
|
|
68
|
+
Always explicitly mention both **Issue** and **PR**, including their numbers, URLs, and observed states. If either was not created or reused, say so and explain why; never substitute a Dependabot alert number for an issue number.
|
|
69
|
+
State whether the PR was actually merged, with the merge commit when available, or give the concrete pending condition or blocker. Include verification results and remaining alerts. Do not claim completion of a requested merge based only on PR creation, auto-merge configuration, or queue entry.
|
|
@@ -11,6 +11,8 @@ description: 仅在显式调用 git-release 时使用:准备或执行版本发
|
|
|
11
11
|
只读检索、本地草稿、可回滚编辑和常规测试可自主推断并继续;从相关文件和直接依赖开始,仅在证据不足或影响跨模块时扩大范围,复用仍有效的验证。
|
|
12
12
|
仅在不可逆或难以回滚的修改缺少具体授权时,先完成预览、草稿与验证,再说明对象和影响并等待确认;会话中已有授权继续有效。缺少事实时先查证、标注假设并推进独立工作,确实无法确定操作目标时才询问。对外发送消息须有明确授权。
|
|
13
13
|
|
|
14
|
+
用户显式调用本技能执行发布,即明确授权在本次目标仓库创建并推送目标版本的 tag,以及创建并发布对应的 GitHub Release。完成准备和验证后连续执行至发布结果核验,无需再次询问是否推送 tag 或创建 Release。用户明确限定为仅准备、草稿、审计或修改技能时,按该范围执行;本授权不包含覆盖冲突 tag、删除既有 Release 或额外的生产操作。
|
|
15
|
+
|
|
14
16
|
## 发布准备
|
|
15
17
|
|
|
16
18
|
从当前 release 分支、用户指定版本或项目版本策略确定目标版本,说明来源。若项目采用 `release/vX.Y.Z`(也支持 alpha/beta/rc)则沿用;其他命名服从项目规则。没有版本策略时可以准备下一补丁版本的本地草稿,公开发布前核实目标。
|
|
@@ -27,7 +29,7 @@ description: 仅在显式调用 git-release 时使用:准备或执行版本发
|
|
|
27
29
|
|
|
28
30
|
## 创建发布
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
执行前核对目标仓库、版本、commit 和发行说明,确认发布检查通过、目标 tag 无冲突,然后依据「执行边界与优先级」中的明确授权直接推送 tag 并创建 GitHub Release。发布流程可能自动触发制品发布或部署,不因此追加确认步骤;仅在目标无法确定、检查失败或远端状态冲突时暂停受阻操作并说明原因。
|
|
31
33
|
|
|
32
34
|
按项目发布流程创建 tag、推送并创建 Release;例如:
|
|
33
35
|
```bash
|