@wwkit/opm 1.0.2 → 1.0.3
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 +194 -11
- package/bin/index.js +23 -0
- package/docs/managers/git.md +103 -0
- package/docs/managers/proc.md +77 -0
- package/docs/managers/uv.md +9 -3
- package/docs/opm.html +154 -11
- package/docs/packages/opencode.md +46 -15
- package/docs/test/linux-environment-test-report.md +147 -0
- package/package.json +3 -2
- package/src/cli/groups/docker.js +10 -5
- package/src/cli/groups/homebrew.js +9 -5
- package/src/cli/groups/nvm.js +6 -2
- package/src/cli/groups/ping.js +461 -0
- package/src/cli/groups/proc.js +232 -0
- package/src/cli/groups/uv.js +9 -8
- package/src/cli/groups/version-manager.js +34 -8
- package/src/cli/index.js +6 -0
- package/src/config.json5 +66 -7
- package/src/harnesses/base.js +34 -0
- package/src/harnesses/opencode/index.js +56 -0
- package/src/index.js +4 -0
- package/src/managers/brew.js +6 -6
- package/src/managers/docker.js +10 -7
- package/src/managers/node.js +8 -2
- package/src/managers/proc.js +424 -0
- package/src/managers/python.js +7 -4
- package/src/presets.js +3 -0
- package/src/tools/ftp/index.js +151 -16
- package/src/tools/git/config.js +374 -0
- package/src/tools/git/index.js +378 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opm git 命令组 — Git 配置管理
|
|
3
|
+
*
|
|
4
|
+
* 命令:init / config / user / log / export / import / help
|
|
5
|
+
* 聚焦冷门或多步骤操作,log 支持指定 workdir 免切换目录查看提交历史。
|
|
6
|
+
* export 保存两个 commit 之间的差异到文件,默认从 mark 到 HEAD。
|
|
7
|
+
* mark 用 lightweight git tag 标记基准 commit,对用户透明。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
import fs from 'node:fs'
|
|
12
|
+
import { execSync } from 'node:child_process'
|
|
13
|
+
import { parseFlags } from '../../cli/helpers/args.js'
|
|
14
|
+
import { output } from '../../formatter.js'
|
|
15
|
+
import {
|
|
16
|
+
listConfig, getConfigValue, setConfigValue, unsetConfigValue,
|
|
17
|
+
getUser, setUser,
|
|
18
|
+
initGitConfig, saveDiff, importDiff,
|
|
19
|
+
getMark, setMark, unsetMark,
|
|
20
|
+
getGitVersion, installGit, uninstallGit, upgradeGit,
|
|
21
|
+
} from './config.js'
|
|
22
|
+
|
|
23
|
+
const ACTIONS = {
|
|
24
|
+
version: { desc: 'Show installed git version' },
|
|
25
|
+
install: { desc: 'Install git via system package manager (dnf/yum/apt/apk)' },
|
|
26
|
+
uninstall: { desc: 'Uninstall git via system package manager' },
|
|
27
|
+
upgrade: { desc: 'Upgrade git via system package manager' },
|
|
28
|
+
init: { desc: 'Initialize git global config from opm config (user/ssl/pull.rebase)' },
|
|
29
|
+
config: { desc: 'View/set/unset git config (with --scope global|local|system)' },
|
|
30
|
+
user: { desc: 'View/set user.name and user.email' },
|
|
31
|
+
log: { desc: 'Show commit history (with -d/--dir for workdir, -n for count)' },
|
|
32
|
+
export: { desc: 'Export diff to file (default: mark -> HEAD, or -f <n> override)' },
|
|
33
|
+
import: { desc: 'Import diff file into working tree (git apply --reject)' },
|
|
34
|
+
help: { desc: 'Show this help' },
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class GitGroup {
|
|
38
|
+
constructor() {
|
|
39
|
+
this.name = 'git'
|
|
40
|
+
this.desc = 'Git config management (init/config/user/log/export)'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async run(argv) {
|
|
44
|
+
const [action, ...rest] = argv
|
|
45
|
+
|
|
46
|
+
if (!action || action === '-h' || action === '--help' || action === 'help') {
|
|
47
|
+
this.printHelp()
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const parsed = parseFlags(rest)
|
|
52
|
+
|
|
53
|
+
switch (action) {
|
|
54
|
+
case 'version':
|
|
55
|
+
return this._version()
|
|
56
|
+
case 'install':
|
|
57
|
+
return this._install(parsed)
|
|
58
|
+
case 'uninstall':
|
|
59
|
+
return this._uninstall()
|
|
60
|
+
case 'upgrade':
|
|
61
|
+
return this._upgrade(parsed)
|
|
62
|
+
case 'init':
|
|
63
|
+
return this._init(parsed)
|
|
64
|
+
case 'config':
|
|
65
|
+
return this._config(parsed)
|
|
66
|
+
case 'user':
|
|
67
|
+
return this._user(parsed)
|
|
68
|
+
case 'log':
|
|
69
|
+
return this._log(parsed)
|
|
70
|
+
case 'export':
|
|
71
|
+
return this._export(parsed)
|
|
72
|
+
case 'import':
|
|
73
|
+
return this._import(parsed)
|
|
74
|
+
default:
|
|
75
|
+
console.error(`Unknown action: ${action}`)
|
|
76
|
+
this.printHelp()
|
|
77
|
+
process.exit(1)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_resolveProxy(parsed) {
|
|
82
|
+
let proxy = parsed.flags.p || parsed.flags.proxy || ''
|
|
83
|
+
if (proxy === 'true') proxy = ''
|
|
84
|
+
return proxy
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_version() {
|
|
88
|
+
const result = getGitVersion()
|
|
89
|
+
output(result)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
_install(parsed) {
|
|
93
|
+
const proxy = this._resolveProxy(parsed)
|
|
94
|
+
const result = installGit(proxy)
|
|
95
|
+
output(result)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
_uninstall() {
|
|
99
|
+
const result = uninstallGit()
|
|
100
|
+
output(result)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_upgrade(parsed) {
|
|
104
|
+
const proxy = this._resolveProxy(parsed)
|
|
105
|
+
const result = upgradeGit(proxy)
|
|
106
|
+
output(result)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
_getScope(parsed) {
|
|
110
|
+
return parsed.flags.scope || 'global'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async _init(parsed) {
|
|
114
|
+
const force = parsed.flags.f === 'true' || parsed.flags.force === 'true'
|
|
115
|
+
const result = await initGitConfig(force)
|
|
116
|
+
output(result)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async _config(parsed) {
|
|
120
|
+
const scope = this._getScope(parsed)
|
|
121
|
+
const unsetKey = parsed.flags.unset || ''
|
|
122
|
+
|
|
123
|
+
if (unsetKey) {
|
|
124
|
+
const ok = unsetConfigValue(unsetKey, scope)
|
|
125
|
+
output({ scope, key: unsetKey, unset: ok })
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const [key, value] = parsed.positional
|
|
130
|
+
|
|
131
|
+
if (key && value) {
|
|
132
|
+
const ok = setConfigValue(key, value, scope)
|
|
133
|
+
output({ scope, key, value, set: ok })
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (key) {
|
|
138
|
+
const val = getConfigValue(key, scope)
|
|
139
|
+
output({ scope, key, value: val })
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const result = listConfig(scope)
|
|
144
|
+
output({ scope, ...result })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async _user(parsed) {
|
|
148
|
+
const scope = this._getScope(parsed)
|
|
149
|
+
const name = parsed.flags.n || parsed.flags.name || ''
|
|
150
|
+
const email = parsed.flags.e || parsed.flags.email || ''
|
|
151
|
+
|
|
152
|
+
if (name || email) {
|
|
153
|
+
const result = setUser(name, email, scope)
|
|
154
|
+
output({ scope, ...result })
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const result = getUser(scope)
|
|
159
|
+
output({ scope, ...result })
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_log(parsed) {
|
|
163
|
+
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
164
|
+
const resolvedDir = path.resolve(workDir)
|
|
165
|
+
if (!this._getGitDir(resolvedDir)) {
|
|
166
|
+
console.error(`Not a git repository: ${resolvedDir}`)
|
|
167
|
+
process.exit(1)
|
|
168
|
+
}
|
|
169
|
+
const n = parseInt(parsed.flags.n || parsed.flags.count || '10', 10) || 10
|
|
170
|
+
const opts = { encoding: 'utf8', timeout: 5000, cwd: resolvedDir, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
171
|
+
const raw = execSync(`git log --oneline -${n}`, opts).trim()
|
|
172
|
+
if (!raw) {
|
|
173
|
+
output({ commits: [], message: 'No commits yet' })
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
const commits = raw.split('\n').map((line) => {
|
|
177
|
+
const idx = line.indexOf(' ')
|
|
178
|
+
return { hash: line.slice(0, idx), subject: line.slice(idx + 1) }
|
|
179
|
+
})
|
|
180
|
+
output({ dir: resolvedDir, count: commits.length, commits })
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
_printRecentLog(workDir, count = 5) {
|
|
184
|
+
const resolvedDir = path.resolve(workDir)
|
|
185
|
+
if (!this._getGitDir(resolvedDir)) return
|
|
186
|
+
const opts = { encoding: 'utf8', timeout: 5000, cwd: resolvedDir, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
187
|
+
try {
|
|
188
|
+
const raw = execSync(`git log --oneline -${count}`, opts).trim()
|
|
189
|
+
if (raw) {
|
|
190
|
+
console.error(`\nRecent ${count} commits:`)
|
|
191
|
+
for (const line of raw.split('\n')) {
|
|
192
|
+
console.error(` ${line}`)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch {}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async _export(parsed) {
|
|
199
|
+
const [sub] = parsed.positional
|
|
200
|
+
|
|
201
|
+
if (sub === 'mark') {
|
|
202
|
+
return this._exportMark(parsed)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return this._exportDiff(parsed)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_exportMark(parsed) {
|
|
209
|
+
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
210
|
+
const ref = parsed.positional[1]
|
|
211
|
+
|
|
212
|
+
if (parsed.flags.unset === 'true') {
|
|
213
|
+
const ok = unsetMark(workDir)
|
|
214
|
+
output({ mark: 'removed', ok })
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!ref) {
|
|
219
|
+
const mark = getMark(workDir)
|
|
220
|
+
if (!mark) {
|
|
221
|
+
output({ mark: 'not-set' })
|
|
222
|
+
console.error('\nTips: mark not set, set a diff base first:')
|
|
223
|
+
console.error(' opm git export mark HEAD # mark current HEAD as diff base')
|
|
224
|
+
console.error(' opm git export mark <commit> # mark a commit as diff base')
|
|
225
|
+
console.error(' opm git log -d <dir> -n 10 # view commit history without cd')
|
|
226
|
+
this._printRecentLog(workDir)
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
output({ mark: 'set', ...mark })
|
|
230
|
+
console.error('\nTips:')
|
|
231
|
+
console.error(' opm git log -d <dir> -n 10 # view commit history without cd')
|
|
232
|
+
console.error(' opm git export mark <commit> # change diff base to another commit')
|
|
233
|
+
console.error(' opm git export mark --unset # remove current mark')
|
|
234
|
+
console.error(' opm git export # export diff (mark -> HEAD)')
|
|
235
|
+
this._printRecentLog(workDir)
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const resolvedRef = ref === '0' || ref.toLowerCase() === 'head' ? 'HEAD' : ref
|
|
240
|
+
const result = setMark(workDir, resolvedRef)
|
|
241
|
+
output({ mark: 'set', ...result })
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
_exportDiff(parsed) {
|
|
245
|
+
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
246
|
+
const resolvedDir = path.resolve(workDir)
|
|
247
|
+
const gitDir = this._getGitDir(resolvedDir)
|
|
248
|
+
if (!gitDir) {
|
|
249
|
+
console.error(`Not a git repository: ${resolvedDir}`)
|
|
250
|
+
process.exit(1)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const fromRaw = parsed.flags.f || parsed.flags.from || ''
|
|
254
|
+
let fromRef
|
|
255
|
+
if (fromRaw) {
|
|
256
|
+
const n = parseInt(fromRaw, 10)
|
|
257
|
+
fromRef = isNaN(n) ? fromRaw : `HEAD~${n}`
|
|
258
|
+
} else {
|
|
259
|
+
const mark = getMark(resolvedDir)
|
|
260
|
+
if (!mark) {
|
|
261
|
+
console.error('No mark found. Set a mark first:')
|
|
262
|
+
console.error(' opm git export mark HEAD # mark current HEAD as diff base')
|
|
263
|
+
console.error(' opm git export mark <commit> # mark a commit as diff base')
|
|
264
|
+
console.error(' opm git log -d <dir> -n 10 # view commit history without cd')
|
|
265
|
+
process.exit(1)
|
|
266
|
+
}
|
|
267
|
+
fromRef = mark.commit
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
let outFile = parsed.flags.o || parsed.flags.output || ''
|
|
271
|
+
if (!outFile) {
|
|
272
|
+
const opts = { encoding: 'utf8', timeout: 5000, cwd: resolvedDir, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
273
|
+
const gitRoot = execSync('git rev-parse --show-toplevel', opts).trim()
|
|
274
|
+
const name = parsed.flags.n || parsed.flags.name || ''
|
|
275
|
+
const fileName = name ? `${name}.diff` : (() => {
|
|
276
|
+
const fromShort = execSync(`git rev-parse --short ${fromRef}`, opts).trim()
|
|
277
|
+
const toShort = execSync('git rev-parse --short HEAD', opts).trim()
|
|
278
|
+
return `changes-${fromShort}-to-${toShort}.diff`
|
|
279
|
+
})()
|
|
280
|
+
outFile = path.join(gitRoot, '.webwork', 'git', fileName)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const filePath = path.resolve(outFile)
|
|
284
|
+
const dir = path.dirname(filePath)
|
|
285
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
286
|
+
|
|
287
|
+
const result = saveDiff(resolvedDir, filePath, fromRef)
|
|
288
|
+
output(result)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async _import(parsed) {
|
|
292
|
+
const diffFile = parsed.positional[0]
|
|
293
|
+
if (!diffFile) {
|
|
294
|
+
console.error('Usage: opm git import <difffile> [-d <workdir>]')
|
|
295
|
+
process.exit(1)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
299
|
+
const resolvedDir = path.resolve(workDir)
|
|
300
|
+
const gitDir = this._getGitDir(resolvedDir)
|
|
301
|
+
if (!gitDir) {
|
|
302
|
+
console.error(`Not a git repository: ${resolvedDir}`)
|
|
303
|
+
process.exit(1)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const result = importDiff(resolvedDir, diffFile)
|
|
307
|
+
output(result)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_getGitDir(dir) {
|
|
311
|
+
try {
|
|
312
|
+
execSync('git rev-parse --git-dir', { encoding: 'utf8', timeout: 5000, cwd: dir, stdio: ['pipe', 'pipe', 'pipe'] })
|
|
313
|
+
return true
|
|
314
|
+
} catch {
|
|
315
|
+
return false
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
printHelp() {
|
|
320
|
+
const actionLines = Object.entries(ACTIONS)
|
|
321
|
+
.map(([sig, { desc }]) => ` ${sig.padEnd(12)} ${desc}`)
|
|
322
|
+
.join('\n')
|
|
323
|
+
|
|
324
|
+
console.log(`
|
|
325
|
+
Usage: opm git <action> [options]
|
|
326
|
+
|
|
327
|
+
Actions:
|
|
328
|
+
${actionLines}
|
|
329
|
+
|
|
330
|
+
Export subcommands:
|
|
331
|
+
export mark [-d dir] Show current diff base mark
|
|
332
|
+
export mark 0|HEAD [-d dir] Set diff base mark to current HEAD
|
|
333
|
+
export mark <commit> [-d dir] Set diff base mark to a commit
|
|
334
|
+
export mark --unset [-d dir] Remove the diff base mark
|
|
335
|
+
export [-d dir] Export diff (mark -> HEAD)
|
|
336
|
+
export -f <n> [-d dir] Export diff (HEAD~n -> HEAD, override mark)
|
|
337
|
+
export -f <ref> [-d dir] Export diff (<ref> -> HEAD, override mark)
|
|
338
|
+
|
|
339
|
+
Options:
|
|
340
|
+
-d, --dir <dir> Git repository directory (default: cwd)
|
|
341
|
+
-p, --proxy <url> Proxy for install/upgrade
|
|
342
|
+
--scope <global|local|system> Config scope (default: global)
|
|
343
|
+
-n, --name <name> User name (user) / diff file name (export)
|
|
344
|
+
-n, --count <n> Number of commits to show (log, default: 10)
|
|
345
|
+
-e, --email <email> User email (user)
|
|
346
|
+
-o, --output <file> Output file path (export)
|
|
347
|
+
-f, --from <n>|<ref> Override from ref (export)
|
|
348
|
+
-f, --force Force overwrite existing config (init)
|
|
349
|
+
--unset <key> Unset a config key (config)
|
|
350
|
+
-h, --help Show this help
|
|
351
|
+
|
|
352
|
+
Examples:
|
|
353
|
+
opm git version Show installed git version
|
|
354
|
+
opm git install Install git via system package manager
|
|
355
|
+
opm git upgrade Upgrade git to latest
|
|
356
|
+
opm git uninstall Uninstall git
|
|
357
|
+
opm git init Initialize global config from opm config
|
|
358
|
+
opm git config List all global config
|
|
359
|
+
opm git config pull.rebase true Set pull to use rebase
|
|
360
|
+
opm git user Show current user.name and user.email
|
|
361
|
+
opm git log Show recent 10 commits (cwd)
|
|
362
|
+
opm git log -n 5 Show recent 5 commits (cwd)
|
|
363
|
+
opm git log -d /path/to/repo Show commits in specified repo
|
|
364
|
+
opm git export mark Show current diff base mark (cwd)
|
|
365
|
+
opm git export mark 0 Set diff base to current HEAD (cwd)
|
|
366
|
+
opm git export mark HEAD Set diff base to current HEAD (cwd)
|
|
367
|
+
opm git export mark 3af967b Set diff base at commit (cwd)
|
|
368
|
+
opm git export mark --unset Remove diff base mark (cwd)
|
|
369
|
+
opm git export mark abc -d /repo Set diff base at commit in /repo
|
|
370
|
+
opm git export Export diff in current repo (mark -> HEAD)
|
|
371
|
+
opm git export -f 2 Override: HEAD~2 -> HEAD (current repo)
|
|
372
|
+
opm git export -d /path/to/repo Export diff in specified repo
|
|
373
|
+
opm git export -n feature Save as feature.diff
|
|
374
|
+
opm git import file.diff Import diff file (current repo)
|
|
375
|
+
opm git import file.diff -d /repo Import diff file into specified repo
|
|
376
|
+
`)
|
|
377
|
+
}
|
|
378
|
+
}
|