@taole/deploy-helper 1.1.4 → 1.1.5-beta.2

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.
@@ -0,0 +1,61 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { PassThrough } from "stream";
4
+ import archiver from "archiver";
5
+
6
+ export function archiveToBuffer(setupArchive) {
7
+ return new Promise((resolve, reject) => {
8
+ const chunks = [];
9
+ const output = new PassThrough();
10
+ output.on("data", (chunk) => chunks.push(chunk));
11
+ output.on("end", () => resolve(Buffer.concat(chunks)));
12
+ output.on("error", reject);
13
+
14
+ const archive = archiver("zip", { zlib: { level: 1 } });
15
+ archive.on("error", reject);
16
+ archive.pipe(output);
17
+
18
+ setupArchive(archive);
19
+ archive.finalize();
20
+ });
21
+ }
22
+
23
+ function appendDirToArchive(archive, dir, zipPrefix) {
24
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
25
+ const fullPath = path.join(dir, entry.name);
26
+ const entryZipPath = zipPrefix
27
+ ? path.posix.join(zipPrefix, entry.name)
28
+ : entry.name;
29
+ if (entry.isDirectory()) {
30
+ appendDirToArchive(archive, fullPath, entryZipPath);
31
+ } else {
32
+ archive.file(fullPath, { name: entryZipPath });
33
+ }
34
+ }
35
+ }
36
+
37
+ export function genProjectArchiveBuffer(distPath) {
38
+ return archiveToBuffer((archive) => {
39
+ archive.directory(distPath, false);
40
+ });
41
+ }
42
+
43
+ /**
44
+ * lib 类型 CDN 打包:若 dist 内仅有名为 version 的子目录则原样打包;
45
+ * 若无子目录则为 zip 内所有文件加上 version 前缀。
46
+ */
47
+ export function genLibArchiveBuffer(distPath, version) {
48
+ const subdirs = fs
49
+ .readdirSync(distPath, { withFileTypes: true })
50
+ .filter((entry) => entry.isDirectory());
51
+
52
+ return archiveToBuffer((archive) => {
53
+ if (subdirs.length === 1 && subdirs[0].name === version) {
54
+ archive.directory(distPath, false);
55
+ } else if (subdirs.length === 0) {
56
+ appendDirToArchive(archive, distPath, version);
57
+ } else {
58
+ archive.directory(distPath, false);
59
+ }
60
+ });
61
+ }
@@ -2,7 +2,10 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import { log } from "./util.mjs";
4
4
  import { getCache as getLoginCache } from "./login.mjs";
5
- import { genArchive } from "./project.mjs";
5
+ import {
6
+ genLibArchiveBuffer,
7
+ genProjectArchiveBuffer,
8
+ } from "./archive.mjs";
6
9
 
7
10
  const isDev = false;
8
11
  const apiHost = isDev ? "http://localhost:9000" : "https://fapi.tuwan.com";
@@ -19,7 +22,7 @@ function getErrorMessage(resJson, fallback) {
19
22
  }
20
23
 
21
24
  async function apiCdnUpload(
22
- { distZipPath, prefix, mode, includeHtml, entryHtmlMap, type, version },
25
+ { distZipBuffer, prefix, mode, includeHtml, entryHtmlMap, type, version },
23
26
  userCache
24
27
  ) {
25
28
  const formData = new FormData();
@@ -41,7 +44,7 @@ async function apiCdnUpload(
41
44
  }
42
45
  formData.append(
43
46
  "file",
44
- new Blob([fs.readFileSync(distZipPath)], { type: "application/zip" }),
47
+ new Blob([distZipBuffer], { type: "application/zip" }),
45
48
  "dist.zip"
46
49
  );
47
50
 
@@ -69,6 +72,11 @@ export async function lightDeploy(config, mode) {
69
72
  fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8")
70
73
  );
71
74
  const version = projectJson.version;
75
+ // 校验cdnType只能是lib或者project
76
+ const cdnType = config.cdnType;
77
+ if (cdnType !== "lib" && cdnType !== "project") {
78
+ throw new Error("配置项 cdnType 仅支持 'lib' 或 'project'");
79
+ }
72
80
  log(`light模式, 开始部署`, {
73
81
  mode,
74
82
  prefix: config.prefix,
@@ -98,14 +106,12 @@ export async function lightDeploy(config, mode) {
98
106
  throw new Error(`${distPath} 不存在,请先执行构建`);
99
107
  }
100
108
 
101
- const distZipPath = path.join(workDir, "cdnDist.zip");
102
- if (fs.existsSync(distZipPath)) {
103
- fs.unlinkSync(distZipPath);
104
- }
105
-
106
109
  log(`开始打包 ${distPath}`);
107
- await genArchive(distZipPath, distPath);
108
- log(`打包完成: ${distZipPath}`);
110
+ const distZipBuffer =
111
+ cdnType === "lib"
112
+ ? await genLibArchiveBuffer(distPath, version)
113
+ : await genProjectArchiveBuffer(distPath);
114
+ log(`打包完成,大小 ${distZipBuffer.length} 字节`);
109
115
 
110
116
  const includeHtml = config.includeHtml === true;
111
117
  log(
@@ -113,25 +119,17 @@ export async function lightDeploy(config, mode) {
113
119
  config.cdnType || "project"
114
120
  }, version=${version}, includeHtml=${includeHtml}`
115
121
  );
116
- try {
117
- const result = await apiCdnUpload(
118
- {
119
- distZipPath,
120
- prefix,
121
- mode,
122
- includeHtml,
123
- entryHtmlMap: config.entryHtmlMap,
124
- type: config.cdnType,
125
- version,
126
- },
127
- userCache
128
- );
129
- log(`CDN 上传成功`, result);
130
- } catch (error) {
131
- throw error;
132
- } finally {
133
- if (fs.existsSync(distZipPath)) {
134
- fs.unlinkSync(distZipPath);
135
- }
136
- }
122
+ const result = await apiCdnUpload(
123
+ {
124
+ distZipBuffer,
125
+ prefix,
126
+ mode,
127
+ includeHtml,
128
+ entryHtmlMap: config.entryHtmlMap,
129
+ type: config.cdnType,
130
+ version,
131
+ },
132
+ userCache
133
+ );
134
+ log(`CDN 上传成功`, result);
137
135
  }
package/lib/project.mjs CHANGED
@@ -2,31 +2,30 @@
2
2
  import { simpleGit } from "simple-git";
3
3
  import path from "path";
4
4
  import fs from "fs";
5
- import archiver from "archiver";
6
5
  import { log, getJsonConfig, runCmdAsync } from "./util.mjs";
7
6
  import { getCache as getLoginCache } from "./login.mjs";
7
+ import { genProjectArchiveBuffer } from "./archive.mjs";
8
8
 
9
9
  const isDev = false;
10
10
  const apiHost = isDev ? "http://localhost:9000" : "https://fapi.tuwan.com";
11
+ const WEB_ACT_REPO_PATH = "69b3821af7b43e00d420cb32/Web-Act";
11
12
 
12
- export function genArchive(outputPath, dir) {
13
- return new Promise((resolve, reject) => {
14
- const output = fs.createWriteStream(outputPath);
15
- const archive = archiver("zip", {
16
- zlib: { level: 1 }, // Sets the compression level.
17
- });
18
- archive.on("error", reject);
19
- archive.on("finish", () => {
20
- // console.log("genArchive finish");
21
- setTimeout(() => {
22
- // console.log("genArchive finish true");
23
- resolve();
24
- }, 300);
25
- });
26
- archive.pipe(output);
27
- archive.directory(dir, false);
28
- archive.finalize();
29
- });
13
+ function extractWebActProjectNameFromRemote(remoteUrl) {
14
+ if (!remoteUrl) {
15
+ return null;
16
+ }
17
+ const match = remoteUrl.match(new RegExp(`${WEB_ACT_REPO_PATH}/([^/.]+)(?:\\.git)?/?$`));
18
+ return match ? match[1] : null;
19
+ }
20
+
21
+ async function checkPackageNameWithGitRemote(git, packageName) {
22
+ const remotes = await git.getRemotes(true);
23
+ const remoteUrls = remotes.flatMap((remote) => [remote.refs.fetch, remote.refs.push]).filter(Boolean);
24
+ const gitProjectName = remoteUrls.map(extractWebActProjectNameFromRemote).find(Boolean);
25
+
26
+ if (gitProjectName && gitProjectName !== packageName) {
27
+ throw new Error(`项目名称不匹配: git仓库项目名称为${gitProjectName}, package.json name为${packageName}`);
28
+ }
30
29
  }
31
30
 
32
31
  async function apiDeployProject(name, version, env, userCache) {
@@ -84,14 +83,14 @@ async function apiCreateProject(name, userCache) {
84
83
  }
85
84
  }
86
85
 
87
- async function apiPublishProject(name, version, distZipPath, userCache) {
86
+ async function apiPublishProject(name, version, distZipBuffer, userCache) {
88
87
  const urlPath = `/h5projects/${name}/publish`;
89
88
  const formData = new FormData();
90
89
  formData.append("updaterUid", userCache.userInfo.uid);
91
90
  formData.append("version", version);
92
91
  formData.append(
93
92
  "file",
94
- new Blob([fs.readFileSync(distZipPath)], { type: "application/zip" }),
93
+ new Blob([distZipBuffer], { type: "application/zip" }),
95
94
  "dist.zip"
96
95
  );
97
96
  const res = await fetch(`${apiHost}${urlPath}`, {
@@ -240,6 +239,7 @@ export const cmdProjectPublish = async () => {
240
239
  log("package.json或者其version字段或name字段不存在");
241
240
  process.exit(1);
242
241
  }
242
+ await checkPackageNameWithGitRemote(git, packageJson.name);
243
243
 
244
244
  const newVersion = await apiGetProjectVersion(packageJson.name, userCache);
245
245
  packageJson.version = newVersion;
@@ -257,21 +257,17 @@ export const cmdProjectPublish = async () => {
257
257
  await runCmdAsync(buildCmd);
258
258
  log(`构建项目成功,开始打包构建产物`);
259
259
  const distPath = path.join(process.cwd(), "dist");
260
- const distZipPath = path.join(process.cwd(), "dist.zip");
261
260
  if (!fs.existsSync(distPath)) {
262
261
  log("dist目录不存在");
263
262
  process.exit(1);
264
263
  }
265
- // 把dist目录打包为zip包
266
- if (fs.existsSync(distZipPath)) {
267
- fs.unlinkSync(distZipPath);
268
- }
269
- await genArchive(distZipPath, distPath);
270
- log(`dist目录打包为zip包: ${distZipPath}, 开始推送构建产物`);
264
+ log(`开始打包 ${distPath}`);
265
+ const distZipBuffer = await genProjectArchiveBuffer(distPath);
266
+ log(`打包完成,大小 ${distZipBuffer.length} 字节,开始推送构建产物`);
271
267
  const _publishResult = await apiPublishProject(
272
268
  packageJson.name,
273
269
  packageJson.version,
274
- distZipPath,
270
+ distZipBuffer,
275
271
  userCache
276
272
  );
277
273
  log(`构建产物推送成功, 开始部署项目至${env}环境`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taole/deploy-helper",
3
- "version": "1.1.4",
3
+ "version": "1.1.5-beta.2",
4
4
  "description": "脚本部署工具,用于将项目部署到测试环境或生产环境",
5
5
  "main": "index.mjs",
6
6
  "type": "module",