@lazycatcloud/lzc-cli 1.2.16 → 1.2.18

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.
@@ -9,6 +9,7 @@ import {
9
9
  envTemplateFile,
10
10
  isValidPackageName,
11
11
  tarContentDir,
12
+ isPngWithFile,
12
13
  } from "../utils.js";
13
14
  import { spawnSync } from "child_process";
14
15
  import { LpkManifest } from "./lpk_create.js";
@@ -63,6 +64,13 @@ async function fetchIconTo(options, cwd, destDir) {
63
64
  return;
64
65
  }
65
66
 
67
+ if (!isPngWithFile(iconPath)) {
68
+ logger.warn(`图标icon ${iconPath} 验证失败(不是一个png格式)`);
69
+ return;
70
+ } else {
71
+ logger.debug(`图标icon ${iconPath} 验证成功(png格式)`);
72
+ }
73
+
66
74
  fs.copyFileSync(iconPath, path.join(destDir, "icon.png"));
67
75
  return;
68
76
  }
@@ -70,27 +78,29 @@ async function fetchIconTo(options, cwd, destDir) {
70
78
  // 提供一些方便的环境变量,可以在 lzc-build.yml 中直接使用
71
79
  // - LocalIP 本地局域网ip
72
80
  function localIp() {
81
+ const regex = /inet6 (fc03:1136:[0-9a-fA-F:]+)\/\d+ scope global/;
82
+
83
+ let output = "";
73
84
  if (isMacos) {
74
- const result = spawnSync("sh", [
75
- "-c",
76
- "ifconfig | awk '/inet /&&!/127.0.0.1/{print $2;exit}'",
77
- ]);
78
- if (result.error) {
85
+ const result = spawnSync("sh", ["-c", "ifconfig", "heiyu-0"], {
86
+ encoding: "utf-8",
87
+ });
88
+ if (result.status != 0 || result.error) {
79
89
  logger.debug("macos get current ip is error", result.error);
80
90
  return "";
81
- } else {
82
- const ip = result.stdout.toString().trim();
83
- return ip;
84
91
  }
92
+ output = result.stdout;
93
+ } else {
94
+ const result = spawnSync("ip", ["addr", "show", "heiyu-0"], {
95
+ encoding: "utf-8",
96
+ });
97
+ if (result.status !== 0 || result.error) {
98
+ logger.debug("run ip addr show heiyu-0 failed", result.error);
99
+ return "";
100
+ }
101
+ output = result.stdout;
85
102
  }
86
- const output = spawnSync("ip", ["route", "get", "1.1.1.1"], {
87
- encoding: "utf-8",
88
- });
89
- if (output.status !== 0) {
90
- logger.debug("run ip route error", output.status, output.stderr);
91
- return "";
92
- }
93
- const match = output.stdout.match(/src\ (.+)\ uid/i);
103
+ const match = output.match(regex);
94
104
  if (match) {
95
105
  return match[1];
96
106
  } else {
@@ -90,7 +90,7 @@ export class DebugBridge {
90
90
  return this.common(this.sshCmd, [`uninstall --uid ${this.uid}`, appId]);
91
91
  }
92
92
 
93
- async devshell(appId, isUserApp) {
93
+ async devshell(appId, isUserApp, onconnect = null) {
94
94
  const stream = spawn(
95
95
  this.sshCmd,
96
96
  [
@@ -110,6 +110,9 @@ export class DebugBridge {
110
110
  stream.on("close", (code) => {
111
111
  code == 0 ? resolve() : reject();
112
112
  });
113
+ if (onconnect) {
114
+ stream.on("spawn", onconnect);
115
+ }
113
116
  });
114
117
  }
115
118
 
@@ -217,7 +217,7 @@ export class AppDevShell {
217
217
 
218
218
  // 添加 devshell 必要路由
219
219
  routes.push(
220
- "/__debug.bridge=exec://22222,/lzcapp/pkg/content/devshell/init_debug_bridge.sh"
220
+ "/__debug.bridge=exec://80,/lzcapp/pkg/content/devshell/init_debug_bridge.sh"
221
221
  );
222
222
  routes.push("/__isdevshell=file:///lzcapp/pkg/devshell");
223
223
 
@@ -244,14 +244,17 @@ export class AppDevShell {
244
244
  });
245
245
 
246
246
  // 在生成 manifest.yml 之前合并 lzc-build.yml devshell 字段的值
247
- // 并加上 health_check 字段
247
+ // 并加上 health_check 字段, 当处于 devshell 的情况时,禁用 health_check
248
+ // 避免应用永远处于 unhealth 导致状态卡在 starting
248
249
  this.lpkBuild.onBeforeDumpYaml(async (manifest, options) => {
249
250
  logger.debug("merge lzc-build.yml devshell services\n", options);
251
+ const userapp = this.isUserApp ? shellApi.uid + "." : "";
250
252
  const devshell = {
251
253
  application: {
252
254
  devshell: options["devshell"],
253
255
  health_check: {
254
- test_url: `http://app.${manifest["package"]}.lzcapp/__isdevshell`,
256
+ test_url: `http://${userapp}app.${manifest["package"]}.lzcapp/__isdevshell`,
257
+ disable: true,
255
258
  },
256
259
  },
257
260
  };
@@ -497,26 +500,26 @@ class DevShell {
497
500
  "all",
498
501
  debounce(() => {
499
502
  this.syncProject(appId);
500
- }),
501
- 1000
503
+ }, 1000)
502
504
  );
503
505
  }
506
+
504
507
  async shell() {
505
508
  try {
506
- // 当进入shell的时候,都同步一次
507
- await this.syncProject(this.appId);
508
- // 注册watch函数
509
+ // 监听文件
509
510
  await this.watchFile(this.appId);
510
-
511
- await this.connectShell();
511
+ await this.connectShell(async () => {
512
+ // 在连接成功的时候,同步一次文件
513
+ await this.syncProject(this.appId);
514
+ });
512
515
  } catch (e) {
513
516
  console.log(e);
514
517
  return Promise.reject(e);
515
518
  }
516
519
  }
517
520
 
518
- async connectShell() {
521
+ async connectShell(onconnect = null) {
519
522
  const bridge = new DebugBridge();
520
- await bridge.devshell(this.appId, this.isUserApp);
523
+ await bridge.devshell(this.appId, this.isUserApp, onconnect);
521
524
  }
522
525
  }
@@ -5,7 +5,7 @@ import fs from "node:fs";
5
5
  import inquirer from "inquirer";
6
6
  import { spawnSync } from "node:child_process";
7
7
  import path from "node:path";
8
- import { isFileExist } from "../utils.js";
8
+ import { isFileExist, isPngWithFile } from "../utils.js";
9
9
 
10
10
  async function askChangeLog() {
11
11
  const noEmpty = (value) => value != "";
@@ -24,16 +24,40 @@ export class Publish {
24
24
  this.baseUrl = baseUrl;
25
25
  }
26
26
 
27
- async publish(pkgPath, changelog) {
27
+ /**
28
+ * @param {string} raw
29
+ */
30
+ isJSON(raw) {
31
+ const ml = raw.length
32
+ if (ml <= 1) return false
33
+ return raw[0] == '{' && raw[ml - 1] == '}'
34
+ }
35
+
36
+ preCheck(pkgPath) {
28
37
  const tempDir = fs.mkdtempSync(".lzc-cli-publish");
29
38
  try {
30
39
  spawnSync("unzip", ["-d", tempDir, pkgPath]);
31
40
  if (isFileExist(path.join(tempDir, "devshell"))) {
32
- throw "不能发布一个devshell的版本,请重新使用 `lzc-cli project build` 构建";
41
+ logger.error("不能发布一个devshell的版本,请重新使用 `lzc-cli project build` 构建")
42
+ return false
43
+ }
44
+ const tempIcon = path.join(tempDir, "icon.png")
45
+ if (!isFileExist(tempIcon) || !isPngWithFile(tempIcon)) {
46
+ logger.error("icon 必须是 png 格式")
47
+ return false
33
48
  }
49
+ return true
34
50
  } finally {
35
51
  fs.rmSync(tempDir, { recursive: true });
36
52
  }
53
+ }
54
+
55
+ /**
56
+ * @param {string} pkgPath
57
+ * @param {string} changelog
58
+ */
59
+ async publish(pkgPath, changelog) {
60
+ if (!this.preCheck(pkgPath)) return
37
61
 
38
62
  await autoLogin();
39
63
 
@@ -41,27 +65,43 @@ export class Publish {
41
65
  const answer = await askChangeLog();
42
66
  changelog = answer.changelog;
43
67
  }
68
+ changelog = changelog.trim() // clean space ^:)
44
69
 
45
70
  logger.info("正在提交审核...");
46
71
  const form = new FormData();
47
72
  form.append("file", fs.createReadStream(pkgPath));
48
73
 
49
- const res = await request(this.baseUrl + "/upload_lpk", {
74
+ const uploadURL = this.baseUrl + "/upload_lpk"
75
+ logger.debug("upload url is", uploadURL)
76
+
77
+ const res = await request(uploadURL, {
50
78
  method: "POST",
51
79
  body: form,
52
80
  });
53
- const lpkInfo = await res.json();
81
+ const text = (await res.text()).trim()
82
+ if (!this.isJSON(text)) {
83
+ logger.info("upload lpk fail", text);
84
+ return
85
+ }
86
+ const lpkInfo = await JSON.parse(text)
54
87
  logger.debug("upload lpk response", lpkInfo);
55
88
 
56
- return request(this.baseUrl + `/apps/${lpkInfo.package}/reviews`, {
89
+ const sendURL = this.baseUrl + `/apps/${lpkInfo.package}/reviews`
90
+ logger.debug("publish url is", sendURL)
91
+
92
+ const formData = {
93
+ changelog,
94
+ iconPath: lpkInfo.iconPath,
95
+ pkgPath: lpkInfo.url,
96
+ supportPC: lpkInfo.supportPC,
97
+ supportMobile: lpkInfo.supportMobile,
98
+ }
99
+
100
+ logger.debug("form data is", formData)
101
+
102
+ return request(sendURL, {
57
103
  method: "POST",
58
- body: JSON.stringify({
59
- changelog,
60
- iconPath: lpkInfo.iconPath,
61
- pkgPath: lpkInfo.url,
62
- supportPC: lpkInfo.supportPC,
63
- supportMobile: lpkInfo.supportMobile,
64
- }),
104
+ body: JSON.stringify(formData),
65
105
  }).then(async (res) => {
66
106
  if (res.status >= 400) {
67
107
  logger.error("发布应用出错,错误状态码为: ", res.status);
package/lib/shellapi.js CHANGED
@@ -5,6 +5,9 @@ import path from "node:path";
5
5
  import fs from "node:fs";
6
6
  import os from "node:os";
7
7
  import logger from "loglevel";
8
+ import fetch from "node-fetch";
9
+
10
+ const bannerfileContent = `˄=ᆽ=ᐟ \\`;
8
11
 
9
12
  /**
10
13
  * @link {https://www.npmjs.com/package/appdirs}
@@ -35,6 +38,7 @@ class ShellApi {
35
38
  md.add("lzc-shellapi-cred", cred);
36
39
  this.metadata = md;
37
40
  this.info = await this.initBoxInfo();
41
+ this.checkDevTools();
38
42
  }
39
43
 
40
44
  get boxname() {
@@ -97,6 +101,22 @@ class ShellApi {
97
101
  throw "没有默认盒子信息, 请先使用 lzc-cli box switch 设置默认的盒子信息";
98
102
  }
99
103
 
104
+ async checkDevTools() {
105
+ const url = `https://dev.${this.info.boxname}.heiyu.space/bannerfile`;
106
+ fetch(url, { redirect: "error" })
107
+ .then(async (res) => {
108
+ const content = await res.text();
109
+ if (res.status != 200 || content != bannerfileContent) {
110
+ logger.warn(
111
+ `检测到你还没有安装懒猫微服开发者工具,请先到商店中搜索安装`
112
+ );
113
+ }
114
+ })
115
+ .catch(() => {
116
+ logger.warn(`你的懒猫微服开发者工具版本较低,请从商店中更新`);
117
+ });
118
+ }
119
+
100
120
  async setDefaultBox(boxname) {
101
121
  const boxes = await this.boxList();
102
122
  const box = boxes.find((b) => b.box_name === boxname);
package/lib/utils.js CHANGED
@@ -341,3 +341,29 @@ export async function tarContentDir(from, to, cwd = "./") {
341
341
  });
342
342
  });
343
343
  }
344
+
345
+ /**
346
+ * check buffer is png(magic header)
347
+ * refer: https://github.com/sindresorhus/is-png/blob/main/index.js
348
+ * @param {Buffer} buffer
349
+ * @returns {boolean}
350
+ */
351
+ function isPngWithBuffer(buffer) {
352
+ if (!buffer || buffer.length < 8) {
353
+ return false;
354
+ }
355
+ return buffer[0] === 0x89
356
+ && buffer[1] === 0x50
357
+ && buffer[2] === 0x4E
358
+ && buffer[3] === 0x47
359
+ && buffer[4] === 0x0D
360
+ && buffer[5] === 0x0A
361
+ && buffer[6] === 0x1A
362
+ && buffer[7] === 0x0A;
363
+ }
364
+
365
+ export function isPngWithFile(filepath) {
366
+ if (!isFileExist(filepath)) return false
367
+ const buf = fs.readFileSync(filepath)
368
+ return isPngWithBuffer(buf)
369
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazycatcloud/lzc-cli",
3
- "version": "1.2.16",
3
+ "version": "1.2.18",
4
4
  "description": "lazycat cloud developer kit",
5
5
  "files": [
6
6
  "template",