@lazycatcloud/lzc-cli 1.1.13 → 1.2.1
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 +30 -62
- package/lib/app/index.js +33 -35
- package/lib/app/lpk_debug_bridge.js +101 -0
- package/lib/app/lpk_devshell.js +55 -114
- package/lib/app/lpk_installer.js +43 -40
- package/lib/appstore/login.js +1 -1
- 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 +16 -2
- package/lib/shellapi.js +95 -0
- package/lib/shellapi.proto +301 -0
- package/lib/utils.js +2 -103
- package/package.json +4 -26
- 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/core.proto +0 -118
- package/lib/docker/promise.js +0 -91
- package/lib/docker-compose.js +0 -50
- package/lib/fetch.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/README.md
CHANGED
|
@@ -1,79 +1,47 @@
|
|
|
1
|
-
#
|
|
2
|
-
```
|
|
3
|
-
<command> [options]
|
|
4
|
-
|
|
5
|
-
Commands:
|
|
6
|
-
lzc-cli box 盒子管理 lzc-cli app 应用管理 lzc-cli project 项目管理 lzc-cli appstore 应用商店
|
|
7
|
-
Options:
|
|
8
|
-
-h, --help [boolean] [default: false]
|
|
9
|
-
--version Show version number [boolean]
|
|
10
|
-
--log log level 'trace', 'debug', 'info', 'warn', 'error'
|
|
11
|
-
[string] [default: "info"]
|
|
12
|
-
```
|
|
1
|
+
# lzc-cli 指南
|
|
13
2
|
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
npm install -g @lazycatcloud/lzc-cli
|
|
17
|
-
```
|
|
3
|
+
#### 依赖
|
|
18
4
|
|
|
19
|
-
|
|
5
|
+
1. `ssh`
|
|
6
|
+
2. `ssh-copy-id`
|
|
7
|
+
3. `rsync`
|
|
20
8
|
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
lzc-cli box create
|
|
24
|
-
```
|
|
25
|
-
将从 https://dl.lazycat.cloud 中下载最新的盒子系统数据,跟创建对应的 qemu 虚拟盒子。
|
|
9
|
+
#### 快速上手
|
|
26
10
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
lzc-cli
|
|
30
|
-
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @lazycatcloud/lzc-cli
|
|
13
|
+
# 将 lzc-cli 添加 bash/zsh 补全支持
|
|
14
|
+
lzc-cli completion >> ~/.zshrc
|
|
31
15
|
```
|
|
32
|
-
启动盒子成功后,就可以在 https://<mydemobox>.heiyu.space 中看到盒子的主页了。
|
|
33
16
|
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
lzc-cli box switch <otherBoxName>
|
|
37
|
-
```
|
|
17
|
+
下面开始使用 `lzc-cli` 去创建一个项目吧!
|
|
38
18
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
lzc-cli box info
|
|
42
|
-
```
|
|
19
|
+
```bash
|
|
20
|
+
lzc-cli project create you_project
|
|
43
21
|
|
|
44
|
-
|
|
22
|
+
# 构建懒猫云平台lpk包
|
|
23
|
+
lzc-cli project build
|
|
45
24
|
|
|
46
|
-
|
|
25
|
+
# 将lpk包安装到盒子中去
|
|
26
|
+
lzc-cli app install
|
|
47
27
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
lzc-cli app install https://repo.lazycat.cloud/pkgs/cloud.lazycat.app.kityminder/cloud.lazycat.app.kityminder-v1.0.3.lpk
|
|
51
|
-
```
|
|
28
|
+
# 使用 devshell 可以让你在盒子中开发调试
|
|
29
|
+
lzc-cli project devshell
|
|
52
30
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
lzc-cli app install ./kityminder.lpk
|
|
31
|
+
# 经过测试后,将包发布到懒猫云商店中去
|
|
32
|
+
lzc-cli appstore publish
|
|
56
33
|
```
|
|
57
34
|
|
|
58
|
-
###
|
|
59
|
-
```
|
|
60
|
-
lzc-cli app uninstall cloud.lazycat.app.kityminder
|
|
61
|
-
```
|
|
35
|
+
### FAQ
|
|
62
36
|
|
|
63
|
-
|
|
37
|
+
#### 1. 开发者工具 ssh 的帐号密码是多少?
|
|
64
38
|
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
lzc-cli project create yourappname
|
|
68
|
-
```
|
|
39
|
+
现在帐号密码设置为 box:box,后面会使用 pam 模块对接盒子帐号体系。
|
|
69
40
|
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
lzc-cli project build -o yourappname.lpk
|
|
73
|
-
```
|
|
41
|
+
#### 2. 如何切换默认的开发的盒子?
|
|
74
42
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
43
|
+
目前不支持通过 lzc-cli 工具切换默认的盒子,所以你需要在 lzc-client-desktop 客户端上点击你要使用的盒子来切换。
|
|
44
|
+
|
|
45
|
+
#### 3. 如果在盒子中构建镜像? 不想在本地构建
|
|
46
|
+
|
|
47
|
+
现在已经把在盒子中构建镜像的功能去掉了,只能在本地或者其他地方构建好,然后指定 image 字段来选择你需要的开发环境。
|
package/lib/app/index.js
CHANGED
|
@@ -3,12 +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 { LpkUninstaller } from "./lpk_uninstall.js";
|
|
7
|
-
import { LpkStatuser } from "./lpk_status.js";
|
|
8
|
-
import { LpkLogger } from "./lpk_log.js";
|
|
9
6
|
import logger from "loglevel";
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
7
|
+
import { sleep } from "../utils.js";
|
|
8
|
+
import { DebugBridge } from "./lpk_debug_bridge.js";
|
|
9
|
+
import shellApi from "../shellapi.js";
|
|
12
10
|
|
|
13
11
|
export function lpkProjectCommand(program) {
|
|
14
12
|
let subCommands = [
|
|
@@ -16,6 +14,8 @@ export function lpkProjectCommand(program) {
|
|
|
16
14
|
command: "create <name>",
|
|
17
15
|
desc: "创建懒猫云应用",
|
|
18
16
|
handler: async ({ name }) => {
|
|
17
|
+
await shellApi.init();
|
|
18
|
+
|
|
19
19
|
await lpkCreate(name);
|
|
20
20
|
},
|
|
21
21
|
},
|
|
@@ -35,6 +35,8 @@ export function lpkProjectCommand(program) {
|
|
|
35
35
|
});
|
|
36
36
|
},
|
|
37
37
|
handler: async ({ context, output, file }) => {
|
|
38
|
+
await shellApi.init();
|
|
39
|
+
|
|
38
40
|
const lpk = await new LpkBuild(context, file).init();
|
|
39
41
|
// 正常的打包逻辑不需要 devshell
|
|
40
42
|
lpk.onBeforeBuildPackage(async (options) => {
|
|
@@ -52,18 +54,6 @@ export function lpkProjectCommand(program) {
|
|
|
52
54
|
command: "devshell [context]",
|
|
53
55
|
desc: "进入盒子的开发环境",
|
|
54
56
|
builder: (args) => {
|
|
55
|
-
args.option("r", {
|
|
56
|
-
alias: "rsync",
|
|
57
|
-
deprecate: true,
|
|
58
|
-
type: "boolean",
|
|
59
|
-
default: true,
|
|
60
|
-
});
|
|
61
|
-
args.option("s", {
|
|
62
|
-
alias: "shell",
|
|
63
|
-
describe: "指定你最喜欢的shell",
|
|
64
|
-
type: "string",
|
|
65
|
-
default: "bash",
|
|
66
|
-
});
|
|
67
57
|
args.option("f", {
|
|
68
58
|
alias: "force",
|
|
69
59
|
describe: "强制重新构建",
|
|
@@ -80,7 +70,9 @@ export function lpkProjectCommand(program) {
|
|
|
80
70
|
type: "boolean",
|
|
81
71
|
});
|
|
82
72
|
},
|
|
83
|
-
handler: async ({ context,
|
|
73
|
+
handler: async ({ context, force, config, contentdir }) => {
|
|
74
|
+
await shellApi.init();
|
|
75
|
+
|
|
84
76
|
const cwd = context ? path.resolve(context) : process.cwd();
|
|
85
77
|
const lpkBuild = await new LpkBuild(cwd, config).init();
|
|
86
78
|
lpkBuild.onBeforeBuildPackage(async (options) => {
|
|
@@ -94,11 +86,11 @@ export function lpkProjectCommand(program) {
|
|
|
94
86
|
}
|
|
95
87
|
return options;
|
|
96
88
|
});
|
|
97
|
-
await fetch(`${sdkEnv.sdkUrl}/api/v1/ping`);
|
|
98
89
|
const app = new AppDevShell(cwd, lpkBuild, force);
|
|
99
90
|
await app.init();
|
|
100
91
|
await app.build();
|
|
101
|
-
await
|
|
92
|
+
await sleep(2000);
|
|
93
|
+
await app.rsyncShell();
|
|
102
94
|
},
|
|
103
95
|
},
|
|
104
96
|
];
|
|
@@ -114,30 +106,36 @@ export function lpkProjectCommand(program) {
|
|
|
114
106
|
export function lpkAppCommand(program) {
|
|
115
107
|
let subCommands = [
|
|
116
108
|
{
|
|
117
|
-
command: "install
|
|
118
|
-
desc: "部署应用至设备,
|
|
119
|
-
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();
|
|
120
115
|
const installer = new LpkInstaller();
|
|
121
116
|
await installer.init();
|
|
122
|
-
await installer.install(
|
|
117
|
+
await installer.install(pkgPath);
|
|
123
118
|
},
|
|
124
119
|
},
|
|
125
120
|
{
|
|
126
121
|
command: "uninstall <pkgId>",
|
|
127
122
|
desc: "从设备中卸载某一个应用",
|
|
128
123
|
handler: async ({ pkgId }) => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
await shellApi.init();
|
|
125
|
+
|
|
126
|
+
const bridge = new DebugBridge();
|
|
127
|
+
await bridge.uninstall(pkgId);
|
|
132
128
|
},
|
|
133
129
|
},
|
|
134
130
|
{
|
|
135
131
|
command: "status <pkgId>",
|
|
136
132
|
desc: "获取某一个应用的状态",
|
|
137
133
|
handler: async ({ pkgId }) => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
await shellApi.init();
|
|
135
|
+
|
|
136
|
+
const bridge = new DebugBridge();
|
|
137
|
+
const status = await bridge.status(pkgId);
|
|
138
|
+
console.log(status);
|
|
141
139
|
},
|
|
142
140
|
},
|
|
143
141
|
{
|
|
@@ -151,17 +149,17 @@ export function lpkAppCommand(program) {
|
|
|
151
149
|
default: false,
|
|
152
150
|
});
|
|
153
151
|
},
|
|
154
|
-
handler: async (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
handler: async () => {
|
|
153
|
+
await shellApi.init();
|
|
154
|
+
|
|
155
|
+
throw "还没有实现";
|
|
158
156
|
},
|
|
159
157
|
},
|
|
160
158
|
];
|
|
161
159
|
program.command({
|
|
162
160
|
command: "app",
|
|
163
161
|
desc: "应用管理",
|
|
164
|
-
builder: (args) => {
|
|
162
|
+
builder: async (args) => {
|
|
165
163
|
args.command(subCommands);
|
|
166
164
|
},
|
|
167
165
|
});
|
|
@@ -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,19 +99,6 @@ 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 {
|
|
@@ -155,11 +128,6 @@ export class AppDevShell {
|
|
|
155
128
|
}
|
|
156
129
|
|
|
157
130
|
async devshellBuild() {
|
|
158
|
-
// 确保 sdk key ,并上传到 sdk
|
|
159
|
-
const k = new Key();
|
|
160
|
-
await k.ensure(sdkEnv.sdkUrl);
|
|
161
|
-
const pairs = await k.getKeyPair();
|
|
162
|
-
|
|
163
131
|
this.lpkBuild.onBeforeBuildPackage(async (options) => {
|
|
164
132
|
const devshell = options["devshell"];
|
|
165
133
|
if (!devshell) {
|
|
@@ -174,6 +142,30 @@ export class AppDevShell {
|
|
|
174
142
|
return options;
|
|
175
143
|
});
|
|
176
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
|
+
|
|
177
169
|
// 复制 exec.sh 到 devshell 中去
|
|
178
170
|
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
179
171
|
const execScriptPath = path.join(
|
|
@@ -186,15 +178,6 @@ export class AppDevShell {
|
|
|
186
178
|
fs.chmodSync(dest, 0o775);
|
|
187
179
|
});
|
|
188
180
|
|
|
189
|
-
// 用 sdk ssh key 并复制到 contentdir 目录
|
|
190
|
-
// docker 中 sshd 的配置中已改成 /lzcapp/pkg/content/devshell/authorized_keys
|
|
191
|
-
this.lpkBuild.onBeforeTarContent(async (contentdir) => {
|
|
192
|
-
let publicKey = pairs["public"];
|
|
193
|
-
let dest = path.join(contentdir, "devshell", "authorized_keys");
|
|
194
|
-
ensureDir(dest);
|
|
195
|
-
fs.copyFileSync(publicKey, dest);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
181
|
// 复制 setupscript 脚本
|
|
199
182
|
this.lpkBuild.onBeforeTarContent(async (contentdir, options) => {
|
|
200
183
|
const devshell = options["devshell"];
|
|
@@ -227,6 +210,13 @@ export class AppDevShell {
|
|
|
227
210
|
const routes = devshell["routes"];
|
|
228
211
|
logger.debug("options devshell delete 'routes' field");
|
|
229
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
|
+
|
|
230
220
|
// 如果 devshell 中的 router 和 manifest 中的 prefix 出现冲突
|
|
231
221
|
// 优先使用 devshell 中的。
|
|
232
222
|
routes.forEach((r) => {
|
|
@@ -274,32 +264,7 @@ export class AppDevShell {
|
|
|
274
264
|
return manifest;
|
|
275
265
|
}
|
|
276
266
|
|
|
277
|
-
|
|
278
|
-
logger.debug("开始创建 Dockerfile 文件");
|
|
279
|
-
const tag = `${await md5String(depsStr)}:latest`;
|
|
280
|
-
|
|
281
|
-
const tempDir = fs.mkdtempSync(".lzc-cli-build-dependencies");
|
|
282
|
-
try {
|
|
283
|
-
const dockerfilePath = path.join(
|
|
284
|
-
contextDirname(import.meta.url),
|
|
285
|
-
"../../template/_lpk/Dockerfile.in"
|
|
286
|
-
);
|
|
287
|
-
await createTemplateFileCommon(
|
|
288
|
-
dockerfilePath,
|
|
289
|
-
path.join(tempDir, "Dockerfile"),
|
|
290
|
-
{ dependencies: depsStr }
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
logger.debug(`开始在盒子中构建 ${tag} 镜像 from ${tempDir}`);
|
|
294
|
-
const sdk = new SdkDocker();
|
|
295
|
-
await sdk.buildImage(tag, tempDir, tempDir);
|
|
296
|
-
} finally {
|
|
297
|
-
fs.rmSync(tempDir, { recursive: true });
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
delete manifest["application"]["devshell"];
|
|
301
|
-
manifest["application"]["image"] = tag;
|
|
302
|
-
return manifest;
|
|
267
|
+
throw "lzc-cli 不支持在盒子中构建 docker 镜像,请从其他地方构建,然后指定image字段";
|
|
303
268
|
});
|
|
304
269
|
|
|
305
270
|
// 如果 services 中有 devshell 的字段,需要检测是否需要提前构建
|
|
@@ -314,16 +279,7 @@ export class AppDevShell {
|
|
|
314
279
|
return manifest;
|
|
315
280
|
}
|
|
316
281
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
logger.debug(`开始在盒子中构建 ${tag} 镜像`);
|
|
320
|
-
|
|
321
|
-
const sdk = new SdkDocker();
|
|
322
|
-
await sdk.buildImage(tag, config["build"], process.cwd());
|
|
323
|
-
|
|
324
|
-
delete manifest["application"]["devshell"];
|
|
325
|
-
manifest["application"]["image"] = tag;
|
|
326
|
-
return manifest;
|
|
282
|
+
throw "lzc-cli 不支持在盒子中构建 docker 镜像,请从其他地方构建,然后指定image字段";
|
|
327
283
|
});
|
|
328
284
|
|
|
329
285
|
// 如果 devshell 中指定了 image 字段将使用 image 字段
|
|
@@ -361,13 +317,9 @@ export class AppDevShell {
|
|
|
361
317
|
let installer = new LpkInstaller();
|
|
362
318
|
await installer.init();
|
|
363
319
|
await installer.deploy(this.lpkBuild, true);
|
|
364
|
-
|
|
365
|
-
await sleep(2000);
|
|
366
320
|
}
|
|
367
321
|
|
|
368
|
-
async rsyncShell(
|
|
369
|
-
const k = new Key();
|
|
370
|
-
const pairs = await k.getKeyPair();
|
|
322
|
+
async rsyncShell() {
|
|
371
323
|
const manifest = await this.lpkBuild.getManifest();
|
|
372
324
|
const pkgId = manifest["package"];
|
|
373
325
|
|
|
@@ -386,7 +338,7 @@ export class AppDevShell {
|
|
|
386
338
|
}
|
|
387
339
|
|
|
388
340
|
const uid = await getUidByManifest(manifest);
|
|
389
|
-
const devshell = new DevShell(
|
|
341
|
+
const devshell = new DevShell(pkgId, uid);
|
|
390
342
|
if (isSync) {
|
|
391
343
|
await devshell.shell();
|
|
392
344
|
} else {
|
|
@@ -400,29 +352,22 @@ export class AppDevShell {
|
|
|
400
352
|
}
|
|
401
353
|
|
|
402
354
|
class DevShell {
|
|
403
|
-
constructor(
|
|
404
|
-
logger.debug("keyFile", keyFile);
|
|
405
|
-
logger.debug("appid", appId);
|
|
406
|
-
|
|
407
|
-
this.keyFile = keyFile;
|
|
355
|
+
constructor(appId, uid = "") {
|
|
408
356
|
this.appId = appId;
|
|
409
357
|
this.uid = uid;
|
|
410
|
-
this.runShell = runShell;
|
|
411
358
|
}
|
|
412
359
|
|
|
413
|
-
async syncProject(
|
|
414
|
-
const host = sdkSSHHost();
|
|
415
|
-
const port = sdkSSHPort();
|
|
360
|
+
async syncProject(appId) {
|
|
416
361
|
// prettier-ignore
|
|
417
362
|
let rsh = [
|
|
418
363
|
"ssh",
|
|
419
|
-
"-
|
|
364
|
+
"-J", `box@dev.${shellApi.boxname}.heiyu.space:22222`,
|
|
365
|
+
"-p", `22222`,
|
|
420
366
|
"-o", `"StrictHostKeyChecking=no"`,
|
|
421
367
|
"-o", `"UserKnownHostsFile=/dev/null"`,
|
|
422
368
|
"-o", `"ConnectionAttempts=3"`,
|
|
423
369
|
"-o", `"ConnectTimeout=30"`,
|
|
424
370
|
"-o", `"LogLevel=ERROR"`,
|
|
425
|
-
"-i", keyFile,
|
|
426
371
|
].join(" ");
|
|
427
372
|
// 检查rsync工具是否存在:提示用户
|
|
428
373
|
const rsyncExisted = commandExists.sync("rsync");
|
|
@@ -431,13 +376,11 @@ class DevShell {
|
|
|
431
376
|
process.exit(1);
|
|
432
377
|
}
|
|
433
378
|
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
let storePath = `/lzcsys/run/data/app/cache/${appId}${userId}/devshell`;
|
|
437
|
-
// FIXME: 下方执行命令不确定是否有兼容性问题
|
|
379
|
+
const rsyncDebug = process.env.RSYNCDEBUG ? "-P" : "";
|
|
380
|
+
const dest = `root@app.${appId}.lzcapp:/lzcapp/cache/devshell`;
|
|
438
381
|
try {
|
|
439
382
|
execSync(
|
|
440
|
-
`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}`,
|
|
441
384
|
{ stdio: ["ignore", "inherit", "inherit"] }
|
|
442
385
|
);
|
|
443
386
|
} catch (err) {
|
|
@@ -487,7 +430,7 @@ class DevShell {
|
|
|
487
430
|
|
|
488
431
|
// 监听非.gitignore文件
|
|
489
432
|
// TODO: 目前仅仅监听process.cwd()以下的文件
|
|
490
|
-
async watchFile(
|
|
433
|
+
async watchFile(appId) {
|
|
491
434
|
const ignore = new GitIgnore(process.cwd());
|
|
492
435
|
await ignore.collect();
|
|
493
436
|
chokidar
|
|
@@ -502,29 +445,27 @@ class DevShell {
|
|
|
502
445
|
.on(
|
|
503
446
|
"all",
|
|
504
447
|
debounce(() => {
|
|
505
|
-
this.syncProject(
|
|
448
|
+
this.syncProject(appId);
|
|
506
449
|
}),
|
|
507
450
|
1000
|
|
508
451
|
);
|
|
509
452
|
}
|
|
510
453
|
async shell() {
|
|
511
|
-
let keyFile = this.keyFile;
|
|
512
454
|
try {
|
|
513
455
|
// 当进入shell的时候,都同步一次
|
|
514
|
-
await this.syncProject(
|
|
456
|
+
await this.syncProject(this.appId);
|
|
515
457
|
// 注册watch函数
|
|
516
|
-
await this.watchFile(
|
|
458
|
+
await this.watchFile(this.appId);
|
|
517
459
|
|
|
518
460
|
await this.connectShell();
|
|
519
461
|
} catch (e) {
|
|
520
462
|
console.log(e);
|
|
521
|
-
// this.reset();
|
|
522
463
|
return Promise.reject(e);
|
|
523
464
|
}
|
|
524
465
|
}
|
|
525
466
|
|
|
526
467
|
async connectShell() {
|
|
527
|
-
const
|
|
528
|
-
await
|
|
468
|
+
const bridge = new DebugBridge();
|
|
469
|
+
await bridge.devshell(this.appId);
|
|
529
470
|
}
|
|
530
471
|
}
|