pipecraft 0.0.0-releaseit

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 (176) hide show
  1. package/.claude/settings.local.json +35 -0
  2. package/.github/actions/calculate-version/action.yml +106 -0
  3. package/.github/actions/create-pr/action.yml +122 -0
  4. package/.github/actions/create-release/action.yml +74 -0
  5. package/.github/actions/create-tag/action.yml +94 -0
  6. package/.github/actions/detect-changes/action.yml +62 -0
  7. package/.github/actions/manage-branch/action.yml +113 -0
  8. package/.github/actions/promote-branch/action.yml +352 -0
  9. package/.github/example/workflows/job.analyze.code.yml +26 -0
  10. package/.github/example/workflows/job.analyze.docker.yml +32 -0
  11. package/.github/example/workflows/job.app.api.deploy.yml +127 -0
  12. package/.github/example/workflows/job.app.api.test.yml +102 -0
  13. package/.github/example/workflows/job.app.docs.deploy.yml +40 -0
  14. package/.github/example/workflows/job.app.docs.test.yml +50 -0
  15. package/.github/example/workflows/job.app.web.deploy.yml +96 -0
  16. package/.github/example/workflows/job.app.web.test.yml +49 -0
  17. package/.github/example/workflows/job.changes.yml +82 -0
  18. package/.github/example/workflows/job.create-pr.yml +96 -0
  19. package/.github/example/workflows/job.env-check.yml +43 -0
  20. package/.github/example/workflows/job.fast-forward.yml +103 -0
  21. package/.github/example/workflows/job.lint.yml +64 -0
  22. package/.github/example/workflows/job.pr-name.yml +70 -0
  23. package/.github/example/workflows/job.pr-squash-name.yml +27 -0
  24. package/.github/example/workflows/job.tag.yml +38 -0
  25. package/.github/example/workflows/job.version.yml +99 -0
  26. package/.github/example/workflows/lib.plugin-base.publish.yml +59 -0
  27. package/.github/example/workflows/pipe.yml +178 -0
  28. package/.github/example/workflows/scripts/fastforward.sh +51 -0
  29. package/.github/example/workflows/scripts/pipe-cleaner.sh +70 -0
  30. package/.github/scripts/fastforward.sh +51 -0
  31. package/.github/scripts/pipe-cleaner.sh +70 -0
  32. package/.github/workflows/pipeline.yml +220 -0
  33. package/.github/workflows/pr-title-check.yml +70 -0
  34. package/.github/workflows/publish.yml +39 -0
  35. package/.pipecraftrc.json +78 -0
  36. package/.release-it.cjs +71 -0
  37. package/PIPELINE_TESTING_PLAN.md +499 -0
  38. package/README.md +1101 -0
  39. package/TRUNK_FLOW_PLAN.md +401 -0
  40. package/assets/logo_banner.png +0 -0
  41. package/assets/logo_banner.webp +0 -0
  42. package/dist/cli/index.d.ts +3 -0
  43. package/dist/cli/index.d.ts.map +1 -0
  44. package/dist/cli/index.js +364 -0
  45. package/dist/cli/index.js.map +1 -0
  46. package/dist/generators/init.tpl.d.ts +3 -0
  47. package/dist/generators/init.tpl.d.ts.map +1 -0
  48. package/dist/generators/init.tpl.js +117 -0
  49. package/dist/generators/init.tpl.js.map +1 -0
  50. package/dist/generators/workflows.tpl.d.ts +7 -0
  51. package/dist/generators/workflows.tpl.d.ts.map +1 -0
  52. package/dist/generators/workflows.tpl.js +73 -0
  53. package/dist/generators/workflows.tpl.js.map +1 -0
  54. package/dist/templates/actions/calculate-version.yml.tpl.d.ts +3 -0
  55. package/dist/templates/actions/calculate-version.yml.tpl.d.ts.map +1 -0
  56. package/dist/templates/actions/calculate-version.yml.tpl.js +122 -0
  57. package/dist/templates/actions/calculate-version.yml.tpl.js.map +1 -0
  58. package/dist/templates/actions/create-pr.yml.tpl.d.ts +3 -0
  59. package/dist/templates/actions/create-pr.yml.tpl.d.ts.map +1 -0
  60. package/dist/templates/actions/create-pr.yml.tpl.js +145 -0
  61. package/dist/templates/actions/create-pr.yml.tpl.js.map +1 -0
  62. package/dist/templates/actions/create-tag.yml.tpl.d.ts +3 -0
  63. package/dist/templates/actions/create-tag.yml.tpl.d.ts.map +1 -0
  64. package/dist/templates/actions/create-tag.yml.tpl.js +111 -0
  65. package/dist/templates/actions/create-tag.yml.tpl.js.map +1 -0
  66. package/dist/templates/actions/detect-changes.yml.tpl.d.ts +3 -0
  67. package/dist/templates/actions/detect-changes.yml.tpl.d.ts.map +1 -0
  68. package/dist/templates/actions/detect-changes.yml.tpl.js +79 -0
  69. package/dist/templates/actions/detect-changes.yml.tpl.js.map +1 -0
  70. package/dist/templates/actions/manage-branch.yml.tpl.d.ts +3 -0
  71. package/dist/templates/actions/manage-branch.yml.tpl.d.ts.map +1 -0
  72. package/dist/templates/actions/manage-branch.yml.tpl.js +130 -0
  73. package/dist/templates/actions/manage-branch.yml.tpl.js.map +1 -0
  74. package/dist/templates/actions/promote-branch.yml.tpl.d.ts +3 -0
  75. package/dist/templates/actions/promote-branch.yml.tpl.d.ts.map +1 -0
  76. package/dist/templates/actions/promote-branch.yml.tpl.js +362 -0
  77. package/dist/templates/actions/promote-branch.yml.tpl.js.map +1 -0
  78. package/dist/templates/workflows/pipeline-path-based.yml.tpl.d.ts +16 -0
  79. package/dist/templates/workflows/pipeline-path-based.yml.tpl.d.ts.map +1 -0
  80. package/dist/templates/workflows/pipeline-path-based.yml.tpl.js +652 -0
  81. package/dist/templates/workflows/pipeline-path-based.yml.tpl.js.map +1 -0
  82. package/dist/types/index.d.ts +63 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +2 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/dist/utils/ast-path-operations.d.ts +287 -0
  87. package/dist/utils/ast-path-operations.d.ts.map +1 -0
  88. package/dist/utils/ast-path-operations.js +419 -0
  89. package/dist/utils/ast-path-operations.js.map +1 -0
  90. package/dist/utils/config.d.ts +3 -0
  91. package/dist/utils/config.d.ts.map +1 -0
  92. package/dist/utils/config.js +49 -0
  93. package/dist/utils/config.js.map +1 -0
  94. package/dist/utils/github-setup.d.ts +74 -0
  95. package/dist/utils/github-setup.d.ts.map +1 -0
  96. package/dist/utils/github-setup.js +418 -0
  97. package/dist/utils/github-setup.js.map +1 -0
  98. package/dist/utils/idempotency.d.ts +47 -0
  99. package/dist/utils/idempotency.d.ts.map +1 -0
  100. package/dist/utils/idempotency.js +176 -0
  101. package/dist/utils/idempotency.js.map +1 -0
  102. package/dist/utils/preflight.d.ts +55 -0
  103. package/dist/utils/preflight.d.ts.map +1 -0
  104. package/dist/utils/preflight.js +261 -0
  105. package/dist/utils/preflight.js.map +1 -0
  106. package/dist/utils/versioning.d.ts +41 -0
  107. package/dist/utils/versioning.d.ts.map +1 -0
  108. package/dist/utils/versioning.js +219 -0
  109. package/dist/utils/versioning.js.map +1 -0
  110. package/docs/USER_JOURNEY_ERRORS.md +352 -0
  111. package/eslint.config.js +77 -0
  112. package/examples/basic-config.json +41 -0
  113. package/examples/monorepo-config.json +49 -0
  114. package/examples/usage.md +289 -0
  115. package/package.json +58 -0
  116. package/scripts/debug-workflows.sh +339 -0
  117. package/scripts/parse-pipeline.js +29 -0
  118. package/scripts/test-job-order.cjs +140 -0
  119. package/scripts/validate-pipeline.cjs +379 -0
  120. package/scripts/verify-job-order.sh +30 -0
  121. package/src/cli/index.ts +414 -0
  122. package/src/generators/init.tpl.ts +126 -0
  123. package/src/generators/workflows.tpl.ts +80 -0
  124. package/src/templates/actions/calculate-version.yml.tpl.ts +124 -0
  125. package/src/templates/actions/create-pr.yml.tpl.ts +147 -0
  126. package/src/templates/actions/create-tag.yml.tpl.ts +112 -0
  127. package/src/templates/actions/detect-changes.yml.tpl.ts +87 -0
  128. package/src/templates/actions/manage-branch.yml.tpl.ts +132 -0
  129. package/src/templates/actions/promote-branch.yml.tpl.ts +364 -0
  130. package/src/templates/workflows/pipeline-path-based.yml.tpl.ts +706 -0
  131. package/src/types/index.ts +64 -0
  132. package/src/utils/README-ast-path-operations.md +390 -0
  133. package/src/utils/ast-path-operations.ts +581 -0
  134. package/src/utils/config.ts +64 -0
  135. package/src/utils/github-setup.ts +558 -0
  136. package/src/utils/idempotency.ts +215 -0
  137. package/src/utils/preflight.ts +306 -0
  138. package/src/utils/versioning.ts +244 -0
  139. package/tests/README.md +229 -0
  140. package/tests/TEST_STRUCTURE.md +256 -0
  141. package/tests/act/run-act-tests.sh +345 -0
  142. package/tests/debugging/debug-utils.ts +538 -0
  143. package/tests/debugging/debug-workflow.test.ts +339 -0
  144. package/tests/debugging/debug-workflows.sh +339 -0
  145. package/tests/debugging/iterative-debug.ts +652 -0
  146. package/tests/debugging/run-debug-tests.sh +431 -0
  147. package/tests/fixtures/basic-config.json +51 -0
  148. package/tests/fixtures/invalid-config.json +9 -0
  149. package/tests/fixtures/pipeline-generated.yml +235 -0
  150. package/tests/fixtures/pipeline-preserve-comments.yml +245 -0
  151. package/tests/fixtures/pipeline-user-modified.yml +245 -0
  152. package/tests/fixtures/test-config.json +58 -0
  153. package/tests/github-live/README.md +250 -0
  154. package/tests/github-local/JOB_WORKFLOW_TESTS.md +296 -0
  155. package/tests/github-local/README.md +240 -0
  156. package/tests/github-local/run-all-tests.sh +422 -0
  157. package/tests/github-local/test-job-workflows.sh +631 -0
  158. package/tests/github-local/test-pipeline-workflow.sh +440 -0
  159. package/tests/integration/generators.test.ts +578 -0
  160. package/tests/integration/path-based-template.test.ts +510 -0
  161. package/tests/integration/simple-path-based.test.ts +415 -0
  162. package/tests/setup.ts +56 -0
  163. package/tests/unit/ast-path-operations-extended.test.ts +302 -0
  164. package/tests/unit/cli.test.ts +541 -0
  165. package/tests/unit/config-extended.test.ts +412 -0
  166. package/tests/unit/config.test.ts +152 -0
  167. package/tests/unit/github-setup.test.ts +189 -0
  168. package/tests/unit/idempotency-isolated.test.ts +297 -0
  169. package/tests/unit/job-order.test.ts +157 -0
  170. package/tests/unit/pipeline-path-based.test.ts +511 -0
  171. package/tests/unit/validate-pipeline.test.ts +384 -0
  172. package/tests/unit/versioning-extended.test.ts +279 -0
  173. package/tests/unit/versioning.test.ts +241 -0
  174. package/tsconfig.debug.json +17 -0
  175. package/tsconfig.json +28 -0
  176. package/vitest.config.ts +51 -0
@@ -0,0 +1,414 @@
1
+ #!/usr/bin/env node
2
+
3
+ // PipeCraft CLI - Automated CI/CD pipeline generator
4
+ // Test change for deploy flow validation
5
+ import { Command } from 'commander'
6
+ import { cosmiconfigSync } from 'cosmiconfig'
7
+ import { runModule, prompt } from '@featherscloud/pinion'
8
+ import { join } from 'path'
9
+ import { fileURLToPath } from 'url'
10
+ import { dirname } from 'path'
11
+ import { IdempotencyManager } from '../utils/idempotency.js'
12
+ import { VersionManager } from '../utils/versioning.js'
13
+ import { loadConfig, validateConfig } from '../utils/config.js'
14
+ import { PipecraftConfig } from '../types/index.js'
15
+ import { setupGitHubPermissions } from '../utils/github-setup.js'
16
+ import { runPreflightChecks, formatPreflightResults, checkNodeVersion } from '../utils/preflight.js'
17
+
18
+ const __filename = fileURLToPath(import.meta.url)
19
+ const __dirname = dirname(__filename)
20
+
21
+ const program = new Command()
22
+
23
+ // Configure the CLI
24
+ program
25
+ .name('pipecraft')
26
+ .description('CLI tool for managing trunk-based development workflows')
27
+ .version('1.0.0')
28
+
29
+ console.log("pipecraft edit")
30
+
31
+ // Global options
32
+ program
33
+ .option('-c, --config <path>', 'path to config file', '.pipecraftrc.json')
34
+ .option('-p, --pipeline <path>', 'path to existing pipeline file for merging', '.github/workflows/pipeline.yml')
35
+ .option('-o, --output-pipeline <path>', 'path to output pipeline file (for testing)', '.github/workflows/pipeline.yml')
36
+ .option('-v, --verbose', 'verbose output')
37
+ .option('--force', 'force regeneration even if files unchanged')
38
+ .option('--dry-run', 'show what would be done without making changes')
39
+
40
+ // Init command - Initialize configuration
41
+ program
42
+ .command('init')
43
+ .description('Initialize pipecraft configuration')
44
+ .option('-f, --force', 'overwrite existing config file')
45
+ .option('-i, --interactive', 'run interactive setup wizard')
46
+ .option('--with-versioning', 'include version management setup')
47
+ .option('--ci-provider <provider>', 'CI provider (github|gitlab)', 'github')
48
+ .option('--merge-strategy <strategy>', 'merge strategy (fast-forward|merge)', 'fast-forward')
49
+ .option('--initial-branch <branch>', 'initial development branch', 'develop')
50
+ .option('--final-branch <branch>', 'final production branch', 'main')
51
+ .action(async (options) => {
52
+ try {
53
+ const globalOptions = program.opts()
54
+
55
+ await runModule(join(__dirname, '../generators/init.tpl.ts'), {
56
+ cwd: process.cwd(),
57
+ argv: process.argv,
58
+ pinion: {
59
+ logger: {
60
+ ...console,
61
+ notice: console.log
62
+ },
63
+ prompt: prompt as any,
64
+ cwd: process.cwd(),
65
+ force: options.force || globalOptions.force || false,
66
+ trace: [],
67
+ exec: async (command: string, args: string[]) => {
68
+ const { spawn } = await import('child_process')
69
+ return new Promise((resolve, reject) => {
70
+ const child = spawn(command, args, { stdio: 'inherit', shell: true })
71
+ child.once('exit', (code: number) => (code === 0 ? resolve(code) : reject(code)))
72
+ })
73
+ }
74
+ }
75
+ })
76
+
77
+ // Setup version management if requested
78
+ if (options.withVersioning) {
79
+ const config = loadConfig(globalOptions.config)
80
+ const versionManager = new VersionManager(config)
81
+ versionManager.setupVersionManagement()
82
+ console.log('✅ Version management setup completed!')
83
+ }
84
+
85
+ console.log('✅ Configuration initialized successfully!')
86
+ } catch (error: any) {
87
+ console.error('❌ Failed to initialize configuration:', error.message)
88
+ process.exit(1)
89
+ }
90
+ })
91
+
92
+ // Generate command - Generate workflow files
93
+ program
94
+ .command('generate')
95
+ .description('Generate CI/CD workflows from configuration')
96
+ .option('-o, --output <path>', 'output directory for generated workflows', '.github/workflows')
97
+ .option('--skip-unchanged', 'skip files that haven\'t changed')
98
+ .option('--skip-checks', 'skip pre-flight checks (not recommended)')
99
+ .action(async (options) => {
100
+ try {
101
+ const globalOptions = program.opts()
102
+ const configPath = globalOptions.config
103
+ const pipelinePath = globalOptions.pipeline
104
+ const outputPipelinePath = globalOptions.outputPipeline
105
+
106
+ // Run pre-flight checks unless skipped
107
+ if (!options.skipChecks) {
108
+ console.log('🔍 Running pre-flight checks...\n')
109
+
110
+ const checks = runPreflightChecks()
111
+ const { allPassed, output, nextSteps } = formatPreflightResults(checks)
112
+
113
+ console.log(output)
114
+ console.log()
115
+
116
+ if (!allPassed) {
117
+ console.error('❌ Pre-flight checks failed. Fix the issues above and try again.')
118
+ console.error(' Or use --skip-checks to bypass (not recommended)\n')
119
+ process.exit(1)
120
+ }
121
+
122
+ console.log('✅ All pre-flight checks passed!')
123
+
124
+ // Store next steps for later display (after successful generation)
125
+ if (nextSteps) {
126
+ (options as any)._nextSteps = nextSteps
127
+ }
128
+
129
+ console.log()
130
+ }
131
+
132
+ if (globalOptions.verbose) {
133
+ console.log(`📖 Reading config from: ${configPath}`)
134
+ console.log(`📖 Reading pipeline from: ${pipelinePath}`)
135
+ }
136
+
137
+ // Load configuration
138
+ const config = loadConfig(configPath) as PipecraftConfig
139
+
140
+ // Check idempotency if not forcing
141
+ if (!globalOptions.force && !globalOptions.dryRun) {
142
+ const idempotencyManager = new IdempotencyManager(config)
143
+
144
+ if (!(await idempotencyManager.hasChanges())) {
145
+ console.log('ℹ️ No changes detected. Use --force to regenerate anyway.')
146
+ return
147
+ }
148
+
149
+ if (globalOptions.verbose) {
150
+ console.log('🔄 Changes detected, regenerating workflows...')
151
+ }
152
+ }
153
+
154
+ if (globalOptions.dryRun) {
155
+ console.log('🔍 Dry run mode - would generate workflows')
156
+ return
157
+ }
158
+
159
+ await runModule(join(__dirname, '../generators/workflows.tpl.js'), {
160
+ cwd: process.cwd(),
161
+ argv: process.argv,
162
+ pipelinePath: pipelinePath,
163
+ outputPipelinePath: outputPipelinePath,
164
+ config: config,
165
+ pinion: {
166
+ logger: {
167
+ ...console,
168
+ notice: console.log
169
+ },
170
+ prompt: prompt as any,
171
+ cwd: process.cwd(),
172
+ force: globalOptions.force || false,
173
+ trace: [],
174
+ exec: async (command: string, args: string[]) => {
175
+ const { spawn } = await import('child_process')
176
+ return new Promise((resolve, reject) => {
177
+ const child = spawn(command, args, { stdio: 'inherit', shell: true })
178
+ child.once('exit', (code: number) => (code === 0 ? resolve(code) : reject(code)))
179
+ })
180
+ }
181
+ }
182
+ } as any)
183
+
184
+ // Update idempotency cache
185
+ const idempotencyManager = new IdempotencyManager(config)
186
+ await idempotencyManager.updateCache()
187
+
188
+ console.log(`✅ Generated workflows in: ${options.output}`)
189
+
190
+ // Display next steps if available
191
+ if ((options as any)._nextSteps) {
192
+ console.log()
193
+ const steps = (options as any)._nextSteps as string[]
194
+ steps.forEach((step: string) => console.log(step))
195
+ }
196
+ } catch (error: any) {
197
+ console.error('❌ Failed to generate workflows:', error.message)
198
+ process.exit(1)
199
+ }
200
+ })
201
+
202
+ // Validate command - Validate configuration
203
+ program
204
+ .command('validate')
205
+ .description('Validate configuration file')
206
+ .action(async (options) => {
207
+ try {
208
+ const globalOptions = program.opts()
209
+ const configPath = globalOptions.config
210
+
211
+ const config = loadConfig(configPath)
212
+ validateConfig(config)
213
+
214
+ console.log('✅ Configuration is valid!')
215
+ } catch (error: any) {
216
+ console.error('❌ Configuration validation failed:', error.message)
217
+ process.exit(1)
218
+ }
219
+ })
220
+
221
+ // Verify command - Check if setup is correct
222
+ program
223
+ .command('verify')
224
+ .description('Verify that pipecraft is properly set up')
225
+ .action(async () => {
226
+ try {
227
+ const explorer = cosmiconfigSync('trunkflow')
228
+ const result = explorer.search()
229
+
230
+ if (!result) {
231
+ console.log('⚠️ No configuration file found. Run "pipecraft init" to get started.')
232
+ process.exit(1)
233
+ }
234
+
235
+ console.log(`✅ Found configuration at: ${result.filepath}`)
236
+
237
+ const config = result.config
238
+ validateConfig(config)
239
+ console.log('✅ Configuration is valid!')
240
+
241
+ // Check if workflows exist
242
+ const fs = await import('fs')
243
+ const path = await import('path')
244
+
245
+ if (config.ciProvider === 'github') {
246
+ const workflowPath = path.join(process.cwd(), '.github/workflows/pipeline.yml')
247
+ if (fs.existsSync(workflowPath)) {
248
+ console.log('✅ GitHub Actions workflows exist!')
249
+ } else {
250
+ console.log('⚠️ GitHub Actions workflows not found. Run "pipecraft generate" to create them.')
251
+ }
252
+ }
253
+
254
+ } catch (error: any) {
255
+ console.error('❌ Verification failed:', error.message)
256
+ process.exit(1)
257
+ }
258
+ })
259
+
260
+ // Version command - Version management
261
+ program
262
+ .command('version')
263
+ .description('Version management commands')
264
+ .option('--check', 'check current version and next version')
265
+ .option('--bump', 'bump version using conventional commits')
266
+ .option('--release', 'create release with version bump')
267
+ .action(async (options) => {
268
+ try {
269
+ const globalOptions = program.opts()
270
+ const config = loadConfig(globalOptions.config) as PipecraftConfig
271
+ const versionManager = new VersionManager(config)
272
+
273
+ if (options.check) {
274
+ const currentVersion = versionManager.getCurrentVersion()
275
+ const nextVersion = versionManager.calculateNextVersion()
276
+
277
+ console.log(`📦 Current version: ${currentVersion}`)
278
+ console.log(`📦 Next version: ${nextVersion.version} (${nextVersion.type})`)
279
+
280
+ // Check conventional commits
281
+ const isValid = versionManager.validateConventionalCommits()
282
+ console.log(`📝 Conventional commits: ${isValid ? '✅ Valid' : '❌ Invalid'}`)
283
+ }
284
+
285
+ if (options.bump) {
286
+ console.log('🔄 Bumping version...')
287
+ // This would run release-it in dry-run mode first
288
+ console.log('✅ Version bump completed!')
289
+ }
290
+
291
+ if (options.release) {
292
+ console.log('🚀 Creating release...')
293
+ // This would run the actual release process
294
+ console.log('✅ Release created!')
295
+ }
296
+
297
+ } catch (error: any) {
298
+ console.error('❌ Version command failed:', error.message)
299
+ process.exit(1)
300
+ }
301
+ })
302
+
303
+ // Setup command - Create necessary branches
304
+ program
305
+ .command('setup')
306
+ .description('Set up the repository with necessary branches from branch flow')
307
+ .option('--force', 'Force creation even if branches exist')
308
+ .action(async (options) => {
309
+ try {
310
+ const globalOptions = program.opts()
311
+ const configPath = globalOptions.config
312
+
313
+ if (globalOptions.verbose) {
314
+ console.log(`📖 Reading config from: ${configPath}`)
315
+ }
316
+
317
+ // Load configuration
318
+ const config = loadConfig(configPath) as PipecraftConfig
319
+
320
+ if (!config.branchFlow || config.branchFlow.length === 0) {
321
+ console.log('⚠️ No branch flow configured in config file')
322
+ return
323
+ }
324
+
325
+ console.log(`🌿 Setting up branches: ${config.branchFlow.join(' → ')}`)
326
+
327
+ // Check current branch
328
+ const { execSync } = await import('child_process')
329
+ const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim()
330
+ console.log(`📍 Current branch: ${currentBranch}`)
331
+
332
+ // Check which branches exist
333
+ const existingBranches = execSync('git branch -a', { encoding: 'utf8' })
334
+ .split('\n')
335
+ .map(line => line.trim().replace('* ', '').replace('remotes/origin/', ''))
336
+ .filter(line => line.length > 0)
337
+
338
+ console.log(`📋 Existing branches: ${existingBranches.join(', ')}`)
339
+
340
+ // Create missing branches
341
+ for (const branch of config.branchFlow) {
342
+ if (existingBranches.includes(branch)) {
343
+ console.log(`✅ Branch '${branch}' already exists locally`)
344
+ } else {
345
+ console.log(`🌱 Creating branch '${branch}'...`)
346
+ try {
347
+ execSync(`git checkout -b ${branch}`, { stdio: 'inherit' })
348
+ console.log(`✅ Created branch '${branch}'`)
349
+ } catch (error: any) {
350
+ if (error.message.includes('already exists')) {
351
+ console.log(`ℹ️ Branch '${branch}' already exists (checked out from remote)`)
352
+ } else {
353
+ throw error
354
+ }
355
+ }
356
+ }
357
+
358
+ // Push branch to remote if it doesn't exist there
359
+ try {
360
+ console.log(`📤 Checking if '${branch}' exists on remote...`)
361
+ execSync(`git ls-remote --heads origin ${branch}`, { stdio: 'pipe' })
362
+ console.log(`✅ Branch '${branch}' already exists on remote`)
363
+ } catch (error: any) {
364
+ console.log(`🚀 Pushing branch '${branch}' to remote...`)
365
+ execSync(`git push -u origin ${branch}`, { stdio: 'inherit' })
366
+ console.log(`✅ Pushed branch '${branch}' to remote`)
367
+ }
368
+ }
369
+
370
+ // Return to original branch
371
+ execSync(`git checkout ${currentBranch}`, { stdio: 'inherit' })
372
+ console.log(`🔄 Returned to original branch: ${currentBranch}`)
373
+
374
+ console.log('✅ Branch setup complete!')
375
+
376
+ } catch (error: any) {
377
+ console.error('❌ Setup command failed:', error.message)
378
+ process.exit(1)
379
+ }
380
+ })
381
+
382
+ // Setup GitHub command - Configure GitHub Actions permissions
383
+ program
384
+ .command('setup-github')
385
+ .description('Configure GitHub Actions workflow permissions for PipeCraft')
386
+ .option('--apply', 'Automatically apply changes without prompting')
387
+ .option('--force', 'Alias for --apply')
388
+ .action(async (options) => {
389
+ try {
390
+ const autoApply = options.apply || options.force
391
+ await setupGitHubPermissions(autoApply)
392
+ } catch (error: any) {
393
+ console.error('❌ GitHub setup failed:', error.message)
394
+ if (error.stack) {
395
+ console.error(error.stack)
396
+ }
397
+ process.exit(1)
398
+ }
399
+ })
400
+
401
+ // Parse command line arguments
402
+ program.parse()
403
+
404
+ // Show help if no command provided
405
+ if (!process.argv.slice(2).length) {
406
+ program.outputHelp()
407
+ }
408
+ // Test comment for deploy job validation
409
+ // Final test for deploy-core job
410
+ // Test commit to trigger deploy-core job - Wed Oct 15 13:47:31 EDT 2025
411
+ // Test commit to trigger deploy-core job - Wed Oct 15 13:51:31 EDT 2025
412
+
413
+ // test commit # on Wed Oct 15 14:07:07 EDT 2025
414
+ // test commit # on Wed Oct 15 15:12:47 EDT 2025
@@ -0,0 +1,126 @@
1
+ import { PinionContext, toFile, renderTemplate, prompt, when, writeJSON } from '@featherscloud/pinion'
2
+ import { existsSync } from 'fs'
3
+ import { IdempotencyManager } from '../utils/idempotency.js'
4
+ import { VersionManager } from '../utils/versioning.js'
5
+ import { PipecraftConfig } from '../types/index.js'
6
+
7
+ const defaultConfig = {
8
+ ciProvider: 'github' as const,
9
+ mergeStrategy: 'fast-forward' as const,
10
+ requireConventionalCommits: true,
11
+ initialBranch: 'develop',
12
+ finalBranch: 'main',
13
+ branchFlow: ['develop', 'staging', 'main'],
14
+ semver: {
15
+ bumpRules: {
16
+ feat: 'minor',
17
+ fix: 'patch',
18
+ breaking: 'major'
19
+ }
20
+ },
21
+ actions: {
22
+ onDevelopMerge: ['runTests', 'fastForwardToStaging'],
23
+ onStagingMerge: ['runTests', 'calculateVersion', 'createOrFastForwardToMain']
24
+ },
25
+ domains: {
26
+ api: {
27
+ paths: ['apps/api/**'],
28
+ description: 'API application changes'
29
+ },
30
+ web: {
31
+ paths: ['apps/web/**'],
32
+ description: 'Web application changes'
33
+ },
34
+ libs: {
35
+ paths: ['libs/**'],
36
+ description: 'Shared library changes'
37
+ },
38
+ cicd: {
39
+ paths: ['.github/workflows/**'],
40
+ description: 'CI/CD configuration changes'
41
+ }
42
+ }
43
+ }
44
+
45
+ const configTemplate = (ctx: PipecraftConfig) => {
46
+ const config = {
47
+ ciProvider: ctx.ciProvider,
48
+ mergeStrategy: ctx.mergeStrategy,
49
+ requireConventionalCommits: ctx.requireConventionalCommits,
50
+ initialBranch: ctx.initialBranch,
51
+ finalBranch: ctx.finalBranch,
52
+ branchFlow: ctx.branchFlow,
53
+ semver: {
54
+ bumpRules: ctx.semver.bumpRules
55
+ },
56
+ actions: {
57
+ onDevelopMerge: ctx.actions.onDevelopMerge,
58
+ onStagingMerge: ctx.actions.onStagingMerge
59
+ },
60
+ domains: ctx.domains
61
+ }
62
+
63
+ return JSON.stringify(config, null, 2)
64
+ }
65
+
66
+
67
+ export const generate = (ctx: PinionContext) =>
68
+ Promise.resolve(ctx)
69
+ .then(prompt([
70
+ {
71
+ type: 'input',
72
+ name: 'projectName',
73
+ message: 'What is your project name?',
74
+ default: 'my-project'
75
+ },
76
+ {
77
+ type: 'list',
78
+ name: 'ciProvider',
79
+ message: 'Which CI provider are you using?',
80
+ choices: [
81
+ { name: 'GitHub Actions', value: 'github' },
82
+ { name: 'GitLab CI/CD', value: 'gitlab' }
83
+ ],
84
+ default: 'github'
85
+ },
86
+ {
87
+ type: 'list',
88
+ name: 'mergeStrategy',
89
+ message: 'What merge strategy do you prefer?',
90
+ choices: [
91
+ { name: 'Fast-forward only (recommended)', value: 'fast-forward' },
92
+ { name: 'Merge commits', value: 'merge' }
93
+ ],
94
+ default: 'fast-forward'
95
+ },
96
+ {
97
+ type: 'confirm',
98
+ name: 'requireConventionalCommits',
99
+ message: 'Require conventional commit format for PR titles?',
100
+ default: true
101
+ },
102
+ {
103
+ type: 'input',
104
+ name: 'initialBranch',
105
+ message: 'What is your development branch name?',
106
+ default: 'develop'
107
+ },
108
+ {
109
+ type: 'input',
110
+ name: 'finalBranch',
111
+ message: 'What is your production branch name?',
112
+ default: 'main'
113
+ },
114
+ {
115
+ type: 'input',
116
+ name: 'branchFlow',
117
+ message: 'Enter your branch flow (comma-separated)',
118
+ default: 'develop,staging,main',
119
+ filter: (input: string) => input.split(',').map(b => b.trim())
120
+ }
121
+ ]))
122
+ .then((ctx) => ({ ...ctx, ...defaultConfig } as PipecraftConfig))
123
+ .then((config) => {
124
+ const content = configTemplate(config)
125
+ return writeJSON(() => content, toFile('.pipecraftrc.json'))(ctx)
126
+ })
@@ -0,0 +1,80 @@
1
+ import { PinionContext, toFile, renderTemplate, loadJSON, when } from '@featherscloud/pinion'
2
+ import { IdempotencyManager } from '../utils/idempotency.js'
3
+ import { PipecraftConfig } from '../types/index.js'
4
+ import { readFileSync, existsSync } from 'fs'
5
+ import { parse } from 'yaml'
6
+
7
+ // Import individual workflow templates
8
+ import { generate as generateTagWorkflow } from '../templates/actions/create-tag.yml.tpl.js'
9
+ import { generate as generateChangesWorkflow } from '../templates/actions/detect-changes.yml.tpl.js'
10
+ import { generate as generateVersionWorkflow } from '../templates/actions/calculate-version.yml.tpl.js'
11
+ import { generate as generateCreatePRWorkflow } from '../templates/actions/create-pr.yml.tpl.js'
12
+ import { generate as generateBranchWorkflow } from '../templates/actions/manage-branch.yml.tpl.js'
13
+ import { generate as generatePromoteBranchWorkflow } from '../templates/actions/promote-branch.yml.tpl.js'
14
+ import { generate as generatePathBasedPipeline } from '../templates/workflows/pipeline-path-based.yml.tpl.js'
15
+
16
+ const defaultConfig = {
17
+ ciProvider: 'github' as const,
18
+ mergeStrategy: 'fast-forward' as const,
19
+ requireConventionalCommits: true,
20
+ initialBranch: 'develop',
21
+ finalBranch: 'main',
22
+ branchFlow: ['develop', 'staging', 'main'],
23
+ semver: {
24
+ bumpRules: {
25
+ feat: 'minor',
26
+ fix: 'patch',
27
+ breaking: 'major'
28
+ }
29
+ },
30
+ actions: {
31
+ onDevelopMerge: ['runTests', 'fastForwardToStaging'],
32
+ onStagingMerge: ['runTests', 'calculateVersion', 'createOrFastForwardToMain']
33
+ },
34
+ domains: {
35
+ api: { paths: ['apps/api/**'], description: 'API application changes' },
36
+ web: { paths: ['apps/web/**'], description: 'Web application changes' },
37
+ libs: { paths: ['libs/**'], description: 'Shared library changes' },
38
+ cicd: { paths: ['.github/workflows/**'], description: 'CI/CD configuration changes' }
39
+ }
40
+ }
41
+
42
+ export const generate = (ctx: PinionContext & { pipelinePath?: string, outputPipelinePath?: string, config?: any }) =>
43
+ Promise.resolve(ctx)
44
+ .then((ctx) => {
45
+ console.log('🔧 Generating GitHub Actions...')
46
+
47
+ // Load existing pipeline if provided
48
+ let existingPipeline = null
49
+ let existingPipelineContent = null
50
+ if (ctx.pipelinePath && existsSync(ctx.pipelinePath)) {
51
+ try {
52
+ existingPipelineContent = readFileSync(ctx.pipelinePath, 'utf8')
53
+ existingPipeline = parse(existingPipelineContent)
54
+ console.log(`📖 Loaded existing pipeline from: ${ctx.pipelinePath}`)
55
+ } catch (error) {
56
+ console.warn(`⚠️ Failed to load existing pipeline: ${error}`)
57
+ }
58
+ }
59
+
60
+ return { ...ctx, ...defaultConfig, ...ctx.config, ...ctx, existingPipeline, existingPipelineContent, outputPipelinePath: ctx.outputPipelinePath }
61
+ })
62
+ .then((ctx) => {
63
+ // Generate individual GitHub Actions
64
+ return Promise.all([
65
+ generateChangesWorkflow(ctx),
66
+ generateTagWorkflow(ctx),
67
+ generateVersionWorkflow(ctx),
68
+ generateCreatePRWorkflow(ctx),
69
+ generateBranchWorkflow(ctx),
70
+ generatePromoteBranchWorkflow(ctx)
71
+ ]).then(() => ctx)
72
+ })
73
+ .then((ctx) => {
74
+ // Generate the main pipeline
75
+ return generatePathBasedPipeline(ctx)
76
+ })
77
+ .then((ctx) => {
78
+ console.log('✅ Generated workflows in: .github/workflows')
79
+ return ctx
80
+ })