daodou-command 1.0.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.
@@ -0,0 +1,361 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const JSON5 = require('json5');
5
+
6
+ class ConfigManager {
7
+ constructor() {
8
+ this.configDir = path.join(os.homedir(), '.daodou');
9
+ this.configFile = path.join(this.configDir, 'config.json');
10
+ this.projectConfigFile = path.join(process.cwd(), '.daodourc');
11
+ this.config = this.loadConfig();
12
+ }
13
+
14
+ /**
15
+ * 加载配置文件
16
+ */
17
+ loadConfig() {
18
+ let projectConfig = {};
19
+ let globalConfig = {};
20
+
21
+ // 读取项目配置文件
22
+ if (fs.existsSync(this.projectConfigFile)) {
23
+ projectConfig = this.loadConfigFile(this.projectConfigFile);
24
+ }
25
+
26
+ // 读取全局配置文件
27
+ if (fs.existsSync(this.configFile)) {
28
+ globalConfig = this.loadConfigFile(this.configFile);
29
+ }
30
+
31
+ // 合并配置:项目配置优先,全局配置作为回退
32
+ return this.mergeConfigs(projectConfig, globalConfig);
33
+ }
34
+
35
+ /**
36
+ * 加载配置并创建默认配置(仅用于 build 命令)
37
+ */
38
+ loadConfigWithDefault() {
39
+ let projectConfig = {};
40
+ let globalConfig = {};
41
+
42
+ // 读取项目配置文件
43
+ if (fs.existsSync(this.projectConfigFile)) {
44
+ projectConfig = this.loadConfigFile(this.projectConfigFile);
45
+ } else {
46
+ // 如果项目配置文件不存在,创建默认配置
47
+ console.log('⚠️ 未找到项目配置文件,正在创建默认配置...');
48
+ this.createDefaultConfig();
49
+ console.log('✅ 默认配置文件已创建');
50
+ console.log('📝 请编辑配置文件,取消注释并填写 Jenkins 相关配置后重新运行命令');
51
+ console.log('🔄 修改完成后请重新运行命令');
52
+ process.exit(0);
53
+ }
54
+
55
+ // 读取全局配置文件
56
+ if (fs.existsSync(this.configFile)) {
57
+ globalConfig = this.loadConfigFile(this.configFile);
58
+ }
59
+
60
+ // 合并配置:项目配置优先,全局配置作为回退
61
+ return this.mergeConfigs(projectConfig, globalConfig);
62
+ }
63
+
64
+ /**
65
+ * 加载单个配置文件
66
+ */
67
+ loadConfigFile(filePath) {
68
+ try {
69
+ const data = fs.readFileSync(filePath, 'utf8');
70
+ return this.parseConfig(data);
71
+ } catch (error) {
72
+ console.warn(`加载配置文件失败 ${filePath}:`, error.message);
73
+ return {};
74
+ }
75
+ }
76
+
77
+ /**
78
+ * 合并配置:项目配置优先,全局配置作为回退
79
+ */
80
+ mergeConfigs(projectConfig, globalConfig) {
81
+ const merged = { ...globalConfig };
82
+
83
+ // 合并每个命令的配置
84
+ for (const [command, config] of Object.entries(projectConfig)) {
85
+ if (typeof config === 'object' && config !== null) {
86
+ // 对于 build 命令,某些参数只能从本地配置读取
87
+ if (command === 'build') {
88
+ merged[command] = this.mergeBuildConfig(globalConfig[command] || {}, config);
89
+ } else {
90
+ merged[command] = { ...globalConfig[command], ...config };
91
+ }
92
+ } else {
93
+ merged[command] = config;
94
+ }
95
+ }
96
+
97
+ return merged;
98
+ }
99
+
100
+ /**
101
+ * 合并 build 命令配置,jobName 等参数只能从本地配置读取
102
+ */
103
+ mergeBuildConfig(globalBuildConfig, localBuildConfig) {
104
+ const merged = { ...globalBuildConfig };
105
+
106
+ // 合并所有本地配置
107
+ Object.assign(merged, localBuildConfig);
108
+
109
+ // 如果本地配置中有 jobName,则使用本地的;否则清空全局的
110
+ if (localBuildConfig.jobName) {
111
+ merged.jobName = localBuildConfig.jobName;
112
+ } else {
113
+ delete merged.jobName;
114
+ }
115
+
116
+
117
+ return merged;
118
+ }
119
+
120
+ /**
121
+ * 解析配置文件(支持 JSON5 和 INI 格式)
122
+ */
123
+ parseConfig(content) {
124
+ // 检测是否为 JSON5 格式
125
+ if (this.isJson5Format(content)) {
126
+ return JSON5.parse(content);
127
+ }
128
+
129
+ // 回退到 INI 格式解析
130
+ return this.parseIniFormat(content);
131
+ }
132
+
133
+ /**
134
+ * 检测是否为 JSON5 格式
135
+ */
136
+ isJson5Format(content) {
137
+ const trimmed = content.trim();
138
+ return trimmed.startsWith('{') && trimmed.endsWith('}');
139
+ }
140
+
141
+ /**
142
+ * 解析 INI 格式配置
143
+ */
144
+ parseIniFormat(content) {
145
+ const config = {};
146
+ const lines = content.split(/\r?\n/);
147
+
148
+ for (const line of lines) {
149
+ if (!line.trim() || line.trim().startsWith('#')) continue;
150
+ const idx = line.indexOf('=');
151
+ if (idx > 0) {
152
+ const key = line.slice(0, idx).trim();
153
+ const value = line.slice(idx + 1).trim();
154
+ config[key] = value;
155
+ }
156
+ }
157
+
158
+ return config;
159
+ }
160
+
161
+ /**
162
+ * 保存配置文件
163
+ */
164
+ saveConfig() {
165
+ try {
166
+ // 确保配置目录存在
167
+ if (!fs.existsSync(this.configDir)) {
168
+ fs.mkdirSync(this.configDir, { recursive: true });
169
+ }
170
+
171
+ // 保存为 JSON5 格式
172
+ const content = JSON5.stringify(this.config, null, 2);
173
+ fs.writeFileSync(this.configFile, content);
174
+ } catch (error) {
175
+ console.error('保存配置文件失败:', error.message);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * 创建默认配置文件
181
+ */
182
+ createDefaultConfig() {
183
+ // 创建带注释的配置文件内容
184
+ const configContent = `{
185
+ // build 命令配置
186
+ build: {
187
+ // Jenkins 基础配置
188
+ // jenkinsUrl: "https://jenkins.example.com/",
189
+ // jenkinsBase: "https://jenkins.example.com/job",
190
+ // jenkinsToken: "your-jenkins-token",
191
+ // jenkinsUsername: "your-username",
192
+ // jenkinsPassword: "your-password",
193
+
194
+ // 构建任务配置(必须配置)
195
+ // jobName: "your-job-name", // 必须配置的任务名称
196
+
197
+ // 构建参数配置
198
+ // buildParams: {
199
+ // token: "your-jenkins-token",
200
+ // BUILD_ENV: "test",
201
+ // version: "0.0.1"
202
+ // }
203
+ },
204
+
205
+ // lang 命令配置
206
+ lang: {
207
+ defaultLang: "en",
208
+ defaultDir: "./public/locales",
209
+ fileName: "common.json",
210
+ // 代理相关配置
211
+ proxyListUrl: "https://free-proxy-list.net/",
212
+ proxyTestUrl: "https://httpbin.org/ip"
213
+ }
214
+ }`;
215
+
216
+ fs.writeFileSync(this.projectConfigFile, configContent);
217
+
218
+ // 返回解析后的配置对象(用于验证)
219
+ const defaultConfig = {
220
+ build: {},
221
+ lang: {
222
+ defaultLang: "en",
223
+ defaultDir: "./public/locales",
224
+ fileName: "common.json"
225
+ }
226
+ };
227
+
228
+ return defaultConfig;
229
+ }
230
+
231
+ /**
232
+ * 获取配置值
233
+ * @param {string} key 配置键
234
+ * @param {*} defaultValue 默认值
235
+ */
236
+ get(key, defaultValue = null) {
237
+ const keys = key.split('.');
238
+ let value = this.config;
239
+
240
+ for (const k of keys) {
241
+ if (value && typeof value === 'object' && k in value) {
242
+ value = value[k];
243
+ } else {
244
+ return defaultValue;
245
+ }
246
+ }
247
+
248
+ return value;
249
+ }
250
+
251
+ /**
252
+ * 设置配置值
253
+ * @param {string} key 配置键
254
+ * @param {*} value 配置值
255
+ */
256
+ set(key, value) {
257
+ const keys = key.split('.');
258
+ let current = this.config;
259
+
260
+ for (let i = 0; i < keys.length - 1; i++) {
261
+ const k = keys[i];
262
+ if (!current[k] || typeof current[k] !== 'object') {
263
+ current[k] = {};
264
+ }
265
+ current = current[k];
266
+ }
267
+
268
+ current[keys[keys.length - 1]] = value;
269
+ this.saveConfig();
270
+ }
271
+
272
+ /**
273
+ * 删除配置值
274
+ * @param {string} key 配置键
275
+ */
276
+ delete(key) {
277
+ const keys = key.split('.');
278
+ let current = this.config;
279
+
280
+ for (let i = 0; i < keys.length - 1; i++) {
281
+ const k = keys[i];
282
+ if (!current[k] || typeof current[k] !== 'object') {
283
+ return;
284
+ }
285
+ current = current[k];
286
+ }
287
+
288
+ delete current[keys[keys.length - 1]];
289
+ this.saveConfig();
290
+ }
291
+
292
+ /**
293
+ * 获取所有配置
294
+ */
295
+ getAll() {
296
+ return { ...this.config };
297
+ }
298
+
299
+ /**
300
+ * 设置Jenkins配置
301
+ * @param {Object} jenkinsConfig Jenkins配置
302
+ */
303
+ setJenkinsConfig(jenkinsConfig) {
304
+ this.set('jenkins', jenkinsConfig);
305
+ }
306
+
307
+ /**
308
+ * 获取命令配置
309
+ */
310
+ getCommandConfig(commandName) {
311
+ return this.get(commandName, {});
312
+ }
313
+
314
+ /**
315
+ * 获取 build 命令配置
316
+ */
317
+ getBuildConfig() {
318
+ return this.getCommandConfig('build');
319
+ }
320
+
321
+ /**
322
+ * 获取 build 命令配置(带默认配置创建)
323
+ */
324
+ getBuildConfigWithDefault() {
325
+ const config = this.loadConfigWithDefault();
326
+ return config.build || {};
327
+ }
328
+
329
+ /**
330
+ * 获取 lang 命令配置
331
+ */
332
+ getLangConfig() {
333
+ return this.getCommandConfig('lang');
334
+ }
335
+
336
+ /**
337
+ * 获取Jenkins配置(向后兼容)
338
+ */
339
+ getJenkinsConfig() {
340
+ return this.getBuildConfig();
341
+ }
342
+
343
+ /**
344
+ * 验证Jenkins配置
345
+ * @param {Object} config Jenkins配置
346
+ */
347
+ validateJenkinsConfig(config) {
348
+ const required = ['jenkinsUrl', 'jenkinsUsername', 'jenkinsToken'];
349
+ const missing = required.filter(key => !config[key]);
350
+
351
+ if (missing.length > 0) {
352
+ throw new Error(`缺少必要的Jenkins配置: ${missing.join(', ')}`);
353
+ }
354
+
355
+ return true;
356
+ }
357
+ }
358
+
359
+ module.exports = {
360
+ ConfigManager
361
+ };
@@ -0,0 +1,44 @@
1
+ const simpleGit = require('simple-git');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * 获取当前Git分支名称
6
+ * @returns {Promise<string>} 分支名称
7
+ */
8
+ async function getCurrentBranch() {
9
+ try {
10
+ const git = simpleGit(process.cwd());
11
+
12
+ // 检查是否为Git仓库
13
+ const isRepo = await git.checkIsRepo();
14
+ if (!isRepo) {
15
+ throw new Error('当前目录不是Git仓库');
16
+ }
17
+
18
+ // 获取当前分支
19
+ const branch = await git.branch();
20
+ return branch.current;
21
+ } catch (error) {
22
+ throw new Error(`获取分支失败: ${error.message}`);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * 检查是否有未提交的更改
28
+ * @returns {Promise<boolean>} 是否有未提交的更改
29
+ */
30
+ async function hasUncommittedChanges() {
31
+ try {
32
+ const git = simpleGit(process.cwd());
33
+ const status = await git.status();
34
+
35
+ return !status.isClean();
36
+ } catch (error) {
37
+ throw new Error(`检查未提交更改失败: ${error.message}`);
38
+ }
39
+ }
40
+
41
+ module.exports = {
42
+ getCurrentBranch,
43
+ hasUncommittedChanges
44
+ };
@@ -0,0 +1,151 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ class LangFileManager {
5
+ /**
6
+ * 添加键值对到 JSON 文件
7
+ * @param {string} filePath 文件路径
8
+ * @param {string} key 键名
9
+ * @param {string} value 值
10
+ * @returns {Object} 操作结果
11
+ */
12
+ async addKey(filePath, key, value) {
13
+ try {
14
+ // 检查目录是否存在
15
+ const dir = path.dirname(filePath);
16
+ if (!fs.existsSync(dir)) {
17
+ throw new Error(`目录不存在: ${dir}`);
18
+ }
19
+
20
+ // 检查文件是否存在
21
+ if (!fs.existsSync(filePath)) {
22
+ throw new Error(`文件不存在: ${filePath}`);
23
+ }
24
+
25
+ // 读取文件内容
26
+ const content = fs.readFileSync(filePath, 'utf8');
27
+ let data;
28
+
29
+ try {
30
+ data = JSON.parse(content);
31
+ } catch (error) {
32
+ throw new Error(`JSON 格式错误: ${error.message}`);
33
+ }
34
+
35
+ // 检查键是否已存在
36
+ if (data.hasOwnProperty(key)) {
37
+ return { success: false, skipped: true, message: '键已存在' };
38
+ }
39
+
40
+ // 添加键值对
41
+ data[key] = value;
42
+
43
+ // 写入文件
44
+ const newContent = JSON.stringify(data, null, 2);
45
+ fs.writeFileSync(filePath, newContent, 'utf8');
46
+
47
+ return { success: true, message: '添加成功' };
48
+
49
+ } catch (error) {
50
+ throw error;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * 从 JSON 文件删除键
56
+ * @param {string} filePath 文件路径
57
+ * @param {string} key 键名
58
+ * @returns {Object} 操作结果
59
+ */
60
+ async removeKey(filePath, key) {
61
+ try {
62
+ // 检查目录是否存在
63
+ const dir = path.dirname(filePath);
64
+ if (!fs.existsSync(dir)) {
65
+ throw new Error(`目录不存在: ${dir}`);
66
+ }
67
+
68
+ // 检查文件是否存在
69
+ if (!fs.existsSync(filePath)) {
70
+ throw new Error(`文件不存在: ${filePath}`);
71
+ }
72
+
73
+ // 读取文件内容
74
+ const content = fs.readFileSync(filePath, 'utf8');
75
+ let data;
76
+
77
+ try {
78
+ data = JSON.parse(content);
79
+ } catch (error) {
80
+ throw new Error(`JSON 格式错误: ${error.message}`);
81
+ }
82
+
83
+ // 检查键是否存在
84
+ if (!data.hasOwnProperty(key)) {
85
+ return { success: false, notFound: true, message: '键不存在' };
86
+ }
87
+
88
+ // 删除键
89
+ delete data[key];
90
+
91
+ // 写入文件
92
+ const newContent = JSON.stringify(data, null, 2);
93
+ fs.writeFileSync(filePath, newContent, 'utf8');
94
+
95
+ return { success: true, message: '删除成功' };
96
+
97
+ } catch (error) {
98
+ throw error;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * 检查文件是否存在
104
+ * @param {string} filePath 文件路径
105
+ * @returns {boolean} 文件是否存在
106
+ */
107
+ fileExists(filePath) {
108
+ return fs.existsSync(filePath);
109
+ }
110
+
111
+ /**
112
+ * 检查目录是否存在
113
+ * @param {string} dirPath 目录路径
114
+ * @returns {boolean} 目录是否存在
115
+ */
116
+ dirExists(dirPath) {
117
+ return fs.existsSync(dirPath);
118
+ }
119
+
120
+ /**
121
+ * 读取 JSON 文件
122
+ * @param {string} filePath 文件路径
123
+ * @returns {Object} JSON 数据
124
+ */
125
+ readJsonFile(filePath) {
126
+ try {
127
+ const content = fs.readFileSync(filePath, 'utf8');
128
+ return JSON.parse(content);
129
+ } catch (error) {
130
+ throw new Error(`读取文件失败: ${error.message}`);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * 写入 JSON 文件
136
+ * @param {string} filePath 文件路径
137
+ * @param {Object} data JSON 数据
138
+ */
139
+ writeJsonFile(filePath, data) {
140
+ try {
141
+ const content = JSON.stringify(data, null, 2);
142
+ fs.writeFileSync(filePath, content, 'utf8');
143
+ } catch (error) {
144
+ throw new Error(`写入文件失败: ${error.message}`);
145
+ }
146
+ }
147
+ }
148
+
149
+ module.exports = {
150
+ LangFileManager
151
+ };