@lazycatcloud/lzc-cli 1.1.0 → 1.1.3
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/api.js +34 -36
- package/lib/archiver.js +50 -31
- package/lib/box/hportal.js +113 -0
- package/lib/box/index.js +135 -0
- package/lib/box/qemu_vm_mgr.js +553 -0
- package/lib/box/schemes/vm_box_system_debian.json +47 -0
- package/lib/builder.js +154 -35
- package/lib/dev.js +39 -31
- package/lib/env.js +276 -58
- package/lib/generator.js +31 -0
- package/lib/git/git-commit.sh +7 -0
- package/lib/git/git-reset.sh +15 -0
- package/lib/key.js +14 -25
- package/lib/sdk.js +7 -10
- package/lib/utils.js +149 -52
- package/package.json +14 -2
- package/scripts/auto-completion.sh +46 -0
- package/scripts/cli.js +134 -70
- package/template/_lazycat/app-config +1 -0
- package/template/_lazycat/docker-compose.yml.in +3 -5
- package/template/golang/README.md +3 -4
- package/template/golang/assets/css/bootstrap-responsive.css +26 -23
- package/template/golang/assets/css/bootstrap-responsive.min.css +1065 -1
- package/template/golang/assets/css/bootstrap.css +733 -362
- package/template/golang/assets/css/bootstrap.min.css +5299 -1
- package/template/golang/assets/css/rego.css +17 -17
- package/template/golang/assets/js/bootstrap.js +1340 -1311
- package/template/golang/assets/js/bootstrap.min.js +1240 -5
- package/template/golang/assets/js/rego.js +80 -69
- package/template/golang/index.html +61 -59
- package/template/ionic_vue3/README.md +46 -0
- package/template/ionic_vue3/_eslintrc.cjs +24 -0
- package/template/ionic_vue3/_gitignore +29 -0
- package/template/ionic_vue3/_vscode/extensions.json +6 -0
- package/template/ionic_vue3/capacitor.config.ts +10 -0
- package/template/ionic_vue3/env.d.ts +1 -0
- package/template/ionic_vue3/index.html +13 -0
- package/template/ionic_vue3/ionic.config.json +7 -0
- package/template/ionic_vue3/package.json +52 -0
- package/template/ionic_vue3/postcss.config.js +6 -0
- package/template/ionic_vue3/public/favicon.ico +0 -0
- package/template/ionic_vue3/src/App.vue +11 -0
- package/template/ionic_vue3/src/assets/logo.svg +1 -0
- package/template/ionic_vue3/src/index.css +3 -0
- package/template/ionic_vue3/src/main.ts +35 -0
- package/template/ionic_vue3/src/router/index.ts +15 -0
- package/template/ionic_vue3/src/theme/variables.css +231 -0
- package/template/ionic_vue3/src/views/Home.vue +38 -0
- package/template/ionic_vue3/tailwind.config.js +7 -0
- package/template/ionic_vue3/tsconfig.json +16 -0
- package/template/ionic_vue3/tsconfig.vite-config.json +8 -0
- package/template/ionic_vue3/vite.config.ts +28 -0
- package/template/release/golang/build.sh +1 -2
- package/template/release/ionic_vue3/Dockerfile +10 -0
- package/template/release/ionic_vue3/build.sh +9 -0
- package/template/release/ionic_vue3/docker-compose.yml.in +8 -0
- package/template/release/vue/Dockerfile +3 -2
- package/template/release/vue/build.sh +4 -2
- package/template/vue/README.md +5 -0
- package/template/vue/babel.config.js +2 -4
package/lib/builder.js
CHANGED
|
@@ -9,15 +9,17 @@ import { DockerfileParser } from "dockerfile-ast";
|
|
|
9
9
|
import glob from "fast-glob";
|
|
10
10
|
import ignore from "@balena/dockerignore";
|
|
11
11
|
import { createLogUpdate } from "log-update";
|
|
12
|
+
import env from "./env.js";
|
|
13
|
+
import Dockerode from "dockerode";
|
|
12
14
|
|
|
13
15
|
export const PRE_BUILD_FILE = "build.sh";
|
|
14
16
|
|
|
15
|
-
export async function execPreBuild(targetDir, env = {}) {
|
|
17
|
+
export async function execPreBuild(targetDir, context, env = {}) {
|
|
16
18
|
const buildFile = path.join(targetDir, PRE_BUILD_FILE);
|
|
17
19
|
if (!fs.existsSync(buildFile)) return;
|
|
18
20
|
|
|
19
21
|
const subproces = execa(buildFile, {
|
|
20
|
-
cwd:
|
|
22
|
+
cwd: context,
|
|
21
23
|
env: env,
|
|
22
24
|
stdio: ["ignore", "inherit", "inherit"],
|
|
23
25
|
});
|
|
@@ -97,48 +99,135 @@ export default class Builder {
|
|
|
97
99
|
return src;
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
async dockerRemoteBuildV2(contextDir, dockerfile) {
|
|
102
|
-
const env = this.env;
|
|
102
|
+
async dockerLocalBuild(contextDir, dockerfile, tag = env.get("APP_ID")) {
|
|
103
103
|
const src = await this.collectContextFromDockerFile(contextDir, dockerfile);
|
|
104
|
-
|
|
105
|
-
const docker = await new DockerClient(
|
|
104
|
+
|
|
105
|
+
const docker = await new DockerClient().init();
|
|
106
106
|
try {
|
|
107
107
|
console.log(chalk.green("开始在设备中构建镜像"));
|
|
108
|
-
|
|
108
|
+
|
|
109
|
+
const stream = await docker.buildImage(
|
|
109
110
|
{
|
|
110
111
|
context: contextDir,
|
|
111
112
|
src: src,
|
|
112
113
|
},
|
|
113
114
|
{
|
|
114
|
-
buildargs: env,
|
|
115
|
+
buildargs: env.all,
|
|
115
116
|
dockerfile: src[0],
|
|
116
|
-
t:
|
|
117
|
+
t: tag,
|
|
117
118
|
}
|
|
118
119
|
);
|
|
119
|
-
await new Promise((resolve, reject) => {
|
|
120
|
-
let
|
|
121
|
-
let logUpdate;
|
|
120
|
+
const finished = await new Promise((resolve, reject) => {
|
|
121
|
+
let refresher = new StatusRefresher();
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
docker.modem.followProgress(
|
|
124
|
+
stream,
|
|
125
|
+
(err, res) => {
|
|
126
|
+
err ? reject(err) : resolve(res);
|
|
127
|
+
},
|
|
128
|
+
(res) => {
|
|
129
|
+
if (res.status) {
|
|
130
|
+
if (res.id) {
|
|
131
|
+
if (["Downloading", "Extracting"].includes(res.status)) {
|
|
132
|
+
refresher.updateStatus({
|
|
133
|
+
id: res.id,
|
|
134
|
+
content: `${res.status} ${res.progress}`,
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
refresher.updateStatus({ id: res.id, content: res.status });
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
process.stdout.write(res.status);
|
|
141
|
+
process.stdout.write("\n");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
126
144
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
id: id,
|
|
137
|
-
content: content,
|
|
138
|
-
});
|
|
145
|
+
if (res.stream) {
|
|
146
|
+
if (res.stream.startsWith("Step")) {
|
|
147
|
+
refresher.reset();
|
|
148
|
+
}
|
|
149
|
+
process.stdout.write(res.stream);
|
|
150
|
+
}
|
|
151
|
+
if (res.error) {
|
|
152
|
+
reject(res.error);
|
|
153
|
+
}
|
|
139
154
|
}
|
|
140
|
-
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
await this.pushImage(tag);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async pushImage(tag) {
|
|
164
|
+
console.log(chalk.green(`开始推送镜像`));
|
|
165
|
+
const docker = await new DockerClient().init();
|
|
166
|
+
|
|
167
|
+
const image = new Dockerode.Image(docker.modem, tag);
|
|
168
|
+
|
|
169
|
+
// TODO hardcode 未来开发者需要获取推送至 lazycat 的token
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
image
|
|
172
|
+
.push({
|
|
173
|
+
authconfig: { auth: "bG5rczpsbmtzMjAyMA==" },
|
|
174
|
+
})
|
|
175
|
+
.then((stream) => {
|
|
176
|
+
let refresher = new StatusRefresher();
|
|
177
|
+
docker.modem.followProgress(
|
|
178
|
+
stream,
|
|
179
|
+
(err, res) => {
|
|
180
|
+
err ? reject(err) : resolve(res);
|
|
181
|
+
},
|
|
182
|
+
(res) => {
|
|
183
|
+
if (res.status) {
|
|
184
|
+
if (res.id) {
|
|
185
|
+
refresher.updateStatus({ id: res.id, content: res.status });
|
|
186
|
+
} else {
|
|
187
|
+
process.stdout.write(res.status);
|
|
188
|
+
process.stdout.write("\n");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
141
191
|
|
|
192
|
+
if (res.stream) {
|
|
193
|
+
process.stdout.write(res.stream);
|
|
194
|
+
}
|
|
195
|
+
if (res.error) {
|
|
196
|
+
reject(res.error);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* v2 版本会收集所有需要的context 文件,将其传入buildImage 方法中
|
|
206
|
+
* @param contextDir
|
|
207
|
+
* @param dockerfile
|
|
208
|
+
* @param tag
|
|
209
|
+
*/
|
|
210
|
+
async dockerRemoteBuildV2(contextDir, dockerfile, tag = env.get("APP_ID")) {
|
|
211
|
+
const src = await this.collectContextFromDockerFile(contextDir, dockerfile);
|
|
212
|
+
const host = new URL(env.get("SDK_URL")).host;
|
|
213
|
+
console.log(host);
|
|
214
|
+
const docker = await new DockerClient(host).init();
|
|
215
|
+
try {
|
|
216
|
+
return new Promise(async (resolve, reject) => {
|
|
217
|
+
let refresher = new StatusRefresher();
|
|
218
|
+
|
|
219
|
+
console.log(chalk.green("开始在设备中构建镜像"));
|
|
220
|
+
const stream = await docker.buildImage(
|
|
221
|
+
{
|
|
222
|
+
context: contextDir,
|
|
223
|
+
src: src,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
buildargs: this.env,
|
|
227
|
+
dockerfile: src[0],
|
|
228
|
+
t: tag,
|
|
229
|
+
}
|
|
230
|
+
);
|
|
142
231
|
docker.modem.followProgress(
|
|
143
232
|
stream,
|
|
144
233
|
(err, res) => {
|
|
@@ -148,15 +237,16 @@ export default class Builder {
|
|
|
148
237
|
if (res.status) {
|
|
149
238
|
if (res.id) {
|
|
150
239
|
if (["Downloading", "Extracting"].includes(res.status)) {
|
|
151
|
-
|
|
240
|
+
refresher.updateStatus({
|
|
152
241
|
id: res.id,
|
|
153
242
|
content: `${res.status} ${res.progress}`,
|
|
154
243
|
});
|
|
155
244
|
} else {
|
|
156
|
-
|
|
245
|
+
refresher.updateStatus({
|
|
246
|
+
id: res.id,
|
|
247
|
+
content: res.status,
|
|
248
|
+
});
|
|
157
249
|
}
|
|
158
|
-
|
|
159
|
-
refresh();
|
|
160
250
|
} else {
|
|
161
251
|
process.stdout.write(res.status);
|
|
162
252
|
process.stdout.write("\n");
|
|
@@ -165,8 +255,7 @@ export default class Builder {
|
|
|
165
255
|
|
|
166
256
|
if (res.stream) {
|
|
167
257
|
if (res.stream.startsWith("Step")) {
|
|
168
|
-
|
|
169
|
-
logUpdate = createLogUpdate(process.stdout);
|
|
258
|
+
refresher.reset();
|
|
170
259
|
}
|
|
171
260
|
process.stdout.write(res.stream);
|
|
172
261
|
}
|
|
@@ -181,3 +270,33 @@ export default class Builder {
|
|
|
181
270
|
}
|
|
182
271
|
}
|
|
183
272
|
}
|
|
273
|
+
|
|
274
|
+
class StatusRefresher {
|
|
275
|
+
constructor() {
|
|
276
|
+
this.reset();
|
|
277
|
+
}
|
|
278
|
+
refresh() {
|
|
279
|
+
this.logUpdate(this.pulls.map((p) => `${p.id}: ${p.content}`).join("\n"));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
reset() {
|
|
283
|
+
this.pulls = [];
|
|
284
|
+
this.logUpdate = createLogUpdate(process.stdout);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
updateStatus({ id, content }) {
|
|
288
|
+
let index = this.pulls.findIndex((p) => p.id == id);
|
|
289
|
+
if (index > -1) {
|
|
290
|
+
this.pulls[index] = {
|
|
291
|
+
id: id,
|
|
292
|
+
content: content,
|
|
293
|
+
};
|
|
294
|
+
} else {
|
|
295
|
+
this.pulls.push({
|
|
296
|
+
id: id,
|
|
297
|
+
content: content,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
this.refresh();
|
|
301
|
+
}
|
|
302
|
+
}
|
package/lib/dev.js
CHANGED
|
@@ -9,10 +9,11 @@ import {
|
|
|
9
9
|
APP_SDK_HOSTNAME,
|
|
10
10
|
} from "./utils.js";
|
|
11
11
|
import path from "path";
|
|
12
|
-
import
|
|
13
|
-
import Env from "./env.js";
|
|
12
|
+
import env from "./env.js";
|
|
14
13
|
import _get from "lodash.get";
|
|
15
14
|
import { execPreBuild } from "./builder.js";
|
|
15
|
+
import Key from "./key.js";
|
|
16
|
+
import chokidar from "chokidar";
|
|
16
17
|
|
|
17
18
|
import Archiver from "../lib/archiver.js";
|
|
18
19
|
|
|
@@ -30,7 +31,7 @@ class DevModule {
|
|
|
30
31
|
this.appId = appId;
|
|
31
32
|
this.tempDir = path.join(cwd, "output/debug");
|
|
32
33
|
this.cwd = cwd;
|
|
33
|
-
this.env = Env(cwd);
|
|
34
|
+
// this.env = Env(cwd);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
reset() {
|
|
@@ -44,12 +45,12 @@ class DevModule {
|
|
|
44
45
|
const buildDir = path.join(this.cwd, `debug/${command}`);
|
|
45
46
|
try {
|
|
46
47
|
const allEnv = {
|
|
47
|
-
...
|
|
48
|
-
APP_IMAGE_NAME:
|
|
48
|
+
...env.all,
|
|
49
|
+
APP_IMAGE_NAME: env.get("APP_ID"),
|
|
49
50
|
};
|
|
50
|
-
await execPreBuild(buildDir, allEnv);
|
|
51
|
+
await execPreBuild(buildDir, buildDir, allEnv);
|
|
51
52
|
let arch = new Archiver(this.tempDir);
|
|
52
|
-
await arch.load(this.cwd);
|
|
53
|
+
await arch.load(this.cwd, allEnv);
|
|
53
54
|
await arch.add(buildDir);
|
|
54
55
|
return await arch.finalize();
|
|
55
56
|
} catch (e) {
|
|
@@ -67,17 +68,17 @@ class DevModule {
|
|
|
67
68
|
|
|
68
69
|
try {
|
|
69
70
|
if (!localaddr) {
|
|
70
|
-
let port =
|
|
71
|
+
let port = env.get("DEV_PORT");
|
|
71
72
|
localaddr = `0.0.0.0:${port}`;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
localServer = execa.command(
|
|
75
|
+
localServer = execa.command(env.get("DEV_CMD"), {
|
|
75
76
|
stdio: "inherit",
|
|
76
77
|
});
|
|
77
78
|
|
|
78
79
|
const appAddr = this.getAddress();
|
|
79
80
|
|
|
80
|
-
let jump = await sdkSSHAddr(
|
|
81
|
+
let jump = await sdkSSHAddr(env);
|
|
81
82
|
const subproces = execa(
|
|
82
83
|
"ssh",
|
|
83
84
|
[
|
|
@@ -117,7 +118,7 @@ class DevModule {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
async syncProject(keyFile, appAddr) {
|
|
120
|
-
let jump = await sdkSSHAddr(
|
|
121
|
+
let jump = await sdkSSHAddr(env);
|
|
121
122
|
let rsh = [
|
|
122
123
|
"ssh",
|
|
123
124
|
"-J",
|
|
@@ -144,13 +145,17 @@ class DevModule {
|
|
|
144
145
|
// fallback fs.watch on not darwin and windows platform
|
|
145
146
|
// FILEPATH directory or file path
|
|
146
147
|
// CALLBACK => function(eventType, filename)
|
|
147
|
-
// fs.watch
|
|
148
|
+
// fs.watch 虽然不支持递归,但可以直接监听整个文件夹的变动
|
|
148
149
|
async fallbackWatch(filepath, gitignore, callback) {
|
|
149
150
|
if (gitignore.contain(filepath)) {
|
|
150
151
|
return Promise.resolve();
|
|
151
152
|
}
|
|
152
153
|
|
|
153
|
-
|
|
154
|
+
if (filepath.endsWith(".git") || filepath.endsWith(".lazycat")) {
|
|
155
|
+
return Promise.resolve();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
fs.watch(filepath, callback(filepath));
|
|
154
159
|
|
|
155
160
|
// 如果为一个文件夹,则扫描当中是否含有子文件夹
|
|
156
161
|
if (isDirSync(filepath)) {
|
|
@@ -179,22 +184,20 @@ class DevModule {
|
|
|
179
184
|
// 监听非.gitignore文件
|
|
180
185
|
// TODO: 目前仅仅监听process.cwd()以下的文件
|
|
181
186
|
async watchFile(keyFile, appAddr) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return Promise.reject(e);
|
|
197
|
-
}
|
|
187
|
+
const ignore = new GitIgnore(process.cwd());
|
|
188
|
+
await ignore.collect();
|
|
189
|
+
chokidar
|
|
190
|
+
.watch(".", {
|
|
191
|
+
ignored: (path) => {
|
|
192
|
+
if ([".git", ".lazycat"].some((p) => path.startsWith(p))) return true;
|
|
193
|
+
|
|
194
|
+
return ignore.contain(path);
|
|
195
|
+
},
|
|
196
|
+
ignoreInitial: true,
|
|
197
|
+
})
|
|
198
|
+
.on("all", (event, path) => {
|
|
199
|
+
this.syncProject(keyFile, appAddr);
|
|
200
|
+
});
|
|
198
201
|
}
|
|
199
202
|
|
|
200
203
|
// 保存一个dev shell状态的文件,如果重复执行dev shell
|
|
@@ -249,6 +252,9 @@ class DevModule {
|
|
|
249
252
|
let appAddr;
|
|
250
253
|
|
|
251
254
|
try {
|
|
255
|
+
// 确保 sdk 能通过公钥正常访问
|
|
256
|
+
await new Key().ensure(env.get("SDK_URL"));
|
|
257
|
+
|
|
252
258
|
keyFile = path.join(this.tempDir, "debug/ssh/id_ed25519");
|
|
253
259
|
appAddr = this.getAddress();
|
|
254
260
|
|
|
@@ -271,7 +277,7 @@ class DevModule {
|
|
|
271
277
|
}
|
|
272
278
|
|
|
273
279
|
async connectShell(keyFile, appAddr) {
|
|
274
|
-
let jump = await sdkSSHAddr(
|
|
280
|
+
let jump = await sdkSSHAddr(env);
|
|
275
281
|
const subproces = execa(
|
|
276
282
|
"ssh",
|
|
277
283
|
[
|
|
@@ -290,8 +296,10 @@ class DevModule {
|
|
|
290
296
|
"root@" + appAddr,
|
|
291
297
|
"-i",
|
|
292
298
|
keyFile,
|
|
299
|
+
"-t",
|
|
300
|
+
`"cd ~/${env.get("APP_ID")}; exec \\$SHELL -l"`,
|
|
293
301
|
],
|
|
294
|
-
{ stdio: "inherit" }
|
|
302
|
+
{ stdio: "inherit", shell: true }
|
|
295
303
|
);
|
|
296
304
|
await subproces;
|
|
297
305
|
}
|