@lazycatcloud/lzc-cli 1.2.53 → 1.2.55
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/changelog.md +15 -0
- package/lib/app/apkshell.js +1 -1
- package/lib/app/index.js +3 -1
- package/lib/app/lpk_build.js +28 -1
- package/lib/app/lpk_devshell.js +8 -27
- package/lib/app/lpk_installer.js +2 -1
- package/lib/{app/lpk_debug_bridge.js → debug_bridge.js} +79 -31
- package/lib/docker/index.js +105 -0
- package/lib/log.js +47 -0
- package/lib/utils.js +22 -39
- package/package.json +4 -2
- package/scripts/cli.js +36 -55
- package/scripts/lzc-docker-compose.js +43 -0
- package/scripts/lzc-docker.js +43 -0
- package/template/_lpk/exec.sh +0 -8
- package/template/_lpk/init_debug_bridge.sh +1 -40
- package/template/_lpk/vue.lzc-build.yml.in +3 -0
package/changelog.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# 1.2.55
|
|
2
|
+
|
|
3
|
+
1. 添加 lzc-docker 命令
|
|
4
|
+
2. 添加 lzc-docker-compose 命令
|
|
5
|
+
3. 添加 deploy_params 打包支持
|
|
6
|
+
4. 添加 lzc-cli 版本检测
|
|
7
|
+
5. 修复 lzc-cli zsh/bash 补全错误
|
|
8
|
+
6. 隐藏 triggerApk 错误
|
|
9
|
+
|
|
10
|
+
# 1.2.54
|
|
11
|
+
|
|
12
|
+
1. 添加 lzc-cli docker 命令
|
|
13
|
+
2. 添加 lzc-cli docker-compose 命令
|
|
14
|
+
3. 修复其他同步卡顿问题
|
|
15
|
+
|
|
1
16
|
# 1.2.53
|
|
2
17
|
|
|
3
18
|
1. 修复 devshell 模型下,环境变量中有空格导致export错误
|
package/lib/app/apkshell.js
CHANGED
package/lib/app/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { AppDevShell } from "./lpk_devshell.js"
|
|
|
5
5
|
import { LpkInstaller, installConfig } from "./lpk_installer.js"
|
|
6
6
|
import logger from "loglevel"
|
|
7
7
|
import { sleep, checkRsync } from "../utils.js"
|
|
8
|
-
import { DebugBridge } from "
|
|
8
|
+
import { DebugBridge } from "../debug_bridge.js"
|
|
9
9
|
import shellApi from "../shellapi.js"
|
|
10
10
|
import { generate } from "./lpk_create_generator.js"
|
|
11
11
|
|
|
@@ -147,6 +147,7 @@ export function lpkAppCommand(program) {
|
|
|
147
147
|
await shellApi.init()
|
|
148
148
|
|
|
149
149
|
const bridge = new DebugBridge()
|
|
150
|
+
await bridge.init()
|
|
150
151
|
await bridge.uninstall(pkgId)
|
|
151
152
|
}
|
|
152
153
|
},
|
|
@@ -157,6 +158,7 @@ export function lpkAppCommand(program) {
|
|
|
157
158
|
await shellApi.init()
|
|
158
159
|
|
|
159
160
|
const bridge = new DebugBridge()
|
|
161
|
+
await bridge.init()
|
|
160
162
|
const status = await bridge.status(pkgId)
|
|
161
163
|
console.log(status)
|
|
162
164
|
}
|
package/lib/app/lpk_build.js
CHANGED
|
@@ -76,6 +76,33 @@ async function fetchIconTo(options, cwd, destDir) {
|
|
|
76
76
|
return
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
async function fetchLzcDeployParamTo(options, cwd, destDir) {
|
|
80
|
+
let deployParamsPath = options["deploy_params"]
|
|
81
|
+
if (!deployParamsPath) {
|
|
82
|
+
logger.debug("deploy_params 没有指定")
|
|
83
|
+
let cwdDeployParamsPath = path.join(cwd, "lzc-deploy-params.yml")
|
|
84
|
+
if (isFileExist(cwdDeployParamsPath)) {
|
|
85
|
+
deployParamsPath = cwdDeployParamsPath
|
|
86
|
+
logger.debug("检测当前目录下具有 lzc-deploy-params.yml")
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!deployParamsPath) {
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!path.isAbsolute(deployParamsPath)) {
|
|
94
|
+
deployParamsPath = path.resolve(cwd, deployParamsPath)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!isFileExist(deployParamsPath)) {
|
|
98
|
+
logger.warn(`deploy_params ${deployParamsPath} 不存在`)
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fs.copyFileSync(deployParamsPath, path.join(destDir, "deploy_params.yml"))
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
79
106
|
// 提供一些方便的环境变量,可以在 lzc-build.yml 中直接使用
|
|
80
107
|
// - LocalIP 本地局域网ip
|
|
81
108
|
function localIp() {
|
|
@@ -137,7 +164,7 @@ export class LpkBuild {
|
|
|
137
164
|
this.beforeBuildPackageFn = []
|
|
138
165
|
this.beforeDumpYamlFn = []
|
|
139
166
|
this.beforeTarContentFn = []
|
|
140
|
-
this.beforeDumpLpkFn = [fetchIconTo]
|
|
167
|
+
this.beforeDumpLpkFn = [fetchIconTo, fetchLzcDeployParamTo]
|
|
141
168
|
}
|
|
142
169
|
|
|
143
170
|
// init 时替换 lzc-build.yml 中的模板字段
|
package/lib/app/lpk_devshell.js
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
md5String,
|
|
15
15
|
md5File,
|
|
16
16
|
loadFromYaml,
|
|
17
|
-
FileLocker,
|
|
18
17
|
isUserApp,
|
|
19
18
|
createTemplateFileCommon,
|
|
20
19
|
isDebugMode,
|
|
@@ -27,7 +26,7 @@ import {
|
|
|
27
26
|
import os from "node:os"
|
|
28
27
|
import chokidar from "chokidar"
|
|
29
28
|
import _ from "lodash"
|
|
30
|
-
import { DebugBridge } from "
|
|
29
|
+
import { DebugBridge } from "../debug_bridge.js"
|
|
31
30
|
import shellApi from "../shellapi.js"
|
|
32
31
|
import { collectContextFromDockerFile } from "./lpk_devshell_docker.js"
|
|
33
32
|
|
|
@@ -70,6 +69,7 @@ class AppDevShellMonitor {
|
|
|
70
69
|
ensureDir(this.cacheFilePath)
|
|
71
70
|
|
|
72
71
|
await this.updateHash()
|
|
72
|
+
await this.bridge.init()
|
|
73
73
|
|
|
74
74
|
return this
|
|
75
75
|
}
|
|
@@ -280,7 +280,6 @@ export class AppDevShell {
|
|
|
280
280
|
application: {
|
|
281
281
|
devshell: options["devshell"],
|
|
282
282
|
health_check: {
|
|
283
|
-
test_url: `http://${userapp}app.${manifest["package"]}.lzcapp/__isdevshell`,
|
|
284
283
|
disable: true
|
|
285
284
|
}
|
|
286
285
|
}
|
|
@@ -329,6 +328,7 @@ export class AppDevShell {
|
|
|
329
328
|
path.resolve(tempDir, "Dockerfile")
|
|
330
329
|
)
|
|
331
330
|
const bridge = new DebugBridge()
|
|
331
|
+
await bridge.init()
|
|
332
332
|
const tag = await bridge.buildImage(label, contextTar)
|
|
333
333
|
delete manifest["application"]["devshell"]
|
|
334
334
|
manifest["application"]["image"] = tag
|
|
@@ -359,6 +359,7 @@ export class AppDevShell {
|
|
|
359
359
|
)
|
|
360
360
|
|
|
361
361
|
const bridge = new DebugBridge()
|
|
362
|
+
await bridge.init()
|
|
362
363
|
const tag = await bridge.buildImage(label, contextTar)
|
|
363
364
|
delete manifest["application"]["devshell"]
|
|
364
365
|
manifest["application"]["image"] = tag
|
|
@@ -410,28 +411,9 @@ export class AppDevShell {
|
|
|
410
411
|
async rsyncShell() {
|
|
411
412
|
const manifest = await this.lpkBuild.getManifest()
|
|
412
413
|
const pkgId = manifest["package"]
|
|
413
|
-
|
|
414
|
-
let isSync = false
|
|
415
|
-
try {
|
|
416
|
-
const locker = new FileLocker(pkgId)
|
|
417
|
-
locker.lock()
|
|
418
|
-
process.on("exit", () => {
|
|
419
|
-
logger.debug("filelock unlock")
|
|
420
|
-
locker.unlock()
|
|
421
|
-
})
|
|
422
|
-
isSync = true
|
|
423
|
-
} catch (err) {
|
|
424
|
-
logger.debug("filelock catch")
|
|
425
|
-
logger.debug(err)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
414
|
const devshell = new DevShell(pkgId, this.isUserApp)
|
|
429
415
|
try {
|
|
430
|
-
|
|
431
|
-
await devshell.shell()
|
|
432
|
-
} else {
|
|
433
|
-
await devshell.connectShell()
|
|
434
|
-
}
|
|
416
|
+
await devshell.shell()
|
|
435
417
|
} catch (e) {
|
|
436
418
|
logger.error(`devshell 错误: ${e}`)
|
|
437
419
|
}
|
|
@@ -453,10 +435,8 @@ class DevShell {
|
|
|
453
435
|
`dev.${shellApi.boxname}.heiyu.space`
|
|
454
436
|
)
|
|
455
437
|
const rsyncDebug = isDebugMode() ? "-P" : ""
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
: `app.${appId}.lzcapp`
|
|
459
|
-
const dest = `rsync://${host}@[${resolvedIp}]:873/lzcapp/cache/devshell`
|
|
438
|
+
const destDir = `${appId}${this.isUserApp ? "/" + shellApi.uid : ""}`
|
|
439
|
+
const dest = `rsync://${shellApi.uid}@[${resolvedIp}]:874/lzcapp_cache/${destDir}/devshell`
|
|
460
440
|
const rsyncCmd = isWindows
|
|
461
441
|
? path.join(
|
|
462
442
|
contextDirname(import.meta.url),
|
|
@@ -564,6 +544,7 @@ class DevShell {
|
|
|
564
544
|
|
|
565
545
|
async connectShell(onconnect = null) {
|
|
566
546
|
const bridge = new DebugBridge()
|
|
547
|
+
await bridge.init()
|
|
567
548
|
await bridge.devshell(this.appId, this.isUserApp, onconnect)
|
|
568
549
|
}
|
|
569
550
|
}
|
package/lib/app/lpk_installer.js
CHANGED
|
@@ -2,7 +2,7 @@ import logger from "loglevel"
|
|
|
2
2
|
import fs from "node:fs"
|
|
3
3
|
import path from "node:path"
|
|
4
4
|
import { loadFromYaml, Downloader, unzipSync } from "../utils.js"
|
|
5
|
-
import { DebugBridge } from "
|
|
5
|
+
import { DebugBridge } from "../debug_bridge.js"
|
|
6
6
|
import shellapi from "../shellapi.js"
|
|
7
7
|
import { triggerApk } from "./apkshell.js"
|
|
8
8
|
|
|
@@ -118,6 +118,7 @@ export class LpkInstaller {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
const bridge = new DebugBridge()
|
|
121
|
+
await bridge.init()
|
|
121
122
|
logger.info("开始安装应用")
|
|
122
123
|
await bridge.install(pkgPath, manifest["package"])
|
|
123
124
|
logger.info("\n")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import spawn from "cross-spawn"
|
|
2
2
|
import fs from "node:fs"
|
|
3
|
-
import shellApi from "
|
|
3
|
+
import shellApi from "./shellapi.js"
|
|
4
4
|
import {
|
|
5
5
|
isDebugMode,
|
|
6
6
|
isTraceMode,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
isWindows,
|
|
11
11
|
contextDirname,
|
|
12
12
|
compareVersions
|
|
13
|
-
} from "
|
|
13
|
+
} from "./utils.js"
|
|
14
14
|
import logger from "loglevel"
|
|
15
15
|
import commandExists from "command-exists"
|
|
16
16
|
import path from "node:path"
|
|
@@ -40,6 +40,13 @@ export class DebugBridge {
|
|
|
40
40
|
this.domain = `dev.${this.boxname}.heiyu.space`
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
async init() {
|
|
44
|
+
if (!(await this.canPublicKey())) {
|
|
45
|
+
// 如果不能 ssh public key 登录则提示授权申请,否则后面可能会出现 rsync 询问密码的问题
|
|
46
|
+
await this.sshApplyGrant()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
async common(cmd, args) {
|
|
44
51
|
const resolvedIp = await resolveDomain(this.domain)
|
|
45
52
|
args = args.map((arg) => arg.replace(this.domain, resolvedIp))
|
|
@@ -59,11 +66,6 @@ export class DebugBridge {
|
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
async install(lpkPath, pkgId) {
|
|
62
|
-
if (!(await this.canPublicKey())) {
|
|
63
|
-
// 如果不能 ssh public key 登录则直接调用 ssh-copy-id 复制,否则后面可能会出现 rsync 询问密码的问题
|
|
64
|
-
await this.sshCopyId()
|
|
65
|
-
}
|
|
66
|
-
|
|
67
69
|
const stream = fs.createReadStream(lpkPath)
|
|
68
70
|
const resolvedIp = await resolveDomain(this.domain)
|
|
69
71
|
const ssh = spawn(
|
|
@@ -86,36 +88,28 @@ export class DebugBridge {
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
async canPublicKey() {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
try {
|
|
92
|
+
await this.common(sshBinary(), [...sshCmdArgs(`box@${this.domain}`)])
|
|
93
|
+
return true
|
|
94
|
+
} catch {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
91
97
|
}
|
|
92
98
|
|
|
93
|
-
async
|
|
99
|
+
async sshApplyGrant() {
|
|
94
100
|
const sshInfo = await findSshPublicKey()
|
|
95
101
|
logger.debug("ssh public key info", sshInfo)
|
|
96
102
|
|
|
97
|
-
const
|
|
98
|
-
logger.debug("ip", resolvedIp)
|
|
103
|
+
const pk = Buffer.from(sshInfo.content.trimLeft()).toString("base64")
|
|
99
104
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
shell: true,
|
|
107
|
-
encoding: "utf-8",
|
|
108
|
-
stdio: "pipe"
|
|
109
|
-
}
|
|
105
|
+
logger.warn(
|
|
106
|
+
`检测到您当前没有授权,请点击下面的连接在浏览器中完成授权,才能使用当前的机器的 ssh public key 访问开发者工具.
|
|
107
|
+
-> https://${this.domain}/auth?key=${pk}
|
|
108
|
+
|
|
109
|
+
如果您的 '懒猫开发者工具' 不是 0.2.0 及以上版本,请先到应用商店进行升级.
|
|
110
|
+
`
|
|
110
111
|
)
|
|
111
|
-
|
|
112
|
-
const stderr = stream.stderr.toString("utf-8")
|
|
113
|
-
const stdout = stream.stdout.toString("utf-8")
|
|
114
|
-
throw stdout + stderr
|
|
115
|
-
}
|
|
116
|
-
if (stream.error) {
|
|
117
|
-
throw stream.error
|
|
118
|
-
}
|
|
112
|
+
throw "请在授权完成后重试!"
|
|
119
113
|
}
|
|
120
114
|
|
|
121
115
|
async status(appId) {
|
|
@@ -127,9 +121,10 @@ export class DebugBridge {
|
|
|
127
121
|
}
|
|
128
122
|
|
|
129
123
|
async isDevshell(appId) {
|
|
124
|
+
await this.backendVersion020()
|
|
130
125
|
const stdout = await this.common(sshBinary(), [
|
|
131
126
|
...sshCmdArgs(`box@${this.domain}`),
|
|
132
|
-
`
|
|
127
|
+
`isDevshellV2 --uid ${this.uid}`,
|
|
133
128
|
appId
|
|
134
129
|
])
|
|
135
130
|
return stdout == "true"
|
|
@@ -166,6 +161,8 @@ export class DebugBridge {
|
|
|
166
161
|
}
|
|
167
162
|
|
|
168
163
|
async devshell(appId, isUserApp, onconnect = null) {
|
|
164
|
+
await this.backendVersion020()
|
|
165
|
+
|
|
169
166
|
let waiting = true
|
|
170
167
|
while (waiting) {
|
|
171
168
|
if (await this.isDevshell(appId)) {
|
|
@@ -237,4 +234,55 @@ export class DebugBridge {
|
|
|
237
234
|
fs.rmSync(contextTar)
|
|
238
235
|
})
|
|
239
236
|
}
|
|
237
|
+
|
|
238
|
+
async lzcDocker(argv) {
|
|
239
|
+
await this.backendVersion020()
|
|
240
|
+
|
|
241
|
+
const resolvedIp = await resolveDomain(this.domain)
|
|
242
|
+
const stream = spawn(
|
|
243
|
+
sshBinary(),
|
|
244
|
+
[...sshCmdArgs(`box@${resolvedIp}`), "-t", "lzc-docker", ...argv],
|
|
245
|
+
{
|
|
246
|
+
shell: true,
|
|
247
|
+
stdio: "inherit"
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
stream.on("close", (code) => {
|
|
252
|
+
code == 0 ? resolve() : reject()
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async lzcDockerCompose(argv) {
|
|
258
|
+
await this.backendVersion020()
|
|
259
|
+
|
|
260
|
+
const resolvedIp = await resolveDomain(this.domain)
|
|
261
|
+
const stream = spawn(
|
|
262
|
+
sshBinary(),
|
|
263
|
+
[...sshCmdArgs(`box@${resolvedIp}`), "-t", "lzc-docker-compose", ...argv],
|
|
264
|
+
{
|
|
265
|
+
shell: true,
|
|
266
|
+
stdio: "inherit"
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
return new Promise((resolve, reject) => {
|
|
270
|
+
stream.on("close", (code) => {
|
|
271
|
+
code == 0 ? resolve() : reject()
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async backendVersion020() {
|
|
277
|
+
const backendVersion = await this.version()
|
|
278
|
+
if (compareVersions("0.2.0", backendVersion) < 0) {
|
|
279
|
+
logger.warn(`
|
|
280
|
+
检测到您当前的 lzc-cli 版本较新,而 '懒猫开发者工具' 比较旧,请先到微服的商店升级 '懒猫开发者工具',点击下面的连接跳转:
|
|
281
|
+
|
|
282
|
+
-> https://appstore.${this.boxname}.heiyu.space/#/shop/detail/cloud.lazycat.developer.tools
|
|
283
|
+
`)
|
|
284
|
+
process.exit(1)
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
}
|
|
240
288
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { DebugBridge } from "../debug_bridge.js"
|
|
2
|
+
import shellApi from "../shellapi.js"
|
|
3
|
+
import { isFileExist, isUserApp, loadFromYaml } from "../utils.js"
|
|
4
|
+
import logger from "loglevel"
|
|
5
|
+
import path from "node:path"
|
|
6
|
+
|
|
7
|
+
function dockerMiddleware(argv, yargs) {
|
|
8
|
+
if (argv.$0 == "lzc-docker" || argv.$0 == "lzc-docker-compose") {
|
|
9
|
+
argv.dockerArgs = argv._
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
argv.dockerArgs = []
|
|
14
|
+
// 找到 'docker-compose' 或者 ’docker’ 在参数中的位置
|
|
15
|
+
let index = argv._.indexOf("docker-compose")
|
|
16
|
+
if (index === -1) {
|
|
17
|
+
index = argv._.indexOf("docker")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (index !== -1 && index < argv._.length - 1) {
|
|
21
|
+
// 获取原始的命令行参数
|
|
22
|
+
const originalArgs = argv._
|
|
23
|
+
|
|
24
|
+
// 将 'docker-compose' 或者 ’docker’ 之后的所有参数(包括选项)移到 dockerArgs 数组中
|
|
25
|
+
argv.dockerArgs = originalArgs.slice(index + 1)
|
|
26
|
+
|
|
27
|
+
// 从原始参数中移除这些参数
|
|
28
|
+
argv._ = argv._.slice(0, index + 1)
|
|
29
|
+
|
|
30
|
+
// 移除已经被处理的参数
|
|
31
|
+
for (const arg of argv.dockerArgs) {
|
|
32
|
+
delete argv[arg]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return argv
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function builder(args) {
|
|
39
|
+
// 禁用严格模式,允许未知参数
|
|
40
|
+
return args
|
|
41
|
+
.parserConfiguration({
|
|
42
|
+
"unknown-options-as-args": true
|
|
43
|
+
})
|
|
44
|
+
.middleware(dockerMiddleware)
|
|
45
|
+
.strict(false)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function singleLzcDockerCommand(commandName = "docker") {
|
|
49
|
+
return {
|
|
50
|
+
command: commandName,
|
|
51
|
+
desc: "微服应用 docker 管理",
|
|
52
|
+
builder: builder,
|
|
53
|
+
handler: async (args) => {
|
|
54
|
+
logger.debug("args: ", args)
|
|
55
|
+
await shellApi.init()
|
|
56
|
+
const bridge = new DebugBridge()
|
|
57
|
+
await bridge.init()
|
|
58
|
+
logger.debug("docker", args.dockerArgs)
|
|
59
|
+
await bridge.lzcDocker(args.dockerArgs)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function singleLzcDockerComposeCommand(commandName = "docker-compose") {
|
|
65
|
+
return {
|
|
66
|
+
command: commandName,
|
|
67
|
+
desc: "微服应用 docker-compose 管理",
|
|
68
|
+
builder: builder,
|
|
69
|
+
handler: async (args) => {
|
|
70
|
+
logger.debug("args: ", args)
|
|
71
|
+
await shellApi.init()
|
|
72
|
+
let manifest = null
|
|
73
|
+
try {
|
|
74
|
+
for (let name of ["lzc-manifest.yml", "manifest.yml"]) {
|
|
75
|
+
const mpath = path.join(process.cwd(), name)
|
|
76
|
+
if (isFileExist(mpath)) {
|
|
77
|
+
manifest = loadFromYaml(mpath)
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {
|
|
82
|
+
logger.debug("load manifest: ", e)
|
|
83
|
+
}
|
|
84
|
+
const pkgId = manifest ? manifest["package"] : ""
|
|
85
|
+
const userApp = manifest ? isUserApp(manifest) : false
|
|
86
|
+
|
|
87
|
+
const bridge = new DebugBridge()
|
|
88
|
+
await bridge.init()
|
|
89
|
+
const options = args.dockerArgs
|
|
90
|
+
if (pkgId != "" && !options.some((o) => o == "--project-directory")) {
|
|
91
|
+
options.unshift(
|
|
92
|
+
"--project-directory",
|
|
93
|
+
`/lzcapp/run/lzc-docker/compose/${pkgId}${userApp ? "/" + shellapi.uid : ""}`
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
logger.debug("docker-compose", options.join(" "))
|
|
97
|
+
await bridge.lzcDockerCompose(options)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function lzcDockerCommand(program) {
|
|
103
|
+
program.command(singleLzcDockerCommand())
|
|
104
|
+
program.command(singleLzcDockerComposeCommand())
|
|
105
|
+
}
|
package/lib/log.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import chalk from "chalk"
|
|
2
|
+
import logger from "loglevel"
|
|
3
|
+
|
|
4
|
+
export function initLogger() {
|
|
5
|
+
// logger level middleware
|
|
6
|
+
const logLevelOriginalFactory = logger.methodFactory
|
|
7
|
+
logger.methodFactory = function (methodName, logLevel, loggerName) {
|
|
8
|
+
let debug = logLevel <= logger.levels.DEBUG
|
|
9
|
+
let rawMethod = logLevelOriginalFactory(methodName, logLevel, loggerName)
|
|
10
|
+
return function (...args) {
|
|
11
|
+
let color = (msg) => chalk.gray
|
|
12
|
+
switch (methodName) {
|
|
13
|
+
case "trace":
|
|
14
|
+
color = chalk.dim.cyan
|
|
15
|
+
break
|
|
16
|
+
case "debug":
|
|
17
|
+
color = chalk.blue
|
|
18
|
+
break
|
|
19
|
+
case "info":
|
|
20
|
+
color = chalk.green
|
|
21
|
+
break
|
|
22
|
+
case "warn":
|
|
23
|
+
color = chalk.magenta
|
|
24
|
+
break
|
|
25
|
+
case "error":
|
|
26
|
+
color = chalk.bold.red
|
|
27
|
+
break
|
|
28
|
+
}
|
|
29
|
+
const prefix = loggerName ?? methodName
|
|
30
|
+
rawMethod(
|
|
31
|
+
(debug ? `[${prefix}] ` : "") +
|
|
32
|
+
args
|
|
33
|
+
.map((a) => {
|
|
34
|
+
let res = a
|
|
35
|
+
if (typeof a == "object") {
|
|
36
|
+
res = JSON.stringify(a, undefined, 4)
|
|
37
|
+
}
|
|
38
|
+
return color(res)
|
|
39
|
+
})
|
|
40
|
+
.join(" ")
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
logger.setDefaultLevel("info")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
initLogger()
|
package/lib/utils.js
CHANGED
|
@@ -19,6 +19,7 @@ import dns from "node:dns/promises"
|
|
|
19
19
|
import axios from "axios"
|
|
20
20
|
import AdmZip from "adm-zip"
|
|
21
21
|
import commandExists from "command-exists"
|
|
22
|
+
import fetch from "node-fetch"
|
|
22
23
|
|
|
23
24
|
// lzc-cli 包的 pkgInfo 信息
|
|
24
25
|
export const pkgInfo = JSON.parse(
|
|
@@ -27,6 +28,27 @@ export const pkgInfo = JSON.parse(
|
|
|
27
28
|
)
|
|
28
29
|
)
|
|
29
30
|
|
|
31
|
+
export async function getLatestVersion(controller, pkgName = pkgInfo.name) {
|
|
32
|
+
const url = `https://data.jsdelivr.com/v1/package/npm/${pkgName}`
|
|
33
|
+
try {
|
|
34
|
+
logger.debug("check latest lzc-cli version...")
|
|
35
|
+
const data = await fetch(url, {
|
|
36
|
+
signal: controller.signal
|
|
37
|
+
})
|
|
38
|
+
const content = await data.json()
|
|
39
|
+
const latestVersion = content["tags"]["latest"]
|
|
40
|
+
if (latestVersion != pkgInfo.version) {
|
|
41
|
+
logger.warn(
|
|
42
|
+
`检测到 ${pkgName} 最新版本为 ${latestVersion},使用 'npm i -g ${pkgName}@${latestVersion}' 升级到最新版本!`
|
|
43
|
+
)
|
|
44
|
+
} else {
|
|
45
|
+
logger.debug(`已经在最新版本: ${latestVersion}`)
|
|
46
|
+
}
|
|
47
|
+
} catch (err) {
|
|
48
|
+
logger.debug(`请求 ${url} 失败, error: ${err}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
30
52
|
// 创建 Axios 实例
|
|
31
53
|
export const api = axios.create()
|
|
32
54
|
|
|
@@ -353,45 +375,6 @@ export class Downloader {
|
|
|
353
375
|
}
|
|
354
376
|
}
|
|
355
377
|
|
|
356
|
-
export class FileLocker {
|
|
357
|
-
constructor(id) {
|
|
358
|
-
this.filename = undefined
|
|
359
|
-
this.fd = undefined
|
|
360
|
-
this.id = id
|
|
361
|
-
}
|
|
362
|
-
lock() {
|
|
363
|
-
this.filename = path.resolve(os.tmpdir(), "lzc-cli-file-lock", this.id)
|
|
364
|
-
ensureDir(this.filename)
|
|
365
|
-
|
|
366
|
-
try {
|
|
367
|
-
this.fd = fs.openSync(this.filename, "wx+")
|
|
368
|
-
} catch (e) {
|
|
369
|
-
if (e.code == "EEXIST" && !this.withOpen(this.filename)) {
|
|
370
|
-
logger.debug("filelock exist open with w+")
|
|
371
|
-
this.fd = fs.openSync(this.filename, "w+")
|
|
372
|
-
} else {
|
|
373
|
-
throw e
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
unlock() {
|
|
378
|
-
if (this.fd) {
|
|
379
|
-
fs.closeSync(this.fd)
|
|
380
|
-
fs.rmSync(this.filename)
|
|
381
|
-
this.fd = undefined
|
|
382
|
-
this.filename = undefined
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
withOpen(filename) {
|
|
386
|
-
const p = spawn.sync("lsof", ["-w", "-t", filename])
|
|
387
|
-
if (p.status === 0) {
|
|
388
|
-
return true
|
|
389
|
-
} else {
|
|
390
|
-
return false
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
378
|
export function isValidAppId(appId) {
|
|
396
379
|
const regex = new RegExp("^(?!-)(?!.*--)[a-z][a-z0-9]*([-][a-z0-9]+)*$")
|
|
397
380
|
return regex.test(appId)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazycatcloud/lzc-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.55",
|
|
4
4
|
"description": "lazycat cloud developer kit",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"prepublishOnly": "node check-changelog.js"
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"changelog.md"
|
|
13
13
|
],
|
|
14
14
|
"bin": {
|
|
15
|
-
"lzc-cli": "scripts/cli.js"
|
|
15
|
+
"lzc-cli": "scripts/cli.js",
|
|
16
|
+
"lzc-docker": "scripts/lzc-docker.js",
|
|
17
|
+
"lzc-docker-compose": "scripts/lzc-docker-compose.js"
|
|
16
18
|
},
|
|
17
19
|
"type": "module",
|
|
18
20
|
"keywords": [
|
package/scripts/cli.js
CHANGED
|
@@ -5,58 +5,32 @@ import fs from "fs"
|
|
|
5
5
|
import logger from "loglevel"
|
|
6
6
|
import yargs from "yargs"
|
|
7
7
|
import { hideBin } from "yargs/helpers"
|
|
8
|
-
import
|
|
8
|
+
import "../lib/log.js" // init logger
|
|
9
9
|
|
|
10
|
-
import { contextDirname, pkgInfo } from "../lib/utils.js"
|
|
10
|
+
import { contextDirname, pkgInfo, getLatestVersion } from "../lib/utils.js"
|
|
11
11
|
import { boxCommand } from "../lib/box/index.js"
|
|
12
12
|
import { appstoreCommand } from "../lib/appstore/index.js"
|
|
13
13
|
import { lpkAppCommand, lpkProjectCommand } from "../lib/app/index.js"
|
|
14
14
|
import { configCommand } from "../lib/config/index.js"
|
|
15
|
+
import { lzcDockerCommand } from "../lib/docker/index.js"
|
|
15
16
|
|
|
16
|
-
// logger level middleware
|
|
17
|
-
const logLevelOriginalFactory = logger.methodFactory
|
|
18
|
-
logger.methodFactory = function (methodName, logLevel, loggerName) {
|
|
19
|
-
let debug = logLevel <= logger.levels.DEBUG
|
|
20
|
-
let rawMethod = logLevelOriginalFactory(methodName, logLevel, loggerName)
|
|
21
|
-
return function (...args) {
|
|
22
|
-
let color = (msg) => chalk.gray
|
|
23
|
-
switch (methodName) {
|
|
24
|
-
case "trace":
|
|
25
|
-
color = chalk.dim.cyan
|
|
26
|
-
break
|
|
27
|
-
case "debug":
|
|
28
|
-
color = chalk.blue
|
|
29
|
-
break
|
|
30
|
-
case "info":
|
|
31
|
-
color = chalk.green
|
|
32
|
-
break
|
|
33
|
-
case "warn":
|
|
34
|
-
color = chalk.magenta
|
|
35
|
-
break
|
|
36
|
-
case "error":
|
|
37
|
-
color = chalk.bold.red
|
|
38
|
-
break
|
|
39
|
-
}
|
|
40
|
-
const prefix = loggerName ?? methodName
|
|
41
|
-
rawMethod(
|
|
42
|
-
(debug ? `[${prefix}] ` : "") +
|
|
43
|
-
args
|
|
44
|
-
.map((a) => {
|
|
45
|
-
let res = a
|
|
46
|
-
if (typeof a == "object") {
|
|
47
|
-
res = JSON.stringify(a, undefined, 4)
|
|
48
|
-
}
|
|
49
|
-
return color(res)
|
|
50
|
-
})
|
|
51
|
-
.join(" ")
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
logger.setDefaultLevel("info")
|
|
56
17
|
function setLoggerLevel({ log }) {
|
|
57
18
|
logger.setLevel(log, false)
|
|
58
19
|
}
|
|
59
20
|
|
|
21
|
+
function checkLatestVersion(controller) {
|
|
22
|
+
return ({ _: args }) => {
|
|
23
|
+
if (args.length > 0) {
|
|
24
|
+
switch (args[0]) {
|
|
25
|
+
case "project":
|
|
26
|
+
case "app":
|
|
27
|
+
case "appstore":
|
|
28
|
+
getLatestVersion(controller)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
60
34
|
const program = yargs(hideBin(process.argv))
|
|
61
35
|
.scriptName("lzc-cli")
|
|
62
36
|
.usage("<command> [options]")
|
|
@@ -78,27 +52,34 @@ boxCommand(program)
|
|
|
78
52
|
lpkAppCommand(program)
|
|
79
53
|
lpkProjectCommand(program)
|
|
80
54
|
appstoreCommand(program)
|
|
55
|
+
lzcDockerCommand(program)
|
|
81
56
|
|
|
82
57
|
// 当没有参数的时候,默认显示帮助。
|
|
83
58
|
;(async () => {
|
|
59
|
+
const controller = new AbortController()
|
|
60
|
+
|
|
84
61
|
const parser = program
|
|
85
62
|
.strict()
|
|
86
63
|
.showHelpOnFail(false, "使用 lzc-cli help 查看更多帮助")
|
|
87
|
-
.middleware([setLoggerLevel])
|
|
64
|
+
.middleware([setLoggerLevel, checkLatestVersion(controller)])
|
|
88
65
|
.parse()
|
|
89
66
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
67
|
+
try {
|
|
68
|
+
const argv = await parser
|
|
69
|
+
if (argv._.length == 1) {
|
|
70
|
+
switch (argv._[0]) {
|
|
71
|
+
case "box":
|
|
72
|
+
case "app":
|
|
73
|
+
case "project":
|
|
74
|
+
case "appstore":
|
|
75
|
+
case "config":
|
|
76
|
+
program.showHelp()
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
} else if (argv._.length == 0) {
|
|
80
|
+
program.showHelp()
|
|
100
81
|
}
|
|
101
|
-
}
|
|
102
|
-
|
|
82
|
+
} finally {
|
|
83
|
+
controller.abort()
|
|
103
84
|
}
|
|
104
85
|
})()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from "process"
|
|
3
|
+
import logger from "loglevel"
|
|
4
|
+
import yargs from "yargs"
|
|
5
|
+
import { hideBin } from "yargs/helpers"
|
|
6
|
+
|
|
7
|
+
import "../lib/log.js"
|
|
8
|
+
import { pkgInfo } from "../lib/utils.js"
|
|
9
|
+
import { singleLzcDockerComposeCommand } from "../lib/docker/index.js"
|
|
10
|
+
|
|
11
|
+
function setLoggerLevel({ log }) {
|
|
12
|
+
logger.setLevel(log, false)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const program = yargs(hideBin(process.argv))
|
|
16
|
+
.scriptName("lzc-docker-compose")
|
|
17
|
+
.usage("<command> [options]")
|
|
18
|
+
.version(`lzc-docker-compose ${pkgInfo.version}`)
|
|
19
|
+
.option("log", {
|
|
20
|
+
type: "string",
|
|
21
|
+
default: "info",
|
|
22
|
+
describe: "log level 'trace', 'debug', 'info', 'warn', 'error'"
|
|
23
|
+
})
|
|
24
|
+
.option("help", {
|
|
25
|
+
alias: "h",
|
|
26
|
+
type: "boolean",
|
|
27
|
+
default: false
|
|
28
|
+
})
|
|
29
|
+
.command(singleLzcDockerComposeCommand("$0"))
|
|
30
|
+
|
|
31
|
+
// 当没有参数的时候,默认显示帮助。
|
|
32
|
+
;(async () => {
|
|
33
|
+
const parser = program
|
|
34
|
+
.strict(false)
|
|
35
|
+
.showHelpOnFail(false, "使用 lzc-cli help 查看更多帮助")
|
|
36
|
+
.middleware([setLoggerLevel])
|
|
37
|
+
.parse()
|
|
38
|
+
|
|
39
|
+
const argv = await parser
|
|
40
|
+
if (argv._.length == 0) {
|
|
41
|
+
program.showHelp()
|
|
42
|
+
}
|
|
43
|
+
})()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from "process"
|
|
3
|
+
import logger from "loglevel"
|
|
4
|
+
import yargs from "yargs"
|
|
5
|
+
import { hideBin } from "yargs/helpers"
|
|
6
|
+
|
|
7
|
+
import "../lib/log.js"
|
|
8
|
+
import { pkgInfo } from "../lib/utils.js"
|
|
9
|
+
import { singleLzcDockerCommand } from "../lib/docker/index.js"
|
|
10
|
+
|
|
11
|
+
function setLoggerLevel({ log }) {
|
|
12
|
+
logger.setLevel(log, false)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const program = yargs(hideBin(process.argv))
|
|
16
|
+
.scriptName("lzc-docker")
|
|
17
|
+
.usage("<command> [options]")
|
|
18
|
+
.version(`lzc-docker ${pkgInfo.version}`)
|
|
19
|
+
.option("log", {
|
|
20
|
+
type: "string",
|
|
21
|
+
default: "info",
|
|
22
|
+
describe: "log level 'trace', 'debug', 'info', 'warn', 'error'"
|
|
23
|
+
})
|
|
24
|
+
.option("help", {
|
|
25
|
+
alias: "h",
|
|
26
|
+
type: "boolean",
|
|
27
|
+
default: false
|
|
28
|
+
})
|
|
29
|
+
.command(singleLzcDockerCommand("$0"))
|
|
30
|
+
|
|
31
|
+
// 当没有参数的时候,默认显示帮助。
|
|
32
|
+
;(async () => {
|
|
33
|
+
const parser = program
|
|
34
|
+
.strict(false)
|
|
35
|
+
.showHelpOnFail(false, "使用 lzc-cli help 查看更多帮助")
|
|
36
|
+
.middleware([setLoggerLevel])
|
|
37
|
+
.parse()
|
|
38
|
+
|
|
39
|
+
const argv = await parser
|
|
40
|
+
if (argv._.length == 0) {
|
|
41
|
+
program.showHelp()
|
|
42
|
+
}
|
|
43
|
+
})()
|
package/template/_lpk/exec.sh
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
|
-
# 设置 lzcapp 环境变量
|
|
4
|
-
# 从1号进程读取环境变量
|
|
5
|
-
cat > /tmp/lzcapp_env.sh << EOF
|
|
6
|
-
#!/bin/sh
|
|
7
|
-
$(/usr/lib/debug.bridge/busybox tr '\0' '\n' < /proc/1/environ | /usr/lib/debug.bridge/busybox xargs -I{} echo 'export "{}"')
|
|
8
|
-
EOF
|
|
9
|
-
. /tmp/lzcapp_env.sh
|
|
10
|
-
|
|
11
3
|
# 执行 setupscript 脚本
|
|
12
4
|
SETUPSCRIPT=/lzcapp/pkg/content/devshell/setupscript
|
|
13
5
|
if [ -f $SETUPSCRIPT ];then
|
|
@@ -1,43 +1,4 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
set -e
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
mkdir -p /usr/lib/debug.bridge/
|
|
7
|
-
# 不能直接对 pkg content 中的 busybox chmod(Read-Only权限)
|
|
8
|
-
LZCBUSYBOX=/usr/lib/debug.bridge/busybox
|
|
9
|
-
cp /lzcapp/pkg/content/devshell/busybox ${LZCBUSYBOX}
|
|
10
|
-
chmod +x ${LZCBUSYBOX}
|
|
11
|
-
|
|
12
|
-
mkdir -p /root/.ssh
|
|
13
|
-
mkdir -p /etc/debug.bridge
|
|
14
|
-
|
|
15
|
-
if ! [ -f /usr/bin/dropbearmulti ]; then
|
|
16
|
-
${LZCBUSYBOX} wget ${DEBUG_BRIDGE_LZCAPP}/resources/dropbearmulti -O /usr/bin/dropbearmulti
|
|
17
|
-
chmod +x /usr/bin/dropbearmulti
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
if ! [ -f /usr/bin/rsync ]; then
|
|
21
|
-
${LZCBUSYBOX} wget ${DEBUG_BRIDGE_LZCAPP}/resources/rsync -O /usr/bin/rsync
|
|
22
|
-
chmod +x /usr/bin/rsync
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
${LZCBUSYBOX} wget ${DEBUG_BRIDGE_LZCAPP}/resources/authorized_keys -O /root/.ssh/authorized_keys
|
|
26
|
-
chmod 0644 /root/.ssh/authorized_keys
|
|
27
|
-
|
|
28
|
-
${LZCBUSYBOX} wget ${DEBUG_BRIDGE_LZCAPP}/bannerfile -O /etc/debug.bridge/bannerfile
|
|
29
|
-
|
|
30
|
-
if ${LZCBUSYBOX} netstat -tln | grep ':22222' >/dev/null; then
|
|
31
|
-
echo "端口22222正在监听"
|
|
32
|
-
${LZCBUSYBOX} sleep infinity
|
|
33
|
-
else
|
|
34
|
-
echo "启动 rsync daemon."
|
|
35
|
-
rsync --daemon --no-detach --config=/lzcapp/pkg/content/devshell/rsyncd.conf &
|
|
36
|
-
while ! ${LZCBUSYBOX} nc -z localhost 873; do
|
|
37
|
-
sleep 1
|
|
38
|
-
done
|
|
39
|
-
echo "rsync daemon 已成功监听 873 端口"
|
|
40
|
-
|
|
41
|
-
mkdir -p /etc/dropbear
|
|
42
|
-
exec dropbearmulti dropbear -R -F -E -B -p 22222
|
|
43
|
-
fi
|
|
4
|
+
echo "init don't nothing..."
|