@lazycatcloud/lzc-cli 1.2.27 → 1.2.29
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/lib/app/apkshell.js +35 -0
- package/lib/app/index.js +89 -78
- package/lib/app/lpk_build.js +113 -117
- package/lib/app/lpk_create.js +78 -148
- package/lib/app/lpk_create_generator.js +103 -74
- package/lib/app/lpk_debug_bridge.js +61 -73
- package/lib/app/lpk_devshell.js +230 -229
- package/lib/app/lpk_devshell_docker.js +28 -28
- package/lib/app/lpk_installer.js +64 -49
- package/lib/appstore/index.js +29 -29
- package/lib/appstore/login.js +63 -68
- package/lib/appstore/prePublish.js +68 -68
- package/lib/appstore/publish.js +55 -55
- package/lib/box/index.js +25 -25
- package/lib/env.js +18 -18
- package/lib/shellapi.js +55 -58
- package/lib/utils.js +217 -164
- package/package.json +7 -1
- package/scripts/cli.js +56 -56
- package/template/_lpk/manifest.yml.in +8 -8
- package/template/vue/README.md +29 -0
- package/template/vue/index.html +13 -0
- package/template/vue/lzc-build.yml +59 -0
- package/template/vue/lzc-icon.png +0 -0
- package/template/vue/package.json +20 -0
- package/template/vue/public/vite.svg +1 -0
- package/template/vue/src/App.vue +30 -0
- package/template/vue/src/assets/vue.svg +1 -0
- package/template/vue/src/components/HelloWorld.vue +41 -0
- package/template/vue/src/main.ts +5 -0
- package/template/vue/src/style.css +79 -0
- package/template/vue/src/vite-env.d.ts +1 -0
- package/template/vue/tsconfig.app.json +24 -0
- package/template/vue/tsconfig.json +7 -0
- package/template/vue/tsconfig.node.json +22 -0
- package/template/vue/vite.config.ts +7 -0
- package/template/ionic_vue3/package-lock.json +0 -8100
package/lib/app/lpk_devshell.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// lzc-cli app devshell
|
|
2
|
-
import path from "node:path"
|
|
3
|
-
import fs from "node:fs"
|
|
4
|
-
import logger from "loglevel"
|
|
5
|
-
import { execSync } from "node:child_process"
|
|
6
|
-
import { LpkInstaller } from "./lpk_installer.js"
|
|
7
|
-
import debounce from "lodash.debounce"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import fs from "node:fs"
|
|
4
|
+
import logger from "loglevel"
|
|
5
|
+
import { execSync } from "node:child_process"
|
|
6
|
+
import { LpkInstaller } from "./lpk_installer.js"
|
|
7
|
+
import debounce from "lodash.debounce"
|
|
8
8
|
import {
|
|
9
9
|
mergeYamlInMemory,
|
|
10
10
|
contextDirname,
|
|
@@ -18,15 +18,16 @@ import {
|
|
|
18
18
|
isUserApp,
|
|
19
19
|
createTemplateFileCommon,
|
|
20
20
|
isDebugMode,
|
|
21
|
-
resolveDomain
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import
|
|
21
|
+
resolveDomain,
|
|
22
|
+
sleep
|
|
23
|
+
} from "../utils.js"
|
|
24
|
+
import os from "node:os"
|
|
25
|
+
import commandExists from "command-exists"
|
|
26
|
+
import chokidar from "chokidar"
|
|
27
|
+
import _ from "lodash"
|
|
28
|
+
import { DebugBridge } from "./lpk_debug_bridge.js"
|
|
29
|
+
import shellApi from "../shellapi.js"
|
|
30
|
+
import { collectContextFromDockerFile } from "./lpk_devshell_docker.js"
|
|
30
31
|
|
|
31
32
|
// 判断是否需要重新构建
|
|
32
33
|
// - 先判断 lzc-build.yml 是否发生改变
|
|
@@ -36,39 +37,39 @@ import { collectContextFromDockerFile } from "./lpk_devshell_docker.js";
|
|
|
36
37
|
// - 根据在 backend api 中判断同步的目录下是否存在文件 判断当前运行的 app 是否已经有一个挂载的实例,避免重复挂载
|
|
37
38
|
class AppDevShellMonitor {
|
|
38
39
|
constructor(cwd, pkgId, buildConfigFile) {
|
|
39
|
-
this.pwd = cwd ? path.resolve(cwd) : process.cwd()
|
|
40
|
-
this.pkgId = pkgId
|
|
40
|
+
this.pwd = cwd ? path.resolve(cwd) : process.cwd()
|
|
41
|
+
this.pkgId = pkgId
|
|
41
42
|
|
|
42
|
-
this.optionsFilePath = path.join(this.pwd, buildConfigFile)
|
|
43
|
-
this.options = loadFromYaml(this.optionsFilePath)
|
|
43
|
+
this.optionsFilePath = path.join(this.pwd, buildConfigFile)
|
|
44
|
+
this.options = loadFromYaml(this.optionsFilePath)
|
|
44
45
|
|
|
45
46
|
this.manifestFilePath = this.options["manifest"]
|
|
46
47
|
? path.join(this.pwd, this.options["manifest"])
|
|
47
|
-
: path.join(this.pwd, "lzc-manifest.yml")
|
|
48
|
+
: path.join(this.pwd, "lzc-manifest.yml")
|
|
48
49
|
|
|
49
50
|
this.hashObject = {
|
|
50
51
|
build: "",
|
|
51
|
-
manifest: ""
|
|
52
|
-
}
|
|
53
|
-
this.cacheFilePath = undefined
|
|
54
|
-
this.oldHash = undefined
|
|
55
|
-
this.newHash = undefined
|
|
56
|
-
this.bridge = new DebugBridge()
|
|
52
|
+
manifest: ""
|
|
53
|
+
}
|
|
54
|
+
this.cacheFilePath = undefined
|
|
55
|
+
this.oldHash = undefined
|
|
56
|
+
this.newHash = undefined
|
|
57
|
+
this.bridge = new DebugBridge()
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
async init() {
|
|
60
|
-
const pathId = await md5String(this.pwd)
|
|
61
|
+
const pathId = await md5String(this.pwd)
|
|
61
62
|
this.cacheFilePath = path.resolve(
|
|
62
63
|
os.tmpdir(),
|
|
63
64
|
"lzc-cli-devshell",
|
|
64
65
|
pathId,
|
|
65
66
|
"hash"
|
|
66
|
-
)
|
|
67
|
-
ensureDir(this.cacheFilePath)
|
|
67
|
+
)
|
|
68
|
+
ensureDir(this.cacheFilePath)
|
|
68
69
|
|
|
69
|
-
await this.updateHash()
|
|
70
|
+
await this.updateHash()
|
|
70
71
|
|
|
71
|
-
return this
|
|
72
|
+
return this
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
async shouldBuild() {
|
|
@@ -76,373 +77,376 @@ class AppDevShellMonitor {
|
|
|
76
77
|
this.change() ||
|
|
77
78
|
(await this.bridge.status(this.pkgId)) === "NotInstalled" ||
|
|
78
79
|
!(await this.bridge.isDevshell(this.pkgId))
|
|
79
|
-
)
|
|
80
|
+
)
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
change() {
|
|
83
|
-
logger.debug("oldHash", this.oldHash)
|
|
84
|
-
logger.debug("newHash", this.newHash)
|
|
85
|
-
return !_.isEqual(this.oldHash, this.newHash)
|
|
84
|
+
logger.debug("oldHash", this.oldHash)
|
|
85
|
+
logger.debug("newHash", this.newHash)
|
|
86
|
+
return !_.isEqual(this.oldHash, this.newHash)
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
async updateHash() {
|
|
89
90
|
this.oldHash = isFileExist(this.cacheFilePath)
|
|
90
91
|
? JSON.parse(fs.readFileSync(this.cacheFilePath))
|
|
91
|
-
: {}
|
|
92
|
+
: {}
|
|
92
93
|
const buildHash = isFileExist(this.optionsFilePath)
|
|
93
94
|
? await md5File(this.optionsFilePath)
|
|
94
|
-
: ""
|
|
95
|
+
: ""
|
|
95
96
|
const manifestHash = isFileExist(this.manifestFilePath)
|
|
96
97
|
? await md5File(this.manifestFilePath)
|
|
97
|
-
: ""
|
|
98
|
+
: ""
|
|
98
99
|
this.newHash = {
|
|
99
100
|
build: buildHash,
|
|
100
|
-
manifest: manifestHash
|
|
101
|
-
}
|
|
101
|
+
manifest: manifestHash
|
|
102
|
+
}
|
|
102
103
|
if (!_.isEqual(this.oldHash, this.newHash)) {
|
|
103
|
-
fs.writeFileSync(this.cacheFilePath, JSON.stringify(this.newHash))
|
|
104
|
+
fs.writeFileSync(this.cacheFilePath, JSON.stringify(this.newHash))
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
export class AppDevShell {
|
|
109
110
|
constructor(cwd, lpkBuild, forceBuild, buildConfigFile) {
|
|
110
|
-
this.cwd = cwd
|
|
111
|
-
this.lpkBuild = lpkBuild
|
|
112
|
-
this.forceBuild = forceBuild
|
|
113
|
-
this.buildConfigFile = buildConfigFile
|
|
114
|
-
this.isUserApp = false
|
|
115
|
-
this.monitor = undefined
|
|
111
|
+
this.cwd = cwd
|
|
112
|
+
this.lpkBuild = lpkBuild
|
|
113
|
+
this.forceBuild = forceBuild
|
|
114
|
+
this.buildConfigFile = buildConfigFile
|
|
115
|
+
this.isUserApp = false
|
|
116
|
+
this.monitor = undefined
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
async init() {
|
|
119
|
-
const manifest = await this.lpkBuild.getManifest()
|
|
120
|
+
const manifest = await this.lpkBuild.getManifest()
|
|
120
121
|
this.monitor = await new AppDevShellMonitor(
|
|
121
122
|
this.cwd,
|
|
122
123
|
manifest["package"],
|
|
123
124
|
this.buildConfigFile
|
|
124
|
-
).init()
|
|
125
|
-
this.isUserApp = isUserApp(manifest)
|
|
125
|
+
).init()
|
|
126
|
+
this.isUserApp = isUserApp(manifest)
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
async build() {
|
|
129
130
|
// 先判断是否需要重新构建
|
|
130
131
|
if (this.forceBuild || (await this.monitor.shouldBuild())) {
|
|
131
|
-
logger.debug("build...")
|
|
132
|
-
await this.devshellBuild()
|
|
132
|
+
logger.debug("build...")
|
|
133
|
+
await this.devshellBuild()
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
async devshellBuild() {
|
|
137
138
|
this.lpkBuild.onBeforeBuildPackage(async (options) => {
|
|
138
|
-
const devshell = options["devshell"]
|
|
139
|
+
const devshell = options["devshell"]
|
|
139
140
|
if (!devshell) {
|
|
140
|
-
throw "devshell 模式下,devshell 字段必须要指定"
|
|
141
|
+
throw "devshell 模式下,devshell 字段必须要指定"
|
|
141
142
|
}
|
|
142
143
|
|
|
143
|
-
const routes = devshell["routes"]
|
|
144
|
+
const routes = devshell["routes"]
|
|
144
145
|
if (!routes || routes.length == 0) {
|
|
145
|
-
throw "devshell 模式下,必须要指定 routes 内容"
|
|
146
|
+
throw "devshell 模式下,必须要指定 routes 内容"
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
return options
|
|
149
|
-
})
|
|
149
|
+
return options
|
|
150
|
+
})
|
|
150
151
|
|
|
151
152
|
// 复制 busybox 到 devshell 中去
|
|
152
153
|
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
153
154
|
const busyboxPath = path.join(
|
|
154
155
|
contextDirname(import.meta.url),
|
|
155
156
|
"../../template/_lpk/busybox-1.35.0"
|
|
156
|
-
)
|
|
157
|
-
let dest = path.join(contentdir, "devshell", "busybox")
|
|
158
|
-
ensureDir(dest)
|
|
159
|
-
fs.copyFileSync(busyboxPath, dest)
|
|
160
|
-
fs.chmodSync(dest, 0o775)
|
|
161
|
-
})
|
|
157
|
+
)
|
|
158
|
+
let dest = path.join(contentdir, "devshell", "busybox")
|
|
159
|
+
ensureDir(dest)
|
|
160
|
+
fs.copyFileSync(busyboxPath, dest)
|
|
161
|
+
fs.chmodSync(dest, 0o775)
|
|
162
|
+
})
|
|
162
163
|
|
|
163
164
|
// 复制 init_debug_bridge.sh 到 devshell 中去
|
|
164
165
|
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
165
166
|
const initPath = path.join(
|
|
166
167
|
contextDirname(import.meta.url),
|
|
167
168
|
"../../template/_lpk/init_debug_bridge.sh"
|
|
168
|
-
)
|
|
169
|
-
let dest = path.join(contentdir, "devshell", "init_debug_bridge.sh")
|
|
170
|
-
ensureDir(dest)
|
|
171
|
-
fs.copyFileSync(initPath, dest)
|
|
172
|
-
fs.chmodSync(dest, 0o775)
|
|
173
|
-
})
|
|
169
|
+
)
|
|
170
|
+
let dest = path.join(contentdir, "devshell", "init_debug_bridge.sh")
|
|
171
|
+
ensureDir(dest)
|
|
172
|
+
fs.copyFileSync(initPath, dest)
|
|
173
|
+
fs.chmodSync(dest, 0o775)
|
|
174
|
+
})
|
|
174
175
|
|
|
175
176
|
// 复制 exec.sh 到 devshell 中去
|
|
176
177
|
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
177
178
|
const execScriptPath = path.join(
|
|
178
179
|
contextDirname(import.meta.url),
|
|
179
180
|
"../../template/_lpk/exec.sh"
|
|
180
|
-
)
|
|
181
|
-
let dest = path.join(contentdir, "devshell", "exec.sh")
|
|
182
|
-
ensureDir(dest)
|
|
183
|
-
fs.copyFileSync(execScriptPath, dest)
|
|
184
|
-
fs.chmodSync(dest, 0o775)
|
|
185
|
-
})
|
|
181
|
+
)
|
|
182
|
+
let dest = path.join(contentdir, "devshell", "exec.sh")
|
|
183
|
+
ensureDir(dest)
|
|
184
|
+
fs.copyFileSync(execScriptPath, dest)
|
|
185
|
+
fs.chmodSync(dest, 0o775)
|
|
186
|
+
})
|
|
186
187
|
|
|
187
188
|
// 复制 setupscript 脚本
|
|
188
189
|
this.lpkBuild.onBeforeTarContent(async (contentdir, options) => {
|
|
189
|
-
const devshell = options["devshell"]
|
|
190
|
+
const devshell = options["devshell"]
|
|
190
191
|
if (!devshell["setupscript"]) {
|
|
191
|
-
return
|
|
192
|
+
return
|
|
192
193
|
}
|
|
193
194
|
|
|
194
|
-
logger.debug("处理 setupscript ")
|
|
195
|
-
const dest = path.join(contentdir, "devshell", "setupscript")
|
|
196
|
-
ensureDir(dest)
|
|
195
|
+
logger.debug("处理 setupscript ")
|
|
196
|
+
const dest = path.join(contentdir, "devshell", "setupscript")
|
|
197
|
+
ensureDir(dest)
|
|
197
198
|
|
|
198
199
|
// 先判断是否文件
|
|
199
|
-
const filePath = path.resolve(devshell["setupscript"])
|
|
200
|
+
const filePath = path.resolve(devshell["setupscript"])
|
|
200
201
|
if (isFileExist(filePath)) {
|
|
201
|
-
fs.copyFileSync(filePath, dest)
|
|
202
|
+
fs.copyFileSync(filePath, dest)
|
|
202
203
|
} else {
|
|
203
|
-
fs.writeFileSync(
|
|
204
|
-
dest,
|
|
205
|
-
`#!/bin/sh\nset -ex\n${devshell["setupscript"]}`
|
|
206
|
-
);
|
|
204
|
+
fs.writeFileSync(dest, `#!/bin/sh\nset -ex\n${devshell["setupscript"]}`)
|
|
207
205
|
}
|
|
208
|
-
fs.chmodSync(dest, 0o775)
|
|
209
|
-
})
|
|
206
|
+
fs.chmodSync(dest, 0o775)
|
|
207
|
+
})
|
|
210
208
|
|
|
211
209
|
// 在生成 manifest.yml 之前合并 devshell manifest 模板字段
|
|
212
210
|
this.lpkBuild.onBeforeDumpYaml(async (manifest, options) => {
|
|
213
|
-
logger.debug("merge lzc-build.yml devshell routes field")
|
|
214
|
-
const devshell = options["devshell"]
|
|
211
|
+
logger.debug("merge lzc-build.yml devshell routes field")
|
|
212
|
+
const devshell = options["devshell"]
|
|
215
213
|
|
|
216
|
-
const routes = devshell["routes"]
|
|
217
|
-
logger.debug("options devshell delete 'routes' field")
|
|
218
|
-
delete options["devshell"]["routes"]
|
|
214
|
+
const routes = devshell["routes"]
|
|
215
|
+
logger.debug("options devshell delete 'routes' field")
|
|
216
|
+
delete options["devshell"]["routes"]
|
|
219
217
|
|
|
220
218
|
// 添加 devshell 必要路由
|
|
221
219
|
routes.push(
|
|
222
220
|
"/__debug.bridge=exec://80,/lzcapp/pkg/content/devshell/init_debug_bridge.sh"
|
|
223
|
-
)
|
|
224
|
-
routes.push("/__isdevshell=file:///lzcapp/pkg/devshell")
|
|
221
|
+
)
|
|
222
|
+
routes.push("/__isdevshell=file:///lzcapp/pkg/devshell")
|
|
225
223
|
|
|
226
224
|
// 如果 devshell 中的 router 和 manifest 中的 prefix 出现冲突
|
|
227
225
|
// 优先使用 devshell 中的。
|
|
228
226
|
routes.forEach((r) => {
|
|
229
227
|
if (!r) {
|
|
230
|
-
return
|
|
228
|
+
return
|
|
231
229
|
}
|
|
232
230
|
|
|
233
|
-
let prefix = r.split("=")[0]
|
|
231
|
+
let prefix = r.split("=")[0]
|
|
234
232
|
let index = manifest["application"]["routes"].findIndex((mr) => {
|
|
235
233
|
if (!mr) {
|
|
236
|
-
return false
|
|
234
|
+
return false
|
|
237
235
|
}
|
|
238
|
-
return mr.split("=")[0] == prefix
|
|
239
|
-
})
|
|
236
|
+
return mr.split("=")[0] == prefix
|
|
237
|
+
})
|
|
240
238
|
if (index > -1) {
|
|
241
|
-
manifest["application"]["routes"].splice(index, 1)
|
|
239
|
+
manifest["application"]["routes"].splice(index, 1)
|
|
242
240
|
}
|
|
243
|
-
})
|
|
244
|
-
const application = { routes }
|
|
245
|
-
return mergeYamlInMemory([manifest, { application }])
|
|
246
|
-
})
|
|
241
|
+
})
|
|
242
|
+
const application = { routes }
|
|
243
|
+
return mergeYamlInMemory([manifest, { application }])
|
|
244
|
+
})
|
|
247
245
|
|
|
248
246
|
// 在生成 manifest.yml 之前合并 lzc-build.yml devshell 字段的值
|
|
249
247
|
// 并加上 health_check 字段, 当处于 devshell 的情况时,禁用 health_check
|
|
250
248
|
// 避免应用永远处于 unhealth 导致状态卡在 starting
|
|
251
249
|
this.lpkBuild.onBeforeDumpYaml(async (manifest, options) => {
|
|
252
|
-
logger.debug("merge lzc-build.yml devshell services\n", options)
|
|
253
|
-
const userapp = this.isUserApp ? shellApi.uid + "." : ""
|
|
250
|
+
logger.debug("merge lzc-build.yml devshell services\n", options)
|
|
251
|
+
const userapp = this.isUserApp ? shellApi.uid + "." : ""
|
|
254
252
|
const devshell = {
|
|
255
253
|
application: {
|
|
256
254
|
devshell: options["devshell"],
|
|
257
255
|
health_check: {
|
|
258
256
|
test_url: `http://${userapp}app.${manifest["package"]}.lzcapp/__isdevshell`,
|
|
259
|
-
disable: true
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return mergeYamlInMemory([manifest, devshell])
|
|
264
|
-
})
|
|
257
|
+
disable: true
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return mergeYamlInMemory([manifest, devshell])
|
|
262
|
+
})
|
|
265
263
|
|
|
266
264
|
// 如果 services/devshell 中有 dependencies 字段,优先使用
|
|
267
265
|
this.lpkBuild.onBeforeDumpYaml(async (manifest) => {
|
|
268
|
-
const config = manifest["application"]["devshell"]
|
|
266
|
+
const config = manifest["application"]["devshell"]
|
|
269
267
|
if (!config || !config["dependencies"]) {
|
|
270
|
-
return manifest
|
|
268
|
+
return manifest
|
|
271
269
|
}
|
|
272
270
|
|
|
273
|
-
const deps = config["dependencies"]
|
|
271
|
+
const deps = config["dependencies"]
|
|
274
272
|
if (deps.length == 0) {
|
|
275
|
-
logger.warn("dependencies 内容为空,跳过 dependencies")
|
|
276
|
-
delete manifest["application"]["devshell"]["dependencies"]
|
|
277
|
-
return manifest
|
|
273
|
+
logger.warn("dependencies 内容为空,跳过 dependencies")
|
|
274
|
+
delete manifest["application"]["devshell"]["dependencies"]
|
|
275
|
+
return manifest
|
|
278
276
|
}
|
|
279
277
|
|
|
280
|
-
const depsStr = deps.sort().join(" ")
|
|
281
|
-
logger.debug("开始创建 Dockerfile 文件")
|
|
278
|
+
const depsStr = deps.sort().join(" ")
|
|
279
|
+
logger.debug("开始创建 Dockerfile 文件")
|
|
282
280
|
|
|
283
|
-
const tempDir = fs.mkdtempSync(".lzc-cli-build-dependencies")
|
|
281
|
+
const tempDir = fs.mkdtempSync(".lzc-cli-build-dependencies")
|
|
284
282
|
try {
|
|
285
283
|
const dockerfilePath = path.join(
|
|
286
284
|
contextDirname(import.meta.url),
|
|
287
285
|
"../../template/_lpk/Dockerfile.in"
|
|
288
|
-
)
|
|
286
|
+
)
|
|
289
287
|
await createTemplateFileCommon(
|
|
290
288
|
dockerfilePath,
|
|
291
289
|
path.join(tempDir, "Dockerfile"),
|
|
292
290
|
{ dependencies: depsStr }
|
|
293
|
-
)
|
|
291
|
+
)
|
|
294
292
|
|
|
295
|
-
const label = `${await md5String(depsStr)}:latest
|
|
296
|
-
logger.debug(`开始在盒子中构建 ${label} 镜像 from ${tempDir}`)
|
|
293
|
+
const label = `${await md5String(depsStr)}:latest`
|
|
294
|
+
logger.debug(`开始在盒子中构建 ${label} 镜像 from ${tempDir}`)
|
|
297
295
|
|
|
298
296
|
const contextTar = await collectContextFromDockerFile(
|
|
299
297
|
tempDir,
|
|
300
298
|
path.resolve(tempDir, "Dockerfile")
|
|
301
|
-
)
|
|
302
|
-
const bridge = new DebugBridge()
|
|
303
|
-
const tag = await bridge.buildImage(label, contextTar)
|
|
304
|
-
delete manifest["application"]["devshell"]
|
|
305
|
-
manifest["application"]["image"] = tag
|
|
299
|
+
)
|
|
300
|
+
const bridge = new DebugBridge()
|
|
301
|
+
const tag = await bridge.buildImage(label, contextTar)
|
|
302
|
+
delete manifest["application"]["devshell"]
|
|
303
|
+
manifest["application"]["image"] = tag
|
|
306
304
|
} finally {
|
|
307
|
-
fs.rmSync(tempDir, { recursive: true })
|
|
305
|
+
fs.rmSync(tempDir, { recursive: true })
|
|
308
306
|
}
|
|
309
|
-
return manifest
|
|
310
|
-
})
|
|
307
|
+
return manifest
|
|
308
|
+
})
|
|
311
309
|
|
|
312
310
|
// 如果 services 中有 devshell 的字段,需要检测是否需要提前构建
|
|
313
311
|
this.lpkBuild.onBeforeDumpYaml(async (manifest) => {
|
|
314
|
-
const application = manifest["application"]
|
|
312
|
+
const application = manifest["application"]
|
|
315
313
|
if (!application || !application["devshell"]) {
|
|
316
|
-
return manifest
|
|
314
|
+
return manifest
|
|
317
315
|
}
|
|
318
316
|
|
|
319
|
-
const config = manifest["application"]["devshell"]
|
|
317
|
+
const config = manifest["application"]["devshell"]
|
|
320
318
|
if (!config || !config["build"]) {
|
|
321
|
-
return manifest
|
|
319
|
+
return manifest
|
|
322
320
|
}
|
|
323
321
|
|
|
324
|
-
const label = `${manifest["package"]}-devshell:${manifest["version"]}
|
|
325
|
-
logger.debug(`开始在盒子中构建 ${label} 镜像`)
|
|
322
|
+
const label = `${manifest["package"]}-devshell:${manifest["version"]}`
|
|
323
|
+
logger.debug(`开始在盒子中构建 ${label} 镜像`)
|
|
326
324
|
|
|
327
325
|
const contextTar = await collectContextFromDockerFile(
|
|
328
326
|
process.cwd(),
|
|
329
327
|
path.resolve(process.cwd(), config["build"], "Dockerfile")
|
|
330
|
-
)
|
|
328
|
+
)
|
|
331
329
|
|
|
332
|
-
const bridge = new DebugBridge()
|
|
333
|
-
const tag = await bridge.buildImage(label, contextTar)
|
|
334
|
-
delete manifest["application"]["devshell"]
|
|
335
|
-
manifest["application"]["image"] = tag
|
|
336
|
-
return manifest
|
|
337
|
-
})
|
|
330
|
+
const bridge = new DebugBridge()
|
|
331
|
+
const tag = await bridge.buildImage(label, contextTar)
|
|
332
|
+
delete manifest["application"]["devshell"]
|
|
333
|
+
manifest["application"]["image"] = tag
|
|
334
|
+
return manifest
|
|
335
|
+
})
|
|
338
336
|
|
|
339
337
|
// 如果 devshell 中指定了 image 字段将使用 image 字段
|
|
340
338
|
this.lpkBuild.onBeforeDumpYaml(async (manifest) => {
|
|
341
|
-
const config = manifest["application"]
|
|
339
|
+
const config = manifest["application"]
|
|
342
340
|
if (config["devshell"] && config["devshell"]["image"]) {
|
|
343
|
-
manifest["application"]["image"] = config["devshell"]["image"]
|
|
344
|
-
delete manifest["application"]["devshell"]
|
|
341
|
+
manifest["application"]["image"] = config["devshell"]["image"]
|
|
342
|
+
delete manifest["application"]["devshell"]
|
|
345
343
|
}
|
|
346
|
-
return manifest
|
|
347
|
-
})
|
|
344
|
+
return manifest
|
|
345
|
+
})
|
|
348
346
|
|
|
349
347
|
// 如果没有找到 devshell 中没有指定 image 不存在,将默认使用的 lzc-cli/devshell 容器
|
|
350
348
|
this.lpkBuild.onBeforeDumpYaml(async (manifest) => {
|
|
351
|
-
delete manifest["application"]["devshell"]
|
|
349
|
+
delete manifest["application"]["devshell"]
|
|
352
350
|
|
|
353
|
-
const config = manifest["application"]
|
|
351
|
+
const config = manifest["application"]
|
|
354
352
|
if (config["image"]) {
|
|
355
|
-
return manifest
|
|
353
|
+
return manifest
|
|
356
354
|
}
|
|
357
355
|
|
|
358
|
-
logger.debug("use default lzc-cli/devshell image")
|
|
359
|
-
manifest["application"][
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
});
|
|
356
|
+
logger.debug("use default lzc-cli/devshell image")
|
|
357
|
+
manifest["application"]["image"] =
|
|
358
|
+
`registry.lazycat.cloud/lzc-cli/devshell:v0.0.5`
|
|
359
|
+
return manifest
|
|
360
|
+
})
|
|
364
361
|
|
|
365
362
|
// 添加一个 devshell 的标记在 lpk 中,标记当前 lpk 为一个 debug 版本
|
|
366
363
|
this.lpkBuild.onBeforeDumpLpk(async (options, cwd, destDir) => {
|
|
367
|
-
fs.writeFileSync(path.resolve(destDir, "devshell"), "")
|
|
368
|
-
})
|
|
364
|
+
fs.writeFileSync(path.resolve(destDir, "devshell"), "")
|
|
365
|
+
})
|
|
369
366
|
|
|
370
367
|
// 在构建生成 lpk 包后,调用 deploy 进行部署
|
|
371
|
-
let installer = new LpkInstaller()
|
|
372
|
-
await installer.init()
|
|
373
|
-
await installer.deploy(this.lpkBuild, true)
|
|
368
|
+
let installer = new LpkInstaller()
|
|
369
|
+
await installer.init()
|
|
370
|
+
await installer.deploy(this.lpkBuild, true)
|
|
374
371
|
}
|
|
375
372
|
|
|
376
373
|
async rsyncShell() {
|
|
377
|
-
const manifest = await this.lpkBuild.getManifest()
|
|
378
|
-
const pkgId = manifest["package"]
|
|
374
|
+
const manifest = await this.lpkBuild.getManifest()
|
|
375
|
+
const pkgId = manifest["package"]
|
|
379
376
|
|
|
380
|
-
let isSync = false
|
|
377
|
+
let isSync = false
|
|
381
378
|
try {
|
|
382
|
-
const locker = new FileLocker(pkgId)
|
|
383
|
-
locker.lock()
|
|
379
|
+
const locker = new FileLocker(pkgId)
|
|
380
|
+
locker.lock()
|
|
384
381
|
process.on("exit", () => {
|
|
385
|
-
logger.debug("filelock unlock")
|
|
386
|
-
locker.unlock()
|
|
387
|
-
})
|
|
388
|
-
isSync = true
|
|
382
|
+
logger.debug("filelock unlock")
|
|
383
|
+
locker.unlock()
|
|
384
|
+
})
|
|
385
|
+
isSync = true
|
|
389
386
|
} catch (err) {
|
|
390
|
-
logger.debug("filelock catch")
|
|
391
|
-
logger.debug(err)
|
|
387
|
+
logger.debug("filelock catch")
|
|
388
|
+
logger.debug(err)
|
|
392
389
|
}
|
|
393
390
|
|
|
394
|
-
const devshell = new DevShell(pkgId, this.isUserApp)
|
|
391
|
+
const devshell = new DevShell(pkgId, this.isUserApp)
|
|
395
392
|
if (isSync) {
|
|
396
|
-
await devshell.shell()
|
|
393
|
+
await devshell.shell()
|
|
397
394
|
} else {
|
|
398
|
-
await devshell.connectShell()
|
|
395
|
+
await devshell.connectShell()
|
|
399
396
|
}
|
|
400
|
-
logger.debug("exit shell")
|
|
397
|
+
logger.debug("exit shell")
|
|
401
398
|
// TODO: shell 在正常情况下,按 Ctrl-D 就会退出,回到原来的本地的 shell ,但
|
|
402
399
|
// 现在会一直卡在退出状态后,必须要另外手动的指定 pkill node
|
|
403
|
-
process.exit(0)
|
|
400
|
+
process.exit(0)
|
|
404
401
|
}
|
|
405
402
|
}
|
|
406
403
|
|
|
407
404
|
class DevShell {
|
|
408
405
|
constructor(appId, isUserApp) {
|
|
409
|
-
this.appId = appId
|
|
410
|
-
this.isUserApp = isUserApp
|
|
406
|
+
this.appId = appId
|
|
407
|
+
this.isUserApp = isUserApp
|
|
411
408
|
}
|
|
412
409
|
|
|
413
410
|
async syncProject(appId) {
|
|
414
411
|
// prettier-ignore
|
|
415
|
-
const resolvedIp = await resolveDomain(
|
|
412
|
+
const resolvedIp = await resolveDomain(`dev.${shellApi.boxname}.heiyu.space`);
|
|
416
413
|
let rsh = [
|
|
417
414
|
"ssh",
|
|
418
|
-
"-J",
|
|
419
|
-
|
|
420
|
-
"-
|
|
421
|
-
|
|
422
|
-
"-o",
|
|
423
|
-
|
|
424
|
-
"-o",
|
|
425
|
-
|
|
415
|
+
"-J",
|
|
416
|
+
`box@[${resolvedIp}]:22222`,
|
|
417
|
+
"-p",
|
|
418
|
+
`22222`,
|
|
419
|
+
"-o",
|
|
420
|
+
`"StrictHostKeyChecking=no"`,
|
|
421
|
+
"-o",
|
|
422
|
+
`"UserKnownHostsFile=/dev/null"`,
|
|
423
|
+
"-o",
|
|
424
|
+
`"ConnectionAttempts=3"`,
|
|
425
|
+
"-o",
|
|
426
|
+
`"ConnectTimeout=30"`,
|
|
427
|
+
"-o",
|
|
428
|
+
`${isDebugMode() ? '"LogLevel=DEBUG"' : '"LogLevel=ERROR"'}`
|
|
429
|
+
].join(" ")
|
|
426
430
|
// 检查rsync工具是否存在:提示用户
|
|
427
|
-
const rsyncExisted = commandExists.sync("rsync")
|
|
431
|
+
const rsyncExisted = commandExists.sync("rsync")
|
|
428
432
|
if (!rsyncExisted) {
|
|
429
|
-
logger.error("请检查 rsync 是否安装,路径是否正确!")
|
|
430
|
-
process.exit(1)
|
|
433
|
+
logger.error("请检查 rsync 是否安装,路径是否正确!")
|
|
434
|
+
process.exit(1)
|
|
431
435
|
}
|
|
432
436
|
|
|
433
|
-
const rsyncDebug = isDebugMode() ? "-P" : ""
|
|
437
|
+
const rsyncDebug = isDebugMode() ? "-P" : ""
|
|
434
438
|
const host = this.isUserApp
|
|
435
439
|
? `${shellApi.uid}.app.${appId}.lzcapp`
|
|
436
|
-
: `app.${appId}.lzcapp
|
|
437
|
-
const dest = `root@${host}:/lzcapp/cache/devshell
|
|
440
|
+
: `app.${appId}.lzcapp`
|
|
441
|
+
const dest = `root@${host}:/lzcapp/cache/devshell`
|
|
438
442
|
try {
|
|
439
443
|
execSync(
|
|
440
444
|
`rsync ${rsyncDebug} --rsh='${rsh}' --recursive --relative --perms --update -F --filter=':- .gitignore' --ignore-errors . ${dest}`,
|
|
441
445
|
{ stdio: ["ignore", "inherit", "inherit"] }
|
|
442
|
-
)
|
|
446
|
+
)
|
|
443
447
|
} catch (err) {
|
|
444
|
-
logger.error("rsync 同步失败")
|
|
445
|
-
logger.debug(err)
|
|
448
|
+
logger.error("rsync 同步失败")
|
|
449
|
+
logger.debug(err)
|
|
446
450
|
}
|
|
447
451
|
}
|
|
448
452
|
|
|
@@ -452,77 +456,74 @@ class DevShell {
|
|
|
452
456
|
// fs.watch 虽然不支持递归,但可以直接监听整个文件夹的变动
|
|
453
457
|
async fallbackWatch(filepath, gitignore, callback) {
|
|
454
458
|
if (gitignore.contain(filepath)) {
|
|
455
|
-
return Promise.resolve()
|
|
459
|
+
return Promise.resolve()
|
|
456
460
|
}
|
|
457
461
|
|
|
458
462
|
if (filepath.endsWith(".git") || filepath.endsWith(".lazycat")) {
|
|
459
|
-
return Promise.resolve()
|
|
463
|
+
return Promise.resolve()
|
|
460
464
|
}
|
|
461
465
|
|
|
462
|
-
fs.watch(filepath, callback(filepath))
|
|
466
|
+
fs.watch(filepath, callback(filepath))
|
|
463
467
|
|
|
464
468
|
// 如果为一个文件夹,则扫描当中是否含有子文件夹
|
|
465
469
|
if (isDirSync(filepath)) {
|
|
466
470
|
return gitignore.readdir(filepath, (err, files) => {
|
|
467
471
|
if (err) {
|
|
468
|
-
throw err
|
|
472
|
+
throw err
|
|
469
473
|
}
|
|
470
474
|
|
|
471
475
|
if (files.length <= 0) {
|
|
472
|
-
return
|
|
476
|
+
return
|
|
473
477
|
}
|
|
474
478
|
|
|
475
479
|
files.forEach((f) => {
|
|
476
480
|
if (f.isDirectory()) {
|
|
477
|
-
this.fallbackWatch(
|
|
478
|
-
path.join(filepath, f.name),
|
|
479
|
-
gitignore,
|
|
480
|
-
callback
|
|
481
|
-
);
|
|
481
|
+
this.fallbackWatch(path.join(filepath, f.name), gitignore, callback)
|
|
482
482
|
}
|
|
483
|
-
})
|
|
484
|
-
})
|
|
483
|
+
})
|
|
484
|
+
})
|
|
485
485
|
}
|
|
486
486
|
}
|
|
487
487
|
|
|
488
488
|
// 监听非.gitignore文件
|
|
489
489
|
// TODO: 目前仅仅监听process.cwd()以下的文件
|
|
490
490
|
async watchFile(appId) {
|
|
491
|
-
const ignore = new GitIgnore(process.cwd())
|
|
492
|
-
await ignore.collect()
|
|
491
|
+
const ignore = new GitIgnore(process.cwd())
|
|
492
|
+
await ignore.collect()
|
|
493
493
|
chokidar
|
|
494
494
|
.watch(".", {
|
|
495
495
|
ignored: (path) => {
|
|
496
|
-
if ([".git", ".lazycat"].some((p) => path.startsWith(p))) return true
|
|
496
|
+
if ([".git", ".lazycat"].some((p) => path.startsWith(p))) return true
|
|
497
497
|
|
|
498
|
-
return ignore.contain(path)
|
|
498
|
+
return ignore.contain(path)
|
|
499
499
|
},
|
|
500
|
-
ignoreInitial: true
|
|
500
|
+
ignoreInitial: true
|
|
501
501
|
})
|
|
502
502
|
.on(
|
|
503
503
|
"all",
|
|
504
504
|
debounce(() => {
|
|
505
|
-
this.syncProject(appId)
|
|
505
|
+
this.syncProject(appId)
|
|
506
506
|
}, 1000)
|
|
507
|
-
)
|
|
507
|
+
)
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
async shell() {
|
|
511
511
|
try {
|
|
512
512
|
// 监听文件
|
|
513
|
-
await this.watchFile(this.appId)
|
|
513
|
+
await this.watchFile(this.appId)
|
|
514
514
|
await this.connectShell(async () => {
|
|
515
515
|
// 在连接成功的时候,同步一次文件
|
|
516
|
-
await
|
|
517
|
-
|
|
516
|
+
await sleep(300)
|
|
517
|
+
await this.syncProject(this.appId)
|
|
518
|
+
})
|
|
518
519
|
} catch (e) {
|
|
519
|
-
console.log(e)
|
|
520
|
-
return Promise.reject(e)
|
|
520
|
+
console.log(e)
|
|
521
|
+
return Promise.reject(e)
|
|
521
522
|
}
|
|
522
523
|
}
|
|
523
524
|
|
|
524
525
|
async connectShell(onconnect = null) {
|
|
525
|
-
const bridge = new DebugBridge()
|
|
526
|
-
await bridge.devshell(this.appId, this.isUserApp, onconnect)
|
|
526
|
+
const bridge = new DebugBridge()
|
|
527
|
+
await bridge.devshell(this.appId, this.isUserApp, onconnect)
|
|
527
528
|
}
|
|
528
529
|
}
|