@lazycatcloud/lzc-cli 1.2.27 → 1.2.29

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 (37) hide show
  1. package/lib/app/apkshell.js +35 -0
  2. package/lib/app/index.js +89 -78
  3. package/lib/app/lpk_build.js +113 -117
  4. package/lib/app/lpk_create.js +78 -148
  5. package/lib/app/lpk_create_generator.js +103 -74
  6. package/lib/app/lpk_debug_bridge.js +61 -73
  7. package/lib/app/lpk_devshell.js +230 -229
  8. package/lib/app/lpk_devshell_docker.js +28 -28
  9. package/lib/app/lpk_installer.js +64 -49
  10. package/lib/appstore/index.js +29 -29
  11. package/lib/appstore/login.js +63 -68
  12. package/lib/appstore/prePublish.js +68 -68
  13. package/lib/appstore/publish.js +55 -55
  14. package/lib/box/index.js +25 -25
  15. package/lib/env.js +18 -18
  16. package/lib/shellapi.js +55 -58
  17. package/lib/utils.js +217 -164
  18. package/package.json +7 -1
  19. package/scripts/cli.js +56 -56
  20. package/template/_lpk/manifest.yml.in +8 -8
  21. package/template/vue/README.md +29 -0
  22. package/template/vue/index.html +13 -0
  23. package/template/vue/lzc-build.yml +59 -0
  24. package/template/vue/lzc-icon.png +0 -0
  25. package/template/vue/package.json +20 -0
  26. package/template/vue/public/vite.svg +1 -0
  27. package/template/vue/src/App.vue +30 -0
  28. package/template/vue/src/assets/vue.svg +1 -0
  29. package/template/vue/src/components/HelloWorld.vue +41 -0
  30. package/template/vue/src/main.ts +5 -0
  31. package/template/vue/src/style.css +79 -0
  32. package/template/vue/src/vite-env.d.ts +1 -0
  33. package/template/vue/tsconfig.app.json +24 -0
  34. package/template/vue/tsconfig.json +7 -0
  35. package/template/vue/tsconfig.node.json +22 -0
  36. package/template/vue/vite.config.ts +7 -0
  37. package/template/ionic_vue3/package-lock.json +0 -8100
@@ -1,54 +1,54 @@
1
- import path from "node:path";
2
- import fs from "node:fs";
3
- import { DockerfileParser } from "dockerfile-ast";
4
- import glob from "fast-glob";
5
- import ignore from "@balena/dockerignore";
6
- import { tarContentDir } from "../utils.js";
1
+ import path from "node:path"
2
+ import fs from "node:fs"
3
+ import { DockerfileParser } from "dockerfile-ast"
4
+ import glob from "fast-glob"
5
+ import ignore from "@balena/dockerignore"
6
+ import { tarContentDir } from "../utils.js"
7
7
 
8
8
  export async function collectContextFromDockerFile(contextDir, dockerfilePath) {
9
9
  if (!fs.existsSync(dockerfilePath)) {
10
- throw "未发现 Dockerfile";
10
+ throw "未发现 Dockerfile"
11
11
  }
12
12
 
13
- let src = [];
13
+ let src = []
14
14
 
15
15
  // 通过 COPY 和 ADD 获取所有context中的文件
16
- const ast = DockerfileParser.parse(fs.readFileSync(dockerfilePath, "utf8"));
16
+ const ast = DockerfileParser.parse(fs.readFileSync(dockerfilePath, "utf8"))
17
17
  for (let a of ast.getInstructions()) {
18
18
  if (["COPY", "ADD"].includes(a.getInstruction())) {
19
- const from = a.getArguments()[0].getValue().replace(/^\//, "");
20
- const fromFullPath = path.resolve(contextDir, from);
19
+ const from = a.getArguments()[0].getValue().replace(/^\//, "")
20
+ const fromFullPath = path.resolve(contextDir, from)
21
21
 
22
22
  if (fs.existsSync(fromFullPath)) {
23
- const stat = fs.statSync(fromFullPath);
23
+ const stat = fs.statSync(fromFullPath)
24
24
  if (stat.isDirectory()) {
25
25
  let files = await glob(path.join(from, "**"), {
26
- cwd: contextDir,
27
- });
28
- src = src.concat(files);
26
+ cwd: contextDir
27
+ })
28
+ src = src.concat(files)
29
29
  } else if (stat.isFile()) {
30
- src.push(from);
30
+ src.push(from)
31
31
  }
32
32
  } else {
33
33
  // try use glob
34
34
  let files = await glob(from, {
35
- cwd: contextDir,
36
- });
37
- src = src.concat(files);
35
+ cwd: contextDir
36
+ })
37
+ src = src.concat(files)
38
38
  }
39
39
  }
40
40
  }
41
41
  // filter by dockerignore
42
- const dockerIgnoreFile = path.join(contextDir, ".dockerignore");
42
+ const dockerIgnoreFile = path.join(contextDir, ".dockerignore")
43
43
  if (fs.existsSync(dockerIgnoreFile)) {
44
- let ig = ignore();
45
- let data = fs.readFileSync(dockerIgnoreFile, "utf8");
46
- ig.add(data.split("\n"));
47
- src = ig.filter(src);
44
+ let ig = ignore()
45
+ let data = fs.readFileSync(dockerIgnoreFile, "utf8")
46
+ ig.add(data.split("\n"))
47
+ src = ig.filter(src)
48
48
  }
49
49
 
50
- const dockerfile = path.relative(contextDir, dockerfilePath);
51
- src.push(dockerfile); // DON'T IGNORE DOCKERFILE
52
- const output = path.join(contextDir, "lzc-build-image-context.tar");
53
- return await tarContentDir(src, output, contextDir);
50
+ const dockerfile = path.relative(contextDir, dockerfilePath)
51
+ src.push(dockerfile) // DON'T IGNORE DOCKERFILE
52
+ const output = path.join(contextDir, "lzc-build-image-context.tar")
53
+ return await tarContentDir(src, output, contextDir)
54
54
  }
@@ -1,26 +1,28 @@
1
- import logger from "loglevel";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { loadFromYaml, Downloader } from "../utils.js";
5
- import { spawnSync } from "node:child_process";
6
- import { DebugBridge } from "./lpk_debug_bridge.js";
7
- import shellapi from "../shellapi.js";
1
+ import logger from "loglevel"
2
+ import fs from "node:fs"
3
+ import path from "node:path"
4
+ import { loadFromYaml, Downloader } from "../utils.js"
5
+ import { spawnSync } from "node:child_process"
6
+ import { DebugBridge } from "./lpk_debug_bridge.js"
7
+ import shellapi from "../shellapi.js"
8
+ import { triggerApk } from "./apkshell.js"
8
9
 
10
+ export const installConfig = { apk: true }
9
11
  // 从一个目录中找出修改时间最新的包
10
12
  function findOnceLpkByDir(dir = process.cwd()) {
11
13
  const pkg = fs
12
14
  .readdirSync(dir)
13
15
  .map((filename) => {
14
- const stat = fs.statSync(path.resolve(dir, filename));
15
- return { filename, mtime: stat.mtime };
16
+ const stat = fs.statSync(path.resolve(dir, filename))
17
+ return { filename, mtime: stat.mtime }
16
18
  })
17
19
  .sort((a, b) => {
18
- return b.mtime - a.mtime;
20
+ return b.mtime - a.mtime
19
21
  })
20
22
  .find((entry) => {
21
- return entry.filename.endsWith(".lpk");
22
- });
23
- return pkg?.filename ? path.resolve(dir, pkg.filename) : "";
23
+ return entry.filename.endsWith(".lpk")
24
+ })
25
+ return pkg?.filename ? path.resolve(dir, pkg.filename) : ""
24
26
  }
25
27
 
26
28
  export class LpkInstaller {
@@ -31,77 +33,90 @@ export class LpkInstaller {
31
33
  // deploy 构建和安装
32
34
  async deploy(builder) {
33
35
  if (!builder) {
34
- throw "deploy 必须传递一个 builder";
36
+ throw "deploy 必须传递一个 builder"
35
37
  }
36
38
 
37
- let manifest = await builder.getManifest();
38
- let pkgPath = await builder.exec();
39
- logger.info("开始部署应用");
40
- const bridge = new DebugBridge();
41
- await bridge.install(pkgPath, manifest["package"]);
42
- const appUrl = `https://${manifest["application"]["subdomain"]}.${shellapi.boxname}.heiyu.space`;
43
- logger.info(`👉 请在浏览器中访问 ${appUrl}`);
39
+ let pkgPath = await builder.exec()
40
+ logger.info("开始部署应用")
41
+ // FIXME: 这里是前面exec输出的pkgPath还有异步的地方没有await到,临时setTimeout处理一下,后期让@llh看一下
42
+ await new Promise((resolve, reject) => {
43
+ setTimeout(async () => {
44
+ await this.installFromFile(pkgPath)
45
+ .then(() => resolve(true))
46
+ .catch(reject)
47
+ }, 50)
48
+ })
44
49
  }
45
50
 
46
51
  async install(pkgPath) {
47
52
  if (pkgPath.startsWith("http")) {
48
- return this.installFromUrl(pkgPath);
53
+ return this.installFromUrl(pkgPath)
49
54
  } else if (fs.statSync(pkgPath).isDirectory()) {
50
- return this.installFromDirectory(pkgPath);
55
+ return this.installFromDirectory(pkgPath)
51
56
  } else {
52
- return this.installFromFile(pkgPath);
57
+ return this.installFromFile(pkgPath)
53
58
  }
54
59
  }
55
60
 
56
61
  async installFromDirectory(dir) {
57
- const lpk = findOnceLpkByDir(dir);
62
+ const lpk = findOnceLpkByDir(dir)
58
63
  if (lpk) {
59
- logger.info(`正在安装自动扫描出来的lpk包(${lpk})`);
64
+ logger.info(`正在安装自动扫描出来的lpk包(${lpk})`)
60
65
  }
61
- return this.installFromFile(lpk);
66
+ return this.installFromFile(lpk)
62
67
  }
63
68
 
64
69
  async installFromUrl(url) {
65
- let downloader = new Downloader();
66
- const tempDir = fs.mkdtempSync(".lzc-cli-install");
70
+ let downloader = new Downloader()
71
+ const tempDir = fs.mkdtempSync(".lzc-cli-install")
67
72
  try {
68
- let pkgPath = path.join(tempDir, "a.lpk");
69
- logger.debug("开始下载lpk");
70
- await downloader.download(url, pkgPath);
71
- logger.debug("下载完成", pkgPath);
72
- await this.installFromFile(pkgPath);
73
+ let pkgPath = path.join(tempDir, "a.lpk")
74
+ logger.debug("开始下载lpk")
75
+ await downloader.download(url, pkgPath)
76
+ logger.debug("下载完成", pkgPath)
77
+ await this.installFromFile(pkgPath)
73
78
  } catch (e) {
74
- throw e;
79
+ throw e
75
80
  } finally {
76
- fs.rmSync(tempDir, { recursive: true });
81
+ fs.rmSync(tempDir, { recursive: true })
77
82
  }
78
83
  }
79
84
 
80
85
  async installFromFile(pkgPath) {
81
86
  if (!pkgPath) {
82
- throw "install 必须指定一个 pkg 路径";
87
+ throw "install 必须指定一个 pkg 路径"
83
88
  }
84
89
 
85
90
  if (!fs.existsSync(pkgPath)) {
86
- throw `${pkgPath} 不存在`;
91
+ throw `${pkgPath} 不存在`
87
92
  }
88
93
 
89
- const tempDir = fs.mkdtempSync(".lzc-cli-install");
90
- let manifest;
94
+ const tempDir = fs.mkdtempSync(".lzc-cli-install")
95
+ let manifest
91
96
  try {
92
- spawnSync("unzip", ["-d", tempDir, pkgPath]);
93
- manifest = loadFromYaml(path.join(tempDir, "manifest.yml"));
94
- logger.debug("lpk manifest", manifest);
97
+ spawnSync("unzip", ["-d", tempDir, pkgPath])
98
+ manifest = loadFromYaml(path.join(tempDir, "manifest.yml"))
99
+ logger.debug("是否生成APK:", installConfig.apk)
100
+ if (installConfig.apk) {
101
+ await triggerApk(
102
+ manifest["package"],
103
+ manifest["name"],
104
+ path.resolve(path.join(tempDir, "icon.png"))
105
+ )
106
+ }
107
+ logger.debug("lpk manifest", manifest)
95
108
  } finally {
96
- fs.rmSync(tempDir, { recursive: true });
109
+ fs.rmSync(tempDir, { recursive: true })
97
110
  }
98
111
 
99
- const bridge = new DebugBridge();
100
- logger.info("开始安装应用");
101
- await bridge.install(pkgPath, manifest["package"]);
102
- logger.info(`安装成功!`);
112
+ const bridge = new DebugBridge()
113
+ logger.info("开始安装应用")
114
+ await bridge.install(pkgPath, manifest["package"])
115
+ logger.info("\n")
116
+ logger.info(`安装成功!`)
103
117
  logger.info(
104
118
  `👉 请在浏览器中访问 https://${manifest["application"]["subdomain"]}.${shellapi.boxname}.heiyu.space`
105
- );
119
+ )
120
+ logger.info(`👉 并使用微服的用户名和密码登录`)
106
121
  }
107
122
  }
@@ -1,7 +1,7 @@
1
- import { Publish } from "./publish.js";
2
- import { PrePublish } from "./prePublish.js";
3
- import { reLogin } from "./login.js";
4
- import fs from "node:fs";
1
+ import { Publish } from "./publish.js"
2
+ import { PrePublish } from "./prePublish.js"
3
+ import { reLogin } from "./login.js"
4
+ import fs from "node:fs"
5
5
 
6
6
  export function appstoreCommand(program) {
7
7
  let subCommands = [
@@ -9,8 +9,8 @@ export function appstoreCommand(program) {
9
9
  command: "login",
10
10
  desc: "登录",
11
11
  handler: async () => {
12
- await reLogin();
13
- },
12
+ await reLogin()
13
+ }
14
14
  },
15
15
  {
16
16
  command: "pre-publish <pkgPath>",
@@ -19,26 +19,26 @@ export function appstoreCommand(program) {
19
19
  args.option("c", {
20
20
  alias: "changelog",
21
21
  describe: "更改日志",
22
- type: "string",
23
- });
22
+ type: "string"
23
+ })
24
24
  args.option("F", {
25
25
  alias: "file",
26
26
  describe: "更改日志文件",
27
- type: "string",
28
- });
27
+ type: "string"
28
+ })
29
29
  args.option("G", {
30
30
  alias: "gid",
31
31
  describe: "内测组ID",
32
- type: "string",
33
- });
32
+ type: "string"
33
+ })
34
34
  },
35
35
  handler: async ({ pkgPath, changelog, file, gid }) => {
36
- const p = new PrePublish();
36
+ const p = new PrePublish()
37
37
  if (!changelog && file) {
38
- changelog = fs.readFileSync(file, "utf8");
38
+ changelog = fs.readFileSync(file, "utf8")
39
39
  }
40
- await p.publish(pkgPath, changelog, gid);
41
- },
40
+ await p.publish(pkgPath, changelog, gid)
41
+ }
42
42
  },
43
43
  {
44
44
  command: "publish <pkgPath>",
@@ -47,29 +47,29 @@ export function appstoreCommand(program) {
47
47
  args.option("c", {
48
48
  alias: "changelog",
49
49
  describe: "更改日志",
50
- type: "string",
51
- });
50
+ type: "string"
51
+ })
52
52
  args.option("F", {
53
53
  alias: "file",
54
54
  describe: "更改日志文件",
55
- type: "string",
56
- });
55
+ type: "string"
56
+ })
57
57
  },
58
58
  handler: async ({ pkgPath, changelog, file }) => {
59
- const p = new Publish();
59
+ const p = new Publish()
60
60
  if (!changelog && file) {
61
- changelog = fs.readFileSync(file, "utf8");
61
+ changelog = fs.readFileSync(file, "utf8")
62
62
  }
63
- await p.publish(pkgPath, changelog);
64
- },
65
- },
66
- ];
63
+ await p.publish(pkgPath, changelog)
64
+ }
65
+ }
66
+ ]
67
67
 
68
68
  program.command({
69
69
  command: "appstore",
70
70
  desc: "应用商店",
71
71
  builder: (args) => {
72
- args.command(subCommands);
73
- },
74
- });
72
+ args.command(subCommands)
73
+ }
74
+ })
75
75
  }
@@ -1,145 +1,140 @@
1
- import fetch from "node-fetch";
2
- import path from "path";
3
- import logger from "loglevel";
4
- import env from "../env.js";
5
- import inquirer from "inquirer";
6
- import FormData from "form-data";
1
+ import fetch from "node-fetch"
2
+ import path from "path"
3
+ import logger from "loglevel"
4
+ import env from "../env.js"
5
+ import inquirer from "inquirer"
7
6
 
8
- const accountServerUrl = "https://account.lazycat.cloud";
7
+ const accountServerUrl = "https://account.lazycat.cloud"
9
8
 
10
9
  function join(...params) {
11
- var x = path.join(...params);
12
- return x;
10
+ var x = path.join(...params)
11
+ return x
13
12
  }
14
13
 
15
- function login(username, password) {
16
- const form = new FormData();
17
- form.append("username", username);
18
- form.append("password", password);
19
-
14
+ async function login(username, password) {
20
15
  return fetch(join(accountServerUrl, "/api/login/signin"), {
21
16
  method: "POST",
22
- headers: {
23
- "content-type": `multipart/form-data; boundary=${form.getBoundary()}`,
24
- },
25
- body: form,
17
+ body: new URLSearchParams({
18
+ username: username,
19
+ password: password
20
+ })
26
21
  })
27
22
  .then(async (res) => {
28
- let bodyText = await res.text();
29
- let body = JSON.parse(bodyText);
23
+ let bodyText = await res.text()
24
+ let body = JSON.parse(bodyText)
30
25
  if (body.success) {
31
- return Promise.resolve(body.data);
26
+ return Promise.resolve(body.data)
32
27
  }
33
- return Promise.reject(body.message);
28
+ return Promise.reject(body.message)
34
29
  })
35
30
  .then((data) => {
36
- env.set({ token: data.token }, true);
37
- logger.info("登录成功!");
38
- });
31
+ env.set({ token: data.token }, true)
32
+ logger.info("登录成功!")
33
+ })
39
34
  }
40
35
 
41
36
  async function isLogin() {
42
37
  try {
43
- let token = env.get("token");
38
+ let token = env.get("token")
44
39
  if (!token) {
45
- return false;
40
+ return false
46
41
  }
47
42
  return fetch(join(accountServerUrl, "/api/user/current"), {
48
43
  method: "GET",
49
44
  headers: {
50
- "X-User-Token": token,
51
- },
45
+ "X-User-Token": token
46
+ }
52
47
  }).then(async (res) => {
53
- let bodyText = await res.text();
54
- let body = JSON.parse(bodyText);
48
+ let bodyText = await res.text()
49
+ let body = JSON.parse(bodyText)
55
50
  if (body.success) {
56
- return token;
51
+ return token
57
52
  }
58
- logger.debug(body.message);
59
- return false;
60
- });
53
+ logger.debug(body.message)
54
+ return false
55
+ })
61
56
  } catch (e) {
62
- logger.trace(e);
63
- return false;
57
+ logger.trace(e)
58
+ return false
64
59
  }
65
60
  }
66
61
 
67
62
  async function askUserInfo() {
68
- const noEmpty = (value) => value != "";
63
+ const noEmpty = (value) => value != ""
69
64
  return await inquirer.prompt([
70
65
  {
71
66
  type: "input",
72
67
  name: "username",
73
68
  message: "请输入登录用户名",
74
- validate: noEmpty,
69
+ validate: noEmpty
75
70
  },
76
71
  {
77
72
  type: "password",
78
73
  mask: "*",
79
74
  name: "password",
80
75
  message: "请输入登录密码",
81
- validate: noEmpty,
82
- },
83
- ]);
76
+ validate: noEmpty
77
+ }
78
+ ])
84
79
  }
85
80
 
86
81
  async function interactiveLogin() {
87
82
  try {
88
- let info = await askUserInfo();
89
- await login(info.username, info.password);
83
+ let info = await askUserInfo()
84
+ await login(info.username, info.password)
90
85
  } catch (e) {
91
- logger.debug("login error: ", e);
92
- logger.info("帐号或者密码错误,请重新输入!");
93
- return interactiveLogin();
86
+ logger.debug("login error: ", e)
87
+ logger.info("帐号或者密码错误,请重新输入!")
88
+ return interactiveLogin()
94
89
  }
95
90
  }
96
91
 
97
92
  export async function reLogin() {
98
- let token = await isLogin();
93
+ let token = await isLogin()
99
94
  if (token) {
100
95
  const questions = [
101
96
  {
102
97
  name: "relogin",
103
98
  type: "input",
104
99
  default: "n",
105
- message: "检测到已经登录,是否重新登录(y/n):",
106
- },
107
- ];
108
- const answers = await inquirer.prompt(questions);
100
+ message: "检测到已经登录,是否重新登录(y/n):"
101
+ }
102
+ ]
103
+ const answers = await inquirer.prompt(questions)
109
104
  if (answers.relogin.toLowerCase() === "n") {
110
- logger.info(`token: ${token}`);
111
- return;
105
+ logger.info(`token: ${token}`)
106
+ return
112
107
  }
113
108
  }
114
- await interactiveLogin();
109
+ await interactiveLogin()
115
110
  }
116
111
 
117
112
  export async function autoLogin() {
118
- let token = await isLogin();
113
+ let token = await isLogin()
119
114
  if (token) {
120
- logger.debug("appstore 已经登录");
121
- return;
115
+ logger.debug("appstore 已经登录")
116
+ return
122
117
  }
123
118
 
124
- logger.debug("token错误,尝试自动登录");
125
- await interactiveLogin();
119
+ logger.debug("token错误,尝试自动登录")
120
+ await interactiveLogin()
126
121
  }
127
122
 
128
123
  export async function request(url, options = {}) {
129
- await autoLogin();
124
+ await autoLogin()
130
125
 
131
- const token = env.get("token");
126
+ const token = env.get("token")
132
127
 
133
128
  let headers = {
134
129
  "X-User-Token": token,
135
- cookie: `userToken=${token}`,
136
- };
130
+ cookie: `userToken=${token}`
131
+ }
137
132
  if (options.headers) {
138
- headers = Object.assign({}, options.headers, headers);
133
+ headers = Object.assign({}, options.headers, headers)
139
134
  }
140
135
 
141
- options = Object.assign({}, options, { headers });
142
- logger.trace(`fetch ${url}`, options);
136
+ options = Object.assign({}, options, { headers })
137
+ logger.trace(`fetch ${url}`, options)
143
138
 
144
- return fetch(url, options);
139
+ return fetch(url, options)
145
140
  }