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,418 @@
1
+ import { execSync } from 'child_process';
2
+ import { prompt } from '@featherscloud/pinion';
3
+ import { loadConfig } from './config.js';
4
+ /**
5
+ * Get repository information from git remote
6
+ */
7
+ export function getRepositoryInfo() {
8
+ try {
9
+ // Get the remote URL
10
+ const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf8' }).trim();
11
+ // Parse GitHub URL
12
+ // Supports: https://github.com/owner/repo.git, git@github.com:owner/repo.git
13
+ const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)(\.git)?$/);
14
+ if (!match) {
15
+ throw new Error('Could not parse GitHub repository URL from git remote');
16
+ }
17
+ return {
18
+ owner: match[1],
19
+ repo: match[2],
20
+ remote: remoteUrl
21
+ };
22
+ }
23
+ catch (error) {
24
+ throw new Error(`Failed to get repository info: ${error.message}`);
25
+ }
26
+ }
27
+ /**
28
+ * Get GitHub token from environment or gh CLI
29
+ */
30
+ export function getGitHubToken() {
31
+ // Check environment variables
32
+ const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
33
+ if (token) {
34
+ return token;
35
+ }
36
+ // Try to get from gh CLI
37
+ try {
38
+ const ghToken = execSync('gh auth token', { encoding: 'utf8', stdio: 'pipe' }).trim();
39
+ if (ghToken) {
40
+ return ghToken;
41
+ }
42
+ }
43
+ catch (error) {
44
+ // gh CLI not available or not authenticated
45
+ }
46
+ throw new Error('GitHub token not found. Please:\n' +
47
+ ' 1. Set GITHUB_TOKEN or GH_TOKEN environment variable, or\n' +
48
+ ' 2. Authenticate with gh CLI: gh auth login');
49
+ }
50
+ /**
51
+ * Get current workflow permissions
52
+ */
53
+ export async function getWorkflowPermissions(owner, repo, token) {
54
+ const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/actions/permissions/workflow`, {
55
+ headers: {
56
+ 'Authorization': `Bearer ${token}`,
57
+ 'Accept': 'application/vnd.github+json',
58
+ 'X-GitHub-Api-Version': '2022-11-28'
59
+ }
60
+ });
61
+ if (!response.ok) {
62
+ const error = await response.text();
63
+ throw new Error(`Failed to get workflow permissions: ${response.status} ${error}`);
64
+ }
65
+ return response.json();
66
+ }
67
+ /**
68
+ * Update workflow permissions
69
+ */
70
+ export async function updateWorkflowPermissions(owner, repo, token, permissions) {
71
+ const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/actions/permissions/workflow`, {
72
+ method: 'PUT',
73
+ headers: {
74
+ 'Authorization': `Bearer ${token}`,
75
+ 'Accept': 'application/vnd.github+json',
76
+ 'X-GitHub-Api-Version': '2022-11-28',
77
+ 'Content-Type': 'application/json'
78
+ },
79
+ body: JSON.stringify(permissions)
80
+ });
81
+ if (!response.ok) {
82
+ const error = await response.text();
83
+ throw new Error(`Failed to update workflow permissions: ${response.status} ${error}`);
84
+ }
85
+ }
86
+ /**
87
+ * Determine required permission changes without prompting
88
+ * Returns: changes object if changes needed, null if already correct
89
+ */
90
+ export function getRequiredPermissionChanges(currentPermissions) {
91
+ // Check if permissions are already correct
92
+ if (currentPermissions.default_workflow_permissions === 'write' &&
93
+ currentPermissions.can_approve_pull_request_reviews === true) {
94
+ return null;
95
+ }
96
+ const changes = {};
97
+ if (currentPermissions.default_workflow_permissions !== 'write') {
98
+ changes.default_workflow_permissions = 'write';
99
+ }
100
+ if (!currentPermissions.can_approve_pull_request_reviews) {
101
+ changes.can_approve_pull_request_reviews = true;
102
+ }
103
+ return Object.keys(changes).length > 0 ? changes : null;
104
+ }
105
+ /**
106
+ * Display current permissions and prompt for changes
107
+ * Returns: changes object if user accepted changes, 'declined' if user declined, null if already correct
108
+ */
109
+ export async function promptPermissionChanges(currentPermissions) {
110
+ console.log('\n📋 Current GitHub Actions Workflow Permissions:');
111
+ console.log(` Default permissions: ${currentPermissions.default_workflow_permissions}`);
112
+ console.log(` Can create/approve PRs: ${currentPermissions.can_approve_pull_request_reviews ? 'Yes' : 'No'}`);
113
+ // Check if permissions are already correct
114
+ if (currentPermissions.default_workflow_permissions === 'write' &&
115
+ currentPermissions.can_approve_pull_request_reviews === true) {
116
+ console.log('\n✅ Permissions are already configured correctly for PipeCraft!');
117
+ return null;
118
+ }
119
+ console.log('\n⚠️ PipeCraft requires the following permissions:');
120
+ console.log(' • Default permissions: write (for creating tags and pushing)');
121
+ console.log(' • Can create/approve PRs: Yes (for automated PR creation)');
122
+ const changes = {};
123
+ let userDeclinedAnyChanges = false;
124
+ // Ask about default permissions
125
+ if (currentPermissions.default_workflow_permissions !== 'write') {
126
+ const response = await prompt({
127
+ type: 'confirm',
128
+ name: 'updatePermissions',
129
+ message: 'Change default workflow permissions from "read" to "write"?',
130
+ default: true
131
+ });
132
+ if (response.updatePermissions) {
133
+ changes.default_workflow_permissions = 'write';
134
+ }
135
+ else {
136
+ console.log('⚠️ Warning: PipeCraft may not work correctly without write permissions');
137
+ userDeclinedAnyChanges = true;
138
+ }
139
+ }
140
+ // Ask about PR creation
141
+ if (!currentPermissions.can_approve_pull_request_reviews) {
142
+ const response = await prompt({
143
+ type: 'confirm',
144
+ name: 'allowPRs',
145
+ message: 'Allow GitHub Actions to create and approve pull requests?',
146
+ default: true
147
+ });
148
+ if (response.allowPRs) {
149
+ changes.can_approve_pull_request_reviews = true;
150
+ }
151
+ else {
152
+ console.log('⚠️ Warning: PipeCraft cannot create PRs without this permission');
153
+ userDeclinedAnyChanges = true;
154
+ }
155
+ }
156
+ // If user declined all changes, return 'declined'
157
+ if (userDeclinedAnyChanges && Object.keys(changes).length === 0) {
158
+ return 'declined';
159
+ }
160
+ return Object.keys(changes).length > 0 ? changes : null;
161
+ }
162
+ /**
163
+ * Get branch protection rules
164
+ */
165
+ export async function getBranchProtection(owner, repo, branch, token) {
166
+ const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/branches/${branch}/protection`, {
167
+ headers: {
168
+ 'Authorization': `Bearer ${token}`,
169
+ 'Accept': 'application/vnd.github+json',
170
+ 'X-GitHub-Api-Version': '2022-11-28'
171
+ }
172
+ });
173
+ if (response.status === 404) {
174
+ // Branch protection not configured
175
+ return null;
176
+ }
177
+ if (!response.ok) {
178
+ const error = await response.text();
179
+ throw new Error(`Failed to get branch protection for ${branch}: ${response.status} ${error}`);
180
+ }
181
+ return response.json();
182
+ }
183
+ /**
184
+ * Update branch protection rules to enable auto-merge
185
+ */
186
+ export async function updateBranchProtection(owner, repo, branch, token) {
187
+ // Minimal branch protection to enable auto-merge
188
+ // GitHub requires at least ONE of the main protection types
189
+ const protection = {
190
+ required_status_checks: {
191
+ strict: false,
192
+ contexts: [] // No specific checks required, but enables the feature
193
+ },
194
+ enforce_admins: false,
195
+ required_pull_request_reviews: null, // No required reviews for auto-merge branches
196
+ restrictions: null,
197
+ allow_force_pushes: false,
198
+ allow_deletions: false,
199
+ required_linear_history: true,
200
+ required_conversation_resolution: false
201
+ };
202
+ const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/branches/${branch}/protection`, {
203
+ method: 'PUT',
204
+ headers: {
205
+ 'Authorization': `Bearer ${token}`,
206
+ 'Accept': 'application/vnd.github+json',
207
+ 'X-GitHub-Api-Version': '2022-11-28',
208
+ 'Content-Type': 'application/json'
209
+ },
210
+ body: JSON.stringify(protection)
211
+ });
212
+ if (!response.ok) {
213
+ const error = await response.text();
214
+ throw new Error(`Failed to update branch protection for ${branch}: ${response.status} ${error}`);
215
+ }
216
+ }
217
+ /**
218
+ * Enable auto-merge feature for the repository
219
+ */
220
+ export async function enableAutoMerge(owner, repo, token) {
221
+ // First check if auto-merge is already enabled
222
+ const checkResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
223
+ headers: {
224
+ 'Authorization': `Bearer ${token}`,
225
+ 'Accept': 'application/vnd.github+json',
226
+ 'X-GitHub-Api-Version': '2022-11-28'
227
+ }
228
+ });
229
+ if (checkResponse.ok) {
230
+ const repoData = await checkResponse.json();
231
+ if (repoData.allow_auto_merge === true) {
232
+ return false; // Already enabled
233
+ }
234
+ }
235
+ // Enable auto-merge
236
+ const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
237
+ method: 'PATCH',
238
+ headers: {
239
+ 'Authorization': `Bearer ${token}`,
240
+ 'Accept': 'application/vnd.github+json',
241
+ 'X-GitHub-Api-Version': '2022-11-28',
242
+ 'Content-Type': 'application/json'
243
+ },
244
+ body: JSON.stringify({
245
+ allow_auto_merge: true
246
+ })
247
+ });
248
+ if (!response.ok) {
249
+ const error = await response.text();
250
+ throw new Error(`Failed to enable auto-merge: ${response.status} ${error}`);
251
+ }
252
+ return true; // Was enabled
253
+ }
254
+ /**
255
+ * Configure branch protection for branches that need auto-merge
256
+ */
257
+ export async function configureBranchProtection(repoInfo, token, autoApply) {
258
+ console.log('\n🔍 Checking auto-merge configuration...');
259
+ // Load config to get autoMerge settings
260
+ let config;
261
+ try {
262
+ config = loadConfig('.pipecraftrc.json');
263
+ }
264
+ catch (error) {
265
+ console.log('⚠️ Could not load .pipecraftrc.json - skipping auto-merge setup');
266
+ return;
267
+ }
268
+ if (!config.autoMerge || !config.branchFlow) {
269
+ console.log('ℹ️ No autoMerge configuration found - skipping auto-merge setup');
270
+ return;
271
+ }
272
+ // Enable repository-level auto-merge feature
273
+ try {
274
+ const wasEnabled = await enableAutoMerge(repoInfo.owner, repoInfo.repo, token);
275
+ if (wasEnabled) {
276
+ console.log('✅ Enabled auto-merge for repository');
277
+ }
278
+ else {
279
+ console.log('✅ Auto-merge already enabled for repository');
280
+ }
281
+ }
282
+ catch (error) {
283
+ console.error(`⚠️ Could not enable auto-merge: ${error.message}`);
284
+ console.log(' You can enable it manually at:');
285
+ console.log(` https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings`);
286
+ return;
287
+ }
288
+ // Determine which branches need auto-merge
289
+ const autoMergeConfig = config.autoMerge;
290
+ const branchesNeedingProtection = [];
291
+ if (typeof autoMergeConfig === 'boolean') {
292
+ // If true for all, protect all intermediate branches
293
+ if (autoMergeConfig && config.branchFlow.length > 1) {
294
+ branchesNeedingProtection.push(...config.branchFlow.slice(1, -1));
295
+ }
296
+ }
297
+ else if (typeof autoMergeConfig === 'object') {
298
+ // Check which branches have autoMerge enabled
299
+ for (const [branch, enabled] of Object.entries(autoMergeConfig)) {
300
+ if (enabled === true) {
301
+ branchesNeedingProtection.push(branch);
302
+ }
303
+ }
304
+ }
305
+ if (branchesNeedingProtection.length === 0) {
306
+ console.log('ℹ️ No branches configured for auto-merge - skipping branch protection setup');
307
+ return;
308
+ }
309
+ console.log(`📋 Branches with auto-merge enabled: ${branchesNeedingProtection.join(', ')}`);
310
+ // Check each branch
311
+ for (const branch of branchesNeedingProtection) {
312
+ try {
313
+ const protection = await getBranchProtection(repoInfo.owner, repoInfo.repo, branch, token);
314
+ if (protection === null) {
315
+ // Branch protection not configured
316
+ if (autoApply) {
317
+ console.log(`🔧 Configuring branch protection for ${branch}...`);
318
+ await updateBranchProtection(repoInfo.owner, repoInfo.repo, branch, token);
319
+ console.log(`✅ Branch protection enabled for ${branch}`);
320
+ }
321
+ else {
322
+ const response = await prompt({
323
+ type: 'confirm',
324
+ name: 'enableProtection',
325
+ message: `Enable branch protection for '${branch}' to support auto-merge?`,
326
+ default: true
327
+ });
328
+ if (response.enableProtection) {
329
+ console.log(`🔧 Configuring branch protection for ${branch}...`);
330
+ await updateBranchProtection(repoInfo.owner, repoInfo.repo, branch, token);
331
+ console.log(`✅ Branch protection enabled for ${branch}`);
332
+ }
333
+ else {
334
+ console.log(`⚠️ Skipped ${branch} - auto-merge will not work without branch protection`);
335
+ }
336
+ }
337
+ }
338
+ else {
339
+ console.log(`✅ Branch protection already configured for ${branch}`);
340
+ }
341
+ }
342
+ catch (error) {
343
+ console.error(`⚠️ Could not configure protection for ${branch}: ${error.message}`);
344
+ }
345
+ }
346
+ }
347
+ /**
348
+ * Main setup function
349
+ */
350
+ export async function setupGitHubPermissions(autoApply = false) {
351
+ console.log('🔍 Checking GitHub repository configuration...\n');
352
+ // Get repository info
353
+ const repoInfo = getRepositoryInfo();
354
+ console.log(`📦 Repository: ${repoInfo.owner}/${repoInfo.repo}`);
355
+ // Get GitHub token
356
+ let token;
357
+ try {
358
+ token = getGitHubToken();
359
+ console.log('✅ GitHub token found');
360
+ }
361
+ catch (error) {
362
+ console.error(`\n❌ ${error.message}`);
363
+ process.exit(1);
364
+ }
365
+ // Get current permissions
366
+ console.log('🔍 Fetching current workflow permissions...');
367
+ const currentPermissions = await getWorkflowPermissions(repoInfo.owner, repoInfo.repo, token);
368
+ let changes;
369
+ let permissionsAlreadyCorrect = false;
370
+ if (autoApply) {
371
+ // Auto-apply mode: determine changes without prompting
372
+ changes = getRequiredPermissionChanges(currentPermissions);
373
+ if (changes === null) {
374
+ console.log('\n✅ Workflow permissions are already configured correctly!');
375
+ permissionsAlreadyCorrect = true;
376
+ }
377
+ else {
378
+ // Show what will be applied
379
+ console.log('\n📋 Current GitHub Actions Workflow Permissions:');
380
+ console.log(` Default permissions: ${currentPermissions.default_workflow_permissions}`);
381
+ console.log(` Can create/approve PRs: ${currentPermissions.can_approve_pull_request_reviews ? 'Yes' : 'No'}`);
382
+ console.log('\n🔧 Applying required changes:');
383
+ if (changes.default_workflow_permissions) {
384
+ console.log(` • Setting default permissions to: ${changes.default_workflow_permissions}`);
385
+ }
386
+ if (changes.can_approve_pull_request_reviews !== undefined) {
387
+ console.log(` • Allowing PR creation/approval: ${changes.can_approve_pull_request_reviews}`);
388
+ }
389
+ }
390
+ }
391
+ else {
392
+ // Interactive mode: prompt for changes
393
+ changes = await promptPermissionChanges(currentPermissions);
394
+ if (changes === null) {
395
+ console.log('\n✅ Workflow permissions are already configured correctly!');
396
+ permissionsAlreadyCorrect = true;
397
+ }
398
+ else if (changes === 'declined') {
399
+ console.log('\n⚠️ Setup incomplete - permissions were not updated');
400
+ console.log('💡 You can run this command again anytime or update permissions manually at:');
401
+ console.log(` https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/actions`);
402
+ // Still continue to check branch protection
403
+ permissionsAlreadyCorrect = true;
404
+ }
405
+ }
406
+ // Apply permission changes if needed
407
+ if (!permissionsAlreadyCorrect && changes && changes !== 'declined') {
408
+ console.log('\n🔄 Updating repository settings...');
409
+ await updateWorkflowPermissions(repoInfo.owner, repoInfo.repo, token, changes);
410
+ console.log('✅ GitHub Actions permissions updated successfully!');
411
+ }
412
+ // Configure branch protection for auto-merge
413
+ await configureBranchProtection(repoInfo, token, autoApply);
414
+ console.log('\n✨ Setup complete!');
415
+ console.log('\n💡 You can verify the changes at:');
416
+ console.log(` https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/actions`);
417
+ }
418
+ //# sourceMappingURL=github-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-setup.js","sourceRoot":"","sources":["../../src/utils/github-setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { PipecraftConfig } from '../types/index.js';
2
+ export interface FileHash {
3
+ path: string;
4
+ hash: string;
5
+ mtime: number;
6
+ size: number;
7
+ }
8
+ export interface RebuildCache {
9
+ files: Record<string, FileHash>;
10
+ configHash: string;
11
+ lastGenerated: number;
12
+ version: string;
13
+ }
14
+ export declare class IdempotencyManager {
15
+ private cacheFile;
16
+ private config;
17
+ constructor(config: PipecraftConfig, cacheFile?: string);
18
+ /**
19
+ * Calculate hash for a file or directory
20
+ */
21
+ calculateHash(filePath: string, algorithm?: string): Promise<string>;
22
+ /**
23
+ * Hash directory contents recursively
24
+ */
25
+ private hashDirectory;
26
+ /**
27
+ * Load rebuild cache from file
28
+ */
29
+ loadCache(): RebuildCache | null;
30
+ /**
31
+ * Save rebuild cache to file
32
+ */
33
+ saveCache(cache: RebuildCache): void;
34
+ /**
35
+ * Check if files have changed since last generation
36
+ */
37
+ hasChanges(): Promise<boolean>;
38
+ /**
39
+ * Update cache with current file states
40
+ */
41
+ updateCache(): Promise<void>;
42
+ /**
43
+ * Check if specific file should be regenerated
44
+ */
45
+ shouldRegenerateFile(filePath: string): Promise<boolean>;
46
+ }
47
+ //# sourceMappingURL=idempotency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/utils/idempotency.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,eAAe,EAAE,SAAS,SAA0B;IAKxE;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,GAAE,MAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBpF;;OAEG;YACW,aAAa;IA0B3B;;OAEG;IACH,SAAS,IAAI,YAAY,GAAG,IAAI;IAchC;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAQpC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAyCpC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAiClC;;OAEG;IACG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAuB/D"}
@@ -0,0 +1,176 @@
1
+ import { createHash } from 'crypto';
2
+ import { readFileSync, existsSync, writeFileSync, statSync } from 'fs';
3
+ export class IdempotencyManager {
4
+ cacheFile;
5
+ config;
6
+ constructor(config, cacheFile = '.pipecraft-cache.json') {
7
+ this.config = config;
8
+ this.cacheFile = cacheFile;
9
+ }
10
+ /**
11
+ * Calculate hash for a file or directory
12
+ */
13
+ async calculateHash(filePath, algorithm = 'sha256') {
14
+ if (!existsSync(filePath)) {
15
+ return '';
16
+ }
17
+ const stats = statSync(filePath);
18
+ if (stats.isDirectory()) {
19
+ // For directories, hash all files recursively
20
+ return await this.hashDirectory(filePath, algorithm);
21
+ }
22
+ else {
23
+ // For files, hash content + metadata
24
+ const content = readFileSync(filePath);
25
+ const hash = createHash(algorithm);
26
+ hash.update(content);
27
+ hash.update(stats.mtime.toString());
28
+ hash.update(stats.size.toString());
29
+ return hash.digest('hex');
30
+ }
31
+ }
32
+ /**
33
+ * Hash directory contents recursively
34
+ */
35
+ async hashDirectory(dirPath, algorithm) {
36
+ const fs = await import('fs');
37
+ const path = await import('path');
38
+ const hash = createHash(algorithm);
39
+ const hashDir = (dir) => {
40
+ const items = fs.readdirSync(dir);
41
+ for (const item of items) {
42
+ const itemPath = path.join(dir, item);
43
+ const stats = fs.statSync(itemPath);
44
+ if (stats.isDirectory()) {
45
+ hashDir(itemPath);
46
+ }
47
+ else {
48
+ const content = fs.readFileSync(itemPath);
49
+ hash.update(content);
50
+ hash.update(stats.mtime.toString());
51
+ hash.update(stats.size.toString());
52
+ }
53
+ }
54
+ };
55
+ hashDir(dirPath);
56
+ return hash.digest('hex');
57
+ }
58
+ /**
59
+ * Load rebuild cache from file
60
+ */
61
+ loadCache() {
62
+ if (!existsSync(this.cacheFile)) {
63
+ return null;
64
+ }
65
+ try {
66
+ const content = readFileSync(this.cacheFile, 'utf8');
67
+ return JSON.parse(content);
68
+ }
69
+ catch (error) {
70
+ console.warn(`Failed to load cache file: ${error.message}`);
71
+ return null;
72
+ }
73
+ }
74
+ /**
75
+ * Save rebuild cache to file
76
+ */
77
+ saveCache(cache) {
78
+ try {
79
+ writeFileSync(this.cacheFile, JSON.stringify(cache, null, 2));
80
+ }
81
+ catch (error) {
82
+ console.warn(`Failed to save cache file: ${error.message}`);
83
+ }
84
+ }
85
+ /**
86
+ * Check if files have changed since last generation
87
+ */
88
+ async hasChanges() {
89
+ if (!this.config.rebuild?.enabled) {
90
+ return true; // Always regenerate if rebuild is disabled
91
+ }
92
+ if (this.config.rebuild?.forceRegenerate) {
93
+ return true; // Force regeneration
94
+ }
95
+ const cache = this.loadCache();
96
+ if (!cache) {
97
+ return true; // No cache, need to regenerate
98
+ }
99
+ // Check if config has changed
100
+ const configHash = await this.calculateHash('.pipecraftrc.json');
101
+ if (cache.configHash !== configHash) {
102
+ return true;
103
+ }
104
+ // Check if template files have changed
105
+ const templateDir = 'src/templates';
106
+ if (existsSync(templateDir)) {
107
+ const templateHash = await this.calculateHash(templateDir);
108
+ if (cache.files[templateDir]?.hash !== templateHash) {
109
+ return true;
110
+ }
111
+ }
112
+ // Check if source files have changed
113
+ const sourceDir = 'src/generators';
114
+ if (existsSync(sourceDir)) {
115
+ const sourceHash = await this.calculateHash(sourceDir);
116
+ if (cache.files[sourceDir]?.hash !== sourceHash) {
117
+ return true;
118
+ }
119
+ }
120
+ return false;
121
+ }
122
+ /**
123
+ * Update cache with current file states
124
+ */
125
+ async updateCache() {
126
+ const cache = {
127
+ files: {},
128
+ configHash: await this.calculateHash('.pipecraftrc.json'),
129
+ lastGenerated: Date.now(),
130
+ version: '1.0.0'
131
+ };
132
+ // Cache template files
133
+ const templateDir = 'src/templates';
134
+ if (existsSync(templateDir)) {
135
+ cache.files[templateDir] = {
136
+ path: templateDir,
137
+ hash: await this.calculateHash(templateDir),
138
+ mtime: statSync(templateDir).mtime.getTime(),
139
+ size: 0 // Directory size not meaningful
140
+ };
141
+ }
142
+ // Cache source files
143
+ const sourceDir = 'src/generators';
144
+ if (existsSync(sourceDir)) {
145
+ cache.files[sourceDir] = {
146
+ path: sourceDir,
147
+ hash: await this.calculateHash(sourceDir),
148
+ mtime: statSync(sourceDir).mtime.getTime(),
149
+ size: 0
150
+ };
151
+ }
152
+ this.saveCache(cache);
153
+ }
154
+ /**
155
+ * Check if specific file should be regenerated
156
+ */
157
+ async shouldRegenerateFile(filePath) {
158
+ if (!this.config.rebuild?.enabled) {
159
+ return true;
160
+ }
161
+ if (this.config.rebuild?.forceRegenerate) {
162
+ return true;
163
+ }
164
+ const cache = this.loadCache();
165
+ if (!cache) {
166
+ return true;
167
+ }
168
+ const currentHash = await this.calculateHash(filePath);
169
+ const cachedFile = cache.files[filePath];
170
+ if (!cachedFile) {
171
+ return true; // File not in cache, needs generation
172
+ }
173
+ return cachedFile.hash !== currentHash;
174
+ }
175
+ }
176
+ //# sourceMappingURL=idempotency.js.map