@lazycatcloud/lzc-cli 1.2.17 → 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
 
@@ -500,26 +500,26 @@ class DevShell {
500
500
  "all",
501
501
  debounce(() => {
502
502
  this.syncProject(appId);
503
- }),
504
- 1000
503
+ }, 1000)
505
504
  );
506
505
  }
506
+
507
507
  async shell() {
508
508
  try {
509
- // 当进入shell的时候,都同步一次
510
- await this.syncProject(this.appId);
511
- // 注册watch函数
509
+ // 监听文件
512
510
  await this.watchFile(this.appId);
513
-
514
- await this.connectShell();
511
+ await this.connectShell(async () => {
512
+ // 在连接成功的时候,同步一次文件
513
+ await this.syncProject(this.appId);
514
+ });
515
515
  } catch (e) {
516
516
  console.log(e);
517
517
  return Promise.reject(e);
518
518
  }
519
519
  }
520
520
 
521
- async connectShell() {
521
+ async connectShell(onconnect = null) {
522
522
  const bridge = new DebugBridge();
523
- await bridge.devshell(this.appId, this.isUserApp);
523
+ await bridge.devshell(this.appId, this.isUserApp, onconnect);
524
524
  }
525
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.17",
3
+ "version": "1.2.18",
4
4
  "description": "lazycat cloud developer kit",
5
5
  "files": [
6
6
  "template",