@kabran-tecnologia/kabran-config 1.5.0 → 1.7.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.
- package/README.md +283 -0
- package/package.json +67 -9
- package/src/schemas/ci-result.v2.schema.json +125 -0
- package/src/scripts/ci/ci-core.sh +61 -0
- package/src/scripts/ci/ci-runner.sh +89 -0
- package/src/scripts/ci-result-history.mjs +245 -0
- package/src/scripts/ci-result-trends.mjs +296 -0
- package/src/scripts/ci-result-utils.mjs +223 -0
- package/src/scripts/generate-ci-result.mjs +79 -11
- package/src/scripts/pr-quality-comment.mjs +326 -0
- package/src/scripts/setup.mjs +91 -4
- package/src/telemetry/config/defaults.mjs +421 -0
- package/src/telemetry/config/index.mjs +132 -0
- package/src/telemetry/edge/index.mjs +446 -0
- package/src/telemetry/frontend/index.mjs +366 -0
- package/src/telemetry/logger/index.mjs +236 -0
- package/src/telemetry/node/index.mjs +386 -0
- package/src/telemetry/shared/helpers.mjs +133 -0
- package/src/telemetry/shared/index.mjs +15 -0
- package/src/telemetry/shared/types.d.ts +123 -0
- package/templates/.github/workflows/ci-quality.yml +111 -0
- package/templates/telemetry/.env.telemetry.example +118 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PR Quality Comment Generator
|
|
5
|
+
*
|
|
6
|
+
* Generates a formatted PR comment comparing CI results between
|
|
7
|
+
* the current branch and a baseline (typically main).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node pr-quality-comment.mjs --current ci-result.json --baseline main-ci-result.json
|
|
11
|
+
* node pr-quality-comment.mjs --current ci-result.json --baseline-branch main
|
|
12
|
+
*
|
|
13
|
+
* Output: Markdown formatted comment to stdout
|
|
14
|
+
*
|
|
15
|
+
* @module pr-quality-comment
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readFileSync, existsSync } from 'node:fs'
|
|
19
|
+
import { execSync } from 'node:child_process'
|
|
20
|
+
import { compareCiResults } from './ci-result-utils.mjs'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get trend emoji
|
|
24
|
+
* @param {'improving'|'stable'|'degrading'} trend
|
|
25
|
+
* @returns {string} Emoji
|
|
26
|
+
*/
|
|
27
|
+
function getTrendEmoji(trend) {
|
|
28
|
+
switch (trend) {
|
|
29
|
+
case 'improving': return '📈'
|
|
30
|
+
case 'degrading': return '📉'
|
|
31
|
+
default: return '➡️'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get status emoji
|
|
37
|
+
* @param {'passing'|'degraded'|'failing'} status
|
|
38
|
+
* @returns {string} Emoji
|
|
39
|
+
*/
|
|
40
|
+
function getStatusEmoji(status) {
|
|
41
|
+
switch (status) {
|
|
42
|
+
case 'passing': return '✅'
|
|
43
|
+
case 'degraded': return '⚠️'
|
|
44
|
+
case 'failing': return '❌'
|
|
45
|
+
default: return '❓'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Format diff with sign and color indicator
|
|
51
|
+
* @param {number} diff - Difference value
|
|
52
|
+
* @param {boolean} inverted - Whether positive is bad (for issues)
|
|
53
|
+
* @returns {string} Formatted diff string
|
|
54
|
+
*/
|
|
55
|
+
function formatDiff(diff, inverted = false) {
|
|
56
|
+
if (diff === 0) return '='
|
|
57
|
+
const sign = diff > 0 ? '+' : ''
|
|
58
|
+
const indicator = inverted
|
|
59
|
+
? (diff > 0 ? '🔴' : '🟢')
|
|
60
|
+
: (diff > 0 ? '🟢' : '🔴')
|
|
61
|
+
return `${sign}${diff} ${indicator}`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Generate markdown comment from comparison
|
|
66
|
+
* @param {Object} comparison - Comparison result from compareCiResults
|
|
67
|
+
* @param {Object} current - Current CI result
|
|
68
|
+
* @param {Object} baseline - Baseline CI result
|
|
69
|
+
* @returns {string} Markdown formatted comment
|
|
70
|
+
*/
|
|
71
|
+
export function generateComment(comparison, current, baseline) {
|
|
72
|
+
const lines = []
|
|
73
|
+
|
|
74
|
+
// Header
|
|
75
|
+
lines.push(`## ${getTrendEmoji(comparison.trend)} Quality Report`)
|
|
76
|
+
lines.push('')
|
|
77
|
+
|
|
78
|
+
// Status summary
|
|
79
|
+
const currentStatus = `${getStatusEmoji(comparison.status.current)} ${comparison.status.current}`
|
|
80
|
+
const baselineStatus = `${getStatusEmoji(comparison.status.baseline)} ${comparison.status.baseline}`
|
|
81
|
+
|
|
82
|
+
lines.push(`| Metric | Current | Baseline | Diff |`)
|
|
83
|
+
lines.push(`|--------|---------|----------|------|`)
|
|
84
|
+
lines.push(`| **Status** | ${currentStatus} | ${baselineStatus} | - |`)
|
|
85
|
+
lines.push(`| **Score** | ${comparison.score.current} | ${comparison.score.baseline} | ${formatDiff(comparison.score.diff)} |`)
|
|
86
|
+
lines.push(`| **Issues** | ${comparison.issues.current} | ${comparison.issues.baseline} | ${formatDiff(comparison.issues.diff, true)} |`)
|
|
87
|
+
lines.push(`| **Blocking** | ${comparison.blocking.current} | ${comparison.blocking.baseline} | ${formatDiff(comparison.blocking.diff, true)} |`)
|
|
88
|
+
lines.push('')
|
|
89
|
+
|
|
90
|
+
// Coverage section (if available)
|
|
91
|
+
if (comparison.coverage) {
|
|
92
|
+
lines.push(`### 📊 Coverage`)
|
|
93
|
+
lines.push('')
|
|
94
|
+
lines.push(`| Type | Current | Baseline | Diff |`)
|
|
95
|
+
lines.push(`|------|---------|----------|------|`)
|
|
96
|
+
|
|
97
|
+
const currentCov = current.checks?.test?.coverage || {}
|
|
98
|
+
const baselineCov = baseline.checks?.test?.coverage || {}
|
|
99
|
+
|
|
100
|
+
if (comparison.coverage.lines !== null) {
|
|
101
|
+
lines.push(`| Lines | ${currentCov.lines || 0}% | ${baselineCov.lines || 0}% | ${formatDiff(comparison.coverage.lines)} |`)
|
|
102
|
+
}
|
|
103
|
+
if (comparison.coverage.branches !== null) {
|
|
104
|
+
lines.push(`| Branches | ${currentCov.branches || 0}% | ${baselineCov.branches || 0}% | ${formatDiff(comparison.coverage.branches)} |`)
|
|
105
|
+
}
|
|
106
|
+
if (comparison.coverage.functions !== null) {
|
|
107
|
+
lines.push(`| Functions | ${currentCov.functions || 0}% | ${baselineCov.functions || 0}% | ${formatDiff(comparison.coverage.functions)} |`)
|
|
108
|
+
}
|
|
109
|
+
lines.push('')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Checks summary
|
|
113
|
+
if (current.checks && Object.keys(current.checks).length > 0) {
|
|
114
|
+
lines.push(`### 🔍 Checks`)
|
|
115
|
+
lines.push('')
|
|
116
|
+
lines.push(`| Check | Status | Errors | Warnings |`)
|
|
117
|
+
lines.push(`|-------|--------|--------|----------|`)
|
|
118
|
+
|
|
119
|
+
for (const [name, check] of Object.entries(current.checks)) {
|
|
120
|
+
const emoji = getStatusEmoji(check.status)
|
|
121
|
+
lines.push(`| ${name} | ${emoji} ${check.status} | ${check.errors || 0} | ${check.warnings || 0} |`)
|
|
122
|
+
}
|
|
123
|
+
lines.push('')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// New issues (if any)
|
|
127
|
+
if (current.issues && current.issues.length > 0) {
|
|
128
|
+
const newIssues = current.issues.filter(i => i.severity === 'error')
|
|
129
|
+
if (newIssues.length > 0) {
|
|
130
|
+
lines.push(`### ⚠️ Blocking Issues`)
|
|
131
|
+
lines.push('')
|
|
132
|
+
for (const issue of newIssues.slice(0, 5)) {
|
|
133
|
+
lines.push(`- \`${issue.rule || issue.id}\`: ${issue.message}`)
|
|
134
|
+
if (issue.file) {
|
|
135
|
+
lines.push(` - 📁 ${issue.file}${issue.line ? `:${issue.line}` : ''}`)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (newIssues.length > 5) {
|
|
139
|
+
lines.push(`- ... and ${newIssues.length - 5} more`)
|
|
140
|
+
}
|
|
141
|
+
lines.push('')
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Trends section (if available)
|
|
146
|
+
if (current.trends && current.trends.data?.total_runs > 1) {
|
|
147
|
+
lines.push(`### 📈 Trends`)
|
|
148
|
+
lines.push('')
|
|
149
|
+
lines.push(`| Metric | Direction | Change (7d) | Avg (30d) |`)
|
|
150
|
+
lines.push(`|--------|-----------|-------------|-----------|`)
|
|
151
|
+
|
|
152
|
+
const scoreTrend = current.trends.score
|
|
153
|
+
if (scoreTrend) {
|
|
154
|
+
const dirEmoji = scoreTrend.direction === 'improving' ? '📈' : scoreTrend.direction === 'degrading' ? '📉' : '➡️'
|
|
155
|
+
lines.push(`| Score | ${dirEmoji} ${scoreTrend.direction} | ${scoreTrend.change_7d !== null ? formatDiff(scoreTrend.change_7d) : 'N/A'} | ${scoreTrend.avg_30d || 'N/A'} |`)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const perfTrend = current.trends.performance
|
|
159
|
+
if (perfTrend && perfTrend.avg_30d) {
|
|
160
|
+
const dirEmoji = perfTrend.direction === 'improving' ? '📈' : perfTrend.direction === 'degrading' ? '📉' : '➡️'
|
|
161
|
+
const change7d = perfTrend.change_7d !== null ? `${(perfTrend.change_7d / 1000).toFixed(1)}s` : 'N/A'
|
|
162
|
+
lines.push(`| Duration | ${dirEmoji} ${perfTrend.direction} | ${change7d} | ${(perfTrend.avg_30d / 1000).toFixed(1)}s |`)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
lines.push('')
|
|
166
|
+
lines.push(`<sub>Based on ${current.trends.data.total_runs} runs</sub>`)
|
|
167
|
+
lines.push('')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Timing info
|
|
171
|
+
lines.push(`---`)
|
|
172
|
+
lines.push(`<sub>`)
|
|
173
|
+
lines.push(`⏱️ Duration: ${current.timing?.total_human || 'N/A'} | `)
|
|
174
|
+
lines.push(`🔀 Branch: ${current.meta?.branch || 'N/A'} | `)
|
|
175
|
+
lines.push(`📝 Commit: ${current.meta?.commit || 'N/A'}`)
|
|
176
|
+
|
|
177
|
+
// Add trace link if available
|
|
178
|
+
if (current.meta?.trace_id) {
|
|
179
|
+
const traceUrl = current.extensions?.telemetry?.trace_url
|
|
180
|
+
if (traceUrl) {
|
|
181
|
+
lines.push(` | 🔍 [Trace](${traceUrl})`)
|
|
182
|
+
} else {
|
|
183
|
+
lines.push(` | 🔍 Trace: ${current.meta.trace_id}`)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
lines.push(`</sub>`)
|
|
188
|
+
|
|
189
|
+
return lines.join('\n')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Fetch baseline CI result from another branch
|
|
194
|
+
* @param {string} branch - Branch name
|
|
195
|
+
* @param {string} filePath - Path to ci-result.json in repo
|
|
196
|
+
* @returns {Object|null} CI result or null
|
|
197
|
+
*/
|
|
198
|
+
export function fetchBaselineFromBranch(branch, filePath = 'docs/quality/ci-result.json') {
|
|
199
|
+
try {
|
|
200
|
+
const content = execSync(`git show ${branch}:${filePath}`, {
|
|
201
|
+
encoding: 'utf8',
|
|
202
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
203
|
+
})
|
|
204
|
+
return JSON.parse(content)
|
|
205
|
+
} catch {
|
|
206
|
+
return null
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Parse command line arguments
|
|
212
|
+
*/
|
|
213
|
+
function parseArgs(args) {
|
|
214
|
+
const options = {
|
|
215
|
+
current: null,
|
|
216
|
+
baseline: null,
|
|
217
|
+
baselineBranch: null,
|
|
218
|
+
output: 'stdout',
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for (let i = 0; i < args.length; i++) {
|
|
222
|
+
const arg = args[i]
|
|
223
|
+
|
|
224
|
+
if (arg === '--current' || arg === '-c') {
|
|
225
|
+
options.current = args[++i]
|
|
226
|
+
} else if (arg === '--baseline' || arg === '-b') {
|
|
227
|
+
options.baseline = args[++i]
|
|
228
|
+
} else if (arg === '--baseline-branch') {
|
|
229
|
+
options.baselineBranch = args[++i]
|
|
230
|
+
} else if (arg === '--output' || arg === '-o') {
|
|
231
|
+
options.output = args[++i]
|
|
232
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
233
|
+
console.log(`
|
|
234
|
+
Usage: pr-quality-comment.mjs [options]
|
|
235
|
+
|
|
236
|
+
Options:
|
|
237
|
+
-c, --current <file> Current CI result JSON file (required)
|
|
238
|
+
-b, --baseline <file> Baseline CI result JSON file
|
|
239
|
+
--baseline-branch <branch> Fetch baseline from git branch
|
|
240
|
+
-o, --output <file> Output file (default: stdout)
|
|
241
|
+
-h, --help Show this help message
|
|
242
|
+
|
|
243
|
+
Examples:
|
|
244
|
+
# Compare with file
|
|
245
|
+
node pr-quality-comment.mjs -c ci-result.json -b main-ci-result.json
|
|
246
|
+
|
|
247
|
+
# Compare with main branch
|
|
248
|
+
node pr-quality-comment.mjs -c ci-result.json --baseline-branch main
|
|
249
|
+
`)
|
|
250
|
+
process.exit(0)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return options
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Main entry point
|
|
259
|
+
*/
|
|
260
|
+
async function main() {
|
|
261
|
+
const args = process.argv.slice(2)
|
|
262
|
+
const options = parseArgs(args)
|
|
263
|
+
|
|
264
|
+
// Validate current file
|
|
265
|
+
if (!options.current) {
|
|
266
|
+
console.error('Error: --current is required')
|
|
267
|
+
process.exit(1)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!existsSync(options.current)) {
|
|
271
|
+
console.error(`Error: Current file not found: ${options.current}`)
|
|
272
|
+
process.exit(1)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Load current result
|
|
276
|
+
const current = JSON.parse(readFileSync(options.current, 'utf8'))
|
|
277
|
+
|
|
278
|
+
// Load baseline result
|
|
279
|
+
let baseline = null
|
|
280
|
+
if (options.baseline) {
|
|
281
|
+
if (!existsSync(options.baseline)) {
|
|
282
|
+
console.error(`Error: Baseline file not found: ${options.baseline}`)
|
|
283
|
+
process.exit(1)
|
|
284
|
+
}
|
|
285
|
+
baseline = JSON.parse(readFileSync(options.baseline, 'utf8'))
|
|
286
|
+
} else if (options.baselineBranch) {
|
|
287
|
+
baseline = fetchBaselineFromBranch(options.baselineBranch)
|
|
288
|
+
if (!baseline) {
|
|
289
|
+
console.error(`Warning: Could not fetch baseline from branch ${options.baselineBranch}`)
|
|
290
|
+
// Create a minimal baseline for first PR
|
|
291
|
+
baseline = {
|
|
292
|
+
summary: { score: 100, total_issues: 0, blocking: 0, status: 'passing' },
|
|
293
|
+
checks: {},
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
// No baseline provided, create empty comparison
|
|
298
|
+
baseline = {
|
|
299
|
+
summary: { score: 100, total_issues: 0, blocking: 0, status: 'passing' },
|
|
300
|
+
checks: {},
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Generate comparison
|
|
305
|
+
const comparison = compareCiResults(current, baseline)
|
|
306
|
+
|
|
307
|
+
// Generate comment
|
|
308
|
+
const comment = generateComment(comparison, current, baseline)
|
|
309
|
+
|
|
310
|
+
// Output
|
|
311
|
+
if (options.output === 'stdout') {
|
|
312
|
+
console.log(comment)
|
|
313
|
+
} else {
|
|
314
|
+
const { writeFileSync } = await import('node:fs')
|
|
315
|
+
writeFileSync(options.output, comment)
|
|
316
|
+
console.log(`Comment written to: ${options.output}`)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Run if called directly
|
|
321
|
+
import { fileURLToPath } from 'node:url'
|
|
322
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
323
|
+
main()
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export { generateComment as default, fetchBaselineFromBranch }
|
package/src/scripts/setup.mjs
CHANGED
|
@@ -79,6 +79,7 @@ export function parseArgs(args) {
|
|
|
79
79
|
skipQualityStandard: false,
|
|
80
80
|
syncWorkflows: false,
|
|
81
81
|
syncHusky: false,
|
|
82
|
+
telemetryEnv: false,
|
|
82
83
|
force: false,
|
|
83
84
|
dryRun: false,
|
|
84
85
|
help: false,
|
|
@@ -97,6 +98,8 @@ export function parseArgs(args) {
|
|
|
97
98
|
options.syncWorkflows = true;
|
|
98
99
|
} else if (arg === '--sync-husky') {
|
|
99
100
|
options.syncHusky = true;
|
|
101
|
+
} else if (arg === '--telemetry-env') {
|
|
102
|
+
options.telemetryEnv = true;
|
|
100
103
|
} else if (arg === '--force') {
|
|
101
104
|
options.force = true;
|
|
102
105
|
} else if (arg === '--dry-run') {
|
|
@@ -132,6 +135,7 @@ ${colors.yellow}OPTIONS:${colors.reset}
|
|
|
132
135
|
--skip-quality-standard Don't create quality-standard.md
|
|
133
136
|
--sync-workflows Overwrite existing workflow files
|
|
134
137
|
--sync-husky Overwrite existing husky hooks
|
|
138
|
+
--telemetry-env Generate .env.example with telemetry variables
|
|
135
139
|
--force Overwrite all existing files
|
|
136
140
|
--dry-run Preview changes without modifying files
|
|
137
141
|
--help, -h Show this help message
|
|
@@ -146,6 +150,9 @@ ${colors.yellow}EXAMPLES:${colors.reset}
|
|
|
146
150
|
# Update workflows only
|
|
147
151
|
npx kabran-setup --sync-workflows
|
|
148
152
|
|
|
153
|
+
# Generate telemetry .env.example
|
|
154
|
+
npx kabran-setup --telemetry-env
|
|
155
|
+
|
|
149
156
|
# Preview changes without modifying
|
|
150
157
|
npx kabran-setup --dry-run
|
|
151
158
|
|
|
@@ -547,6 +554,79 @@ export function setupQualityStandard(projectDir, templatesDir, options) {
|
|
|
547
554
|
return results;
|
|
548
555
|
}
|
|
549
556
|
|
|
557
|
+
/**
|
|
558
|
+
* Setup telemetry .env.example
|
|
559
|
+
* @param {string} projectDir - Project directory
|
|
560
|
+
* @param {string} templatesDir - Templates directory
|
|
561
|
+
* @param {object} options - Setup options
|
|
562
|
+
* @returns {object} Results
|
|
563
|
+
*/
|
|
564
|
+
export function setupTelemetryEnv(projectDir, templatesDir, options) {
|
|
565
|
+
const {force = false, dryRun = false} = options;
|
|
566
|
+
|
|
567
|
+
const results = {
|
|
568
|
+
created: 0,
|
|
569
|
+
overwritten: 0,
|
|
570
|
+
skipped: 0,
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
logInfo('Setting up telemetry .env.example...');
|
|
574
|
+
|
|
575
|
+
const src = join(templatesDir, 'telemetry', '.env.telemetry.example');
|
|
576
|
+
const dest = join(projectDir, '.env.example');
|
|
577
|
+
|
|
578
|
+
// Check if source template exists
|
|
579
|
+
if (!existsSync(src)) {
|
|
580
|
+
logWarn('Template not found: templates/telemetry/.env.telemetry.example');
|
|
581
|
+
return results;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Check if destination exists
|
|
585
|
+
const destExists = existsSync(dest);
|
|
586
|
+
|
|
587
|
+
if (destExists && !force) {
|
|
588
|
+
// If .env.example exists, append telemetry section if not present
|
|
589
|
+
const existingContent = readFileSync(dest, 'utf-8');
|
|
590
|
+
|
|
591
|
+
if (existingContent.includes('Kabran Telemetry Configuration')) {
|
|
592
|
+
if (dryRun) {
|
|
593
|
+
logDry('Would skip .env.example (telemetry section already present)');
|
|
594
|
+
} else {
|
|
595
|
+
logSkip('.env.example (telemetry section already present)');
|
|
596
|
+
}
|
|
597
|
+
results.skipped = 1;
|
|
598
|
+
return results;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Append telemetry section
|
|
602
|
+
const telemetryContent = readFileSync(src, 'utf-8');
|
|
603
|
+
|
|
604
|
+
if (dryRun) {
|
|
605
|
+
logDry('Would append telemetry section to .env.example');
|
|
606
|
+
results.overwritten = 1;
|
|
607
|
+
return results;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const newContent = existingContent.trimEnd() + '\n\n' + telemetryContent;
|
|
611
|
+
writeFileSync(dest, newContent, 'utf-8');
|
|
612
|
+
logSuccess('Appended telemetry section to: .env.example');
|
|
613
|
+
results.overwritten = 1;
|
|
614
|
+
return results;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const status = copyFile(src, dest, {overwrite: force, dryRun});
|
|
618
|
+
|
|
619
|
+
if (status === 'created' || status === 'would_create') {
|
|
620
|
+
results.created = 1;
|
|
621
|
+
} else if (status === 'overwritten' || status === 'would_overwrite') {
|
|
622
|
+
results.overwritten = 1;
|
|
623
|
+
} else {
|
|
624
|
+
results.skipped = 1;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return results;
|
|
628
|
+
}
|
|
629
|
+
|
|
550
630
|
/**
|
|
551
631
|
* Run setup
|
|
552
632
|
* @param {string} projectDir - Project directory
|
|
@@ -561,6 +641,7 @@ export function runSetup(projectDir, options) {
|
|
|
561
641
|
husky: {created: 0, overwritten: 0, skipped: 0},
|
|
562
642
|
configs: {created: 0, overwritten: 0, skipped: 0},
|
|
563
643
|
qualityStandard: {created: 0, overwritten: 0, skipped: 0},
|
|
644
|
+
telemetryEnv: {created: 0, overwritten: 0, skipped: 0},
|
|
564
645
|
};
|
|
565
646
|
|
|
566
647
|
console.log('');
|
|
@@ -592,11 +673,17 @@ export function runSetup(projectDir, options) {
|
|
|
592
673
|
}
|
|
593
674
|
|
|
594
675
|
// Setup quality-standard.md (unless skipped or in sync mode)
|
|
595
|
-
if (!options.skipQualityStandard && !isSyncMode) {
|
|
676
|
+
if (!options.skipQualityStandard && !isSyncMode && !options.telemetryEnv) {
|
|
596
677
|
summary.qualityStandard = setupQualityStandard(projectDir, templatesDir, options);
|
|
597
678
|
console.log('');
|
|
598
679
|
}
|
|
599
680
|
|
|
681
|
+
// Setup telemetry .env.example (only when explicitly requested)
|
|
682
|
+
if (options.telemetryEnv) {
|
|
683
|
+
summary.telemetryEnv = setupTelemetryEnv(projectDir, templatesDir, options);
|
|
684
|
+
console.log('');
|
|
685
|
+
}
|
|
686
|
+
|
|
600
687
|
return summary;
|
|
601
688
|
}
|
|
602
689
|
|
|
@@ -609,10 +696,10 @@ export function printSummary(summary) {
|
|
|
609
696
|
console.log(`${colors.cyan}=== Setup Summary ===${colors.reset}`);
|
|
610
697
|
|
|
611
698
|
const total = {
|
|
612
|
-
created: summary.workflows.created + summary.husky.created + summary.configs.created + summary.qualityStandard.created,
|
|
699
|
+
created: summary.workflows.created + summary.husky.created + summary.configs.created + summary.qualityStandard.created + summary.telemetryEnv.created,
|
|
613
700
|
overwritten:
|
|
614
|
-
summary.workflows.overwritten + summary.husky.overwritten + summary.configs.overwritten + summary.qualityStandard.overwritten,
|
|
615
|
-
skipped: summary.workflows.skipped + summary.husky.skipped + summary.configs.skipped + summary.qualityStandard.skipped,
|
|
701
|
+
summary.workflows.overwritten + summary.husky.overwritten + summary.configs.overwritten + summary.qualityStandard.overwritten + summary.telemetryEnv.overwritten,
|
|
702
|
+
skipped: summary.workflows.skipped + summary.husky.skipped + summary.configs.skipped + summary.qualityStandard.skipped + summary.telemetryEnv.skipped,
|
|
616
703
|
};
|
|
617
704
|
|
|
618
705
|
if (total.created > 0) {
|