@lazycatcloud/lzc-cli 1.1.12 → 1.2.0
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/index.js +52 -33
- package/lib/app/lpk_build.js +1 -5
- package/lib/app/lpk_create.js +9 -3
- package/lib/app/lpk_debug_bridge.js +101 -0
- package/lib/app/lpk_devshell.js +59 -129
- package/lib/app/lpk_installer.js +43 -50
- package/lib/appstore/publish.js +18 -8
- package/lib/box/index.js +2 -86
- package/lib/env.js +18 -178
- package/lib/lzc_sdk.js +75 -17
- package/lib/shellapi.js +95 -0
- package/lib/shellapi.proto +301 -0
- package/lib/utils.js +7 -103
- package/package.json +3 -21
- package/scripts/cli.js +6 -6
- package/template/_lpk/busybox-1.35.0 +0 -0
- package/template/_lpk/exec.sh +1 -10
- package/template/_lpk/init_debug_bridge.sh +24 -0
- package/template/_lpk/vue.lzc-build.yml.in +0 -16
- package/template/golang/lzc-build.yml +0 -15
- package/template/ionic_vue3/lzc-build.yml +0 -16
- package/template/ionic_vue3/package-lock.json +8100 -0
- package/template/lite/lzc-build.yml +0 -28
- package/lib/api.js +0 -155
- package/lib/app/lpk_devshell_docker.js +0 -211
- package/lib/app/lpk_log.js +0 -68
- package/lib/app/lpk_status.js +0 -18
- package/lib/app/lpk_uninstall.js +0 -19
- package/lib/box/api/clientapi.js +0 -1322
- package/lib/box/api/empty.js +0 -35
- package/lib/box/check_qemu.js +0 -51
- package/lib/box/qemu_vm_mgr.js +0 -608
- package/lib/box/schemes/vm_box_system_debian.json +0 -47
- package/lib/docker/promise.js +0 -91
- package/lib/docker-compose.js +0 -50
- package/lib/git/git-commit.sh +0 -7
- package/lib/git/git-reset.sh +0 -15
- package/lib/key.js +0 -102
- package/lib/sdk.js +0 -135
package/lib/app/index.js
CHANGED
|
@@ -3,9 +3,10 @@ import lpkCreate from "./lpk_create.js";
|
|
|
3
3
|
import { LpkBuild } from "./lpk_build.js";
|
|
4
4
|
import { AppDevShell } from "./lpk_devshell.js";
|
|
5
5
|
import { LpkInstaller } from "./lpk_installer.js";
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import logger from "loglevel";
|
|
7
|
+
import { sleep } from "../utils.js";
|
|
8
|
+
import { DebugBridge } from "./lpk_debug_bridge.js";
|
|
9
|
+
import shellApi from "../shellapi.js";
|
|
9
10
|
|
|
10
11
|
export function lpkProjectCommand(program) {
|
|
11
12
|
let subCommands = [
|
|
@@ -13,6 +14,8 @@ export function lpkProjectCommand(program) {
|
|
|
13
14
|
command: "create <name>",
|
|
14
15
|
desc: "创建懒猫云应用",
|
|
15
16
|
handler: async ({ name }) => {
|
|
17
|
+
await shellApi.init();
|
|
18
|
+
|
|
16
19
|
await lpkCreate(name);
|
|
17
20
|
},
|
|
18
21
|
},
|
|
@@ -32,6 +35,8 @@ export function lpkProjectCommand(program) {
|
|
|
32
35
|
});
|
|
33
36
|
},
|
|
34
37
|
handler: async ({ context, output, file }) => {
|
|
38
|
+
await shellApi.init();
|
|
39
|
+
|
|
35
40
|
const lpk = await new LpkBuild(context, file).init();
|
|
36
41
|
// 正常的打包逻辑不需要 devshell
|
|
37
42
|
lpk.onBeforeBuildPackage(async (options) => {
|
|
@@ -49,18 +54,6 @@ export function lpkProjectCommand(program) {
|
|
|
49
54
|
command: "devshell [context]",
|
|
50
55
|
desc: "进入盒子的开发环境",
|
|
51
56
|
builder: (args) => {
|
|
52
|
-
args.option("r", {
|
|
53
|
-
alias: "rsync",
|
|
54
|
-
deprecate: true,
|
|
55
|
-
type: "boolean",
|
|
56
|
-
default: true,
|
|
57
|
-
});
|
|
58
|
-
args.option("s", {
|
|
59
|
-
alias: "shell",
|
|
60
|
-
describe: "指定你最喜欢的shell",
|
|
61
|
-
type: "string",
|
|
62
|
-
default: "bash",
|
|
63
|
-
});
|
|
64
57
|
args.option("f", {
|
|
65
58
|
alias: "force",
|
|
66
59
|
describe: "强制重新构建",
|
|
@@ -72,12 +65,32 @@ export function lpkProjectCommand(program) {
|
|
|
72
65
|
type: "string",
|
|
73
66
|
default: "lzc-build.yml",
|
|
74
67
|
});
|
|
68
|
+
args.option("contentdir", {
|
|
69
|
+
describe: "同时打包 lzc-build.yml 中指定的 contentdir 目录",
|
|
70
|
+
type: "boolean",
|
|
71
|
+
});
|
|
75
72
|
},
|
|
76
|
-
handler: async ({ context,
|
|
77
|
-
|
|
73
|
+
handler: async ({ context, force, config, contentdir }) => {
|
|
74
|
+
await shellApi.init();
|
|
75
|
+
|
|
76
|
+
const cwd = context ? path.resolve(context) : process.cwd();
|
|
77
|
+
const lpkBuild = await new LpkBuild(cwd, config).init();
|
|
78
|
+
lpkBuild.onBeforeBuildPackage(async (options) => {
|
|
79
|
+
// devshell 正常情况下,不需要执行 buildscript 和 contentdir 字段
|
|
80
|
+
logger.debug("devshell delete 'buildscript' field");
|
|
81
|
+
delete options["buildscript"];
|
|
82
|
+
|
|
83
|
+
if (!contentdir) {
|
|
84
|
+
logger.debug("devshell delete 'contentdir' field");
|
|
85
|
+
delete options["contentdir"];
|
|
86
|
+
}
|
|
87
|
+
return options;
|
|
88
|
+
});
|
|
89
|
+
const app = new AppDevShell(cwd, lpkBuild, force);
|
|
78
90
|
await app.init();
|
|
79
91
|
await app.build();
|
|
80
|
-
await
|
|
92
|
+
await sleep(2000);
|
|
93
|
+
await app.rsyncShell();
|
|
81
94
|
},
|
|
82
95
|
},
|
|
83
96
|
];
|
|
@@ -93,30 +106,36 @@ export function lpkProjectCommand(program) {
|
|
|
93
106
|
export function lpkAppCommand(program) {
|
|
94
107
|
let subCommands = [
|
|
95
108
|
{
|
|
96
|
-
command: "install
|
|
97
|
-
desc: "部署应用至设备,
|
|
98
|
-
handler: async ({
|
|
109
|
+
command: "install [pkgPath]",
|
|
110
|
+
desc: "部署应用至设备, pkgPath 可以为路径,或者https://,http://请求地址, 如果不填写,将默认为当前目录下的lpk",
|
|
111
|
+
handler: async ({ pkgPath }) => {
|
|
112
|
+
await shellApi.init();
|
|
113
|
+
|
|
114
|
+
pkgPath = pkgPath ?? process.cwd();
|
|
99
115
|
const installer = new LpkInstaller();
|
|
100
116
|
await installer.init();
|
|
101
|
-
await installer.install(
|
|
117
|
+
await installer.install(pkgPath);
|
|
102
118
|
},
|
|
103
119
|
},
|
|
104
120
|
{
|
|
105
121
|
command: "uninstall <pkgId>",
|
|
106
122
|
desc: "从设备中卸载某一个应用",
|
|
107
123
|
handler: async ({ pkgId }) => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
124
|
+
await shellApi.init();
|
|
125
|
+
|
|
126
|
+
const bridge = new DebugBridge();
|
|
127
|
+
await bridge.uninstall(pkgId);
|
|
111
128
|
},
|
|
112
129
|
},
|
|
113
130
|
{
|
|
114
131
|
command: "status <pkgId>",
|
|
115
132
|
desc: "获取某一个应用的状态",
|
|
116
133
|
handler: async ({ pkgId }) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
await shellApi.init();
|
|
135
|
+
|
|
136
|
+
const bridge = new DebugBridge();
|
|
137
|
+
const status = await bridge.status(pkgId);
|
|
138
|
+
console.log(status);
|
|
120
139
|
},
|
|
121
140
|
},
|
|
122
141
|
{
|
|
@@ -130,17 +149,17 @@ export function lpkAppCommand(program) {
|
|
|
130
149
|
default: false,
|
|
131
150
|
});
|
|
132
151
|
},
|
|
133
|
-
handler: async (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
152
|
+
handler: async () => {
|
|
153
|
+
await shellApi.init();
|
|
154
|
+
|
|
155
|
+
throw "还没有实现";
|
|
137
156
|
},
|
|
138
157
|
},
|
|
139
158
|
];
|
|
140
159
|
program.command({
|
|
141
160
|
command: "app",
|
|
142
161
|
desc: "应用管理",
|
|
143
|
-
builder: (args) => {
|
|
162
|
+
builder: async (args) => {
|
|
144
163
|
args.command(subCommands);
|
|
145
164
|
},
|
|
146
165
|
});
|
package/lib/app/lpk_build.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
isFileExist,
|
|
9
9
|
dumpToYaml,
|
|
10
10
|
envTemplateFile,
|
|
11
|
+
isValidPackageName,
|
|
11
12
|
} from "../utils.js";
|
|
12
13
|
import { spawnSync } from "child_process";
|
|
13
14
|
import { LpkManifest } from "./lpk_create.js";
|
|
@@ -130,11 +131,6 @@ function convenientEnv() {
|
|
|
130
131
|
);
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
function isValidPackageName(packageName) {
|
|
134
|
-
const regex = /^([a-zA-Z_][a-zA-Z0-9_]*\.)*[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
135
|
-
return regex.test(packageName);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
134
|
export class LpkBuild {
|
|
139
135
|
constructor(cwd, lzcBuild = "lzc-build.yml") {
|
|
140
136
|
this.pwd = cwd ?? process.cwd();
|
package/lib/app/lpk_create.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
ensureDir,
|
|
8
8
|
dumpToYaml,
|
|
9
9
|
isFileExist,
|
|
10
|
+
isValidPackageName,
|
|
10
11
|
} from "../utils.js";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import { TemplateConfig } from "./lpk_create_generator.js";
|
|
@@ -74,9 +75,14 @@ export class LpkManifest {
|
|
|
74
75
|
{
|
|
75
76
|
type: "input",
|
|
76
77
|
name: "package",
|
|
77
|
-
message: "请输入应用ID",
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
message: "请输入应用ID, 如 cloud.lazycat.app.video",
|
|
79
|
+
validate: (input) => {
|
|
80
|
+
if (isValidPackageName(input)) {
|
|
81
|
+
return true;
|
|
82
|
+
} else {
|
|
83
|
+
return "应用ID错误,请输入正确的格式, 如 cloud.lazycat.app.video";
|
|
84
|
+
}
|
|
85
|
+
},
|
|
80
86
|
},
|
|
81
87
|
{
|
|
82
88
|
type: "input",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import shellApi from "../shellapi.js";
|
|
4
|
+
import inquirer from "inquirer";
|
|
5
|
+
|
|
6
|
+
export class DebugBridge {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.uid = shellApi.uid;
|
|
9
|
+
this.boxname = shellApi.boxname;
|
|
10
|
+
this.sshCmd = `ssh -p 22222 box@dev.${this.boxname}.heiyu.space`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
common(cmd, args) {
|
|
14
|
+
const ssh = spawnSync(cmd, args, {
|
|
15
|
+
shell: true,
|
|
16
|
+
encoding: "utf-8",
|
|
17
|
+
stdio: "pipe",
|
|
18
|
+
});
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
ssh.status == 0 ? resolve(ssh.stdout) : reject(ssh.stderr);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async install(lpkPath) {
|
|
25
|
+
if (!(await this.canPublicKey())) {
|
|
26
|
+
await this.sshCopyId();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const stream = fs.createReadStream(lpkPath);
|
|
30
|
+
const ssh = spawn(this.sshCmd, [`install --uid ${this.uid}`], {
|
|
31
|
+
shell: true,
|
|
32
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
33
|
+
});
|
|
34
|
+
stream.pipe(ssh.stdin);
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
ssh.on("close", (code) => {
|
|
37
|
+
code == 0 ? resolve() : reject("install 失败");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async canPublicKey() {
|
|
43
|
+
try {
|
|
44
|
+
await this.common(this.sshCmd, [`-o PasswordAuthentication=no`]);
|
|
45
|
+
return true;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async sshCopyId() {
|
|
52
|
+
const questions = [
|
|
53
|
+
{
|
|
54
|
+
name: "upload",
|
|
55
|
+
type: "input",
|
|
56
|
+
default: "y",
|
|
57
|
+
message:
|
|
58
|
+
"检测到你目前使用的密码帐号登录,是否使用 ssh-copy-id 上传密钥 (y/n): ",
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
const answers = await inquirer.prompt(questions);
|
|
62
|
+
if (answers.upload.toLowerCase() == "y") {
|
|
63
|
+
return this.common(`ssh-copy-id`, [
|
|
64
|
+
`-f -p 22222 box@dev.${this.boxname}.heiyu.space`,
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async status(appId) {
|
|
70
|
+
return this.common(this.sshCmd, [`status --uid ${this.uid}`, appId]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async isDevshell(appId) {
|
|
74
|
+
const stdout = await this.common(this.sshCmd, [`isDevshell`, appId]);
|
|
75
|
+
return stdout == "true";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async resume(appId) {
|
|
79
|
+
return this.common(this.sshCmd, [`resume --uid ${this.uid}`, appId]);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async uninstall(appId) {
|
|
83
|
+
return this.common(this.sshCmd, [`uninstall --uid ${this.uid}`, appId]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async devshell(appId) {
|
|
87
|
+
const stream = spawn(
|
|
88
|
+
this.sshCmd,
|
|
89
|
+
["-t", "devshell", appId, "/lzcapp/pkg/content/devshell/exec.sh"],
|
|
90
|
+
{
|
|
91
|
+
shell: true,
|
|
92
|
+
stdio: "inherit",
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
stream.on("close", (code) => {
|
|
97
|
+
code == 0 ? resolve() : reject();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
package/lib/app/lpk_devshell.js
CHANGED
|
@@ -3,7 +3,6 @@ import path from "node:path";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import logger from "loglevel";
|
|
5
5
|
import { execSync } from "node:child_process";
|
|
6
|
-
import { LpkBuild } from "./lpk_build.js";
|
|
7
6
|
import { LpkInstaller } from "./lpk_installer.js";
|
|
8
7
|
import debounce from "lodash.debounce";
|
|
9
8
|
import {
|
|
@@ -12,32 +11,18 @@ import {
|
|
|
12
11
|
ensureDir,
|
|
13
12
|
isFileExist,
|
|
14
13
|
GitIgnore,
|
|
15
|
-
sleep,
|
|
16
14
|
md5String,
|
|
17
15
|
md5File,
|
|
18
|
-
createTemplateFileCommon,
|
|
19
16
|
loadFromYaml,
|
|
20
17
|
FileLocker,
|
|
21
18
|
} from "../utils.js";
|
|
22
|
-
import Key from "../key.js";
|
|
23
19
|
import os from "node:os";
|
|
24
|
-
import { sdkEnv } from "../env.js";
|
|
25
20
|
import commandExists from "command-exists";
|
|
26
21
|
import chokidar from "chokidar";
|
|
27
22
|
import _ from "lodash";
|
|
28
|
-
import BoxAPI from "../api.js";
|
|
29
|
-
import { SdkDocker } from "./lpk_devshell_docker.js";
|
|
30
23
|
import { getUidByManifest } from "../lzc_sdk.js";
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const sdkUrl = sdkEnv.sdkUrl;
|
|
34
|
-
let url = new URL(sdkUrl);
|
|
35
|
-
return `box@${url.hostname}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function sdkSSHPort() {
|
|
39
|
-
return 2222;
|
|
40
|
-
}
|
|
24
|
+
import { DebugBridge } from "./lpk_debug_bridge.js";
|
|
25
|
+
import shellApi from "../shellapi.js";
|
|
41
26
|
|
|
42
27
|
// 判断是否需要重新构建
|
|
43
28
|
// - 先判断 lzc-build.yml 是否发生改变
|
|
@@ -46,11 +31,9 @@ function sdkSSHPort() {
|
|
|
46
31
|
// - 根据 backend api 判断一个 appid 是否属于 running
|
|
47
32
|
// - 根据在 backend api 中判断同步的目录下是否存在文件 判断当前运行的 app 是否已经有一个挂载的实例,避免重复挂载
|
|
48
33
|
class AppDevShellMonitor {
|
|
49
|
-
constructor(cwd, pkgId
|
|
34
|
+
constructor(cwd, pkgId) {
|
|
50
35
|
this.pwd = cwd ? path.resolve(cwd) : process.cwd();
|
|
51
|
-
|
|
52
36
|
this.pkgId = pkgId;
|
|
53
|
-
this.boxapi = new BoxAPI(pkgId, sdkEnv.sdkUrl, uid);
|
|
54
37
|
|
|
55
38
|
this.optionsFilePath = path.join(this.pwd, "lzc-build.yml");
|
|
56
39
|
this.options = loadFromYaml(this.optionsFilePath);
|
|
@@ -66,6 +49,7 @@ class AppDevShellMonitor {
|
|
|
66
49
|
this.cacheFilePath = undefined;
|
|
67
50
|
this.oldHash = undefined;
|
|
68
51
|
this.newHash = undefined;
|
|
52
|
+
this.bridge = new DebugBridge();
|
|
69
53
|
}
|
|
70
54
|
|
|
71
55
|
async init() {
|
|
@@ -85,7 +69,9 @@ class AppDevShellMonitor {
|
|
|
85
69
|
|
|
86
70
|
async shouldBuild() {
|
|
87
71
|
return (
|
|
88
|
-
this.change() ||
|
|
72
|
+
this.change() ||
|
|
73
|
+
(await this.bridge.status(this.pkgId)) === "NotInstalled" ||
|
|
74
|
+
!(await this.bridge.isDevshell(this.pkgId))
|
|
89
75
|
);
|
|
90
76
|
}
|
|
91
77
|
|
|
@@ -113,35 +99,17 @@ class AppDevShellMonitor {
|
|
|
113
99
|
fs.writeFileSync(this.cacheFilePath, JSON.stringify(this.newHash));
|
|
114
100
|
}
|
|
115
101
|
}
|
|
116
|
-
|
|
117
|
-
async notRunning() {
|
|
118
|
-
try {
|
|
119
|
-
const { status } = await this.boxapi.status();
|
|
120
|
-
return status !== "running";
|
|
121
|
-
} catch {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async noDevshell() {
|
|
127
|
-
return !(await this.boxapi.isDevshell());
|
|
128
|
-
}
|
|
129
102
|
}
|
|
130
103
|
|
|
131
104
|
export class AppDevShell {
|
|
132
|
-
constructor(cwd,
|
|
133
|
-
this.cwd = cwd
|
|
134
|
-
this.lpkBuild =
|
|
105
|
+
constructor(cwd, lpkBuild, forceBuild = false) {
|
|
106
|
+
this.cwd = cwd;
|
|
107
|
+
this.lpkBuild = lpkBuild;
|
|
135
108
|
this.monitor = undefined;
|
|
136
109
|
this.forceBuild = forceBuild;
|
|
137
|
-
this.lpkBuildConfig = config;
|
|
138
110
|
}
|
|
139
111
|
|
|
140
112
|
async init() {
|
|
141
|
-
if (!this.lpkBuild) {
|
|
142
|
-
this.lpkBuild = new LpkBuild(this.cwd, this.lpkBuildConfig);
|
|
143
|
-
await this.lpkBuild.init();
|
|
144
|
-
}
|
|
145
113
|
const manifest = await this.lpkBuild.getManifest();
|
|
146
114
|
const uid = await getUidByManifest(manifest);
|
|
147
115
|
this.monitor = await new AppDevShellMonitor(
|
|
@@ -160,18 +128,7 @@ export class AppDevShell {
|
|
|
160
128
|
}
|
|
161
129
|
|
|
162
130
|
async devshellBuild() {
|
|
163
|
-
// 确保 sdk key ,并上传到 sdk
|
|
164
|
-
const k = new Key();
|
|
165
|
-
await k.ensure(sdkEnv.sdkUrl);
|
|
166
|
-
const pairs = await k.getKeyPair();
|
|
167
|
-
|
|
168
|
-
// devshell 不需要执行 buildscript 和 contentdir 也不需要
|
|
169
131
|
this.lpkBuild.onBeforeBuildPackage(async (options) => {
|
|
170
|
-
logger.debug("devshell delete 'buildscript' field");
|
|
171
|
-
logger.debug("devshell delete 'contentdir' field");
|
|
172
|
-
delete options["buildscript"];
|
|
173
|
-
delete options["contentdir"];
|
|
174
|
-
|
|
175
132
|
const devshell = options["devshell"];
|
|
176
133
|
if (!devshell) {
|
|
177
134
|
throw "devshell 模式下,devshell 字段必须要指定";
|
|
@@ -185,6 +142,30 @@ export class AppDevShell {
|
|
|
185
142
|
return options;
|
|
186
143
|
});
|
|
187
144
|
|
|
145
|
+
// 复制 busybox 到 devshell 中去
|
|
146
|
+
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
147
|
+
const busyboxPath = path.join(
|
|
148
|
+
contextDirname(import.meta.url),
|
|
149
|
+
"../../template/_lpk/busybox-1.35.0"
|
|
150
|
+
);
|
|
151
|
+
let dest = path.join(contentdir, "devshell", "busybox");
|
|
152
|
+
ensureDir(dest);
|
|
153
|
+
fs.copyFileSync(busyboxPath, dest);
|
|
154
|
+
fs.chmodSync(dest, 0o775);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// 复制 init_debug_bridge.sh 到 devshell 中去
|
|
158
|
+
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
159
|
+
const initPath = path.join(
|
|
160
|
+
contextDirname(import.meta.url),
|
|
161
|
+
"../../template/_lpk/init_debug_bridge.sh"
|
|
162
|
+
);
|
|
163
|
+
let dest = path.join(contentdir, "devshell", "init_debug_bridge.sh");
|
|
164
|
+
ensureDir(dest);
|
|
165
|
+
fs.copyFileSync(initPath, dest);
|
|
166
|
+
fs.chmodSync(dest, 0o775);
|
|
167
|
+
});
|
|
168
|
+
|
|
188
169
|
// 复制 exec.sh 到 devshell 中去
|
|
189
170
|
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
190
171
|
const execScriptPath = path.join(
|
|
@@ -197,15 +178,6 @@ export class AppDevShell {
|
|
|
197
178
|
fs.chmodSync(dest, 0o775);
|
|
198
179
|
});
|
|
199
180
|
|
|
200
|
-
// 用 sdk ssh key 并复制到 contentdir 目录
|
|
201
|
-
// docker 中 sshd 的配置中已改成 /lzcapp/pkg/content/devshell/authorized_keys
|
|
202
|
-
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
203
|
-
let publicKey = pairs["public"];
|
|
204
|
-
let dest = path.join(contentdir, "devshell", "authorized_keys");
|
|
205
|
-
ensureDir(dest);
|
|
206
|
-
fs.copyFileSync(publicKey, dest);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
181
|
// 复制 setupscript 脚本
|
|
210
182
|
this.lpkBuild.onBeforeTarContent(async (contentdir, options) => {
|
|
211
183
|
const devshell = options["devshell"];
|
|
@@ -238,6 +210,13 @@ export class AppDevShell {
|
|
|
238
210
|
const routes = devshell["routes"];
|
|
239
211
|
logger.debug("options devshell delete 'routes' field");
|
|
240
212
|
delete options["devshell"]["routes"];
|
|
213
|
+
|
|
214
|
+
// 添加 devshell 必要路由
|
|
215
|
+
routes.push(
|
|
216
|
+
"/__debug.bridge=exec://22222,/lzcapp/pkg/content/devshell/init_debug_bridge.sh"
|
|
217
|
+
);
|
|
218
|
+
routes.push("/__isdevshell=file:///lzcapp/pkg/devshell");
|
|
219
|
+
|
|
241
220
|
// 如果 devshell 中的 router 和 manifest 中的 prefix 出现冲突
|
|
242
221
|
// 优先使用 devshell 中的。
|
|
243
222
|
routes.forEach((r) => {
|
|
@@ -285,32 +264,7 @@ export class AppDevShell {
|
|
|
285
264
|
return manifest;
|
|
286
265
|
}
|
|
287
266
|
|
|
288
|
-
|
|
289
|
-
logger.debug("开始创建 Dockerfile 文件");
|
|
290
|
-
const tag = `${await md5String(depsStr)}:latest`;
|
|
291
|
-
|
|
292
|
-
const tempDir = fs.mkdtempSync(".lzc-cli-build-dependencies");
|
|
293
|
-
try {
|
|
294
|
-
const dockerfilePath = path.join(
|
|
295
|
-
contextDirname(import.meta.url),
|
|
296
|
-
"../../template/_lpk/Dockerfile.in"
|
|
297
|
-
);
|
|
298
|
-
await createTemplateFileCommon(
|
|
299
|
-
dockerfilePath,
|
|
300
|
-
path.join(tempDir, "Dockerfile"),
|
|
301
|
-
{ dependencies: depsStr }
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
logger.debug(`开始在盒子中构建 ${tag} 镜像 from ${tempDir}`);
|
|
305
|
-
const sdk = new SdkDocker();
|
|
306
|
-
await sdk.buildImage(tag, tempDir, tempDir);
|
|
307
|
-
} finally {
|
|
308
|
-
fs.rmSync(tempDir, { recursive: true });
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
delete manifest["application"]["devshell"];
|
|
312
|
-
manifest["application"]["image"] = tag;
|
|
313
|
-
return manifest;
|
|
267
|
+
throw "lzc-cli 不支持在盒子中构建 docker 镜像,请从其他地方构建,然后指定image字段";
|
|
314
268
|
});
|
|
315
269
|
|
|
316
270
|
// 如果 services 中有 devshell 的字段,需要检测是否需要提前构建
|
|
@@ -325,16 +279,7 @@ export class AppDevShell {
|
|
|
325
279
|
return manifest;
|
|
326
280
|
}
|
|
327
281
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
logger.debug(`开始在盒子中构建 ${tag} 镜像`);
|
|
331
|
-
|
|
332
|
-
const sdk = new SdkDocker();
|
|
333
|
-
await sdk.buildImage(tag, config["build"], process.cwd());
|
|
334
|
-
|
|
335
|
-
delete manifest["application"]["devshell"];
|
|
336
|
-
manifest["application"]["image"] = tag;
|
|
337
|
-
return manifest;
|
|
282
|
+
throw "lzc-cli 不支持在盒子中构建 docker 镜像,请从其他地方构建,然后指定image字段";
|
|
338
283
|
});
|
|
339
284
|
|
|
340
285
|
// 如果 devshell 中指定了 image 字段将使用 image 字段
|
|
@@ -371,14 +316,10 @@ export class AppDevShell {
|
|
|
371
316
|
// 在构建生成 lpk 包后,调用 deploy 进行部署
|
|
372
317
|
let installer = new LpkInstaller();
|
|
373
318
|
await installer.init();
|
|
374
|
-
await installer.deploy(this.lpkBuild);
|
|
375
|
-
|
|
376
|
-
await sleep(2000);
|
|
319
|
+
await installer.deploy(this.lpkBuild, true);
|
|
377
320
|
}
|
|
378
321
|
|
|
379
|
-
async rsyncShell(
|
|
380
|
-
const k = new Key();
|
|
381
|
-
const pairs = await k.getKeyPair();
|
|
322
|
+
async rsyncShell() {
|
|
382
323
|
const manifest = await this.lpkBuild.getManifest();
|
|
383
324
|
const pkgId = manifest["package"];
|
|
384
325
|
|
|
@@ -397,7 +338,7 @@ export class AppDevShell {
|
|
|
397
338
|
}
|
|
398
339
|
|
|
399
340
|
const uid = await getUidByManifest(manifest);
|
|
400
|
-
const devshell = new DevShell(
|
|
341
|
+
const devshell = new DevShell(pkgId, uid);
|
|
401
342
|
if (isSync) {
|
|
402
343
|
await devshell.shell();
|
|
403
344
|
} else {
|
|
@@ -411,29 +352,22 @@ export class AppDevShell {
|
|
|
411
352
|
}
|
|
412
353
|
|
|
413
354
|
class DevShell {
|
|
414
|
-
constructor(
|
|
415
|
-
logger.debug("keyFile", keyFile);
|
|
416
|
-
logger.debug("appid", appId);
|
|
417
|
-
|
|
418
|
-
this.keyFile = keyFile;
|
|
355
|
+
constructor(appId, uid = "") {
|
|
419
356
|
this.appId = appId;
|
|
420
357
|
this.uid = uid;
|
|
421
|
-
this.runShell = runShell;
|
|
422
358
|
}
|
|
423
359
|
|
|
424
|
-
async syncProject(
|
|
425
|
-
const host = sdkSSHHost();
|
|
426
|
-
const port = sdkSSHPort();
|
|
360
|
+
async syncProject(appId) {
|
|
427
361
|
// prettier-ignore
|
|
428
362
|
let rsh = [
|
|
429
363
|
"ssh",
|
|
430
|
-
"-
|
|
364
|
+
"-J", `box@dev.${shellApi.boxname}.heiyu.space:22222`,
|
|
365
|
+
"-p", `22222`,
|
|
431
366
|
"-o", `"StrictHostKeyChecking=no"`,
|
|
432
367
|
"-o", `"UserKnownHostsFile=/dev/null"`,
|
|
433
368
|
"-o", `"ConnectionAttempts=3"`,
|
|
434
369
|
"-o", `"ConnectTimeout=30"`,
|
|
435
370
|
"-o", `"LogLevel=ERROR"`,
|
|
436
|
-
"-i", keyFile,
|
|
437
371
|
].join(" ");
|
|
438
372
|
// 检查rsync工具是否存在:提示用户
|
|
439
373
|
const rsyncExisted = commandExists.sync("rsync");
|
|
@@ -442,13 +376,11 @@ class DevShell {
|
|
|
442
376
|
process.exit(1);
|
|
443
377
|
}
|
|
444
378
|
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
let storePath = `/run/lzc_boot/data/cache/${appId}${userId}/devshell`;
|
|
448
|
-
// FIXME: 下方执行命令不确定是否有兼容性问题
|
|
379
|
+
const rsyncDebug = process.env.RSYNCDEBUG ? "-P" : "";
|
|
380
|
+
const dest = `root@app.${appId}.lzcapp:/lzcapp/cache/devshell`;
|
|
449
381
|
try {
|
|
450
382
|
execSync(
|
|
451
|
-
`rsync ${rsyncDebug} --rsh='${rsh}' --recursive --relative --perms --update
|
|
383
|
+
`rsync ${rsyncDebug} --rsh='${rsh}' --recursive --relative --perms --update --filter=':- .gitignore' --ignore-errors --usermap=:nobody --groupmap=*:nobody . ${dest}`,
|
|
452
384
|
{ stdio: ["ignore", "inherit", "inherit"] }
|
|
453
385
|
);
|
|
454
386
|
} catch (err) {
|
|
@@ -498,7 +430,7 @@ class DevShell {
|
|
|
498
430
|
|
|
499
431
|
// 监听非.gitignore文件
|
|
500
432
|
// TODO: 目前仅仅监听process.cwd()以下的文件
|
|
501
|
-
async watchFile(
|
|
433
|
+
async watchFile(appId) {
|
|
502
434
|
const ignore = new GitIgnore(process.cwd());
|
|
503
435
|
await ignore.collect();
|
|
504
436
|
chokidar
|
|
@@ -513,29 +445,27 @@ class DevShell {
|
|
|
513
445
|
.on(
|
|
514
446
|
"all",
|
|
515
447
|
debounce(() => {
|
|
516
|
-
this.syncProject(
|
|
448
|
+
this.syncProject(appId);
|
|
517
449
|
}),
|
|
518
450
|
1000
|
|
519
451
|
);
|
|
520
452
|
}
|
|
521
453
|
async shell() {
|
|
522
|
-
let keyFile = this.keyFile;
|
|
523
454
|
try {
|
|
524
455
|
// 当进入shell的时候,都同步一次
|
|
525
|
-
await this.syncProject(
|
|
456
|
+
await this.syncProject(this.appId);
|
|
526
457
|
// 注册watch函数
|
|
527
|
-
await this.watchFile(
|
|
458
|
+
await this.watchFile(this.appId);
|
|
528
459
|
|
|
529
460
|
await this.connectShell();
|
|
530
461
|
} catch (e) {
|
|
531
462
|
console.log(e);
|
|
532
|
-
// this.reset();
|
|
533
463
|
return Promise.reject(e);
|
|
534
464
|
}
|
|
535
465
|
}
|
|
536
466
|
|
|
537
467
|
async connectShell() {
|
|
538
|
-
const
|
|
539
|
-
await
|
|
468
|
+
const bridge = new DebugBridge();
|
|
469
|
+
await bridge.devshell(this.appId);
|
|
540
470
|
}
|
|
541
471
|
}
|