@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.
- package/lib/app/lpk_build.js +26 -16
- package/lib/app/lpk_debug_bridge.js +4 -1
- package/lib/app/lpk_devshell.js +9 -9
- package/lib/appstore/publish.js +53 -13
- package/lib/shellapi.js +20 -0
- package/lib/utils.js +26 -0
- package/package.json +1 -1
package/lib/app/lpk_build.js
CHANGED
|
@@ -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
|
-
"-
|
|
76
|
-
|
|
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
|
|
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
|
|
package/lib/app/lpk_devshell.js
CHANGED
|
@@ -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
|
-
//
|
|
510
|
-
await this.syncProject(this.appId);
|
|
511
|
-
// 注册watch函数
|
|
509
|
+
// 监听文件
|
|
512
510
|
await this.watchFile(this.appId);
|
|
513
|
-
|
|
514
|
-
|
|
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
|
}
|
package/lib/appstore/publish.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|