@lazycatcloud/lzc-cli 1.2.53 → 1.2.54

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 CHANGED
@@ -1,3 +1,9 @@
1
+ # 1.2.54
2
+
3
+ 1. 添加 lzc-cli docker 命令
4
+ 2. 添加 lzc-cli docker-compose 命令
5
+ 3. 修复其他同步卡顿问题
6
+
1
7
  # 1.2.53
2
8
 
3
9
  1. 修复 devshell 模型下,环境变量中有空格导致export错误
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 "./lpk_debug_bridge.js"
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
  }
@@ -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 "./lpk_debug_bridge.js"
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
- if (isSync) {
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 host = this.isUserApp
457
- ? `${shellApi.uid}.app.${appId}.lzcapp`
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
  }
@@ -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 "./lpk_debug_bridge.js"
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 "../shellapi.js"
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 "../utils.js"
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
- // FIXME: 目前没有找到在允许空密码的情况下,检测是否只能通过 publickey 登录的方法。
90
- return false
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 sshCopyId() {
99
+ async sshApplyGrant() {
94
100
  const sshInfo = await findSshPublicKey()
95
101
  logger.debug("ssh public key info", sshInfo)
96
102
 
97
- const resolvedIp = await resolveDomain(this.domain)
98
- logger.debug("ip", resolvedIp)
103
+ const pk = Buffer.from(sshInfo.content.trimLeft()).toString("base64")
99
104
 
100
- // 将当前机器上的ssh public key 写到开发者工具中去,并过滤到重复的,管道在执行前会被清空,需要使用一个中间文件
101
- const stream = spawn.sync(
102
- sshBinary(),
103
- [...sshCmdArgs(`box@${resolvedIp}`), `add-ssh-public-key`],
104
- {
105
- input: sshInfo.content.trimLeft(),
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
- if (stream.status != 0) {
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
- `isDevshell --uid ${this.uid}`,
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,90 @@
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
+ export function lzcDockerCommand(program) {
8
+ program
9
+ .parserConfiguration({
10
+ "unknown-options-as-args": true
11
+ })
12
+ .middleware((argv, yargs) => {
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
+ program.command({
39
+ command: "docker",
40
+ desc: "微服应用 docker 管理",
41
+ builder: (args) => {
42
+ return args.strict(false) // 禁用严格模式,允许未知参数
43
+ },
44
+ handler: async (args) => {
45
+ logger.debug("args: ", args)
46
+ await shellApi.init()
47
+ const bridge = new DebugBridge()
48
+ await bridge.init()
49
+ logger.debug("docker", args.dockerArgs)
50
+ await bridge.lzcDocker(args.dockerArgs)
51
+ }
52
+ })
53
+ program.command({
54
+ command: "docker-compose",
55
+ desc: "微服应用 docker-compose 管理",
56
+ builder: (args) => {
57
+ return args.strict(false) // 禁用严格模式,允许未知参数
58
+ },
59
+ handler: async (args) => {
60
+ logger.debug("args: ", args)
61
+ await shellApi.init()
62
+ let manifest = null
63
+ try {
64
+ for (let name of ["lzc-manifest.yml", "manifest.yml"]) {
65
+ const mpath = path.join(process.cwd(), name)
66
+ if (isFileExist(mpath)) {
67
+ manifest = loadFromYaml(mpath)
68
+ break
69
+ }
70
+ }
71
+ } catch (e) {
72
+ logger.debug("load manifest: ", e)
73
+ }
74
+ const pkgId = manifest ? manifest["package"] : ""
75
+ const userApp = manifest ? isUserApp(manifest) : false
76
+
77
+ const bridge = new DebugBridge()
78
+ await bridge.init()
79
+ const options = args.dockerArgs
80
+ if (pkgId != "" && !options.some((o) => o == "--project-directory")) {
81
+ options.unshift(
82
+ "--project-directory",
83
+ `/lzcapp/run/lzc-docker/compose/${pkgId}${userApp ? "/" + shellapi.uid : ""}`
84
+ )
85
+ }
86
+ logger.debug("docker-compose", options.join(" "))
87
+ await bridge.lzcDockerCompose(options)
88
+ }
89
+ })
90
+ }
package/lib/utils.js CHANGED
@@ -353,45 +353,6 @@ export class Downloader {
353
353
  }
354
354
  }
355
355
 
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
356
  export function isValidAppId(appId) {
396
357
  const regex = new RegExp("^(?!-)(?!.*--)[a-z][a-z0-9]*([-][a-z0-9]+)*$")
397
358
  return regex.test(appId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazycatcloud/lzc-cli",
3
- "version": "1.2.53",
3
+ "version": "1.2.54",
4
4
  "description": "lazycat cloud developer kit",
5
5
  "scripts": {
6
6
  "prepublishOnly": "node check-changelog.js"
package/scripts/cli.js CHANGED
@@ -12,6 +12,7 @@ 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
17
  // logger level middleware
17
18
  const logLevelOriginalFactory = logger.methodFactory
@@ -78,6 +79,7 @@ boxCommand(program)
78
79
  lpkAppCommand(program)
79
80
  lpkProjectCommand(program)
80
81
  appstoreCommand(program)
82
+ lzcDockerCommand(program)
81
83
 
82
84
  // 当没有参数的时候,默认显示帮助。
83
85
  ;(async () => {
@@ -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
- DEBUG_BRIDGE_LZCAPP=http://app.cloud.lazycat.developer.tools.lzcapp
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..."