@lazycatcloud/lzc-cli 1.3.13 → 2.0.0-pre.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.
Files changed (104) hide show
  1. package/README.md +30 -5
  2. package/changelog.md +16 -0
  3. package/lib/app/index.js +174 -58
  4. package/lib/app/lpk_build.js +197 -18
  5. package/lib/app/lpk_build_images.js +728 -0
  6. package/lib/app/lpk_create.js +96 -23
  7. package/lib/app/lpk_create_generator.js +150 -12
  8. package/lib/app/lpk_devshell.js +35 -21
  9. package/lib/app/lpk_embed_images.js +257 -0
  10. package/lib/app/lpk_installer.js +15 -7
  11. package/lib/app/project_cp.js +64 -0
  12. package/lib/app/project_deploy.js +33 -0
  13. package/lib/app/project_exec.js +45 -0
  14. package/lib/app/project_info.js +106 -0
  15. package/lib/app/project_log.js +67 -0
  16. package/lib/app/project_runtime.js +261 -0
  17. package/lib/app/project_start.js +100 -0
  18. package/lib/appstore/index.js +56 -16
  19. package/lib/appstore/publish.js +16 -13
  20. package/lib/box/index.js +103 -6
  21. package/lib/box/ssh_remote.js +259 -0
  22. package/lib/build_remote.js +22 -0
  23. package/lib/config/index.js +4 -3
  24. package/lib/debug_bridge.js +837 -44
  25. package/lib/docker/index.js +30 -10
  26. package/lib/i18n/index.js +1 -0
  27. package/lib/i18n/locales/en/translation.json +263 -250
  28. package/lib/i18n/locales/zh/translation.json +57 -44
  29. package/lib/lpk/core.js +487 -0
  30. package/lib/lpk/index.js +210 -0
  31. package/lib/shellapi.js +5 -5
  32. package/lib/sig/core.js +254 -0
  33. package/lib/sig/index.js +88 -0
  34. package/lib/utils.js +17 -12
  35. package/package.json +4 -3
  36. package/scripts/cli.js +4 -0
  37. package/template/_lpk/README.md +11 -3
  38. package/template/_lpk/gui-vnc.manifest.yml.in +27 -0
  39. package/template/_lpk/manifest.yml.in +4 -2
  40. package/template/_lpk/todolist-golang.manifest.yml.in +16 -0
  41. package/template/_lpk/todolist-java.manifest.yml.in +15 -0
  42. package/template/_lpk/todolist-python.manifest.yml.in +15 -0
  43. package/template/_lpk/vue.lzc-build.yml.in +0 -44
  44. package/template/blank/_gitignore +1 -0
  45. package/template/blank/lzc-build.yml +25 -40
  46. package/template/blank/lzc-manifest.yml +14 -7
  47. package/template/golang/Dockerfile +19 -0
  48. package/template/golang/README.md +33 -0
  49. package/template/golang/_gitignore +3 -0
  50. package/template/golang/go.mod +3 -0
  51. package/template/golang/lzc-build.yml +21 -0
  52. package/template/golang/lzc-icon.png +0 -0
  53. package/template/golang/main.go +252 -0
  54. package/template/golang/run.sh +3 -0
  55. package/template/golang/web/index.html +238 -0
  56. package/template/gui-vnc/README.md +19 -0
  57. package/template/gui-vnc/_gitignore +2 -0
  58. package/template/gui-vnc/images/Dockerfile +30 -0
  59. package/template/gui-vnc/images/kasmvnc.yaml +33 -0
  60. package/template/gui-vnc/images/startup-script.desktop +9 -0
  61. package/template/gui-vnc/images/startup-script.sh +6 -0
  62. package/template/gui-vnc/lzc-build.yml +23 -0
  63. package/template/gui-vnc/lzc-icon.png +0 -0
  64. package/template/python/Dockerfile +15 -0
  65. package/template/python/README.md +33 -0
  66. package/template/python/_gitignore +3 -0
  67. package/template/python/app.py +110 -0
  68. package/template/python/lzc-build.yml +21 -0
  69. package/template/python/lzc-icon.png +0 -0
  70. package/template/python/requirements.txt +1 -0
  71. package/template/python/run.sh +3 -0
  72. package/template/python/web/index.html +238 -0
  73. package/template/springboot/Dockerfile +20 -0
  74. package/template/springboot/README.md +33 -0
  75. package/template/springboot/_gitignore +3 -0
  76. package/template/springboot/lzc-build.yml +21 -0
  77. package/template/springboot/lzc-icon.png +0 -0
  78. package/template/springboot/pom.xml +38 -0
  79. package/template/springboot/run.sh +3 -0
  80. package/template/springboot/src/main/java/cloud/lazycat/app/Application.java +132 -0
  81. package/template/springboot/src/main/resources/application.properties +1 -0
  82. package/template/springboot/src/main/resources/static/index.html +238 -0
  83. package/template/vue/README.md +17 -7
  84. package/template/vue/_gitignore +1 -0
  85. package/template/vue/lzc-build.yml +31 -42
  86. package/template/vue/src/App.vue +36 -25
  87. package/template/vue/src/style.css +106 -49
  88. package/template/vue-minidb/README.md +34 -0
  89. package/template/vue-minidb/_gitignore +26 -0
  90. package/template/vue-minidb/index.html +13 -0
  91. package/template/vue-minidb/lzc-build.yml +48 -0
  92. package/template/vue-minidb/lzc-icon.png +0 -0
  93. package/template/vue-minidb/package.json +21 -0
  94. package/template/vue-minidb/public/vite.svg +1 -0
  95. package/template/vue-minidb/src/App.vue +206 -0
  96. package/template/vue-minidb/src/assets/vue.svg +1 -0
  97. package/template/vue-minidb/src/main.ts +5 -0
  98. package/template/vue-minidb/src/style.css +136 -0
  99. package/template/vue-minidb/src/vite-env.d.ts +1 -0
  100. package/template/vue-minidb/tsconfig.app.json +24 -0
  101. package/template/vue-minidb/tsconfig.json +7 -0
  102. package/template/vue-minidb/tsconfig.node.json +22 -0
  103. package/template/vue-minidb/vite.config.ts +10 -0
  104. /package/template/{vue → vue-minidb}/src/components/HelloWorld.vue +0 -0
@@ -1,6 +1,6 @@
1
1
  import logger from 'loglevel';
2
2
  import inquirer from 'inquirer';
3
- import { contextDirname, loadFromYaml, envTemplateFile, ensureDir, ensureDirectoryExists, dumpToYaml, isFileExist, isValidAppId, fakeLoadManifestYml } from '../utils.js';
3
+ import { contextDirname, loadFromYaml, ensureDir, ensureDirectoryExists, dumpToYaml, isFileExist, isValidAppId, fakeLoadManifestYml, envsubstr } from '../utils.js';
4
4
  import path from 'node:path';
5
5
  import { TemplateConfig } from './lpk_create_generator.js';
6
6
  import fs from 'node:fs';
@@ -10,10 +10,60 @@ import { t } from '../i18n/index.js';
10
10
 
11
11
  let fsPromises = fs.promises;
12
12
 
13
+ const PROJECT_TEMPLATE_OPTIONS = [
14
+ {
15
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_hello_vue',
16
+ defaultText: 'hello-vue (Vue基础模板)',
17
+ value: 'hello-vue',
18
+ },
19
+ {
20
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_java',
21
+ defaultText: 'todolist-java (Java Todo示例)',
22
+ value: 'todolist-java',
23
+ },
24
+ {
25
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_python',
26
+ defaultText: 'todolist-python (Python Todo示例)',
27
+ value: 'todolist-python',
28
+ },
29
+ {
30
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_golang',
31
+ defaultText: 'todolist-golang (Golang Todo示例)',
32
+ value: 'todolist-golang',
33
+ },
34
+ {
35
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_gui_vnc',
36
+ defaultText: 'gui-vnc (GUI VNC Embed示例)',
37
+ value: 'gui-vnc',
38
+ },
39
+ {
40
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_serverless',
41
+ defaultText: 'todolist-serverless (Serverless Todo示例)',
42
+ value: 'todolist-serverless',
43
+ },
44
+ ];
45
+
46
+ export function listProjectTemplateValues() {
47
+ return PROJECT_TEMPLATE_OPTIONS.map((item) => item.value);
48
+ }
49
+
50
+ export function normalizeTemplateType(rawType) {
51
+ const type = String(rawType ?? '').trim();
52
+ if (!type) {
53
+ return '';
54
+ }
55
+ const allowedTypes = listProjectTemplateValues();
56
+ if (!allowedTypes.includes(type)) {
57
+ throw new Error(`Unsupported template "${type}". Available templates: ${allowedTypes.join(', ')}`);
58
+ }
59
+ return type;
60
+ }
61
+
13
62
  export class LpkManifest {
14
63
  constructor(defaultAppID) {
15
64
  this.pwd = contextDirname(import.meta.url);
16
65
  this.manifest = {};
66
+ this.manifestText = '';
17
67
  this.defaultAppID = defaultAppID;
18
68
  }
19
69
 
@@ -24,7 +74,18 @@ export class LpkManifest {
24
74
 
25
75
  if (askInfo) {
26
76
  let answer = await this.askLpkInfo();
27
- this.manifest = yaml.load(await envTemplateFile(manifestFilePath, answer));
77
+ const manifestTemplate = fs.readFileSync(manifestFilePath, 'utf-8');
78
+ this.manifestText = await envsubstr(manifestTemplate, {
79
+ options: {
80
+ envs: Object.entries(answer).map(([key, value]) => ({
81
+ name: key.replace(/-/g, '_'),
82
+ value,
83
+ })),
84
+ syntax: 'default',
85
+ protect: false,
86
+ },
87
+ });
88
+ this.manifest = yaml.load(this.manifestText);
28
89
  } else {
29
90
  try {
30
91
  this.manifest = loadFromYaml(manifestFilePath);
@@ -39,6 +100,10 @@ export class LpkManifest {
39
100
  let outputFilePath = path.join(outputDir, 'lzc-manifest.yml');
40
101
  logger.debug(`create file ${outputFilePath}`);
41
102
  ensureDir(outputFilePath);
103
+ if (this.manifestText) {
104
+ fs.writeFileSync(outputFilePath, this.manifestText);
105
+ return;
106
+ }
42
107
  return dumpToYaml(this.manifest, outputFilePath);
43
108
  }
44
109
 
@@ -74,14 +139,12 @@ export class TemplateInit {
74
139
  constructor(cwd, name, type) {
75
140
  this.cwd = cwd;
76
141
  this.name = name;
77
- this.type = type;
142
+ this.type = normalizeTemplateType(type);
78
143
  this.lpkManifest = new LpkManifest(name);
79
144
  }
80
145
 
81
146
  async init() {
82
- if (!this.type) {
83
- this.type = await chooseTemplate();
84
- }
147
+ this.type = await chooseTemplate(this.type);
85
148
 
86
149
  // 优先使用模板项目自定义的模板文件
87
150
  const typeTemplatePath = path.join(contextDirname(import.meta.url), '..', '..', 'template', '_lpk', `${this.type}.manifest.yml.in`);
@@ -94,28 +157,37 @@ export class TemplateInit {
94
157
  }
95
158
  }
96
159
 
97
- export async function chooseTemplate() {
160
+ export async function chooseTemplate(preselectedType = '') {
161
+ const fixedType = normalizeTemplateType(preselectedType);
162
+ if (fixedType) {
163
+ return fixedType;
164
+ }
165
+
98
166
  return (
99
- await inquirer.prompt([
100
- {
101
- name: 'type',
102
- message: t('lzc_cli.lib.app.lpk_create.choose_template_message', '选择项目构建模板'),
103
- type: 'list',
104
- choices: ['vue3'],
105
- },
106
- ])
107
- )['type'];
167
+ await inquirer.prompt([
168
+ {
169
+ name: 'type',
170
+ message: t('lzc_cli.lib.app.lpk_create.choose_template_message', '选择项目构建模板'),
171
+ type: 'list',
172
+ choices: PROJECT_TEMPLATE_OPTIONS.map((item) => ({
173
+ name: t(item.i18nKey, item.defaultText),
174
+ value: item.value,
175
+ })),
176
+ },
177
+ ])
178
+ )['type'];
108
179
  }
109
180
 
110
181
  // 创建一个应用
111
- // - 先选择模板信息(golang, vue3)
182
+ // - 先选择模板信息(todolist-golang, hello-vue)
112
183
  // - 根据 lpk manifest 模板中所需要的字段填充manifest
113
184
  // - 调用模板的hook
114
185
  class LpkCreate {
115
- constructor(name, cwd = process.cwd()) {
186
+ constructor(name, cwd = process.cwd(), type = '') {
116
187
  this.name = name;
117
188
  this.cwd = cwd ?? process.cwd();
118
189
  this.appDir = path.resolve(this.cwd, this.name);
190
+ this.type = normalizeTemplateType(type);
119
191
  }
120
192
 
121
193
  async init() {
@@ -123,17 +195,18 @@ class LpkCreate {
123
195
  }
124
196
 
125
197
  async exec() {
126
- const type = await chooseTemplate();
198
+ const type = await chooseTemplate(this.type);
127
199
  const template = new TemplateInit(this.cwd, this.name, type);
128
200
  await template.init();
129
201
 
130
- console.log(chalk.green(t('lzc_cli.lib.app.lpk_create.exec_init_project_tips', `✨ 初始化项目 {{name}}`, { name: this.name })));
202
+ console.log(chalk.green(t('lzc_cli.lib.app.lpk_create.exec_init_project_tips', `✨ 初始化项目 {{ name }}`, { name: this.name, interpolation: { escapeValue: false } })));
131
203
  const { isExists, renamedFileName } = ensureDirectoryExists(this.appDir, true);
132
204
  if (isExists && renamedFileName) {
133
205
  console.warn(
134
206
  chalk.yellow(
135
- t('lzc_cli.lib.app.lpk_create.exec_init_project_name_exist_tips', `! 检测到相同目录,已自动将旧目录重命名为 {{renamedFileName}}`, {
207
+ t('lzc_cli.lib.app.lpk_create.exec_init_project_name_exist_tips', `! 检测到相同目录,已自动将旧目录重命名为 {{ renamedFileName }}`, {
136
208
  renamedFileName,
209
+ interpolation: { escapeValue: false }
137
210
  }),
138
211
  ),
139
212
  );
@@ -147,7 +220,7 @@ class LpkCreate {
147
220
  }
148
221
  }
149
222
 
150
- export default async (name, cwd = process.cwd()) => {
151
- const lpk = await new LpkCreate(name, cwd).init();
223
+ export default async (name, cwd = process.cwd(), type = '') => {
224
+ const lpk = await new LpkCreate(name, cwd, type).init();
152
225
  return await lpk.exec();
153
226
  };
@@ -46,7 +46,7 @@ function writeFileTree(target, files) {
46
46
  if (!fs.existsSync(path.dirname(filePath))) {
47
47
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
48
48
  }
49
- logger.debug(t('lzc_cli.lib.app.lpk_create_generator.write_file_tree_create_file', `创建文件 {{filePath}}`, { filePath }));
49
+ logger.debug(t('lzc_cli.lib.app.lpk_create_generator.write_file_tree_create_file', `创建文件 {{ filePath }}`, { filePath, interpolation: { escapeValue: false } }));
50
50
  fs.writeFileSync(filePath, files[p].content, { mode: files[p].mode });
51
51
  });
52
52
  }
@@ -63,33 +63,171 @@ function appCreateSuccessTip(name) {
63
63
  'lzc_cli.lib.app.lpk_create_generator.app_create_success_tip',
64
64
  `
65
65
  ✨ 懒猫微服应用已创建成功 !
66
- 进行下面步骤后即可进入容器开发
67
- cd {{name}}
68
- lzc-cli project devshell
66
+ 先完成第一次部署,确认手机/PC都可以访问
67
+ cd {{ name }}
68
+ npm install
69
+ lzc-cli project deploy
70
+ lzc-cli project info
71
+ `,
72
+ {
73
+ name,
74
+ interpolation: { escapeValue: false }
75
+ },
76
+ ).trim(),
77
+ ),
78
+ );
79
+ }
80
+
81
+ function appCreateSuccessTipByCommands(name, installCommand = '') {
82
+ const installBlock = installCommand ? `\n ${installCommand}` : '';
83
+ console.log(
84
+ chalk.green(
85
+ t(
86
+ 'lzc_cli.lib.app.lpk_create_generator.app_create_success_tip_common',
87
+ `
88
+ ✨ 懒猫微服应用已创建成功 !
89
+ ✨ 先完成第一次部署,确认手机/PC都可以访问
90
+ cd {{ name }}{{ installBlock }}
91
+ lzc-cli project deploy
92
+ lzc-cli project info
69
93
  `,
70
- { name },
94
+ {
95
+ name,
96
+ installBlock,
97
+ interpolation: { escapeValue: false }
98
+ },
71
99
  ).trim(),
72
100
  ),
73
101
  );
74
102
  }
75
103
 
76
104
  export const TemplateConfig = {
77
- vue3: {
105
+ 'hello-vue': {
78
106
  build: async (to, option) => {
79
107
  await generate('vue', to, option);
80
108
  },
81
109
  after: async function (name) {
82
- appCreateSuccessTip(name);
110
+ appCreateSuccessTipByCommands(name);
83
111
  console.log(
84
112
  chalk.green(
85
113
  t(
86
114
  'lzc_cli.lib.app.lpk_create_generator.template_config_vue3_green',
87
115
  `
88
- ⚙️ 进入应用容器后执行下面命令:
89
- npm install
90
- npm run dev
91
- 🚀 启动应用:
92
- 进入微服客户端启动器页面点击应用图标来测试应用
116
+ 🛠 修改源码后,重新执行部署即可查看效果:
117
+ lzc-cli project deploy
118
+ 🔎 如果要排查问题:
119
+ lzc-cli project log -f
120
+ lzc-cli project exec /bin/sh
121
+ `,
122
+ ).trim(),
123
+ ),
124
+ );
125
+ },
126
+ },
127
+ 'todolist-java': {
128
+ build: async (to, option) => {
129
+ await generate('springboot', to, option);
130
+ },
131
+ after: async function (name) {
132
+ appCreateSuccessTipByCommands(name);
133
+ console.log(
134
+ chalk.green(
135
+ t(
136
+ 'lzc_cli.lib.app.lpk_create_generator.template_config_springboot_green',
137
+ `
138
+ 🛠 Spring Boot app starts inside image build/runtime:
139
+ lzc-cli project deploy
140
+ 🔎 For troubleshooting:
141
+ lzc-cli project log -f
142
+ lzc-cli project exec /bin/sh
143
+ `,
144
+ ).trim(),
145
+ ),
146
+ );
147
+ },
148
+ },
149
+ 'todolist-python': {
150
+ build: async (to, option) => {
151
+ await generate('python', to, option);
152
+ },
153
+ after: async function (name) {
154
+ appCreateSuccessTipByCommands(name);
155
+ console.log(
156
+ chalk.green(
157
+ t(
158
+ 'lzc_cli.lib.app.lpk_create_generator.template_config_python_green',
159
+ `
160
+ 🛠 Flask app starts in container runtime:
161
+ lzc-cli project deploy
162
+ 🔎 For troubleshooting:
163
+ lzc-cli project log -f
164
+ lzc-cli project exec /bin/sh
165
+ `,
166
+ ).trim(),
167
+ ),
168
+ );
169
+ },
170
+ },
171
+ 'todolist-golang': {
172
+ build: async (to, option) => {
173
+ await generate('golang', to, option);
174
+ },
175
+ after: async function (name) {
176
+ appCreateSuccessTipByCommands(name);
177
+ console.log(
178
+ chalk.green(
179
+ t(
180
+ 'lzc_cli.lib.app.lpk_create_generator.template_config_golang_green',
181
+ `
182
+ 🛠 Golang app starts in container runtime:
183
+ lzc-cli project deploy
184
+ 🔎 For troubleshooting:
185
+ lzc-cli project log -f
186
+ lzc-cli project exec /bin/sh
187
+ `,
188
+ ).trim(),
189
+ ),
190
+ );
191
+ },
192
+ },
193
+ 'gui-vnc': {
194
+ build: async (to, option) => {
195
+ await generate('gui-vnc', to, option);
196
+ },
197
+ after: async function (name) {
198
+ appCreateSuccessTipByCommands(name);
199
+ console.log(
200
+ chalk.green(
201
+ t(
202
+ 'lzc_cli.lib.app.lpk_create_generator.template_config_gui_vnc_green',
203
+ `
204
+ 🛠 GUI VNC app (embedded image template):
205
+ lzc-cli project deploy
206
+ 🔎 For troubleshooting:
207
+ lzc-cli project log -f
208
+ lzc-cli project exec -s desktop /bin/sh
209
+ `,
210
+ ).trim(),
211
+ ),
212
+ );
213
+ },
214
+ },
215
+ 'todolist-serverless': {
216
+ build: async (to, option) => {
217
+ await generate('vue-minidb', to, option);
218
+ },
219
+ after: async function (name) {
220
+ appCreateSuccessTipByCommands(name);
221
+ console.log(
222
+ chalk.green(
223
+ t(
224
+ 'lzc_cli.lib.app.lpk_create_generator.template_config_vue_minidb_green',
225
+ `
226
+ 🛠 This template includes @lazycatcloud/minidb:
227
+ lzc-cli project deploy
228
+ 🔎 For troubleshooting:
229
+ lzc-cli project log -f
230
+ lzc-cli project exec /bin/sh
93
231
  `,
94
232
  ).trim(),
95
233
  ),
@@ -30,6 +30,7 @@ import { DebugBridge } from '../debug_bridge.js';
30
30
  import shellApi from '../shellapi.js';
31
31
  import { t } from '../i18n/index.js';
32
32
  import { collectContextFromDockerFile } from './lpk_devshell_docker.js';
33
+ import { resolveBuildRemoteFromOptions } from '../build_remote.js';
33
34
 
34
35
  // 判断是否需要重新构建
35
36
  // - 先判断 lzc-build.yml 是否发生改变
@@ -44,6 +45,7 @@ class AppDevShellMonitor {
44
45
 
45
46
  this.optionsFilePath = path.join(this.pwd, buildConfigFile);
46
47
  this.options = loadFromYaml(this.optionsFilePath);
48
+ this.buildRemote = resolveBuildRemoteFromOptions(this.options, `file:${this.optionsFilePath}`);
47
49
 
48
50
  this.manifestFilePath = this.options['manifest'] ? path.join(this.pwd, this.options['manifest']) : path.join(this.pwd, 'lzc-manifest.yml');
49
51
 
@@ -54,7 +56,7 @@ class AppDevShellMonitor {
54
56
  this.cacheFilePath = undefined;
55
57
  this.oldHash = undefined;
56
58
  this.newHash = undefined;
57
- this.bridge = new DebugBridge();
59
+ this.bridge = new DebugBridge(this.pwd, this.buildRemote);
58
60
  }
59
61
 
60
62
  async init() {
@@ -101,6 +103,7 @@ export class AppDevShell {
101
103
  this.buildConfigFile = buildConfigFile;
102
104
  this.isUserApp = false;
103
105
  this.monitor = undefined;
106
+ this.buildRemote = resolveBuildRemoteFromOptions(this.lpkBuild.options, `file:${this.lpkBuild.optionsFilePath}`);
104
107
  }
105
108
 
106
109
  async init() {
@@ -225,7 +228,6 @@ export class AppDevShell {
225
228
  // 避免应用永远处于 unhealth 导致状态卡在 starting
226
229
  this.lpkBuild.onBeforeDumpYaml(async (manifest, options) => {
227
230
  logger.debug('merge lzc-build.yml devshell services\n', options);
228
- const userapp = this.isUserApp ? shellApi.uid + '.' : '';
229
231
  const devshell = {
230
232
  application: {
231
233
  devshell: options['devshell'],
@@ -260,10 +262,10 @@ export class AppDevShell {
260
262
  await createTemplateFileCommon(dockerfilePath, path.join(tempDir, 'Dockerfile'), { dependencies: depsStr });
261
263
 
262
264
  const label = `${await md5String(depsStr)}:latest`;
263
- logger.debug(t('lzc_cli.lib.app.lpk_devshell.devshell_build_image_for_box_tips', `开始在盒子中构建 {{label}} 镜像 from {{tempDir}}`, { label, tempDir }));
265
+ logger.debug(t('lzc_cli.lib.app.lpk_devshell.devshell_build_image_for_box_tips', `开始在盒子中构建 {{ label }} 镜像 from {{ tempDir }}`, { label, tempDir, interpolation: { escapeValue: false } }));
264
266
 
265
267
  const contextTar = await collectContextFromDockerFile(tempDir, path.resolve(tempDir, 'Dockerfile'));
266
- const bridge = new DebugBridge();
268
+ const bridge = new DebugBridge(this.cwd, this.buildRemote);
267
269
  await bridge.init();
268
270
  const tag = await bridge.buildImage(label, contextTar);
269
271
  delete manifest['application']['devshell'];
@@ -287,11 +289,11 @@ export class AppDevShell {
287
289
  }
288
290
 
289
291
  const label = `${manifest['package']}-devshell:${manifest['version']}`;
290
- logger.debug(t('lzc_cli.lib.app.lpk_devshell.devshell_build_label_image_box_tips', `开始在盒子中构建 {{label}} 镜像`, { label }));
292
+ logger.debug(t('lzc_cli.lib.app.lpk_devshell.devshell_build_label_image_box_tips', `开始在盒子中构建 {{ label }} 镜像`, { label, interpolation: { escapeValue: false } }));
291
293
 
292
294
  const contextTar = await collectContextFromDockerFile(process.cwd(), path.resolve(process.cwd(), config['build'], 'Dockerfile'));
293
295
 
294
- const bridge = new DebugBridge();
296
+ const bridge = new DebugBridge(this.cwd, this.buildRemote);
295
297
  await bridge.init();
296
298
  const tag = await bridge.buildImage(label, contextTar);
297
299
  delete manifest['application']['devshell'];
@@ -323,15 +325,9 @@ export class AppDevShell {
323
325
  return manifest;
324
326
  });
325
327
 
326
- // devshell 模式下,默认打开后台常驻
327
- this.lpkBuild.onBeforeDumpYaml(async (manifest) => {
328
- manifest['application']['background_task'] = true;
329
- return manifest;
330
- });
331
-
332
- // 添加一个 devshell 的标记在 lpk 中,标记当前 lpk 为一个 debug 版本
333
- this.lpkBuild.onBeforeDumpLpk(async (options, cwd, destDir) => {
334
- fs.writeFileSync(path.resolve(destDir, 'devshell'), '');
328
+ // 添加一个 devshell 的标记在 lpk 中,标记当前 lpk 为一个 debug 版本
329
+ this.lpkBuild.onBeforeDumpLpk(async (options, cwd, destDir) => {
330
+ fs.writeFileSync(path.resolve(destDir, 'devshell'), '');
335
331
  });
336
332
 
337
333
  // 在构建生成 lpk 包后,调用 deploy 进行部署
@@ -343,7 +339,7 @@ export class AppDevShell {
343
339
  async rsyncShell() {
344
340
  const manifest = await this.lpkBuild.getManifest();
345
341
  const pkgId = manifest['package'];
346
- const devshell = new DevShell(pkgId, this.isUserApp);
342
+ const devshell = new DevShell(pkgId, this.isUserApp, this.cwd, this.buildRemote);
347
343
  try {
348
344
  await devshell.shell();
349
345
  } catch (e) {
@@ -357,16 +353,34 @@ export class AppDevShell {
357
353
  }
358
354
 
359
355
  class DevShell {
360
- constructor(appId, isUserApp) {
356
+ constructor(appId, isUserApp, cwd = process.cwd(), buildRemote = null) {
361
357
  this.appId = appId;
362
358
  this.isUserApp = isUserApp;
359
+ this.cwd = cwd;
360
+ this.buildRemote = buildRemote;
361
+ this.syncUID = '';
362
+ }
363
+
364
+ async resolveUID() {
365
+ if (!this.buildRemote) {
366
+ return shellApi.uid;
367
+ }
368
+ if (this.syncUID) {
369
+ return this.syncUID;
370
+ }
371
+ const bridge = new DebugBridge(this.cwd, this.buildRemote);
372
+ await bridge.init();
373
+ this.syncUID = await bridge.resolveCurrentUID();
374
+ return this.syncUID;
363
375
  }
364
376
 
365
377
  async syncProject(appId) {
366
- const resolvedIp = await resolveDomain(`dev.${shellApi.boxname}.heiyu.space`);
378
+ const uid = await this.resolveUID();
379
+ const resolvedIp = this.buildRemote ? this.buildRemote.sshHost : await resolveDomain(`dev.${shellApi.boxname}.heiyu.space`);
367
380
  const rsyncDebug = isDebugMode() ? '-P' : '';
368
- const destDir = `${appId}${this.isUserApp ? '/' + shellApi.uid : ''}`;
369
- const dest = `rsync://${shellApi.uid}@[${resolvedIp}]:874/lzcapp_cache/${destDir}/devshell`;
381
+ const destDir = `${appId}${this.isUserApp ? '/' + uid : ''}`;
382
+ const rsyncHost = resolvedIp.includes(':') ? `[${resolvedIp}]` : resolvedIp;
383
+ const dest = `rsync://${uid}@${rsyncHost}:874/lzcapp_cache/${destDir}/devshell`;
370
384
  const rsyncCmd = isWindows ? path.join(contextDirname(import.meta.url), '..', '..', 'template', '_lpk', 'win-rsync', 'rsync.exe') : `rsync`;
371
385
 
372
386
  try {
@@ -454,7 +468,7 @@ class DevShell {
454
468
  }
455
469
 
456
470
  async connectShell(onconnect = null) {
457
- const bridge = new DebugBridge();
471
+ const bridge = new DebugBridge(this.cwd, this.buildRemote);
458
472
  await bridge.init();
459
473
  await bridge.devshell(this.appId, this.isUserApp, onconnect);
460
474
  }