@lazycatcloud/lzc-cli 1.1.8 → 1.1.9
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/README.md +69 -11
- package/lib/api.js +71 -39
- package/lib/app/index.js +76 -21
- package/lib/app/lpk_build.js +95 -63
- package/lib/app/lpk_create.js +63 -41
- package/lib/app/lpk_create_generator.js +202 -0
- package/lib/app/lpk_devshell.js +393 -328
- package/lib/app/lpk_devshell_docker.js +211 -0
- package/lib/app/lpk_installer.js +63 -26
- package/lib/app/lpk_log.js +68 -0
- package/lib/app/lpk_status.js +18 -0
- package/lib/app/lpk_uninstall.js +19 -0
- package/lib/appstore/index.js +37 -0
- package/lib/appstore/login.js +96 -93
- package/lib/appstore/publish.js +62 -0
- package/lib/autologin.js +0 -78
- package/lib/box/api/clientapi.js +1322 -0
- package/lib/box/api/empty.js +35 -0
- package/lib/box/check_qemu.js +1 -0
- package/lib/box/index.js +41 -94
- package/lib/box/qemu_vm_mgr.js +208 -239
- package/lib/box/schemes/vm_box_system_debian.json +1 -1
- package/lib/docker-compose.js +1 -2
- package/lib/env.js +19 -101
- package/lib/key.js +1 -0
- package/lib/sdk.js +10 -25
- package/lib/utils.js +156 -132
- package/package.json +19 -10
- package/scripts/cli.js +14 -135
- package/template/_lpk/README.md +31 -0
- package/template/_lpk/exec.sh +19 -0
- package/template/_lpk/golang.manifest.yml.in +16 -0
- package/template/_lpk/lazycat.png +0 -0
- package/template/_lpk/lite.manifest.yml.in +19 -0
- package/template/_lpk/local_devshell/Dockerfile +16 -0
- package/template/_lpk/local_devshell/build.sh +5 -0
- package/template/_lpk/local_devshell/entrypoint.sh +87 -0
- package/template/{_lazycat/debug/shell → _lpk/local_devshell}/sshd_config +8 -8
- package/template/_lpk/manifest.yml.in +0 -1
- package/template/{vue/lzc-build.yml → _lpk/vue.lzc-build.yml.in} +9 -1
- package/template/golang/README.md +0 -2
- package/template/golang/_gitignore +2 -0
- package/template/golang/build.sh +6 -0
- package/template/golang/lazycat.png +0 -0
- package/template/golang/lzc-build.yml +9 -1
- package/template/golang/rego.go +15 -16
- package/template/golang/rego_test.go +13 -0
- package/template/ionic_vue3/lazycat.png +0 -0
- package/template/ionic_vue3/lzc-build.yml +9 -1
- package/template/lite/error_pages/502.html.tpl +13 -0
- package/template/lite/lazycat.png +0 -0
- package/template/lite/lzc-build.yml +60 -0
- package/cmds/app.js +0 -133
- package/cmds/config.js +0 -55
- package/cmds/create.js +0 -55
- package/cmds/dev.js +0 -130
- package/cmds/init.js +0 -125
- package/cmds/log.js +0 -103
- package/cmds/publish.js +0 -116
- package/lib/archiver.js +0 -105
- package/lib/box/hportal.js +0 -120
- package/lib/builder.js +0 -313
- package/lib/dev.js +0 -314
- package/lib/generator.js +0 -146
- package/template/_lazycat/_gitignore +0 -1
- package/template/_lazycat/app-config +0 -1
- package/template/_lazycat/debug/devforward/50x.html +0 -30
- package/template/_lazycat/debug/devforward/Dockerfile +0 -16
- package/template/_lazycat/debug/devforward/docker-compose.override.yml.in +0 -11
- package/template/_lazycat/debug/devforward/entrypoint.sh +0 -10
- package/template/_lazycat/debug/devforward/nginx.conf.template +0 -56
- package/template/_lazycat/debug/devforward/sshd_config +0 -116
- package/template/_lazycat/debug/shell/50x.html +0 -32
- package/template/_lazycat/debug/shell/Dockerfile +0 -18
- package/template/_lazycat/debug/shell/build.sh +0 -15
- package/template/_lazycat/debug/shell/docker-compose.override.yml.in +0 -13
- package/template/_lazycat/debug/shell/entrypoint.sh +0 -12
- package/template/_lazycat/docker-compose.yml.in +0 -15
- package/template/_lazycat/icon.svg +0 -1
- package/template/_lazycat/screenshot.png +0 -0
- package/template/_lpk/sync/Dockerfile +0 -16
- package/template/_lpk/sync/build.sh +0 -5
- package/template/_lpk/sync/entrypoint.sh +0 -8
- package/template/_lpk/sync/sshd_config +0 -117
- package/template/_lpk/sync.manifest.yml.in +0 -3
- package/template/release/golang/Dockerfile +0 -18
- package/template/release/golang/build.sh +0 -13
- package/template/release/ionic_vue3/Dockerfile +0 -10
- package/template/release/ionic_vue3/build.sh +0 -7
- package/template/release/ionic_vue3/docker-compose.yml.in +0 -3
- package/template/release/vue/Dockerfile +0 -10
- package/template/release/vue/build.sh +0 -10
- package/template/release/vue/docker-compose.yml.in +0 -3
- package/template/vue/README.md +0 -29
- package/template/vue/_dockerignore +0 -1
- package/template/vue/babel.config.js +0 -3
- package/template/vue/package.json +0 -43
- package/template/vue/public/favicon.ico +0 -0
- package/template/vue/public/index.html +0 -33
- package/template/vue/src/App.vue +0 -39
- package/template/vue/src/main.js +0 -8
- package/template/vue/src/todo.vue +0 -640
- package/template/vue/src/top-bar.vue +0 -100
- package/template/vue/src/webdav.vue +0 -183
- package/template/vue/vue.config.js +0 -21
package/lib/env.js
CHANGED
|
@@ -6,14 +6,13 @@ import {
|
|
|
6
6
|
GLOBAL_CONFIG_DIR,
|
|
7
7
|
APP_FOLDER,
|
|
8
8
|
} from "./utils.js";
|
|
9
|
-
import { debuglog } from "util";
|
|
10
9
|
import inquirer from "inquirer";
|
|
11
10
|
import chalk from "chalk";
|
|
12
|
-
import
|
|
13
|
-
|
|
11
|
+
import logger from "loglevel";
|
|
12
|
+
import fetch from "node-fetch";
|
|
14
13
|
|
|
15
14
|
const GLOBAL_CONFIG_NAME = "box-config.json";
|
|
16
|
-
const
|
|
15
|
+
const allPermitEnv = [
|
|
17
16
|
{
|
|
18
17
|
name: "DEFAULT_BOXNAME",
|
|
19
18
|
type: "input",
|
|
@@ -24,91 +23,6 @@ const permitGlobalEnv = [
|
|
|
24
23
|
},
|
|
25
24
|
];
|
|
26
25
|
|
|
27
|
-
const _permitLocalEnv = [
|
|
28
|
-
{
|
|
29
|
-
name: "APP_ID",
|
|
30
|
-
type: "input",
|
|
31
|
-
message: "应用名称",
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: "APP_DESCRIPTION",
|
|
35
|
-
type: "input",
|
|
36
|
-
message: "应用描述",
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: "BUILD_CONTEXT",
|
|
40
|
-
message: "",
|
|
41
|
-
type: "input",
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: "DEV_CONTEXT",
|
|
45
|
-
message: "",
|
|
46
|
-
type: "input",
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: "APP_VERSION",
|
|
50
|
-
message: "应用版本号",
|
|
51
|
-
type: "input",
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: "HTTP_SERVICE_PORT",
|
|
55
|
-
message: "服务暴露端口",
|
|
56
|
-
type: "input",
|
|
57
|
-
validate: (input) => {
|
|
58
|
-
if (!/^[^0]\d+$/.test(input)) {
|
|
59
|
-
return "端口必须为数字";
|
|
60
|
-
}
|
|
61
|
-
return true;
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
const permitLocalEnv = _permitLocalEnv.concat(permitGlobalEnv);
|
|
67
|
-
|
|
68
|
-
const allPermits = _permitLocalEnv.concat(permitGlobalEnv);
|
|
69
|
-
|
|
70
|
-
const controller = new AbortController();
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 检查URL是否能被ping通
|
|
74
|
-
* @param url 要检查的URL
|
|
75
|
-
* @param suffix 可选,与URL拼接的后缀
|
|
76
|
-
**/
|
|
77
|
-
async function checkURL(url, suffix = "") {
|
|
78
|
-
const timeout = setTimeout(() => {
|
|
79
|
-
controller.abort();
|
|
80
|
-
}, 5000);
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const resp = await request(url + suffix, { signal: controller.signal });
|
|
84
|
-
if (resp.status != 200) {
|
|
85
|
-
// 设备可以访问 (client 有正常连接), 但是sdk 这个服务无法访问
|
|
86
|
-
throw new Error(
|
|
87
|
-
chalk.red(
|
|
88
|
-
`无法连接 sdk 服务, 请确保 ${chalk.yellow(
|
|
89
|
-
new URL(url).origin
|
|
90
|
-
)} 可以访问或者在应用商店安装 sdk`
|
|
91
|
-
)
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
} catch (error) {
|
|
95
|
-
switch (error.name) {
|
|
96
|
-
case "FetchError":
|
|
97
|
-
console.log(chalk.red("盒子入口地址有误,请核对后再试"));
|
|
98
|
-
process.exit();
|
|
99
|
-
default:
|
|
100
|
-
throw error;
|
|
101
|
-
}
|
|
102
|
-
} finally {
|
|
103
|
-
clearTimeout(timeout);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const allPermitEnv = permitGlobalEnv
|
|
108
|
-
.concat(permitLocalEnv)
|
|
109
|
-
.map((e) => e.name)
|
|
110
|
-
.filter((value, index, self) => self.indexOf(value) == index);
|
|
111
|
-
|
|
112
26
|
class Env {
|
|
113
27
|
constructor(cwd) {
|
|
114
28
|
// 只加载
|
|
@@ -132,7 +46,7 @@ class Env {
|
|
|
132
46
|
this.localEnv = JSON.parse(fs.readFileSync(this.localEnvPath));
|
|
133
47
|
this.existance.local = true;
|
|
134
48
|
} catch (e) {
|
|
135
|
-
debug("local env fail to fetch
|
|
49
|
+
logger.debug("local env fail to fetch ", e.message);
|
|
136
50
|
this.localEnv = {};
|
|
137
51
|
}
|
|
138
52
|
|
|
@@ -167,7 +81,7 @@ class Env {
|
|
|
167
81
|
|
|
168
82
|
stringify() {
|
|
169
83
|
let content = "";
|
|
170
|
-
Object.keys(this.
|
|
84
|
+
Object.keys(this.allEnv).forEach((key) => {
|
|
171
85
|
content += `${key}="${this.allEnv[key]}"\n`;
|
|
172
86
|
});
|
|
173
87
|
return content.trimEnd();
|
|
@@ -221,7 +135,7 @@ class Env {
|
|
|
221
135
|
this.globalEnv = JSON.parse(fs.readFileSync(this.globalEnvPath));
|
|
222
136
|
this.existance.global = true;
|
|
223
137
|
} catch (e) {
|
|
224
|
-
debug("global env fail to fetch
|
|
138
|
+
logger.debug("global env fail to fetch ", e.message);
|
|
225
139
|
this.globalEnv = {};
|
|
226
140
|
}
|
|
227
141
|
}
|
|
@@ -249,10 +163,18 @@ class SDKEnv {
|
|
|
249
163
|
|
|
250
164
|
async ensure() {
|
|
251
165
|
try {
|
|
252
|
-
const resp = await
|
|
253
|
-
if (resp.status != 200)
|
|
166
|
+
const resp = await fetch(`${this.sdkUrl}/api/v1/ping`);
|
|
167
|
+
if (resp.status != 200)
|
|
168
|
+
throw new Error(chalk.red("debug.bridge服务未正常运行"));
|
|
254
169
|
} catch (e) {
|
|
255
|
-
|
|
170
|
+
logger.error(e);
|
|
171
|
+
console.log(
|
|
172
|
+
chalk.yellow(
|
|
173
|
+
`检测到SDK尚未安装,请将盒子"${env.get(
|
|
174
|
+
"DEFAULT_BOXNAME"
|
|
175
|
+
)}"开启开发者模式`
|
|
176
|
+
)
|
|
177
|
+
);
|
|
256
178
|
process.exit(-1);
|
|
257
179
|
}
|
|
258
180
|
}
|
|
@@ -266,11 +188,7 @@ class SDKEnv {
|
|
|
266
188
|
}
|
|
267
189
|
|
|
268
190
|
buildSdkUrl(boxname) {
|
|
269
|
-
return `
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
async checkConnection(host) {
|
|
273
|
-
return await checkURL(host, "/api/v1/ping");
|
|
191
|
+
return `http://sdk.${boxname}.heiyu.space`;
|
|
274
192
|
}
|
|
275
193
|
|
|
276
194
|
/**
|
|
@@ -279,7 +197,7 @@ class SDKEnv {
|
|
|
279
197
|
*/
|
|
280
198
|
async _promptAnswers() {
|
|
281
199
|
const answers = await inquirer.prompt(
|
|
282
|
-
|
|
200
|
+
allPermitEnv.filter((a) => a.name == "DEFAULT_BOXNAME")
|
|
283
201
|
);
|
|
284
202
|
return answers["DEFAULT_BOXNAME"];
|
|
285
203
|
}
|
package/lib/key.js
CHANGED
package/lib/sdk.js
CHANGED
|
@@ -7,6 +7,14 @@ import { Client } from "ssh2";
|
|
|
7
7
|
import Key from "./key.js";
|
|
8
8
|
import logger from "loglevel";
|
|
9
9
|
|
|
10
|
+
const sshDebug = function () {
|
|
11
|
+
if (process.env["SSH2_DEBUG"]) {
|
|
12
|
+
return logger.debug;
|
|
13
|
+
} else {
|
|
14
|
+
return function () {};
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
10
18
|
export async function connectOptions(host) {
|
|
11
19
|
const pairs = await new Key().getKeyPair();
|
|
12
20
|
return {
|
|
@@ -27,6 +35,8 @@ export async function connectOptions(host) {
|
|
|
27
35
|
agent: process.env.SSH_AUTH_SOCK,
|
|
28
36
|
},
|
|
29
37
|
],
|
|
38
|
+
keepaliveInterval: 5000, // 5s
|
|
39
|
+
debug: sshDebug(),
|
|
30
40
|
};
|
|
31
41
|
}
|
|
32
42
|
|
|
@@ -123,28 +133,3 @@ export class SSHClient {
|
|
|
123
133
|
);
|
|
124
134
|
}
|
|
125
135
|
}
|
|
126
|
-
|
|
127
|
-
export async function dockerPullLzcAppsImage(host) {
|
|
128
|
-
logger.warn("* 更新lzcapp镜像, 未来可能会移除此操作");
|
|
129
|
-
const opts = await connectOptions(host);
|
|
130
|
-
const client = new SSHClient(opts);
|
|
131
|
-
await client.connect();
|
|
132
|
-
try {
|
|
133
|
-
const stream = await client.exec(
|
|
134
|
-
"docker pull registry.lazycat.cloud/lzc/lzcapp:0.1"
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
return new Promise((resolve, reject) => {
|
|
138
|
-
stream.stdout.pipe(process.stdout);
|
|
139
|
-
stream.stderr.pipe(process.stdout);
|
|
140
|
-
stream.on("close", () => {
|
|
141
|
-
client.close();
|
|
142
|
-
resolve();
|
|
143
|
-
});
|
|
144
|
-
stream.on("error", (e) => reject(e));
|
|
145
|
-
});
|
|
146
|
-
} catch (e) {
|
|
147
|
-
client.close();
|
|
148
|
-
throw e;
|
|
149
|
-
}
|
|
150
|
-
}
|
package/lib/utils.js
CHANGED
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs";
|
|
3
|
-
import { mkdtemp } from "fs/promises";
|
|
4
3
|
import os from "os";
|
|
5
|
-
import chalk from "chalk";
|
|
6
|
-
import archiver from "archiver";
|
|
7
4
|
import glob from "fast-glob";
|
|
8
5
|
import yaml from "js-yaml";
|
|
9
6
|
import mergeWith from "lodash.mergewith";
|
|
10
|
-
import isArray from "lodash.isarray";
|
|
11
|
-
import { request } from "./autologin.js";
|
|
12
7
|
import { dirname } from "path";
|
|
13
8
|
import { fileURLToPath } from "url";
|
|
14
9
|
import ignore from "ignore";
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
10
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
11
|
+
import https from "node:https";
|
|
12
|
+
import http from "node:http";
|
|
13
|
+
import zlib from "node:zlib";
|
|
14
|
+
import process from "node:process";
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
import logger from "loglevel";
|
|
17
|
+
|
|
18
|
+
export const APP_FOLDER = ".lazycat";
|
|
19
|
+
export const APP_CONFIG_FILE = "app-config";
|
|
20
|
+
export const APP_SDK_HOSTNAME = "box";
|
|
24
21
|
export const GLOBAL_CONFIG_DIR = path.join(os.homedir(), "/.config/lazycat");
|
|
25
22
|
|
|
26
23
|
export const envsubstr = async (templateContents, args) => {
|
|
@@ -33,7 +30,7 @@ export const envsubstr = async (templateContents, args) => {
|
|
|
33
30
|
* @param filePath {string} 如果为文件路径 确保其文件夹存在; 如果为文件夹, 则确保该文件夹存在
|
|
34
31
|
*
|
|
35
32
|
*/
|
|
36
|
-
function ensureDir(filePath) {
|
|
33
|
+
export function ensureDir(filePath) {
|
|
37
34
|
let dirPath;
|
|
38
35
|
if (filePath.endsWith("/")) {
|
|
39
36
|
dirPath = filePath;
|
|
@@ -45,11 +42,11 @@ function ensureDir(filePath) {
|
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
function loadFromYaml(file) {
|
|
45
|
+
export function loadFromYaml(file) {
|
|
49
46
|
return yaml.load(fs.readFileSync(file, "utf8"));
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
function dumpToYaml(template, target) {
|
|
49
|
+
export function dumpToYaml(template, target) {
|
|
53
50
|
fs.writeFileSync(
|
|
54
51
|
target,
|
|
55
52
|
yaml.dump(template, {
|
|
@@ -60,42 +57,10 @@ function dumpToYaml(template, target) {
|
|
|
60
57
|
);
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
function
|
|
64
|
-
function doCheck(next = true) {
|
|
65
|
-
const files = ["docker-compose.yml", "docker-compose.yml.in"];
|
|
66
|
-
for (let f of files) {
|
|
67
|
-
const composeFile = path.join(appDir, f);
|
|
68
|
-
if (fs.existsSync(composeFile)) {
|
|
69
|
-
const doc = yaml.load(fs.readFileSync(composeFile, "utf8"));
|
|
70
|
-
if (doc[META_MARK]) {
|
|
71
|
-
return { appDir, isTemplate: f == "docker-compose.yml.in" };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (next) {
|
|
76
|
-
appDir = path.join(appDir, APP_FOLDER);
|
|
77
|
-
return doCheck(false);
|
|
78
|
-
}
|
|
79
|
-
return { appDir: false, isTemplate: false };
|
|
80
|
-
}
|
|
81
|
-
return doCheck();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// find any valid app path till root folder
|
|
85
|
-
function findAppRootPath(aPath) {
|
|
86
|
-
const sep = "/";
|
|
87
|
-
const folders = aPath.split(sep);
|
|
88
|
-
for (let i = folders.length; i >= 0; i--) {
|
|
89
|
-
if (folders[i] == APP_FOLDER) {
|
|
90
|
-
return folders.slice(0, i + 1).join(sep);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function toPair(object) {
|
|
60
|
+
export function toPair(object) {
|
|
97
61
|
return Object.keys(object).map((key) => {
|
|
98
62
|
let value = object[key] ? object[key].toString() : "";
|
|
63
|
+
key = key.replaceAll("-", "_");
|
|
99
64
|
return {
|
|
100
65
|
name: key,
|
|
101
66
|
value,
|
|
@@ -103,12 +68,7 @@ function toPair(object) {
|
|
|
103
68
|
});
|
|
104
69
|
}
|
|
105
70
|
|
|
106
|
-
function
|
|
107
|
-
const doc = yaml.load(fs.readFileSync(composeFile, "utf8"));
|
|
108
|
-
return doc[META_MARK];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function envTemplateFile(templateFile, env) {
|
|
71
|
+
export async function envTemplateFile(templateFile, env) {
|
|
112
72
|
const template = yaml.load(fs.readFileSync(templateFile, "utf8"));
|
|
113
73
|
const options = {
|
|
114
74
|
envs: toPair(env),
|
|
@@ -125,12 +85,12 @@ async function envTemplateFile(templateFile, env) {
|
|
|
125
85
|
);
|
|
126
86
|
}
|
|
127
87
|
|
|
128
|
-
async function createTemplateFile(templateFile, outputFile, env) {
|
|
88
|
+
export async function createTemplateFile(templateFile, outputFile, env) {
|
|
129
89
|
let output = await envTemplateFile(templateFile, env);
|
|
130
90
|
fs.writeFileSync(outputFile, output);
|
|
131
91
|
}
|
|
132
92
|
|
|
133
|
-
async function createTemplateFileCommon(templateFile, outputFile, env) {
|
|
93
|
+
export async function createTemplateFileCommon(templateFile, outputFile, env) {
|
|
134
94
|
const template = yaml.load(fs.readFileSync(templateFile, "utf8"));
|
|
135
95
|
const options = {
|
|
136
96
|
envs: toPair(env),
|
|
@@ -143,8 +103,17 @@ async function createTemplateFileCommon(templateFile, outputFile, env) {
|
|
|
143
103
|
fs.writeFileSync(outputFile, output);
|
|
144
104
|
}
|
|
145
105
|
|
|
106
|
+
export async function envsubstrDefault(templateContents, env) {
|
|
107
|
+
const options = {
|
|
108
|
+
envs: toPair(env),
|
|
109
|
+
syntax: "default",
|
|
110
|
+
protect: false,
|
|
111
|
+
};
|
|
112
|
+
return envsubstr(templateContents, { options });
|
|
113
|
+
}
|
|
114
|
+
|
|
146
115
|
// this will copy current app to a tmp dir
|
|
147
|
-
async function copyDotAppDir(from, to, opts = {}) {
|
|
116
|
+
export async function copyDotAppDir(from, to, opts = {}) {
|
|
148
117
|
const {
|
|
149
118
|
include = [],
|
|
150
119
|
ignore = ["app-config", "output", "box-config.json"],
|
|
@@ -181,7 +150,7 @@ async function copyDotAppDir(from, to, opts = {}) {
|
|
|
181
150
|
}
|
|
182
151
|
}
|
|
183
152
|
|
|
184
|
-
function mergeYamlInMemory(args) {
|
|
153
|
+
export function mergeYamlInMemory(args) {
|
|
185
154
|
if (args.length == 0) {
|
|
186
155
|
return {};
|
|
187
156
|
} else if (args.length == 1) {
|
|
@@ -189,7 +158,7 @@ function mergeYamlInMemory(args) {
|
|
|
189
158
|
}
|
|
190
159
|
return args.reduce((prev, curr) => {
|
|
191
160
|
let result = mergeWith(prev, curr, (objValue, srcValue) => {
|
|
192
|
-
if (isArray(objValue)) {
|
|
161
|
+
if (Array.isArray(objValue)) {
|
|
193
162
|
return objValue.concat(srcValue);
|
|
194
163
|
}
|
|
195
164
|
});
|
|
@@ -198,14 +167,14 @@ function mergeYamlInMemory(args) {
|
|
|
198
167
|
}
|
|
199
168
|
|
|
200
169
|
// override yaml, notice this will change target file
|
|
201
|
-
function mergeYaml(target, source) {
|
|
170
|
+
export function mergeYaml(target, source) {
|
|
202
171
|
const targetContent = yaml.load(fs.readFileSync(target, "utf8"));
|
|
203
172
|
const sourceContent = yaml.load(fs.readFileSync(source, "utf8"));
|
|
204
173
|
const merged = mergeWith(
|
|
205
174
|
targetContent,
|
|
206
175
|
sourceContent,
|
|
207
176
|
(objValue, srcValue) => {
|
|
208
|
-
if (isArray(objValue)) {
|
|
177
|
+
if (Array.isArray(objValue)) {
|
|
209
178
|
return objValue.concat(srcValue);
|
|
210
179
|
}
|
|
211
180
|
}
|
|
@@ -213,44 +182,16 @@ function mergeYaml(target, source) {
|
|
|
213
182
|
dumpToYaml(merged, target);
|
|
214
183
|
}
|
|
215
184
|
|
|
216
|
-
function
|
|
217
|
-
return new Promise(async (resolve, reject) => {
|
|
218
|
-
if (!fs.existsSync(appDir)) {
|
|
219
|
-
reject(new Error("folder does not exist"));
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const tempDir = await mkdtemp(path.join(os.tmpdir(), "hc-"));
|
|
224
|
-
const out = path.join(tempDir, "app." + format);
|
|
225
|
-
|
|
226
|
-
// console.log(chalk.green("start archive app ..."));
|
|
227
|
-
const output = fs.createWriteStream(out);
|
|
228
|
-
const archive = archiver(format);
|
|
229
|
-
|
|
230
|
-
archive.on("error", (e) => {
|
|
231
|
-
reject(e);
|
|
232
|
-
// console.log(e);
|
|
233
|
-
});
|
|
234
|
-
archive.on("end", () => {
|
|
235
|
-
resolve(output);
|
|
236
|
-
});
|
|
237
|
-
archive.pipe(output);
|
|
238
|
-
|
|
239
|
-
archive.directory(appDir, false);
|
|
240
|
-
archive.finalize();
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function contextDirname(url = import.meta.url) {
|
|
185
|
+
export function contextDirname(url = import.meta.url) {
|
|
245
186
|
return dirname(fileURLToPath(url));
|
|
246
187
|
}
|
|
247
188
|
|
|
248
|
-
async function importDefault(pkgPath) {
|
|
189
|
+
export async function importDefault(pkgPath) {
|
|
249
190
|
let mod = await import(pkgPath);
|
|
250
191
|
return mod.default;
|
|
251
192
|
}
|
|
252
193
|
|
|
253
|
-
class GitIgnore {
|
|
194
|
+
export class GitIgnore {
|
|
254
195
|
constructor(root) {
|
|
255
196
|
this.root = root;
|
|
256
197
|
this.ignores = [];
|
|
@@ -288,12 +229,12 @@ class GitIgnore {
|
|
|
288
229
|
}
|
|
289
230
|
}
|
|
290
231
|
|
|
291
|
-
function isDirSync(path) {
|
|
232
|
+
export function isDirSync(path) {
|
|
292
233
|
let stat = fs.statSync(path);
|
|
293
234
|
return stat.isDirectory();
|
|
294
235
|
}
|
|
295
236
|
|
|
296
|
-
function isDirExist(path) {
|
|
237
|
+
export function isDirExist(path) {
|
|
297
238
|
try {
|
|
298
239
|
return isDirSync(path);
|
|
299
240
|
} catch {
|
|
@@ -301,7 +242,7 @@ function isDirExist(path) {
|
|
|
301
242
|
}
|
|
302
243
|
}
|
|
303
244
|
|
|
304
|
-
function isFileExist(path) {
|
|
245
|
+
export function isFileExist(path) {
|
|
305
246
|
try {
|
|
306
247
|
fs.accessSync(
|
|
307
248
|
path,
|
|
@@ -313,56 +254,139 @@ function isFileExist(path) {
|
|
|
313
254
|
}
|
|
314
255
|
}
|
|
315
256
|
|
|
316
|
-
function urlHostname(url) {
|
|
257
|
+
export function urlHostname(url) {
|
|
317
258
|
let u = new URL(url);
|
|
318
259
|
return u.hostname;
|
|
319
260
|
}
|
|
320
261
|
|
|
321
262
|
// REVIEW: 用户输入的app-id 空格替换为减号并且转换为全小写。
|
|
322
263
|
// 这会影响默认的app-name,因为app-name的默认值依赖app-id,但是如果特定输入了app-name则正常
|
|
323
|
-
function parse2CorrectName(name) {
|
|
264
|
+
export function parse2CorrectName(name) {
|
|
324
265
|
return name.replaceAll(" ", "-").toLowerCase();
|
|
325
266
|
}
|
|
326
267
|
|
|
327
|
-
async function sleep(ms) {
|
|
268
|
+
export async function sleep(ms) {
|
|
328
269
|
return new Promise((resolve) => {
|
|
329
270
|
setTimeout(resolve, ms);
|
|
330
271
|
});
|
|
331
272
|
}
|
|
332
273
|
|
|
333
|
-
async function md5String(str) {
|
|
274
|
+
export async function md5String(str) {
|
|
334
275
|
const hash = createHash("md5");
|
|
335
276
|
hash.update(str);
|
|
336
277
|
return hash.digest("hex");
|
|
337
278
|
}
|
|
338
279
|
|
|
339
|
-
export {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
280
|
+
export async function md5File(filepath) {
|
|
281
|
+
const hash = createHash("md5");
|
|
282
|
+
const input = fs.createReadStream(filepath);
|
|
283
|
+
return new Promise((resolve) => {
|
|
284
|
+
input.on("readable", () => {
|
|
285
|
+
const data = input.read();
|
|
286
|
+
if (data) {
|
|
287
|
+
hash.update(data);
|
|
288
|
+
} else {
|
|
289
|
+
resolve(hash.digest("hex"));
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export class Downloader {
|
|
296
|
+
constructor() {}
|
|
297
|
+
|
|
298
|
+
showDownloadingProgress(received, total) {
|
|
299
|
+
let percentage = ((received * 100) / total).toFixed(2);
|
|
300
|
+
process.stdout.write("\r");
|
|
301
|
+
process.stdout.write(
|
|
302
|
+
percentage +
|
|
303
|
+
"% | " +
|
|
304
|
+
received +
|
|
305
|
+
" bytes downloaded out of " +
|
|
306
|
+
total +
|
|
307
|
+
" bytes."
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
download(url, savePath, enableGzip = false) {
|
|
312
|
+
let tmpPath = savePath + ".tmp";
|
|
313
|
+
const options = new URL(url);
|
|
314
|
+
let request = url.startsWith("https") ? https.request : http.request;
|
|
315
|
+
|
|
316
|
+
return new Promise((resolve, reject) => {
|
|
317
|
+
const req = request(options, (res) => {
|
|
318
|
+
if (res.statusCode != 200) {
|
|
319
|
+
reject(`下载 ${url} 失败`);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
let total = parseInt(res.headers["content-length"]);
|
|
324
|
+
let recive = 0;
|
|
325
|
+
res.on("data", (chunk) => {
|
|
326
|
+
recive += chunk.length;
|
|
327
|
+
this.showDownloadingProgress(recive, total);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
let outputFile = fs.createWriteStream(tmpPath, { flags: "w+" });
|
|
331
|
+
if (enableGzip) {
|
|
332
|
+
let decoder = zlib.createUnzip();
|
|
333
|
+
res.pipe(decoder).pipe(outputFile);
|
|
334
|
+
} else {
|
|
335
|
+
res.pipe(outputFile);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
outputFile.on("error", reject);
|
|
339
|
+
outputFile.on("finish", () => {
|
|
340
|
+
fs.renameSync(tmpPath, savePath);
|
|
341
|
+
process.stdout.write("\n");
|
|
342
|
+
resolve();
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
req.on("error", reject);
|
|
346
|
+
req.end();
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export function randomString(length = 4) {
|
|
352
|
+
return randomBytes(length).toString("hex");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export class FileLocker {
|
|
356
|
+
constructor(id) {
|
|
357
|
+
this.filename = undefined;
|
|
358
|
+
this.fd = undefined;
|
|
359
|
+
this.id = id;
|
|
360
|
+
}
|
|
361
|
+
lock() {
|
|
362
|
+
this.filename = path.resolve(os.tmpdir(), "lzc-cli-file-lock", this.id);
|
|
363
|
+
ensureDir(this.filename);
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
this.fd = fs.openSync(this.filename, "wx+");
|
|
367
|
+
} catch (e) {
|
|
368
|
+
if (e.code == "EEXIST" && !this.withOpen(this.filename)) {
|
|
369
|
+
logger.debug("filelock exist open with w+");
|
|
370
|
+
this.fd = fs.openSync(this.filename, "w+");
|
|
371
|
+
} else {
|
|
372
|
+
throw e;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
unlock() {
|
|
377
|
+
if (this.fd) {
|
|
378
|
+
fs.closeSync(this.fd);
|
|
379
|
+
fs.rmSync(this.filename);
|
|
380
|
+
this.fd = undefined;
|
|
381
|
+
this.filename = undefined;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
withOpen(filename) {
|
|
385
|
+
const p = spawnSync("lsof", ["-w", "-t", filename]);
|
|
386
|
+
if (p.status === 0) {
|
|
387
|
+
return true;
|
|
388
|
+
} else {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|