@lazycatcloud/lzc-cli 1.1.1 → 1.1.4
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/cmds/app.js +137 -0
- package/cmds/config.js +55 -0
- package/cmds/create.js +55 -0
- package/cmds/dev.js +122 -0
- package/cmds/init.js +125 -0
- package/cmds/log.js +103 -0
- package/cmds/publish.js +116 -0
- package/lib/api.js +34 -36
- package/lib/archiver.js +50 -31
- package/lib/box/check_qemu.js +27 -0
- package/lib/box/hportal.js +114 -0
- package/lib/box/index.js +152 -0
- package/lib/box/qemu_vm_mgr.js +625 -0
- package/lib/box/schemes/vm_box_system_debian.json +47 -0
- package/lib/builder.js +154 -35
- package/lib/dev.js +51 -32
- package/lib/env.js +276 -57
- 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 -11
- package/lib/sdk.js +7 -10
- package/lib/utils.js +149 -53
- package/package.json +18 -5
- 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, { sdkEnv } 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(sdkEnv.sdkUrl).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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import execa from "execa";
|
|
2
2
|
import { execSync } from "child_process";
|
|
3
3
|
import fs from "fs";
|
|
4
|
+
import chalk from "chalk";
|
|
4
5
|
import {
|
|
5
6
|
GitIgnore,
|
|
6
7
|
isDirSync,
|
|
@@ -9,15 +10,16 @@ import {
|
|
|
9
10
|
APP_SDK_HOSTNAME,
|
|
10
11
|
} from "./utils.js";
|
|
11
12
|
import path from "path";
|
|
12
|
-
import
|
|
13
|
-
import Env from "./env.js";
|
|
13
|
+
import env, { sdkEnv } from "./env.js";
|
|
14
14
|
import _get from "lodash.get";
|
|
15
15
|
import { execPreBuild } from "./builder.js";
|
|
16
|
+
import Key from "./key.js";
|
|
17
|
+
import chokidar from "chokidar";
|
|
16
18
|
|
|
17
19
|
import Archiver from "../lib/archiver.js";
|
|
18
20
|
|
|
19
21
|
async function sdkSSHAddr(env) {
|
|
20
|
-
let url =
|
|
22
|
+
let url = sdkEnv.sdkUrl;
|
|
21
23
|
return `${APP_SDK_HOSTNAME}@${urlHostname(url)}:2222`;
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -30,7 +32,7 @@ class DevModule {
|
|
|
30
32
|
this.appId = appId;
|
|
31
33
|
this.tempDir = path.join(cwd, "output/debug");
|
|
32
34
|
this.cwd = cwd;
|
|
33
|
-
this.env = Env(cwd);
|
|
35
|
+
// this.env = Env(cwd);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
reset() {
|
|
@@ -44,12 +46,12 @@ class DevModule {
|
|
|
44
46
|
const buildDir = path.join(this.cwd, `debug/${command}`);
|
|
45
47
|
try {
|
|
46
48
|
const allEnv = {
|
|
47
|
-
...
|
|
48
|
-
APP_IMAGE_NAME:
|
|
49
|
+
...env.all,
|
|
50
|
+
APP_IMAGE_NAME: env.get("APP_ID"),
|
|
49
51
|
};
|
|
50
|
-
await execPreBuild(buildDir, allEnv);
|
|
52
|
+
await execPreBuild(buildDir, buildDir, allEnv);
|
|
51
53
|
let arch = new Archiver(this.tempDir);
|
|
52
|
-
await arch.load(this.cwd);
|
|
54
|
+
await arch.load(this.cwd, allEnv);
|
|
53
55
|
await arch.add(buildDir);
|
|
54
56
|
return await arch.finalize();
|
|
55
57
|
} catch (e) {
|
|
@@ -67,17 +69,17 @@ class DevModule {
|
|
|
67
69
|
|
|
68
70
|
try {
|
|
69
71
|
if (!localaddr) {
|
|
70
|
-
let port =
|
|
72
|
+
let port = env.get("DEV_PORT");
|
|
71
73
|
localaddr = `0.0.0.0:${port}`;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
localServer = execa.command(
|
|
76
|
+
localServer = execa.command(env.get("DEV_CMD"), {
|
|
75
77
|
stdio: "inherit",
|
|
76
78
|
});
|
|
77
79
|
|
|
78
80
|
const appAddr = this.getAddress();
|
|
79
81
|
|
|
80
|
-
let jump = await sdkSSHAddr(
|
|
82
|
+
let jump = await sdkSSHAddr(env);
|
|
81
83
|
const subproces = execa(
|
|
82
84
|
"ssh",
|
|
83
85
|
[
|
|
@@ -117,7 +119,7 @@ class DevModule {
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
async syncProject(keyFile, appAddr) {
|
|
120
|
-
let jump = await sdkSSHAddr(
|
|
122
|
+
let jump = await sdkSSHAddr(env);
|
|
121
123
|
let rsh = [
|
|
122
124
|
"ssh",
|
|
123
125
|
"-J",
|
|
@@ -135,6 +137,15 @@ class DevModule {
|
|
|
135
137
|
"-i",
|
|
136
138
|
keyFile,
|
|
137
139
|
].join(" ");
|
|
140
|
+
// 检查rsync工具是否存在:提示用户
|
|
141
|
+
try {
|
|
142
|
+
execSync("rsync -V", {
|
|
143
|
+
stdio: "ignore",
|
|
144
|
+
});
|
|
145
|
+
} catch {
|
|
146
|
+
console.log(chalk.red("请检查 rsync 是否安装,路径是否正确!"));
|
|
147
|
+
process.exit();
|
|
148
|
+
}
|
|
138
149
|
execSync(
|
|
139
150
|
`rsync --rsh='${rsh}' -czrPR . root@${appAddr}:/root/${this.appId} --filter=':- .gitignore' --exclude='node_modules' --exclude='.git' --delete`,
|
|
140
151
|
{ stdio: ["ignore", "ignore", "inherit"] }
|
|
@@ -144,13 +155,17 @@ class DevModule {
|
|
|
144
155
|
// fallback fs.watch on not darwin and windows platform
|
|
145
156
|
// FILEPATH directory or file path
|
|
146
157
|
// CALLBACK => function(eventType, filename)
|
|
147
|
-
// fs.watch
|
|
158
|
+
// fs.watch 虽然不支持递归,但可以直接监听整个文件夹的变动
|
|
148
159
|
async fallbackWatch(filepath, gitignore, callback) {
|
|
149
160
|
if (gitignore.contain(filepath)) {
|
|
150
161
|
return Promise.resolve();
|
|
151
162
|
}
|
|
152
163
|
|
|
153
|
-
|
|
164
|
+
if (filepath.endsWith(".git") || filepath.endsWith(".lazycat")) {
|
|
165
|
+
return Promise.resolve();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
fs.watch(filepath, callback(filepath));
|
|
154
169
|
|
|
155
170
|
// 如果为一个文件夹,则扫描当中是否含有子文件夹
|
|
156
171
|
if (isDirSync(filepath)) {
|
|
@@ -179,22 +194,21 @@ class DevModule {
|
|
|
179
194
|
// 监听非.gitignore文件
|
|
180
195
|
// TODO: 目前仅仅监听process.cwd()以下的文件
|
|
181
196
|
async watchFile(keyFile, appAddr) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
197
|
+
const ignore = new GitIgnore(process.cwd());
|
|
198
|
+
await ignore.collect();
|
|
199
|
+
chokidar
|
|
200
|
+
.watch(".", {
|
|
201
|
+
ignored: (path) => {
|
|
202
|
+
if ([".git", ".lazycat"].some((p) => path.startsWith(p))) return true;
|
|
203
|
+
|
|
204
|
+
return ignore.contain(path);
|
|
205
|
+
},
|
|
206
|
+
ignoreInitial: true,
|
|
207
|
+
})
|
|
208
|
+
.on("all", (event, path) => {
|
|
209
|
+
// console.log(event, path);
|
|
210
|
+
this.syncProject(keyFile, appAddr);
|
|
211
|
+
});
|
|
198
212
|
}
|
|
199
213
|
|
|
200
214
|
// 保存一个dev shell状态的文件,如果重复执行dev shell
|
|
@@ -249,6 +263,9 @@ class DevModule {
|
|
|
249
263
|
let appAddr;
|
|
250
264
|
|
|
251
265
|
try {
|
|
266
|
+
// 确保 sdk 能通过公钥正常访问
|
|
267
|
+
await new Key().ensure(sdkEnv.sdkUrl);
|
|
268
|
+
|
|
252
269
|
keyFile = path.join(this.tempDir, "debug/ssh/id_ed25519");
|
|
253
270
|
appAddr = this.getAddress();
|
|
254
271
|
|
|
@@ -271,7 +288,7 @@ class DevModule {
|
|
|
271
288
|
}
|
|
272
289
|
|
|
273
290
|
async connectShell(keyFile, appAddr) {
|
|
274
|
-
let jump = await sdkSSHAddr(
|
|
291
|
+
let jump = await sdkSSHAddr(env);
|
|
275
292
|
const subproces = execa(
|
|
276
293
|
"ssh",
|
|
277
294
|
[
|
|
@@ -290,8 +307,10 @@ class DevModule {
|
|
|
290
307
|
"root@" + appAddr,
|
|
291
308
|
"-i",
|
|
292
309
|
keyFile,
|
|
310
|
+
"-t",
|
|
311
|
+
`"cd ~/${env.get("APP_ID")}; exec \\$SHELL -l"`,
|
|
293
312
|
],
|
|
294
|
-
{ stdio: "inherit" }
|
|
313
|
+
{ stdio: "inherit", shell: true }
|
|
295
314
|
);
|
|
296
315
|
await subproces;
|
|
297
316
|
}
|