pipecraft 0.29.2 → 0.36.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.
- package/README.md +98 -34
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +66 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/generators/init.tpl.d.ts +4 -4
- package/dist/generators/init.tpl.d.ts.map +1 -1
- package/dist/generators/init.tpl.js +369 -79
- package/dist/generators/init.tpl.js.map +1 -1
- package/dist/generators/workflows.tpl.d.ts +9 -9
- package/dist/generators/workflows.tpl.d.ts.map +1 -1
- package/dist/generators/workflows.tpl.js +33 -24
- package/dist/generators/workflows.tpl.js.map +1 -1
- package/dist/templates/actions/calculate-version.yml.tpl.d.ts +7 -4
- package/dist/templates/actions/calculate-version.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/calculate-version.yml.tpl.js +16 -9
- package/dist/templates/actions/calculate-version.yml.tpl.js.map +1 -1
- package/dist/templates/actions/create-pr.yml.tpl.d.ts +5 -2
- package/dist/templates/actions/create-pr.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/create-pr.yml.tpl.js +9 -6
- package/dist/templates/actions/create-pr.yml.tpl.js.map +1 -1
- package/dist/templates/actions/create-release.yml.tpl.d.ts +5 -2
- package/dist/templates/actions/create-release.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/create-release.yml.tpl.js +9 -6
- package/dist/templates/actions/create-release.yml.tpl.js.map +1 -1
- package/dist/templates/actions/create-tag.yml.tpl.d.ts +5 -2
- package/dist/templates/actions/create-tag.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/create-tag.yml.tpl.js +9 -6
- package/dist/templates/actions/create-tag.yml.tpl.js.map +1 -1
- package/dist/templates/actions/detect-changes.yml.tpl.d.ts +24 -52
- package/dist/templates/actions/detect-changes.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/detect-changes.yml.tpl.js +281 -108
- package/dist/templates/actions/detect-changes.yml.tpl.js.map +1 -1
- package/dist/templates/actions/manage-branch.yml.tpl.d.ts +5 -2
- package/dist/templates/actions/manage-branch.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/manage-branch.yml.tpl.js +9 -6
- package/dist/templates/actions/manage-branch.yml.tpl.js.map +1 -1
- package/dist/templates/actions/promote-branch.yml.tpl.d.ts +5 -2
- package/dist/templates/actions/promote-branch.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/promote-branch.yml.tpl.js +27 -76
- package/dist/templates/actions/promote-branch.yml.tpl.js.map +1 -1
- package/dist/templates/actions/run-nx-affected.yml.tpl.d.ts +33 -0
- package/dist/templates/actions/run-nx-affected.yml.tpl.d.ts.map +1 -0
- package/dist/templates/actions/run-nx-affected.yml.tpl.js +346 -0
- package/dist/templates/actions/run-nx-affected.yml.tpl.js.map +1 -0
- package/dist/templates/release-it.cjs.tpl.d.ts +1 -1
- package/dist/templates/release-it.cjs.tpl.d.ts.map +1 -1
- package/dist/templates/release-it.cjs.tpl.js +24 -26
- package/dist/templates/release-it.cjs.tpl.js.map +1 -1
- package/dist/templates/workflows/enforce-pr-target.yml.tpl.d.ts +1 -1
- package/dist/templates/workflows/enforce-pr-target.yml.tpl.d.ts.map +1 -1
- package/dist/templates/workflows/enforce-pr-target.yml.tpl.js +2 -3
- package/dist/templates/workflows/enforce-pr-target.yml.tpl.js.map +1 -1
- package/dist/templates/workflows/pipeline-nx.yml.tpl.d.ts +2 -2
- package/dist/templates/workflows/pipeline-nx.yml.tpl.d.ts.map +1 -1
- package/dist/templates/workflows/pipeline-nx.yml.tpl.js +300 -93
- package/dist/templates/workflows/pipeline-nx.yml.tpl.js.map +1 -1
- package/dist/templates/workflows/pipeline.yml.tpl.d.ts +2 -2
- package/dist/templates/workflows/pipeline.yml.tpl.d.ts.map +1 -1
- package/dist/templates/workflows/pipeline.yml.tpl.js +347 -74
- package/dist/templates/workflows/pipeline.yml.tpl.js.map +1 -1
- package/dist/templates/workflows/pr-title-check.yml.tpl.d.ts +1 -1
- package/dist/templates/workflows/pr-title-check.yml.tpl.d.ts.map +1 -1
- package/dist/templates/workflows/pr-title-check.yml.tpl.js +101 -22
- package/dist/templates/workflows/pr-title-check.yml.tpl.js.map +1 -1
- package/dist/templates/workflows/shared/index.d.ts +3 -2
- package/dist/templates/workflows/shared/index.d.ts.map +1 -1
- package/dist/templates/workflows/shared/index.js +3 -2
- package/dist/templates/workflows/shared/index.js.map +1 -1
- package/dist/templates/workflows/shared/operations-changes.d.ts +13 -2
- package/dist/templates/workflows/shared/operations-changes.d.ts.map +1 -1
- package/dist/templates/workflows/shared/operations-changes.js +47 -16
- package/dist/templates/workflows/shared/operations-changes.js.map +1 -1
- package/dist/templates/workflows/shared/operations-domain-jobs.d.ts +18 -1
- package/dist/templates/workflows/shared/operations-domain-jobs.d.ts.map +1 -1
- package/dist/templates/workflows/shared/operations-domain-jobs.js +143 -19
- package/dist/templates/workflows/shared/operations-domain-jobs.js.map +1 -1
- package/dist/templates/workflows/shared/operations-gate.d.ts +32 -0
- package/dist/templates/workflows/shared/operations-gate.d.ts.map +1 -0
- package/dist/templates/workflows/shared/operations-gate.js +77 -0
- package/dist/templates/workflows/shared/operations-gate.js.map +1 -0
- package/dist/templates/workflows/shared/operations-header.d.ts +1 -1
- package/dist/templates/workflows/shared/operations-header.d.ts.map +1 -1
- package/dist/templates/workflows/shared/operations-header.js +46 -6
- package/dist/templates/workflows/shared/operations-header.js.map +1 -1
- package/dist/templates/workflows/shared/operations-tag-promote.d.ts +5 -1
- package/dist/templates/workflows/shared/operations-tag-promote.d.ts.map +1 -1
- package/dist/templates/workflows/shared/operations-tag-promote.js +138 -31
- package/dist/templates/workflows/shared/operations-tag-promote.js.map +1 -1
- package/dist/templates/workflows/shared/operations-version.d.ts +3 -1
- package/dist/templates/workflows/shared/operations-version.d.ts.map +1 -1
- package/dist/templates/workflows/shared/operations-version.js +18 -10
- package/dist/templates/workflows/shared/operations-version.js.map +1 -1
- package/dist/templates/yaml-format-utils.d.ts.map +1 -1
- package/dist/templates/yaml-format-utils.js.map +1 -1
- package/dist/types/index.d.ts +57 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/action-reference.d.ts +73 -0
- package/dist/utils/action-reference.d.ts.map +1 -0
- package/dist/utils/action-reference.js +120 -0
- package/dist/utils/action-reference.js.map +1 -0
- package/dist/utils/ast-path-operations.d.ts +3 -3
- package/dist/utils/ast-path-operations.d.ts.map +1 -1
- package/dist/utils/ast-path-operations.js +58 -17
- package/dist/utils/ast-path-operations.js.map +1 -1
- package/dist/utils/config.d.ts +11 -5
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +33 -8
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/github-setup.d.ts +4 -4
- package/dist/utils/github-setup.d.ts.map +1 -1
- package/dist/utils/github-setup.js +57 -36
- package/dist/utils/github-setup.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/messaging.d.ts.map +1 -1
- package/dist/utils/messaging.js +7 -3
- package/dist/utils/messaging.js.map +1 -1
- package/dist/utils/preflight.d.ts +2 -2
- package/dist/utils/preflight.d.ts.map +1 -1
- package/dist/utils/preflight.js +43 -41
- package/dist/utils/preflight.js.map +1 -1
- package/dist/utils/versioning.d.ts +1 -1
- package/dist/utils/versioning.d.ts.map +1 -1
- package/dist/utils/versioning.js +10 -10
- package/dist/utils/versioning.js.map +1 -1
- package/package.json +11 -4
- package/src/generators/init.tpl.ts +434 -96
- package/src/generators/workflows.tpl.ts +54 -45
- package/dist/templates/actions/detect-changes-nx.yml.tpl.d.ts +0 -32
- package/dist/templates/actions/detect-changes-nx.yml.tpl.d.ts.map +0 -1
- package/dist/templates/actions/detect-changes-nx.yml.tpl.js +0 -227
- package/dist/templates/actions/detect-changes-nx.yml.tpl.js.map +0 -1
- package/dist/utils/idempotency.d.ts +0 -187
- package/dist/utils/idempotency.d.ts.map +0 -1
- package/dist/utils/idempotency.js +0 -304
- package/dist/utils/idempotency.js.map +0 -1
|
@@ -1,43 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Init Template Generator
|
|
3
|
-
*
|
|
4
|
-
* Generates the initial PipeCraft configuration file (.pipecraftrc
|
|
3
|
+
*
|
|
4
|
+
* Generates the initial PipeCraft configuration file (.pipecraftrc) with default settings.
|
|
5
5
|
* This generator is invoked by the `pipecraft init` command and prompts the user for
|
|
6
6
|
* basic project configuration preferences.
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* @module generators/init.tpl
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```typescript
|
|
12
12
|
* import { generate } from './generators/init.tpl.js'
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
* // Called by CLI with Pinion context
|
|
15
15
|
* await generate(pinionContext)
|
|
16
|
-
*
|
|
17
|
-
* // Creates .pipecraftrc
|
|
16
|
+
*
|
|
17
|
+
* // Creates .pipecraftrc with:
|
|
18
18
|
* // - CI provider (GitHub/GitLab)
|
|
19
19
|
* // - Merge strategy (fast-forward/merge)
|
|
20
20
|
* // - Branch flow configuration
|
|
21
21
|
* // - Domain-based change detection
|
|
22
22
|
* // - Semantic versioning rules
|
|
23
23
|
* ```
|
|
24
|
-
*
|
|
24
|
+
*
|
|
25
25
|
* @note Current Implementation: The generator currently uses hardcoded defaults
|
|
26
26
|
* from `defaultConfig` regardless of user prompt responses. This is intentional
|
|
27
27
|
* for the initial release to ensure consistent behavior. Future versions will
|
|
28
28
|
* respect user input and allow customization.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
import { PinionContext
|
|
32
|
-
import { existsSync, readFileSync } from 'fs'
|
|
31
|
+
import type { PinionContext } from '@featherscloud/pinion'
|
|
32
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
33
33
|
import inquirer from 'inquirer'
|
|
34
|
-
import { IdempotencyManager } from '../utils/idempotency.js'
|
|
35
|
-
import { VersionManager } from '../utils/versioning.js'
|
|
36
|
-
import { PipecraftConfig } from '../types/index.js'
|
|
37
34
|
|
|
38
35
|
/**
|
|
39
36
|
* Default PipeCraft configuration for new projects.
|
|
40
|
-
*
|
|
37
|
+
*
|
|
41
38
|
* Provides a complete, working trunk-based development workflow with:
|
|
42
39
|
* - GitHub Actions CI/CD
|
|
43
40
|
* - Fast-forward merge strategy
|
|
@@ -45,7 +42,7 @@ import { PipecraftConfig } from '../types/index.js'
|
|
|
45
42
|
* - Conventional commits enforcement
|
|
46
43
|
* - Semantic versioning with standard bump rules
|
|
47
44
|
* - Domain-based change detection for monorepos
|
|
48
|
-
*
|
|
45
|
+
*
|
|
49
46
|
* @const
|
|
50
47
|
*/
|
|
51
48
|
const defaultConfig = {
|
|
@@ -61,8 +58,22 @@ const defaultConfig = {
|
|
|
61
58
|
},
|
|
62
59
|
semver: {
|
|
63
60
|
bumpRules: {
|
|
64
|
-
|
|
61
|
+
test: 'ignore',
|
|
62
|
+
build: 'ignore',
|
|
63
|
+
|
|
64
|
+
ci: 'patch',
|
|
65
|
+
docs: 'patch',
|
|
66
|
+
style: 'patch',
|
|
65
67
|
fix: 'patch',
|
|
68
|
+
perf: 'patch',
|
|
69
|
+
refactor: 'patch',
|
|
70
|
+
chore: 'patch',
|
|
71
|
+
patch: 'patch',
|
|
72
|
+
|
|
73
|
+
feat: 'minor',
|
|
74
|
+
minor: 'minor',
|
|
75
|
+
|
|
76
|
+
major: 'major',
|
|
66
77
|
breaking: 'major'
|
|
67
78
|
}
|
|
68
79
|
},
|
|
@@ -87,54 +98,245 @@ const defaultConfig = {
|
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
/**
|
|
90
|
-
* Generates a
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* @
|
|
99
|
-
*
|
|
101
|
+
* Generates a YAML configuration file with comprehensive comments.
|
|
102
|
+
*
|
|
103
|
+
* Creates a .pipecraftrc file with:
|
|
104
|
+
* - Descriptive section headers
|
|
105
|
+
* - Inline comments for each configuration option
|
|
106
|
+
* - Examples and valid values
|
|
107
|
+
* - Proper YAML formatting
|
|
108
|
+
*
|
|
109
|
+
* @param {any} config - The configuration object to serialize
|
|
110
|
+
* @returns {string} YAML string with comments
|
|
100
111
|
*/
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
const generateYamlConfig = (config: any): string => {
|
|
113
|
+
const lines: string[] = []
|
|
114
|
+
|
|
115
|
+
// Header
|
|
116
|
+
lines.push('# PipeCraft Configuration')
|
|
117
|
+
lines.push('# Trunk-based development workflow automation')
|
|
118
|
+
lines.push('# https://pipecraft.thecraftlab.dev')
|
|
119
|
+
lines.push('')
|
|
120
|
+
|
|
121
|
+
// CI Provider
|
|
122
|
+
lines.push('# CI/CD Platform')
|
|
123
|
+
lines.push(`ciProvider: ${config.ciProvider}`)
|
|
124
|
+
lines.push('')
|
|
125
|
+
|
|
126
|
+
// Merge Strategy
|
|
127
|
+
lines.push('# Merge strategy for automatic promotions')
|
|
128
|
+
lines.push(
|
|
129
|
+
"# Options: 'fast-forward' (clean linear history, recommended) | 'merge' (creates merge commits)"
|
|
130
|
+
)
|
|
131
|
+
lines.push(`mergeStrategy: ${config.mergeStrategy}`)
|
|
132
|
+
lines.push('')
|
|
133
|
+
|
|
134
|
+
// Conventional Commits
|
|
135
|
+
lines.push('# Enforce conventional commit messages (feat:, fix:, etc.)')
|
|
136
|
+
lines.push(`requireConventionalCommits: ${config.requireConventionalCommits}`)
|
|
137
|
+
lines.push('')
|
|
138
|
+
|
|
139
|
+
// Branch Configuration
|
|
140
|
+
lines.push('# Branch flow configuration')
|
|
141
|
+
lines.push(`initialBranch: ${config.initialBranch} # Where features land`)
|
|
142
|
+
lines.push(`finalBranch: ${config.finalBranch} # Production branch`)
|
|
143
|
+
lines.push('')
|
|
144
|
+
|
|
145
|
+
// Branch Flow
|
|
146
|
+
lines.push(`# Promotion flow: ${config.branchFlow.join(' → ')}`)
|
|
147
|
+
lines.push('branchFlow:')
|
|
148
|
+
config.branchFlow.forEach((branch: string, index: number) => {
|
|
149
|
+
const comments = [
|
|
150
|
+
'# Feature integration and initial testing',
|
|
151
|
+
'# Pre-production validation',
|
|
152
|
+
'# Production releases'
|
|
153
|
+
]
|
|
154
|
+
lines.push(` - ${branch} ${comments[index] || ''}`)
|
|
155
|
+
})
|
|
156
|
+
lines.push('')
|
|
157
|
+
|
|
158
|
+
// Auto Merge
|
|
159
|
+
lines.push('# Automatic promotion after successful tests')
|
|
160
|
+
lines.push('autoMerge:')
|
|
161
|
+
const autoMergeEntries = Object.entries(config.autoMerge)
|
|
162
|
+
autoMergeEntries.forEach(([key, value]) => {
|
|
163
|
+
const flowIndex = config.branchFlow.indexOf(key)
|
|
164
|
+
const fromBranch = flowIndex > 0 ? config.branchFlow[flowIndex - 1] : ''
|
|
165
|
+
const comment = fromBranch ? ` # Auto-promote ${fromBranch} → ${key}` : ''
|
|
166
|
+
lines.push(` ${key}: ${value}${comment}`)
|
|
167
|
+
})
|
|
168
|
+
lines.push('')
|
|
169
|
+
|
|
170
|
+
// Package Manager (if present)
|
|
171
|
+
if (config.packageManager) {
|
|
172
|
+
lines.push('# Package manager')
|
|
173
|
+
lines.push(`packageManager: ${config.packageManager}`)
|
|
174
|
+
lines.push('')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Semantic Versioning
|
|
178
|
+
lines.push('# Semantic versioning rules')
|
|
179
|
+
lines.push('# Prefixes are used to determine the type of change and the version bump level')
|
|
180
|
+
lines.push('semver:')
|
|
181
|
+
lines.push(' bumpRules:')
|
|
182
|
+
|
|
183
|
+
const bumpRules = config.semver.bumpRules
|
|
184
|
+
const ruleGroups = [
|
|
185
|
+
{ title: '# Ignored types (no version bump)', keys: ['test', 'build'] },
|
|
186
|
+
{
|
|
187
|
+
title: '# Patch-level changes (0.0.x)',
|
|
188
|
+
keys: ['ci', 'docs', 'style', 'fix', 'perf', 'refactor', 'chore', 'patch']
|
|
112
189
|
},
|
|
113
|
-
|
|
190
|
+
{ title: '# Minor-level changes (0.x.0)', keys: ['feat', 'minor'] },
|
|
191
|
+
{ title: '# Major-level changes (x.0.0)', keys: ['major', 'breaking'] }
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
ruleGroups.forEach((group, groupIndex) => {
|
|
195
|
+
if (groupIndex > 0) lines.push('')
|
|
196
|
+
lines.push(` ${group.title}`)
|
|
197
|
+
group.keys.forEach(key => {
|
|
198
|
+
if (bumpRules[key]) {
|
|
199
|
+
lines.push(` ${key}: '${bumpRules[key]}'`)
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
lines.push('')
|
|
204
|
+
|
|
205
|
+
// Domains
|
|
206
|
+
lines.push('# Domain definitions - what parts of your codebase trigger which jobs')
|
|
207
|
+
lines.push(
|
|
208
|
+
'# Will create placeholder jobs for each domain if no jobs are defined in the pipeline.yml'
|
|
209
|
+
)
|
|
210
|
+
lines.push('domains:')
|
|
211
|
+
|
|
212
|
+
Object.entries(config.domains).forEach(([domainName, domainConfig]: [string, any]) => {
|
|
213
|
+
lines.push(` ${domainName}:`)
|
|
214
|
+
|
|
215
|
+
// Description (always write as field)
|
|
216
|
+
if (domainConfig.description) {
|
|
217
|
+
lines.push(` description: '${domainConfig.description}'`)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Paths
|
|
221
|
+
if (domainConfig.paths) {
|
|
222
|
+
lines.push(' paths:')
|
|
223
|
+
domainConfig.paths.forEach((path: string) => {
|
|
224
|
+
lines.push(` - ${path}`)
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Prefixes (if present)
|
|
229
|
+
if (domainConfig.prefixes) {
|
|
230
|
+
lines.push(` prefixes: [${domainConfig.prefixes.map((p: string) => `'${p}'`).join(', ')}]`)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
lines.push('')
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
// Nx Configuration (if present)
|
|
237
|
+
if (config.nx) {
|
|
238
|
+
lines.push('# Nx integration for monorepo optimization')
|
|
239
|
+
lines.push('nx:')
|
|
240
|
+
lines.push(` enabled: ${config.nx.enabled}`)
|
|
241
|
+
if (config.nx.tasks !== undefined) {
|
|
242
|
+
if (config.nx.tasks.length > 0) {
|
|
243
|
+
lines.push(' tasks:')
|
|
244
|
+
config.nx.tasks.forEach((task: string) => {
|
|
245
|
+
lines.push(` - ${task}`)
|
|
246
|
+
})
|
|
247
|
+
} else {
|
|
248
|
+
lines.push(' tasks: []')
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (config.nx.baseRef) {
|
|
252
|
+
lines.push(` baseRef: ${config.nx.baseRef}`)
|
|
253
|
+
}
|
|
254
|
+
if (config.nx.enableCache !== undefined) {
|
|
255
|
+
lines.push(` enableCache: ${config.nx.enableCache}`)
|
|
256
|
+
}
|
|
257
|
+
lines.push('')
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Versioning (if present)
|
|
261
|
+
if (config.versioning) {
|
|
262
|
+
lines.push('# Version calculation and release automation')
|
|
263
|
+
lines.push('versioning:')
|
|
264
|
+
lines.push(` enabled: ${config.versioning.enabled}`)
|
|
265
|
+
if (config.versioning.releaseItConfig) {
|
|
266
|
+
lines.push(` releaseItConfig: ${config.versioning.releaseItConfig}`)
|
|
267
|
+
}
|
|
268
|
+
if (config.versioning.conventionalCommits !== undefined) {
|
|
269
|
+
lines.push(
|
|
270
|
+
` conventionalCommits: ${config.versioning.conventionalCommits} # Use conventional commits for version bumps`
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
if (config.versioning.autoTag !== undefined) {
|
|
274
|
+
lines.push(
|
|
275
|
+
` autoTag: ${config.versioning.autoTag} # Automatically create git tags`
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
if (config.versioning.autoPush !== undefined) {
|
|
279
|
+
lines.push(
|
|
280
|
+
` autoPush: ${config.versioning.autoPush} # Manual control over git push`
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
if (config.versioning.changelog !== undefined) {
|
|
284
|
+
lines.push(` changelog: ${config.versioning.changelog} # Generate CHANGELOG.md`)
|
|
285
|
+
}
|
|
286
|
+
if (config.versioning.bumpRules) {
|
|
287
|
+
lines.push(' bumpRules:')
|
|
288
|
+
Object.entries(config.versioning.bumpRules).forEach(([key, value]) => {
|
|
289
|
+
lines.push(` ${key}: ${value}`)
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
lines.push('')
|
|
114
293
|
}
|
|
115
294
|
|
|
116
|
-
//
|
|
117
|
-
if (
|
|
118
|
-
|
|
295
|
+
// Rebuild configuration (if present)
|
|
296
|
+
if (config.rebuild) {
|
|
297
|
+
lines.push('# Workflow rebuild optimization')
|
|
298
|
+
lines.push('rebuild:')
|
|
299
|
+
lines.push(` enabled: ${config.rebuild.enabled} # Enable smart rebuild detection`)
|
|
300
|
+
if (config.rebuild.skipIfUnchanged !== undefined) {
|
|
301
|
+
lines.push(
|
|
302
|
+
` skipIfUnchanged: ${config.rebuild.skipIfUnchanged} # Skip regeneration if config hasn't changed`
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
if (config.rebuild.forceRegenerate !== undefined) {
|
|
306
|
+
lines.push(
|
|
307
|
+
` forceRegenerate: ${config.rebuild.forceRegenerate} # Force regenerate even if unchanged`
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
if (config.rebuild.cacheFile) {
|
|
311
|
+
lines.push(` cacheFile: ${config.rebuild.cacheFile}`)
|
|
312
|
+
}
|
|
313
|
+
lines.push('')
|
|
119
314
|
}
|
|
120
315
|
|
|
121
|
-
|
|
316
|
+
// Remove trailing empty line
|
|
317
|
+
while (lines[lines.length - 1] === '') {
|
|
318
|
+
lines.pop()
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
lines.push('') // Single trailing newline
|
|
322
|
+
|
|
323
|
+
return lines.join('\n')
|
|
122
324
|
}
|
|
123
325
|
|
|
124
326
|
/**
|
|
125
327
|
* Init generator main entry point.
|
|
126
|
-
*
|
|
328
|
+
*
|
|
127
329
|
* Orchestrates the initialization process by:
|
|
128
330
|
* 1. Prompting user for project preferences (currently unused - see note)
|
|
129
331
|
* 2. Merging user input with default configuration
|
|
130
332
|
* 3. Generating and writing .pipecraftrc.json file
|
|
131
|
-
*
|
|
333
|
+
*
|
|
132
334
|
* @param {PinionContext} ctx - Pinion generator context from CLI
|
|
133
335
|
* @returns {Promise<PinionContext>} Updated context after file generation
|
|
134
|
-
*
|
|
336
|
+
*
|
|
135
337
|
* @throws {Error} If configuration file cannot be written
|
|
136
338
|
* @throws {Error} If user input validation fails
|
|
137
|
-
*
|
|
339
|
+
*
|
|
138
340
|
* @example
|
|
139
341
|
* ```typescript
|
|
140
342
|
* // Called by Pinion framework when user runs `pipecraft init`
|
|
@@ -143,16 +345,16 @@ const configTemplate = (ctx: PipecraftConfig) => {
|
|
|
143
345
|
* argv: ['init'],
|
|
144
346
|
* pinion: { ... }
|
|
145
347
|
* })
|
|
146
|
-
*
|
|
348
|
+
*
|
|
147
349
|
* // Results in: /path/to/project/.pipecraftrc.json
|
|
148
350
|
* ```
|
|
149
|
-
*
|
|
351
|
+
*
|
|
150
352
|
* @note Current Behavior: Despite prompting for user input, the generator
|
|
151
353
|
* currently overwrites all responses with `defaultConfig` values (line 167).
|
|
152
354
|
* This ensures consistency for the initial release. Future versions will
|
|
153
355
|
* respect user choices and allow customization of branch names, merge strategies,
|
|
154
356
|
* and domain configurations.
|
|
155
|
-
*
|
|
357
|
+
*
|
|
156
358
|
* Prompts Presented (currently unused):
|
|
157
359
|
* - Project name
|
|
158
360
|
* - CI provider (GitHub/GitLab)
|
|
@@ -165,6 +367,34 @@ const configTemplate = (ctx: PipecraftConfig) => {
|
|
|
165
367
|
export const generate = async (ctx: PinionContext) => {
|
|
166
368
|
const cwd = ctx.cwd || process.cwd()
|
|
167
369
|
|
|
370
|
+
// Check if .pipecraftrc already exists (YAML format)
|
|
371
|
+
const configPath = `${cwd}/.pipecraftrc`
|
|
372
|
+
const legacyConfigPath = `${cwd}/.pipecraftrc.json`
|
|
373
|
+
|
|
374
|
+
if (existsSync(configPath) || existsSync(legacyConfigPath)) {
|
|
375
|
+
const existingPath = existsSync(configPath) ? '.pipecraftrc' : '.pipecraftrc.json'
|
|
376
|
+
const overwriteAnswer = await inquirer.prompt([
|
|
377
|
+
{
|
|
378
|
+
type: 'confirm',
|
|
379
|
+
name: 'overwrite',
|
|
380
|
+
message: `⚠️ ${existingPath} already exists. Overwrite it?`,
|
|
381
|
+
default: false
|
|
382
|
+
}
|
|
383
|
+
])
|
|
384
|
+
|
|
385
|
+
if (!overwriteAnswer.overwrite) {
|
|
386
|
+
console.log('\n❌ Init cancelled. Existing configuration preserved.\n')
|
|
387
|
+
return
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
console.log('\n✅ Proceeding with overwrite...\n')
|
|
391
|
+
|
|
392
|
+
// Set force flag to true so Pinion will overwrite the file
|
|
393
|
+
if (ctx.pinion) {
|
|
394
|
+
ctx.pinion.force = true
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
168
398
|
// Detect package manager before prompting
|
|
169
399
|
let detectedPackageManager: 'npm' | 'yarn' | 'pnpm' = 'npm'
|
|
170
400
|
if (existsSync(`${cwd}/pnpm-lock.yaml`)) {
|
|
@@ -175,14 +405,53 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
175
405
|
detectedPackageManager = 'npm'
|
|
176
406
|
}
|
|
177
407
|
|
|
408
|
+
// Detect Nx workspace before prompting
|
|
409
|
+
const nxJsonPath = `${cwd}/nx.json`
|
|
410
|
+
let detectedNxTasks: string[] = []
|
|
411
|
+
let nxDetected = false
|
|
412
|
+
|
|
413
|
+
if (existsSync(nxJsonPath)) {
|
|
414
|
+
try {
|
|
415
|
+
const nxJsonContent = readFileSync(nxJsonPath, 'utf8')
|
|
416
|
+
const nxJson = JSON.parse(nxJsonContent)
|
|
417
|
+
|
|
418
|
+
// Extract tasks from targetDefaults
|
|
419
|
+
const tasks = nxJson.targetDefaults ? Object.keys(nxJson.targetDefaults) : []
|
|
420
|
+
|
|
421
|
+
// Sort tasks in a logical order (quality → test → build → e2e)
|
|
422
|
+
const taskOrder = [
|
|
423
|
+
'lint',
|
|
424
|
+
'typecheck',
|
|
425
|
+
'test',
|
|
426
|
+
'unit-test',
|
|
427
|
+
'build',
|
|
428
|
+
'integration-test',
|
|
429
|
+
'e2e',
|
|
430
|
+
'e2e-ci'
|
|
431
|
+
]
|
|
432
|
+
detectedNxTasks = tasks.sort((a, b) => {
|
|
433
|
+
const aIdx = taskOrder.indexOf(a)
|
|
434
|
+
const bIdx = taskOrder.indexOf(b)
|
|
435
|
+
if (aIdx === -1 && bIdx === -1) return 0
|
|
436
|
+
if (aIdx === -1) return 1
|
|
437
|
+
if (bIdx === -1) return -1
|
|
438
|
+
return aIdx - bIdx
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
nxDetected = true
|
|
442
|
+
console.log('\n✅ Nx workspace detected!')
|
|
443
|
+
console.log(` Found tasks: ${detectedNxTasks.join(', ')}`)
|
|
444
|
+
console.log(' Pipecraft can optimize your CI pipeline using Nx affected commands.\n')
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.warn('\n⚠️ Found nx.json but could not parse it:', error)
|
|
447
|
+
console.log(' Nx integration will use default tasks.\n')
|
|
448
|
+
nxDetected = true
|
|
449
|
+
detectedNxTasks = ['lint', 'test', 'build', 'integration-test']
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
178
453
|
// Run inquirer prompts
|
|
179
454
|
const answers = await inquirer.prompt([
|
|
180
|
-
{
|
|
181
|
-
type: 'input',
|
|
182
|
-
name: 'projectName',
|
|
183
|
-
message: 'What is your project name?',
|
|
184
|
-
default: 'my-project'
|
|
185
|
-
},
|
|
186
455
|
{
|
|
187
456
|
type: 'list',
|
|
188
457
|
name: 'ciProvider',
|
|
@@ -234,54 +503,114 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
234
503
|
message: `Which package manager do you use? (detected: ${detectedPackageManager})`,
|
|
235
504
|
choices: ['npm', 'yarn', 'pnpm'],
|
|
236
505
|
default: detectedPackageManager
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
type: 'confirm',
|
|
509
|
+
name: 'enableNx',
|
|
510
|
+
message: nxDetected
|
|
511
|
+
? `Enable Nx integration? (detected ${detectedNxTasks.length} tasks)`
|
|
512
|
+
: 'Enable Nx integration?',
|
|
513
|
+
default: nxDetected,
|
|
514
|
+
when: () => nxDetected // Only show if Nx is detected
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
type: 'list',
|
|
518
|
+
name: 'domainSelection',
|
|
519
|
+
message: 'What domains exist in your codebase?',
|
|
520
|
+
choices: [
|
|
521
|
+
{ name: 'API + Web (common monorepo)', value: 'api-web' },
|
|
522
|
+
{ name: 'Frontend + Backend', value: 'frontend-backend' },
|
|
523
|
+
{ name: 'Apps + Libs (Nx-style)', value: 'apps-libs' },
|
|
524
|
+
{ name: 'Custom domains', value: 'custom' }
|
|
525
|
+
],
|
|
526
|
+
default: 'api-web'
|
|
237
527
|
}
|
|
238
528
|
])
|
|
239
529
|
|
|
240
|
-
//
|
|
241
|
-
|
|
530
|
+
// Handle custom domain selection
|
|
531
|
+
let selectedDomains = []
|
|
532
|
+
if (answers.domainSelection === 'custom') {
|
|
533
|
+
const customDomainsAnswer = await inquirer.prompt([
|
|
534
|
+
{
|
|
535
|
+
type: 'input',
|
|
536
|
+
name: 'customDomains',
|
|
537
|
+
message: 'Enter your domains (comma-separated)',
|
|
538
|
+
validate: (input: string) => {
|
|
539
|
+
if (!input.trim()) {
|
|
540
|
+
return 'Please enter at least one domain'
|
|
541
|
+
}
|
|
542
|
+
const domains = input
|
|
543
|
+
.split(',')
|
|
544
|
+
.map(d => d.trim())
|
|
545
|
+
.filter(d => d)
|
|
546
|
+
if (domains.length === 0) {
|
|
547
|
+
return 'Please enter valid domain names'
|
|
548
|
+
}
|
|
549
|
+
return true
|
|
550
|
+
},
|
|
551
|
+
filter: (input: string) =>
|
|
552
|
+
input
|
|
553
|
+
.split(',')
|
|
554
|
+
.map(d => d.trim())
|
|
555
|
+
.filter(d => d)
|
|
556
|
+
}
|
|
557
|
+
])
|
|
558
|
+
selectedDomains = customDomainsAnswer.customDomains
|
|
559
|
+
} else {
|
|
560
|
+
// Map predefined selections to domain names
|
|
561
|
+
const domainMappings = {
|
|
562
|
+
'api-web': ['api', 'web'],
|
|
563
|
+
'frontend-backend': ['frontend', 'backend'],
|
|
564
|
+
'apps-libs': ['apps', 'libs']
|
|
565
|
+
}
|
|
566
|
+
selectedDomains = domainMappings[answers.domainSelection as keyof typeof domainMappings] || [
|
|
567
|
+
'api',
|
|
568
|
+
'web'
|
|
569
|
+
]
|
|
570
|
+
}
|
|
242
571
|
|
|
243
|
-
//
|
|
244
|
-
const
|
|
245
|
-
|
|
572
|
+
// Generate domain configuration
|
|
573
|
+
const domainConfig: Record<string, any> = {}
|
|
574
|
+
selectedDomains.forEach((domain: string) => {
|
|
575
|
+
domainConfig[domain] = {
|
|
576
|
+
paths: [`${domain}/**`], // Default path pattern
|
|
577
|
+
description: `${domain} application changes`
|
|
578
|
+
}
|
|
579
|
+
})
|
|
246
580
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
581
|
+
// Add cicd domain for CI/CD changes
|
|
582
|
+
domainConfig.cicd = {
|
|
583
|
+
paths: ['.github/**'],
|
|
584
|
+
description: 'CI/CD configuration changes'
|
|
585
|
+
}
|
|
251
586
|
|
|
252
|
-
|
|
253
|
-
|
|
587
|
+
// Show warning for custom domains about path editing
|
|
588
|
+
if (answers.domainSelection === 'custom') {
|
|
589
|
+
console.log('\n⚠️ Custom domains selected!')
|
|
590
|
+
console.log(' You will need to edit the paths in .pipecraftrc after generation')
|
|
591
|
+
console.log(' to match your actual project structure.\n')
|
|
592
|
+
}
|
|
254
593
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const sortedTasks = tasks.sort((a, b) => {
|
|
258
|
-
const aIdx = taskOrder.indexOf(a)
|
|
259
|
-
const bIdx = taskOrder.indexOf(b)
|
|
260
|
-
if (aIdx === -1 && bIdx === -1) return 0
|
|
261
|
-
if (aIdx === -1) return 1
|
|
262
|
-
if (bIdx === -1) return -1
|
|
263
|
-
return aIdx - bIdx
|
|
264
|
-
})
|
|
594
|
+
// Merge answers with context and defaults
|
|
595
|
+
const mergedCtx = { ...ctx, ...defaultConfig, ...answers } as any
|
|
265
596
|
|
|
266
|
-
|
|
267
|
-
|
|
597
|
+
// Configure Nx based on detection and user confirmation
|
|
598
|
+
let nxConfig:
|
|
599
|
+
| { enabled: boolean; tasks: string[]; baseRef: string; enableCache: boolean }
|
|
600
|
+
| undefined
|
|
601
|
+
if (nxDetected && answers.enableNx) {
|
|
602
|
+
console.log('\n✅ Nx integration enabled!')
|
|
603
|
+
console.log(` Tasks to run: ${detectedNxTasks.join(', ')}\n`)
|
|
268
604
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
} catch (error) {
|
|
276
|
-
console.warn('⚠️ Found nx.json but could not parse it:', error)
|
|
277
|
-
console.log(' Using default Nx configuration')
|
|
278
|
-
nxConfig = {
|
|
279
|
-
enabled: true,
|
|
280
|
-
tasks: ['lint', 'test', 'build', 'integration-test'],
|
|
281
|
-
baseRef: 'origin/main',
|
|
282
|
-
enableCache: true
|
|
283
|
-
}
|
|
605
|
+
nxConfig = {
|
|
606
|
+
enabled: true,
|
|
607
|
+
tasks: detectedNxTasks,
|
|
608
|
+
baseRef: 'origin/main',
|
|
609
|
+
enableCache: true
|
|
284
610
|
}
|
|
611
|
+
} else if (nxDetected && !answers.enableNx) {
|
|
612
|
+
console.log('\n⚠️ Nx detected but integration disabled.')
|
|
613
|
+
console.log(' You can enable it later by editing .pipecraftrc\n')
|
|
285
614
|
}
|
|
286
615
|
|
|
287
616
|
const configData: any = {
|
|
@@ -296,7 +625,7 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
296
625
|
semver: {
|
|
297
626
|
bumpRules: mergedCtx.semver.bumpRules
|
|
298
627
|
},
|
|
299
|
-
domains:
|
|
628
|
+
domains: domainConfig // Use user-selected domains instead of defaults
|
|
300
629
|
}
|
|
301
630
|
|
|
302
631
|
// Add Nx config if detected
|
|
@@ -304,5 +633,14 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
304
633
|
configData.nx = nxConfig
|
|
305
634
|
}
|
|
306
635
|
|
|
307
|
-
|
|
308
|
-
|
|
636
|
+
// Generate YAML content with comments
|
|
637
|
+
const yamlContent = generateYamlConfig(configData)
|
|
638
|
+
|
|
639
|
+
// Write the .pipecraftrc file
|
|
640
|
+
const outputPath = `${cwd}/.pipecraftrc`
|
|
641
|
+
writeFileSync(outputPath, yamlContent, 'utf-8')
|
|
642
|
+
|
|
643
|
+
console.log(`\n✅ Created ${outputPath}\n`)
|
|
644
|
+
|
|
645
|
+
return mergedCtx
|
|
646
|
+
}
|