@taole/deploy-helper 1.0.4 → 1.0.5-beta.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/index.mjs CHANGED
@@ -10,6 +10,8 @@ import path from 'path';
10
10
  import { fileURLToPath } from 'url';
11
11
  import { cmdWhoami, cmdLogout, cmdLogin } from './lib/login.mjs';
12
12
  import { cmdProjectCreate, cmdProjectPublish, cmdProjectPull } from './lib/project.mjs';
13
+ import { cmdCommit } from './lib/git.mjs';
14
+ import { lightDeploy } from './lib/lightDeploy.mjs';
13
15
 
14
16
  const __filename = fileURLToPath(import.meta.url);
15
17
  const __dirname = path.dirname(__filename);
@@ -106,10 +108,10 @@ async function getScpClient() {
106
108
  return scpClient;
107
109
  }
108
110
 
109
- async function getVersion() {
111
+ function getPackageJson() {
110
112
  const packageJsonPath = join(__dirname, "package.json");
111
113
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
112
- return packageJson.version;
114
+ return packageJson;
113
115
  }
114
116
 
115
117
  const commandMap = {};
@@ -128,6 +130,7 @@ function doRegisterCommands() {
128
130
  registerCommand("create", cmdProjectCreate, "创建项目");
129
131
  registerCommand("publish", cmdProjectPublish, "更新项目到H5平台");
130
132
  registerCommand("pull", cmdProjectPull, "拉取已有项目到本地");
133
+ registerCommand("commit", cmdCommit, "按任务号提交(参数为说明,如 feat: xxx)");
131
134
  registerCommand("whoami", cmdWhoami, "查看当前登录用户信息");
132
135
  registerCommand("logout", cmdLogout, "退出登录");
133
136
  registerCommand("login", cmdLogin, "登录");
@@ -136,7 +139,8 @@ function doRegisterCommands() {
136
139
  doRegisterCommands();
137
140
 
138
141
  async function main() {
139
- const version = await getVersion();
142
+ const packageJson = getPackageJson();
143
+ const version = packageJson.version;
140
144
 
141
145
  // process.argv.includes("--debug") ||
142
146
  // 默认开启debug debug目前只是打印日志的时候额外打印时间
@@ -283,7 +287,10 @@ async function main() {
283
287
  process.exit(1);
284
288
  }
285
289
 
286
-
290
+ const needHandleLight = config.type === "ddfe:cdn";
291
+ if(needHandleLight){
292
+ await lightDeploy(config, mode);
293
+ }
287
294
  const needHandleAssets = config.assets && config.assets.dest;
288
295
 
289
296
  // 需要处理entry
package/lib/git.mjs ADDED
@@ -0,0 +1,102 @@
1
+ // Git 辅助操作(基于 simple-git)
2
+ import { simpleGit } from "simple-git";
3
+ import { log } from "./util.mjs";
4
+
5
+ /**
6
+ * @param {string} branch
7
+ * @param {string} latestCommitMsg
8
+ */
9
+ function extractTaskId(branch, latestCommitMsg) {
10
+ const fromBranch = branch.match(/#([A-Z]+-\d+)$/);
11
+ if (fromBranch) return fromBranch[1];
12
+ const fromCommit = latestCommitMsg.match(/-#([A-Z]+-\d+)/);
13
+ if (fromCommit) return fromCommit[1];
14
+ return null;
15
+ }
16
+
17
+ function getUserMessage() {
18
+ const argvMsg = process.argv.slice(3).join(" ").trim();
19
+ if (argvMsg) return argvMsg;
20
+ const envMsg = (
21
+ process.env.npm_config_message ||
22
+ process.env.npm_config_msg ||
23
+ ""
24
+ ).trim();
25
+ return envMsg;
26
+ }
27
+
28
+ /**
29
+ * @param {string} input
30
+ * @returns {{ full: string, typePart: null } | { full: null, typePart: string } | null}
31
+ */
32
+ /** 参数是否已自带 `-#PROJ-123` 形式任务前缀(无需再从分支/历史解析) */
33
+ function hasExplicitTaskPrefix(msg) {
34
+ return /^-#[A-Z]+-\d+/.test(msg.trim());
35
+ }
36
+
37
+ function normalizeMessage(input) {
38
+ const msg = input.trim();
39
+ if (!msg) return null;
40
+ if (msg.startsWith("-#")) {
41
+ return { full: msg, typePart: null };
42
+ }
43
+ const match = msg.match(/^([a-z]+):\s*(.+)$/i);
44
+ if (!match) return null;
45
+ return { full: null, typePart: `${match[1]}: ${match[2]}` };
46
+ }
47
+
48
+ async function getLatestSubject(git) {
49
+ try {
50
+ const logResult = await git.log({ maxCount: 1 });
51
+ if (!logResult.latest) return "";
52
+ return logResult.latest.message.split("\n")[0].trim();
53
+ } catch {
54
+ return "";
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 按任务号规范提交:默认从分支名(末尾 #任务号)或最新提交中提取任务号;若参数已含 `-#DDKH-4169` 形式前缀则直接使用该说明提交。
60
+ * 用法: dh commit "feat: 说明" 或 dh commit "-#DDKH-4169 feat: 完整信息"
61
+ */
62
+ export async function cmdCommit() {
63
+ const git = simpleGit(process.cwd());
64
+ const rawMsg = getUserMessage();
65
+
66
+ let commitMsg;
67
+
68
+ if (rawMsg.trim() && hasExplicitTaskPrefix(rawMsg)) {
69
+ const parsed = normalizeMessage(rawMsg);
70
+ if (!parsed) {
71
+ log('请传入提交说明,例如: dh commit "feat:我的提交信息"');
72
+ process.exit(1);
73
+ }
74
+ commitMsg = parsed.full;
75
+ } else {
76
+ const branch = (await git.status()).current;
77
+ const latestCommitMsg = await getLatestSubject(git);
78
+ const taskId = extractTaskId(branch, latestCommitMsg);
79
+ if (!taskId) {
80
+ log("未能从分支名或最新提交中提取任务号(如 DDKH-4169)");
81
+ process.exit(1);
82
+ }
83
+
84
+ const parsed = normalizeMessage(rawMsg);
85
+ if (!parsed) {
86
+ log('请传入提交说明,例如: dh commit "feat:我的提交信息"');
87
+ process.exit(1);
88
+ }
89
+
90
+ commitMsg = parsed.full || `-#${taskId} ${parsed.typePart}`;
91
+ }
92
+
93
+ const st = await git.status();
94
+ if (st.isClean()) {
95
+ log("没有可提交的改动");
96
+ process.exit(1);
97
+ }
98
+ await git.add(".");
99
+ await git.commit(commitMsg);
100
+ log(`commit 完成: ${commitMsg}`);
101
+ process.exit(0);
102
+ }
@@ -0,0 +1,102 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { log } from "./util.mjs";
4
+ import { getCache as getLoginCache } from "./login.mjs";
5
+ import { genArchive } from "./project.mjs";
6
+
7
+ const isDev = false;
8
+ const apiHost = isDev ? "http://localhost:9000" : "https://fapi.tuwan.com";
9
+
10
+ function getErrorMessage(resJson, fallback) {
11
+ const message = resJson?.message;
12
+ if (Array.isArray(message)) {
13
+ return message.join(", ");
14
+ }
15
+ if (message) {
16
+ return String(message);
17
+ }
18
+ return fallback;
19
+ }
20
+
21
+ async function apiCdnUpload({ distZipPath, prefix, mode, includeHtml, entryHtmlMap }, userCache) {
22
+ const formData = new FormData();
23
+ formData.append("prefix", prefix);
24
+ formData.append("mode", mode);
25
+ formData.append("includeHtml", includeHtml ? "true" : "false");
26
+ if (entryHtmlMap) {
27
+ const entryHtmlMapStr =
28
+ typeof entryHtmlMap === "string" ? entryHtmlMap : JSON.stringify(entryHtmlMap);
29
+ formData.append("entryHtmlMap", entryHtmlMapStr);
30
+ }
31
+ formData.append(
32
+ "file",
33
+ new Blob([fs.readFileSync(distZipPath)], { type: "application/zip" }),
34
+ "dist.zip"
35
+ );
36
+
37
+ const res = await fetch(`${apiHost}/cdn/upload`, {
38
+ method: "POST",
39
+ body: formData,
40
+ headers: {
41
+ Cookie: `Tuwan_Passport=${userCache.Tuwan_Passport}`,
42
+ },
43
+ });
44
+ const resJson = await res.json();
45
+ if (!res.ok) {
46
+ throw new Error(getErrorMessage(resJson, "CDN 上传失败"));
47
+ }
48
+ return resJson;
49
+ }
50
+
51
+ export async function lightDeploy(config, mode) {
52
+ log(`light模式, 开始部署`, { mode, prefix: config.prefix, includeHtml: config.includeHtml });
53
+
54
+ if (!mode || (mode !== "test" && mode !== "prod")) {
55
+ throw new Error("部署环境无效,仅支持 test 或 prod");
56
+ }
57
+
58
+ const prefix = config.prefix;
59
+ if (!prefix) {
60
+ throw new Error("配置缺少 prefix");
61
+ }
62
+
63
+ const userCache = await getLoginCache();
64
+ if (!userCache?.userInfo?.uid) {
65
+ throw new Error("请先登录");
66
+ }
67
+
68
+ const workDir = process.cwd();
69
+ const distDir = config.distDir || "dist";
70
+ const distPath = path.join(workDir, distDir);
71
+ if (!fs.existsSync(distPath)) {
72
+ throw new Error(`${distPath} 不存在,请先执行构建`);
73
+ }
74
+
75
+ const distZipPath = path.join(workDir, "dist.zip");
76
+ if (fs.existsSync(distZipPath)) {
77
+ fs.unlinkSync(distZipPath);
78
+ }
79
+
80
+ log(`开始打包 ${distPath}`);
81
+ await genArchive(distZipPath, distPath);
82
+ log(`打包完成: ${distZipPath}`);
83
+
84
+ const includeHtml = config.includeHtml === true;
85
+ log(`开始上传至 CDN,mode=${mode}, prefix=${prefix}, includeHtml=${includeHtml}`);
86
+ const result = await apiCdnUpload(
87
+ {
88
+ distZipPath,
89
+ prefix,
90
+ mode,
91
+ includeHtml,
92
+ entryHtmlMap: config.entryHtmlMap,
93
+ },
94
+ userCache
95
+ );
96
+
97
+ if (fs.existsSync(distZipPath)) {
98
+ fs.unlinkSync(distZipPath);
99
+ }
100
+
101
+ log(`CDN 上传成功`, result);
102
+ }
package/lib/project.mjs CHANGED
@@ -9,7 +9,7 @@ import { getCache as getLoginCache } from "./login.mjs";
9
9
  const isDev = false;
10
10
  const apiHost = isDev ? "http://localhost:9000" : "https://fapi.tuwan.com";
11
11
 
12
- function genArchive(outputPath, dir) {
12
+ export function genArchive(outputPath, dir) {
13
13
  return new Promise((resolve, reject) => {
14
14
  const output = fs.createWriteStream(outputPath);
15
15
  const archive = archiver("zip", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taole/deploy-helper",
3
- "version": "1.0.4",
3
+ "version": "1.0.5-beta.1",
4
4
  "description": "脚本部署工具,用于将项目部署到测试环境或生产环境",
5
5
  "main": "index.mjs",
6
6
  "type": "module",