pipecraft 0.29.3 → 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 +310 -83
- 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 +142 -17
- 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 +374 -102
- 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('')
|
|
293
|
+
}
|
|
294
|
+
|
|
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('')
|
|
114
314
|
}
|
|
115
315
|
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
316
|
+
// Remove trailing empty line
|
|
317
|
+
while (lines[lines.length - 1] === '') {
|
|
318
|
+
lines.pop()
|
|
119
319
|
}
|
|
120
320
|
|
|
121
|
-
|
|
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',
|
|
@@ -235,6 +504,15 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
235
504
|
choices: ['npm', 'yarn', 'pnpm'],
|
|
236
505
|
default: detectedPackageManager
|
|
237
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
|
+
},
|
|
238
516
|
{
|
|
239
517
|
type: 'list',
|
|
240
518
|
name: 'domainSelection',
|
|
@@ -261,13 +539,20 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
261
539
|
if (!input.trim()) {
|
|
262
540
|
return 'Please enter at least one domain'
|
|
263
541
|
}
|
|
264
|
-
const domains = input
|
|
542
|
+
const domains = input
|
|
543
|
+
.split(',')
|
|
544
|
+
.map(d => d.trim())
|
|
545
|
+
.filter(d => d)
|
|
265
546
|
if (domains.length === 0) {
|
|
266
547
|
return 'Please enter valid domain names'
|
|
267
548
|
}
|
|
268
549
|
return true
|
|
269
550
|
},
|
|
270
|
-
filter: (input: string) =>
|
|
551
|
+
filter: (input: string) =>
|
|
552
|
+
input
|
|
553
|
+
.split(',')
|
|
554
|
+
.map(d => d.trim())
|
|
555
|
+
.filter(d => d)
|
|
271
556
|
}
|
|
272
557
|
])
|
|
273
558
|
selectedDomains = customDomainsAnswer.customDomains
|
|
@@ -278,7 +563,10 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
278
563
|
'frontend-backend': ['frontend', 'backend'],
|
|
279
564
|
'apps-libs': ['apps', 'libs']
|
|
280
565
|
}
|
|
281
|
-
selectedDomains = domainMappings[answers.domainSelection as keyof typeof domainMappings] || [
|
|
566
|
+
selectedDomains = domainMappings[answers.domainSelection as keyof typeof domainMappings] || [
|
|
567
|
+
'api',
|
|
568
|
+
'web'
|
|
569
|
+
]
|
|
282
570
|
}
|
|
283
571
|
|
|
284
572
|
// Generate domain configuration
|
|
@@ -292,62 +580,37 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
292
580
|
|
|
293
581
|
// Add cicd domain for CI/CD changes
|
|
294
582
|
domainConfig.cicd = {
|
|
295
|
-
paths: ['.github
|
|
583
|
+
paths: ['.github/**'],
|
|
296
584
|
description: 'CI/CD configuration changes'
|
|
297
585
|
}
|
|
298
586
|
|
|
299
587
|
// Show warning for custom domains about path editing
|
|
300
588
|
if (answers.domainSelection === 'custom') {
|
|
301
589
|
console.log('\n⚠️ Custom domains selected!')
|
|
302
|
-
console.log(' You will need to edit the paths in .pipecraftrc
|
|
590
|
+
console.log(' You will need to edit the paths in .pipecraftrc after generation')
|
|
303
591
|
console.log(' to match your actual project structure.\n')
|
|
304
592
|
}
|
|
305
593
|
|
|
306
594
|
// Merge answers with context and defaults
|
|
307
595
|
const mergedCtx = { ...ctx, ...defaultConfig, ...answers } as any
|
|
308
596
|
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const nxJson = JSON.parse(nxJsonContent)
|
|
317
|
-
|
|
318
|
-
// Extract tasks from targetDefaults
|
|
319
|
-
const tasks = nxJson.targetDefaults ? Object.keys(nxJson.targetDefaults) : []
|
|
320
|
-
|
|
321
|
-
// Sort tasks in a logical order (quality → test → build → e2e)
|
|
322
|
-
const taskOrder = ['lint', 'typecheck', 'test', 'unit-test', 'build', 'integration-test', 'e2e', 'e2e-ci']
|
|
323
|
-
const sortedTasks = tasks.sort((a, b) => {
|
|
324
|
-
const aIdx = taskOrder.indexOf(a)
|
|
325
|
-
const bIdx = taskOrder.indexOf(b)
|
|
326
|
-
if (aIdx === -1 && bIdx === -1) return 0
|
|
327
|
-
if (aIdx === -1) return 1
|
|
328
|
-
if (bIdx === -1) return -1
|
|
329
|
-
return aIdx - bIdx
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
console.log('✅ Nx workspace detected - enabling Nx integration')
|
|
333
|
-
console.log(` Detected tasks: ${sortedTasks.join(', ')}`)
|
|
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`)
|
|
334
604
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
} catch (error) {
|
|
342
|
-
console.warn('⚠️ Found nx.json but could not parse it:', error)
|
|
343
|
-
console.log(' Using default Nx configuration')
|
|
344
|
-
nxConfig = {
|
|
345
|
-
enabled: true,
|
|
346
|
-
tasks: ['lint', 'test', 'build', 'integration-test'],
|
|
347
|
-
baseRef: 'origin/main',
|
|
348
|
-
enableCache: true
|
|
349
|
-
}
|
|
605
|
+
nxConfig = {
|
|
606
|
+
enabled: true,
|
|
607
|
+
tasks: detectedNxTasks,
|
|
608
|
+
baseRef: 'origin/main',
|
|
609
|
+
enableCache: true
|
|
350
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')
|
|
351
614
|
}
|
|
352
615
|
|
|
353
616
|
const configData: any = {
|
|
@@ -370,5 +633,14 @@ export const generate = async (ctx: PinionContext) => {
|
|
|
370
633
|
configData.nx = nxConfig
|
|
371
634
|
}
|
|
372
635
|
|
|
373
|
-
|
|
374
|
-
|
|
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
|
+
}
|