wukong-gitlog-cli 1.0.38 → 1.0.40

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 (124) hide show
  1. package/.eslintrc +1 -0
  2. package/.prettierrc +2 -1
  3. package/CHANGELOG.md +103 -0
  4. package/README.md +93 -173
  5. package/README.zh-CN.md +85 -137
  6. package/doc//347/233/256/345/275/225/347/273/223/346/236/204.md +2871 -0
  7. package/package.json +33 -29
  8. package/rc/.wukonggitlogrc +53 -0
  9. package/scripts/compareHourlyCounts.mjs +42 -0
  10. package/scripts/compareLatest.mjs +106 -0
  11. package/src/app/analyzeAction.mjs +120 -0
  12. package/src/app/exportAction.mjs +215 -0
  13. package/src/app/exportActionProgress.mjs +37 -0
  14. package/src/app/helpers.mjs +292 -0
  15. package/src/app/initAction.mjs +110 -0
  16. package/src/app/initActionWithTemp.mjs +192 -0
  17. package/src/app/journalAction.mjs +117 -0
  18. package/src/app/overtimeAction.mjs +100 -0
  19. package/src/app/runProfileEnd.mjs +0 -0
  20. package/src/app/serveAction.mjs +73 -0
  21. package/src/app/versionAction.mjs +7 -0
  22. package/src/cli/defineOptions.mjs +209 -0
  23. package/src/cli/index.mjs +0 -0
  24. package/src/cli/parseOptions.mjs +126 -8
  25. package/src/constants/index.mjs +16 -2
  26. package/src/domain/author/analyze.mjs +6 -0
  27. package/src/domain/author/map.mjs +0 -0
  28. package/src/domain/export/exportAuthor.mjs +28 -0
  29. package/src/domain/export/exportAuthorChanges.mjs +27 -0
  30. package/src/domain/export/exportAuthorChangesJson.mjs +31 -0
  31. package/src/domain/export/exportByMonth.mjs +157 -0
  32. package/src/domain/export/exportByWeek.mjs +121 -0
  33. package/src/domain/export/exportCommits.mjs +26 -0
  34. package/src/domain/export/exportCommitsExcel.mjs +45 -0
  35. package/src/domain/export/exportCommitsJson.mjs +31 -0
  36. package/src/domain/export/index.mjs +91 -0
  37. package/src/domain/git/ensureGitAvailable.mjs +66 -0
  38. package/src/domain/git/ensureGitRepo.mjs +41 -0
  39. package/src/domain/git/getGitFeatures.mjs +59 -0
  40. package/src/domain/git/getGitLogs.mjs +326 -0
  41. package/src/domain/git/getGitUser.mjs +44 -0
  42. package/src/domain/git/getRepoRoot.mjs +32 -0
  43. package/src/domain/git/gitCapability.mjs +119 -0
  44. package/src/domain/git/index.mjs +96 -0
  45. package/src/domain/git/resolveGerrit.mjs +102 -0
  46. package/src/domain/overtime/analyze.mjs +48 -0
  47. package/src/domain/overtime/index.mjs +3 -0
  48. package/src/domain/overtime/perPeriod.mjs +15 -0
  49. package/src/domain/overtime/render.mjs +15 -0
  50. package/src/i18n/index.mjs +38 -0
  51. package/src/i18n/resources.mjs +252 -0
  52. package/src/index.mjs +132 -649
  53. package/src/infra/cache.mjs +0 -0
  54. package/src/infra/configStore.mjs +128 -0
  55. package/src/infra/fs.mjs +0 -0
  56. package/src/infra/path.mjs +0 -0
  57. package/src/output/csv/overtime.mjs +12 -0
  58. package/src/output/csv.mjs +0 -0
  59. package/src/output/data/readData.mjs +54 -0
  60. package/src/output/data/writeData.mjs +145 -0
  61. package/src/output/excel/commits.mjs +9 -0
  62. package/src/output/excel/outputExcelDayReport.mjs +92 -0
  63. package/src/output/excel/perPeriod.mjs +24 -0
  64. package/src/{excel.mjs → output/excel.mjs} +3 -2
  65. package/src/output/index.mjs +79 -0
  66. package/src/output/json/overtime.mjs +9 -0
  67. package/src/output/tab/overtime.mjs +12 -0
  68. package/src/output/tab.mjs +0 -0
  69. package/src/output/text/commits.mjs +9 -0
  70. package/src/output/text/index.mjs +3 -0
  71. package/src/output/text/outputTxtDayReport.mjs +74 -0
  72. package/src/output/text/overtime.mjs +18 -0
  73. package/src/output/utils/getEsmJs.mjs +10 -0
  74. package/src/output/utils/index.mjs +14 -0
  75. package/src/output/utils/outputPath.mjs +19 -0
  76. package/src/output/utils/writeFile.mjs +10 -0
  77. package/src/serve/index.mjs +0 -0
  78. package/src/{server.mjs → serve/startServer.mjs} +21 -3
  79. package/src/serve/writeData.mjs +0 -0
  80. package/src/utils/authorNormalizer.mjs +28 -2
  81. package/src/utils/buildAuthorChangeStats.mjs +44 -0
  82. package/src/utils/deepMerge.mjs +13 -0
  83. package/src/utils/getPackage.mjs +11 -0
  84. package/src/utils/getProfileDirFile.mjs +12 -0
  85. package/src/utils/{file.mjs → groupRecords.mjs} +8 -9
  86. package/src/utils/index.mjs +5 -2
  87. package/src/utils/logger.mjs +28 -17
  88. package/src/utils/profiler.mjs +0 -101
  89. package/src/utils/resolve.mjs +11 -0
  90. package/src/utils/showVersionInfo.mjs +6 -2
  91. package/src/utils/time.mjs +0 -0
  92. package/src/utils/wait.mjs +2 -0
  93. package/web/app.js +3233 -260
  94. package/web/index.html +175 -22
  95. package/web/revoke/alpha1/app.js +4324 -0
  96. package/web/revoke/alpha1/index.html +266 -0
  97. package/web/revoke/app.before.js +3139 -0
  98. package/web/revoke/index-before.html +181 -0
  99. package/web/static/style.css +155 -9
  100. package/src/git.mjs +0 -256
  101. package/src/handlers/handleServe.mjs +0 -203
  102. package/src/lib/configStore.mjs +0 -11
  103. package/src/lib/memoize.mjs +0 -14
  104. package/src/utils/analyzeOvertimeCached.mjs +0 -7
  105. package/src/utils/checkUpdate.mjs +0 -130
  106. package/src/utils/exitWithTime.mjs +0 -17
  107. package/src/utils/handleSuccess.mjs +0 -9
  108. package/src/utils/logDev.mjs +0 -19
  109. package/src/utils/output.mjs +0 -26
  110. package/src/utils/profiler/diff.mjs +0 -26
  111. package/src/utils/profiler/format.mjs +0 -11
  112. package/src/utils/profiler/index.mjs +0 -144
  113. package/src/utils/profiler/trace.mjs +0 -26
  114. package/src/utils/time/scopeTimer.mjs +0 -37
  115. package/src/utils/time/timer.mjs +0 -33
  116. package/src/utils/time/withTimer.mjs +0 -11
  117. package/src/utils/timer.mjs +0 -35
  118. /package/src/{overtime → domain/overtime}/createOvertimeStats.mjs +0 -0
  119. /package/src/{overtime → domain/overtime}/overtime.mjs +0 -0
  120. /package/src/{json.mjs → output/json.mjs} +0 -0
  121. /package/src/{renderAuthorMapText.mjs → output/renderAuthorMapText.mjs} +0 -0
  122. /package/src/{stats-text.mjs → output/stats-text.mjs} +0 -0
  123. /package/src/{stats.mjs → output/stats.mjs} +0 -0
  124. /package/src/{text.mjs → output/text.mjs} +0 -0
@@ -0,0 +1,28 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR } from '#src/constants/index.mjs'
4
+ import { renderAuthorMapText } from '#src/output/renderAuthorMapText.mjs'
5
+ import { getEsmJs } from '#src/output/utils/getEsmJs.mjs'
6
+ import { writeTxtFile } from '#src/output/utils/index.mjs'
7
+
8
+ /**
9
+ * @function handleExportAuthor
10
+ * @description 按周导出
11
+ * @param {type}
12
+ * @returns {type}
13
+ */
14
+ export const handleExportAuthor = async ({ opts, records, fileName }) => {
15
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
16
+ const baseDir = `${config.dir}/${EXPORT_DIR}`
17
+
18
+ try {
19
+ const commitFileName = fileName || `authors.txt`
20
+ const rawResult = renderAuthorMapText(records)
21
+ writeTxtFile(baseDir, commitFileName, rawResult)
22
+ } catch (err) {
23
+ console.warn(
24
+ 'handleExportAuthor failed:',
25
+ err && err.message ? err.message : err
26
+ )
27
+ }
28
+ }
@@ -0,0 +1,27 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR } from '#src/constants/index.mjs'
4
+ import { renderChangedLinesText } from '#src/output/text.mjs'
5
+ import { writeTxtFile } from '#src/output/utils/index.mjs'
6
+
7
+ /**
8
+ * @function handleExportAuthorChanges
9
+ * @description 按周导出
10
+ * @param {type}
11
+ * @returns {type}
12
+ */
13
+ export const handleExportAuthorChanges = async ({ opts, records, fileName }) => {
14
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
15
+ const baseDir = `${config.dir}/${EXPORT_DIR}`
16
+
17
+ try {
18
+ const commitFileName = fileName || `author-changes.txt`
19
+ const rawResult = renderChangedLinesText(records)
20
+ writeTxtFile(baseDir, commitFileName, rawResult)
21
+ } catch (err) {
22
+ console.warn(
23
+ 'handleExportAuthorChanges failed:',
24
+ err && err.message ? err.message : err
25
+ )
26
+ }
27
+ }
@@ -0,0 +1,31 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR } from '#src/constants/index.mjs'
4
+ import { renderAuthorChangesJson } from '#src/output/json.mjs'
5
+ import { writeJsonFile } from '#src/output/utils/index.mjs'
6
+
7
+ /**
8
+ * @function handleExportAuthorChangesJson
9
+ * @description 按周导出
10
+ * @param {type}
11
+ * @returns {type}
12
+ */
13
+ export const handleExportAuthorChangesJson = async ({
14
+ opts,
15
+ records,
16
+ fileName
17
+ }) => {
18
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
19
+ const baseDir = `${config.dir}/${EXPORT_DIR}`
20
+
21
+ try {
22
+ const commitFileName = fileName || `author-changes.json`
23
+ const rawResult = renderAuthorChangesJson(records)
24
+ writeJsonFile(baseDir, commitFileName, rawResult)
25
+ } catch (err) {
26
+ console.warn(
27
+ 'handleExportAuthorChangesJson failed:',
28
+ err && err.message ? err.message : err
29
+ )
30
+ }
31
+ }
@@ -0,0 +1,157 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR_MONTH } from '#src/constants/index.mjs'
4
+ import {
5
+ renderOvertimeCsv,
6
+ renderOvertimeTab,
7
+ renderOvertimeText
8
+ } from '#src/domain/overtime/overtime.mjs'
9
+ import {
10
+ exportExcel,
11
+ exportExcelAuthorChangeStats,
12
+ exportExcelPerPeriodFiles,
13
+ exportExcelPerPeriodSheets
14
+ } from '#src/output/excel.mjs'
15
+ import { writeJsonFile, writeTxtFile } from '#src/output/utils/index.mjs'
16
+ import { outFile } from '#src/output/utils/outputPath.mjs'
17
+ import { groupRecords } from '#utils/groupRecords.mjs'
18
+ import { logger } from '#utils/logger.mjs'
19
+
20
+ import { getWorkOvertimeStats } from '../overtime/analyze.mjs'
21
+
22
+ /**
23
+ * @function handleExportByMonth
24
+ * @description 按月导出
25
+ * @param {type}
26
+ * @returns {type}
27
+ */
28
+ export const handleExportByMonth = async ({
29
+ opts,
30
+ records,
31
+ worktimeOptions
32
+ }) => {
33
+ // Always write human readable overtime text to file (default: overtime.txt)
34
+ const outBase = opts.output.out || 'commits'
35
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
36
+ const baseDir = `${config.dir}/${EXPORT_DIR_MONTH}`
37
+
38
+
39
+ // 按月输出 ... 保持原逻辑
40
+ const perPeriodFormats = opts.output.perPeriod.formats
41
+ try {
42
+ const monthGroups = groupRecords(records, 'month')
43
+ const monthlyFileName = `overtime_${outBase}_monthly.txt`
44
+
45
+ let monthlyContent = ''
46
+ const monthKeys = Object.keys(monthGroups).sort()
47
+ monthKeys.forEach((k) => {
48
+ const groupRecs = monthGroups[k]
49
+ const s = getWorkOvertimeStats(groupRecs, worktimeOptions)
50
+ monthlyContent += `===== ${k} =====\n`
51
+ monthlyContent += `${renderOvertimeText(s)}\n\n`
52
+ // Also write a single file per month under 'month/' folder
53
+ try {
54
+ const perMonthFileName = `month/overtime_${outBase}_${k}.txt`
55
+ // const perMonthFile = outFile(perMonthFileName, outDir)
56
+ // writeTextFile(perMonthFile, renderOvertimeText(s))
57
+ writeTxtFile(baseDir, perMonthFileName, renderOvertimeText(s))
58
+ logger.info(`Overtime 月度(${k}) 已导出: ${perMonthFileName}`)
59
+ // per-period CSV / Tab format (按需生成)
60
+ if (perPeriodFormats.includes('csv')) {
61
+ try {
62
+ const perMonthCsvName = `month/overtime_${outBase}_${k}.csv`
63
+ // writeTextFile(
64
+ // outFile(perMonthCsvName, outDir),
65
+ // renderOvertimeCsv(s)
66
+ // )
67
+ writeTxtFile(baseDir, perMonthCsvName, renderOvertimeCsv(s))
68
+
69
+ logger.info(`Overtime 月度(CSV)(${k}) 已导出: ${perMonthCsvName}`)
70
+ } catch (err) {
71
+ console.warn(
72
+ `Write monthly CSV for ${k} failed:`,
73
+ err && err.message ? err.message : err
74
+ )
75
+ }
76
+ }
77
+ if (perPeriodFormats.includes('tab')) {
78
+ try {
79
+ const perMonthTabName = `month/overtime_${outBase}_${k}.tab.txt`
80
+ // writeTextFile(
81
+ // outFile(perMonthTabName, outDir),
82
+ // renderOvertimeTab(s)
83
+ // )
84
+ writeTxtFile(baseDir, perMonthTabName, renderOvertimeTab(s))
85
+
86
+ logger.info(`Overtime 月度(Tab)(${k}) 已导出: ${perMonthTabName}`)
87
+ } catch (err) {
88
+ console.warn(
89
+ `Write monthly Tab for ${k} failed:`,
90
+ err && err.message ? err.message : err
91
+ )
92
+ }
93
+ }
94
+ } catch (err) {
95
+ console.warn(
96
+ `Write monthly file for ${k} failed:`,
97
+ err && err.message ? err.message : err
98
+ )
99
+ }
100
+ })
101
+ if (!opts.perPeriodOnly) {
102
+ // writeTextFile(monthlyFile, monthlyContent)
103
+ writeTxtFile(baseDir, monthlyFileName, monthlyContent)
104
+ logger.info(`Overtime 月度汇总 已导出: ${monthlyFileName}`)
105
+ }
106
+ // per-period Excel (sheets or files)
107
+ if (perPeriodFormats.includes('xlsx')) {
108
+ const perPeriodExcelMode = String(opts.perPeriodExcelMode || 'sheets')
109
+ if (perPeriodExcelMode === 'sheets') {
110
+ try {
111
+ const monthXlsxName = `overtime_${outBase}_monthly.xlsx`
112
+ const monthXlsxFile = outFile(baseDir, monthXlsxName)
113
+ await exportExcelPerPeriodSheets(monthGroups, monthXlsxFile, {
114
+ stats: opts.stats,
115
+ gerrit: opts.gerrit
116
+ })
117
+ logger.info(`Overtime 月度(XLSX) 已导出: ${monthXlsxFile}`)
118
+ } catch (err) {
119
+ console.warn(
120
+ 'Export month XLSX (sheets) failed:',
121
+ err && err.message ? err.message : err
122
+ )
123
+ }
124
+ } else {
125
+ try {
126
+ const monthKeys2 = Object.keys(monthGroups).sort()
127
+ const tasks = monthKeys2.map((k2) => {
128
+ const perMonthXlsxName = `overtime_${outBase}_${k2}.xlsx`
129
+ const perMonthXlsxFile = outFile(baseDir, perMonthXlsxName)
130
+ return exportExcel(monthGroups[k2], null, {
131
+ file: perMonthXlsxFile,
132
+ stats: opts.stats,
133
+ gerrit: opts.gerrit
134
+ }).then(() =>
135
+ console.log(
136
+ chalk.green(
137
+ `Overtime 月度(XLSX)(${k2}) 已导出: ${perMonthXlsxFile}`
138
+ )
139
+ )
140
+ )
141
+ })
142
+ await Promise.all(tasks)
143
+ } catch (err) {
144
+ console.warn(
145
+ 'Export monthly XLSX files failed:',
146
+ err && err.message ? err.message : err
147
+ )
148
+ }
149
+ }
150
+ }
151
+ } catch (err) {
152
+ console.warn(
153
+ 'Generate monthly overtime failed:',
154
+ err && err.message ? err.message : err
155
+ )
156
+ }
157
+ }
@@ -0,0 +1,121 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR_WEEK } from '#src/constants/index.mjs'
4
+ import {
5
+ renderOvertimeCsv,
6
+ renderOvertimeTab,
7
+ renderOvertimeText
8
+ } from '#src/domain/overtime/overtime.mjs'
9
+ import {
10
+ exportExcel,
11
+ exportExcelAuthorChangeStats,
12
+ exportExcelPerPeriodFiles,
13
+ exportExcelPerPeriodSheets
14
+ } from '#src/output/excel.mjs'
15
+ import { writeJsonFile, writeTxtFile } from '#src/output/utils/index.mjs'
16
+ import { outFile } from '#src/output/utils/outputPath.mjs'
17
+ import { groupRecords } from '#utils/groupRecords.mjs'
18
+ import { logger } from '#utils/logger.mjs'
19
+
20
+ import { getWorkOvertimeStats } from '../overtime/analyze.mjs'
21
+
22
+ /**
23
+ * @function handleExportByWeek
24
+ * @description 按周导出
25
+ * @param {type}
26
+ * @returns {type}
27
+ */
28
+ export const handleExportByWeek = async ({
29
+ opts,
30
+ records,
31
+ worktimeOptions
32
+ }) => {
33
+ // Always write human readable overtime text to file (default: overtime.txt)
34
+ const outBase = opts.output.out || 'commits'
35
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
36
+ const baseDir = `${config.dir}/${EXPORT_DIR_WEEK}`
37
+
38
+
39
+
40
+ try {
41
+ const weekGroups = groupRecords(records, 'week')
42
+ const weeklyFileName = `overtime_${outBase}_weekly.txt`
43
+ // const weeklyFile = outputFilePath(weeklyFileName, outDir)
44
+ let weeklyContent = ''
45
+ const weekKeys = Object.keys(weekGroups).sort()
46
+ weekKeys.forEach((k) => {
47
+ const groupRecs = weekGroups[k]
48
+ const s = getWorkOvertimeStats(groupRecs, worktimeOptions)
49
+ weeklyContent += `===== ${k} =====\n`
50
+ weeklyContent += `${renderOvertimeText(s)}\n\n`
51
+ try {
52
+ const perWeekFileName = `week/overtime_${outBase}_${k}.txt`
53
+ // const perWeekFile = outputFilePath(perWeekFileName, outDir)
54
+ // writeTextFile(perWeekFile, renderOvertimeText(s))
55
+ writeTxtFile(baseDir, perWeekFileName, renderOvertimeText(s))
56
+
57
+ // console.log(chalk.green(`Overtime 周度(${k}) 已导出: ${perWeekFile}`))
58
+ logger.info(`Overtime 周度(${k}) 已导出: ${perWeekFileName}`)
59
+
60
+ // eslint-disable-next-line no-shadow
61
+ const perPeriodFormats = String(opts.perPeriodFormats || '')
62
+ .split(',')
63
+ // eslint-disable-next-line no-shadow
64
+ .map((s) =>
65
+ String(s || '')
66
+ .trim()
67
+ .toLowerCase()
68
+ )
69
+ .filter(Boolean)
70
+ if (perPeriodFormats.includes('csv')) {
71
+ try {
72
+ const perWeekCsvName = `week/overtime_${outBase}_${k}.csv`
73
+ // writeTextFile(
74
+ // outputFilePath(perWeekCsvName, outDir),
75
+ // renderOvertimeCsv(s)
76
+ // )
77
+ writeTxtFile(baseDir, perWeekCsvName, renderOvertimeCsv(s))
78
+
79
+ logger.info(`Overtime 周度(CSV)(${k}) 已导出: ${perWeekCsvName}`)
80
+ } catch (err) {
81
+ console.warn(
82
+ `Write weekly CSV for ${k} failed:`,
83
+ err && err.message ? err.message : err
84
+ )
85
+ }
86
+ }
87
+ if (perPeriodFormats.includes('tab')) {
88
+ try {
89
+ const perWeekTabName = `week/overtime_${outBase}_${k}.tab.txt`
90
+ // writeTextFile(
91
+ // outputFilePath(perWeekTabName, outDir),
92
+ // renderOvertimeTab(s)
93
+ // )
94
+ writeTxtFile(baseDir, perWeekTabName, renderOvertimeTab(s))
95
+
96
+ logger.info(`Overtime 周度(Tab)(${k}) 已导出: ${perWeekTabName}`)
97
+ } catch (err) {
98
+ console.warn(
99
+ `Write weekly Tab for ${k} failed:`,
100
+ err && err.message ? err.message : err
101
+ )
102
+ }
103
+ }
104
+ } catch (err) {
105
+ console.warn(
106
+ `Write weekly file for ${k} failed:`,
107
+ err && err.message ? err.message : err
108
+ )
109
+ }
110
+ })
111
+ // writeTextFile(weeklyFile, weeklyContent)
112
+ writeTxtFile(baseDir, weeklyFileName, weeklyContent)
113
+
114
+ logger.info(`Overtime 周度汇总 已导出: ${weeklyFileName}`)
115
+ } catch (err) {
116
+ console.warn(
117
+ 'Generate weekly overtime failed:',
118
+ err && err.message ? err.message : err
119
+ )
120
+ }
121
+ }
@@ -0,0 +1,26 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR } from '#src/constants/index.mjs'
4
+ import { renderText } from '#src/output/text.mjs'
5
+ import { writeTxtFile } from '#src/output/utils/index.mjs'
6
+
7
+ /**
8
+ * @function handleExportCommits
9
+ * @description 按周导出
10
+ * @param {type}
11
+ * @returns {type}
12
+ */
13
+ export const handleExportCommits = async ({ opts, records, fileName }) => {
14
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
15
+ const baseDir = `${config.dir}/${EXPORT_DIR}`
16
+
17
+ try {
18
+ const commitFileName = fileName || `commits.txt`
19
+ writeTxtFile(baseDir, commitFileName, renderText(records))
20
+ } catch (err) {
21
+ console.warn(
22
+ 'handleExportCommits failed:',
23
+ err && err.message ? err.message : err
24
+ )
25
+ }
26
+ }
@@ -0,0 +1,45 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR } from '#src/constants/index.mjs'
4
+ import {
5
+ exportExcel,
6
+ exportExcelAuthorChangeStats,
7
+ exportExcelPerPeriodFiles,
8
+ exportExcelPerPeriodSheets
9
+ } from '#src/output/excel.mjs'
10
+ import { renderText } from '#src/output/text.mjs'
11
+ import { writeTxtFile } from '#src/output/utils/index.mjs'
12
+ import { outFile } from '#src/output/utils/outputPath.mjs'
13
+
14
+ /**
15
+ * @function handleExportCommitsExcel
16
+ * @description 按周导出
17
+ * @param {type}
18
+ * @returns {type}
19
+ */
20
+ export const handleExportCommitsExcel = async ({
21
+ opts,
22
+ records,
23
+ fileName,
24
+ groups
25
+ }) => {
26
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
27
+ const baseDir = `${config.dir}/${EXPORT_DIR}`
28
+
29
+ try {
30
+ const excelFile = fileName || 'commits.xlsx'
31
+
32
+ const excelPath = outFile(baseDir, excelFile)
33
+
34
+ await exportExcel(records, groups, {
35
+ file: excelPath,
36
+ stats: opts.stats,
37
+ gerrit: opts.gerrit
38
+ })
39
+ } catch (err) {
40
+ console.warn(
41
+ 'handleExportCommitsExcel failed:',
42
+ err && err.message ? err.message : err
43
+ )
44
+ }
45
+ }
@@ -0,0 +1,31 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR } from '#src/constants/index.mjs'
4
+ import { renderText } from '#src/output/text.mjs'
5
+ import { writeJsonFile } from '#src/output/utils/index.mjs'
6
+
7
+ /**
8
+ * @function handleExportCommitsJson
9
+ * @description 按周导出
10
+ * @param {type}
11
+ * @returns {type}
12
+ */
13
+ export const handleExportCommitsJson = async ({
14
+ opts,
15
+ records,
16
+ fileName,
17
+ groups
18
+ }) => {
19
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
20
+ const baseDir = `${config.dir}/${EXPORT_DIR}`
21
+
22
+ try {
23
+ const commitFileName = fileName || `commits.json`
24
+ writeJsonFile(baseDir, commitFileName, groups || records)
25
+ } catch (err) {
26
+ console.warn(
27
+ 'handleExportCommitsJson failed:',
28
+ err && err.message ? err.message : err
29
+ )
30
+ }
31
+ }
@@ -0,0 +1,91 @@
1
+ import path from 'path'
2
+
3
+ import { EXPORT_DIR } from '#src/constants/index.mjs'
4
+ import { writeJsonFile, writeTxtFile } from '#src/output/utils/index.mjs'
5
+ import { logger } from '#utils/logger.mjs'
6
+
7
+ /**
8
+ * @handleExport
9
+ * @description
10
+ * @param {type} param - suffix: json,txt,csv
11
+ * @returns {type}
12
+ */
13
+ export const handleExport = ({
14
+ fileName,
15
+ suffix,
16
+ opts,
17
+ result,
18
+ msg = '已导出'
19
+ }) => {
20
+ const config = { dir: opts.output.dir || path.resolve('output-wukong') }
21
+ const baseDir = `${config.dir}/${EXPORT_DIR}`
22
+ if (suffix === 'json') {
23
+ writeJsonFile(baseDir, fileName, result)
24
+ } else {
25
+ writeTxtFile(baseDir, fileName, result)
26
+ }
27
+
28
+ const file = `${baseDir}/${fileName}`
29
+ logger.success(`${fileName}`, file, msg)
30
+ }
31
+
32
+ export const handleExportOvertimeMain = ({
33
+ fileName,
34
+ suffix = 'json',
35
+ opts,
36
+ result
37
+ }) => {
38
+ handleExport({
39
+ fileName,
40
+ suffix,
41
+ opts,
42
+ result,
43
+ msg: 'overtime JSON 已导出'
44
+ })
45
+ }
46
+
47
+ export const handleExportOvertimeTxt = ({
48
+ fileName,
49
+ suffix = 'txt',
50
+ opts,
51
+ result
52
+ }) => {
53
+ handleExport({
54
+ fileName,
55
+ suffix,
56
+ opts,
57
+ result,
58
+ msg: '已导出(Overtime text)'
59
+ })
60
+ }
61
+
62
+ export const handleExportOvertimeTabTxt = ({
63
+ fileName,
64
+ suffix = 'txt',
65
+ opts,
66
+ result
67
+ }) => {
68
+ handleExport({
69
+ fileName,
70
+ suffix,
71
+ opts,
72
+ result,
73
+ msg: '已导出(Overtime table (tabs))'
74
+ })
75
+ }
76
+
77
+ export const handleExportOvertimeCsv = ({
78
+ fileName,
79
+ suffix = 'csv',
80
+ opts,
81
+ result
82
+ }) => {
83
+ handleExport({
84
+ fileName,
85
+ suffix,
86
+ opts,
87
+ result,
88
+ msg: '已导出(Overtime CSV )'
89
+ })
90
+ }
91
+
@@ -0,0 +1,66 @@
1
+ /**
2
+ * CLI 启动时检测 git 是否可用
3
+ *
4
+ * - 不使用 shell
5
+ * - 不触发 WSL
6
+ * - Windows / macOS / Linux 通用
7
+ */
8
+ import { execFile } from 'node:child_process'
9
+ import { promisify } from 'node:util'
10
+
11
+ const execFileAsync = promisify(execFile)
12
+
13
+ export async function ensureGitAvailable() {
14
+ try {
15
+ const { stdout } = await execFileAsync('git', ['--version'], {
16
+ windowsHide: true,
17
+ timeout: 5000,
18
+ maxBuffer: 1024 * 1024
19
+ })
20
+
21
+ // 示例输出:git version 2.45.1.windows.1
22
+ if (!stdout || !stdout.toLowerCase().includes('git version')) {
23
+ throw new Error(`Unexpected git output: ${stdout}`)
24
+ }
25
+
26
+ return stdout.trim()
27
+ } catch (err) {
28
+ /**
29
+ * 统一抛出人类可读错误
30
+ */
31
+ const { platform } = process
32
+
33
+ let hint = ''
34
+
35
+ if (platform === 'win32') {
36
+ hint = `
37
+ 请确认:
38
+ 1️⃣ 已安装 Git for Windows
39
+ https://git-scm.com/download/win
40
+
41
+ 2️⃣ 安装时勾选:
42
+ ✔ "Add Git to PATH"
43
+
44
+ 3️⃣ 关闭并重新打开终端后再试
45
+ `
46
+ } else if (platform === 'darwin') {
47
+ hint = `
48
+ 可通过以下方式安装 git:
49
+ xcode-select --install
50
+
51
+ brew install git
52
+ `
53
+ } else {
54
+ hint = `
55
+ 请使用系统包管理器安装 git,例如:
56
+ apt install git
57
+ yum install git
58
+ `
59
+ }
60
+
61
+ const error = new Error(`❌ Git 不可用,CLI 无法继续运行\n\n${hint}`)
62
+
63
+ error.cause = err
64
+ throw error
65
+ }
66
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 确保当前目录在 git 仓库内
3
+ */
4
+ import chalk from 'chalk'
5
+
6
+ import { execFile } from 'node:child_process'
7
+ import { promisify } from 'node:util'
8
+ import { getGitCapability } from './gitCapability.mjs'
9
+
10
+ const execFileAsync = promisify(execFile)
11
+
12
+ /**
13
+ * @returns {Promise<boolean>}
14
+ */
15
+ export async function ensureGitRepo() {
16
+ await getGitCapability()
17
+
18
+ try {
19
+ const { stdout } = await execFileAsync(
20
+ 'git',
21
+ ['rev-parse', '--is-inside-work-tree'],
22
+ {
23
+ windowsHide: true,
24
+ timeout: 5000,
25
+ maxBuffer: 1024 * 1024
26
+ }
27
+ )
28
+
29
+ if (stdout.trim() !== 'true') {
30
+ throw new Error('Not a git repository')
31
+ }
32
+
33
+ return true
34
+ } catch {
35
+ // throw new Error(
36
+ // `❌ 当前目录不是 Git 仓库\n\n请在 Git 项目根目录下运行该命令`
37
+ // )
38
+ console.log(`${chalk.red("❌")} 当前目录不是 Git 仓库\n\n请在 Git 项目根目录下运行该命令`)
39
+ process.exit(1)
40
+ }
41
+ }