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