@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.
Files changed (60) hide show
  1. package/lib/api.js +34 -36
  2. package/lib/archiver.js +50 -31
  3. package/lib/box/hportal.js +113 -0
  4. package/lib/box/index.js +135 -0
  5. package/lib/box/qemu_vm_mgr.js +553 -0
  6. package/lib/box/schemes/vm_box_system_debian.json +47 -0
  7. package/lib/builder.js +154 -35
  8. package/lib/dev.js +39 -31
  9. package/lib/env.js +276 -58
  10. package/lib/generator.js +31 -0
  11. package/lib/git/git-commit.sh +7 -0
  12. package/lib/git/git-reset.sh +15 -0
  13. package/lib/key.js +14 -25
  14. package/lib/sdk.js +7 -10
  15. package/lib/utils.js +149 -52
  16. package/package.json +14 -2
  17. package/scripts/auto-completion.sh +46 -0
  18. package/scripts/cli.js +134 -70
  19. package/template/_lazycat/app-config +1 -0
  20. package/template/_lazycat/docker-compose.yml.in +3 -5
  21. package/template/golang/README.md +3 -4
  22. package/template/golang/assets/css/bootstrap-responsive.css +26 -23
  23. package/template/golang/assets/css/bootstrap-responsive.min.css +1065 -1
  24. package/template/golang/assets/css/bootstrap.css +733 -362
  25. package/template/golang/assets/css/bootstrap.min.css +5299 -1
  26. package/template/golang/assets/css/rego.css +17 -17
  27. package/template/golang/assets/js/bootstrap.js +1340 -1311
  28. package/template/golang/assets/js/bootstrap.min.js +1240 -5
  29. package/template/golang/assets/js/rego.js +80 -69
  30. package/template/golang/index.html +61 -59
  31. package/template/ionic_vue3/README.md +46 -0
  32. package/template/ionic_vue3/_eslintrc.cjs +24 -0
  33. package/template/ionic_vue3/_gitignore +29 -0
  34. package/template/ionic_vue3/_vscode/extensions.json +6 -0
  35. package/template/ionic_vue3/capacitor.config.ts +10 -0
  36. package/template/ionic_vue3/env.d.ts +1 -0
  37. package/template/ionic_vue3/index.html +13 -0
  38. package/template/ionic_vue3/ionic.config.json +7 -0
  39. package/template/ionic_vue3/package.json +52 -0
  40. package/template/ionic_vue3/postcss.config.js +6 -0
  41. package/template/ionic_vue3/public/favicon.ico +0 -0
  42. package/template/ionic_vue3/src/App.vue +11 -0
  43. package/template/ionic_vue3/src/assets/logo.svg +1 -0
  44. package/template/ionic_vue3/src/index.css +3 -0
  45. package/template/ionic_vue3/src/main.ts +35 -0
  46. package/template/ionic_vue3/src/router/index.ts +15 -0
  47. package/template/ionic_vue3/src/theme/variables.css +231 -0
  48. package/template/ionic_vue3/src/views/Home.vue +38 -0
  49. package/template/ionic_vue3/tailwind.config.js +7 -0
  50. package/template/ionic_vue3/tsconfig.json +16 -0
  51. package/template/ionic_vue3/tsconfig.vite-config.json +8 -0
  52. package/template/ionic_vue3/vite.config.ts +28 -0
  53. package/template/release/golang/build.sh +1 -2
  54. package/template/release/ionic_vue3/Dockerfile +10 -0
  55. package/template/release/ionic_vue3/build.sh +9 -0
  56. package/template/release/ionic_vue3/docker-compose.yml.in +8 -0
  57. package/template/release/vue/Dockerfile +3 -2
  58. package/template/release/vue/build.sh +4 -2
  59. package/template/vue/README.md +5 -0
  60. 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: targetDir,
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
- // v2 版本会收集所有需要的context 文件,将其传入buildImage 方法中
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
- const host = new URL(env["SDK_URL"]).host;
105
- const docker = await new DockerClient(host).init();
104
+
105
+ const docker = await new DockerClient().init();
106
106
  try {
107
107
  console.log(chalk.green("开始在设备中构建镜像"));
108
- let stream = await docker.buildImage(
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: env["APP_ID"],
117
+ t: tag,
117
118
  }
118
119
  );
119
- await new Promise((resolve, reject) => {
120
- let pulls = [];
121
- let logUpdate;
120
+ const finished = await new Promise((resolve, reject) => {
121
+ let refresher = new StatusRefresher();
122
122
 
123
- function refresh() {
124
- logUpdate(pulls.map((p) => `${p.id}: ${p.content}`).join("\n"));
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
- function updatePull({ id, content }) {
128
- let index = pulls.findIndex((p) => p.id == id);
129
- if (index > -1) {
130
- pulls[index] = {
131
- id: id,
132
- content: content,
133
- };
134
- } else {
135
- pulls.push({
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
- updatePull({
240
+ refresher.updateStatus({
152
241
  id: res.id,
153
242
  content: `${res.status} ${res.progress}`,
154
243
  });
155
244
  } else {
156
- updatePull({ id: res.id, content: res.status });
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
- pulls = [];
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 os from "os";
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
- ...this.env.all,
48
- APP_IMAGE_NAME: this.env.get("APP_ID"),
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 = this.env.get("DEV_PORT");
71
+ let port = env.get("DEV_PORT");
71
72
  localaddr = `0.0.0.0:${port}`;
72
73
  }
73
74
 
74
- localServer = execa.command(this.env.get("DEV_CMD"), {
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(this.env);
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(this.env);
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
- fs.watch(filepath, callback);
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
- let callback = (eventType, filename) => {
183
- this.syncProject(keyFile, appAddr);
184
- };
185
-
186
- try {
187
- if (["darwin", "win32"].includes(os.platform())) {
188
- // onle macos and window platform support recursive options
189
- fs.watch(process.cwd(), { recursive: true }, callback);
190
- } else {
191
- // fallback to other platform
192
- let gitignore = new GitIgnore();
193
- this.fallbackWatch(process.cwd(), gitignore, callback);
194
- }
195
- } catch (e) {
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(this.env);
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
  }