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