pipecraft 0.0.0-releaseit → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/README.md +126 -108
  2. package/dist/cli/index.d.ts +69 -0
  3. package/dist/cli/index.d.ts.map +1 -1
  4. package/dist/cli/index.js +94 -22
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/generators/init.tpl.d.ts +70 -0
  7. package/dist/generators/init.tpl.d.ts.map +1 -1
  8. package/dist/generators/init.tpl.js +100 -8
  9. package/dist/generators/init.tpl.js.map +1 -1
  10. package/dist/generators/workflows.tpl.d.ts +86 -0
  11. package/dist/generators/workflows.tpl.d.ts.map +1 -1
  12. package/dist/generators/workflows.tpl.js +101 -9
  13. package/dist/generators/workflows.tpl.js.map +1 -1
  14. package/dist/templates/actions/calculate-version.yml.tpl.d.ts +47 -0
  15. package/dist/templates/actions/calculate-version.yml.tpl.d.ts.map +1 -1
  16. package/dist/templates/actions/calculate-version.yml.tpl.js +72 -3
  17. package/dist/templates/actions/calculate-version.yml.tpl.js.map +1 -1
  18. package/dist/templates/actions/create-pr.yml.tpl.d.ts +14 -0
  19. package/dist/templates/actions/create-pr.yml.tpl.d.ts.map +1 -1
  20. package/dist/templates/actions/create-pr.yml.tpl.js +22 -2
  21. package/dist/templates/actions/create-pr.yml.tpl.js.map +1 -1
  22. package/dist/templates/actions/create-release.yml.tpl.d.ts +18 -0
  23. package/dist/templates/actions/create-release.yml.tpl.d.ts.map +1 -0
  24. package/dist/templates/actions/create-release.yml.tpl.js +141 -0
  25. package/dist/templates/actions/create-release.yml.tpl.js.map +1 -0
  26. package/dist/templates/actions/create-tag.yml.tpl.d.ts +14 -0
  27. package/dist/templates/actions/create-tag.yml.tpl.d.ts.map +1 -1
  28. package/dist/templates/actions/create-tag.yml.tpl.js +22 -2
  29. package/dist/templates/actions/create-tag.yml.tpl.js.map +1 -1
  30. package/dist/templates/actions/detect-changes.yml.tpl.d.ts +70 -0
  31. package/dist/templates/actions/detect-changes.yml.tpl.d.ts.map +1 -1
  32. package/dist/templates/actions/detect-changes.yml.tpl.js +98 -2
  33. package/dist/templates/actions/detect-changes.yml.tpl.js.map +1 -1
  34. package/dist/templates/actions/manage-branch.yml.tpl.d.ts +14 -0
  35. package/dist/templates/actions/manage-branch.yml.tpl.d.ts.map +1 -1
  36. package/dist/templates/actions/manage-branch.yml.tpl.js +22 -2
  37. package/dist/templates/actions/manage-branch.yml.tpl.js.map +1 -1
  38. package/dist/templates/actions/promote-branch.yml.tpl.d.ts +14 -0
  39. package/dist/templates/actions/promote-branch.yml.tpl.d.ts.map +1 -1
  40. package/dist/templates/actions/promote-branch.yml.tpl.js +50 -7
  41. package/dist/templates/actions/promote-branch.yml.tpl.js.map +1 -1
  42. package/dist/templates/workflows/pipeline-path-based.yml.tpl.d.ts +114 -1
  43. package/dist/templates/workflows/pipeline-path-based.yml.tpl.d.ts.map +1 -1
  44. package/dist/templates/workflows/pipeline-path-based.yml.tpl.js +479 -74
  45. package/dist/templates/workflows/pipeline-path-based.yml.tpl.js.map +1 -1
  46. package/dist/types/index.d.ts +231 -8
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/types/index.js +9 -0
  49. package/dist/types/index.js.map +1 -1
  50. package/dist/utils/ast-path-operations.d.ts.map +1 -1
  51. package/dist/utils/ast-path-operations.js +24 -11
  52. package/dist/utils/ast-path-operations.js.map +1 -1
  53. package/dist/utils/config.d.ts +63 -0
  54. package/dist/utils/config.d.ts.map +1 -1
  55. package/dist/utils/config.js +71 -0
  56. package/dist/utils/config.js.map +1 -1
  57. package/dist/utils/github-setup.d.ts +323 -2
  58. package/dist/utils/github-setup.d.ts.map +1 -1
  59. package/dist/utils/github-setup.js +551 -23
  60. package/dist/utils/github-setup.js.map +1 -1
  61. package/dist/utils/idempotency.d.ts +147 -7
  62. package/dist/utils/idempotency.d.ts.map +1 -1
  63. package/dist/utils/idempotency.js +143 -15
  64. package/dist/utils/idempotency.js.map +1 -1
  65. package/dist/utils/logger.d.ts +134 -0
  66. package/dist/utils/logger.d.ts.map +1 -0
  67. package/dist/utils/logger.js +153 -0
  68. package/dist/utils/logger.js.map +1 -0
  69. package/dist/utils/preflight.d.ts +223 -12
  70. package/dist/utils/preflight.d.ts.map +1 -1
  71. package/dist/utils/preflight.js +216 -16
  72. package/dist/utils/preflight.js.map +1 -1
  73. package/dist/utils/versioning.d.ts +160 -8
  74. package/dist/utils/versioning.d.ts.map +1 -1
  75. package/dist/utils/versioning.js +179 -15
  76. package/dist/utils/versioning.js.map +1 -1
  77. package/package.json +26 -5
  78. package/.claude/settings.local.json +0 -35
  79. package/.github/actions/calculate-version/action.yml +0 -106
  80. package/.github/actions/create-pr/action.yml +0 -122
  81. package/.github/actions/create-release/action.yml +0 -74
  82. package/.github/actions/create-tag/action.yml +0 -94
  83. package/.github/actions/detect-changes/action.yml +0 -62
  84. package/.github/actions/manage-branch/action.yml +0 -113
  85. package/.github/actions/promote-branch/action.yml +0 -352
  86. package/.github/example/workflows/job.analyze.code.yml +0 -26
  87. package/.github/example/workflows/job.analyze.docker.yml +0 -32
  88. package/.github/example/workflows/job.app.api.deploy.yml +0 -127
  89. package/.github/example/workflows/job.app.api.test.yml +0 -102
  90. package/.github/example/workflows/job.app.docs.deploy.yml +0 -40
  91. package/.github/example/workflows/job.app.docs.test.yml +0 -50
  92. package/.github/example/workflows/job.app.web.deploy.yml +0 -96
  93. package/.github/example/workflows/job.app.web.test.yml +0 -49
  94. package/.github/example/workflows/job.changes.yml +0 -82
  95. package/.github/example/workflows/job.create-pr.yml +0 -96
  96. package/.github/example/workflows/job.env-check.yml +0 -43
  97. package/.github/example/workflows/job.fast-forward.yml +0 -103
  98. package/.github/example/workflows/job.lint.yml +0 -64
  99. package/.github/example/workflows/job.pr-name.yml +0 -70
  100. package/.github/example/workflows/job.pr-squash-name.yml +0 -27
  101. package/.github/example/workflows/job.tag.yml +0 -38
  102. package/.github/example/workflows/job.version.yml +0 -99
  103. package/.github/example/workflows/lib.plugin-base.publish.yml +0 -59
  104. package/.github/example/workflows/pipe.yml +0 -178
  105. package/.github/example/workflows/scripts/fastforward.sh +0 -51
  106. package/.github/example/workflows/scripts/pipe-cleaner.sh +0 -70
  107. package/.github/scripts/fastforward.sh +0 -51
  108. package/.github/scripts/pipe-cleaner.sh +0 -70
  109. package/.github/workflows/pipeline.yml +0 -220
  110. package/.github/workflows/pr-title-check.yml +0 -70
  111. package/.github/workflows/publish.yml +0 -39
  112. package/.pipecraftrc.json +0 -78
  113. package/.release-it.cjs +0 -71
  114. package/PIPELINE_TESTING_PLAN.md +0 -499
  115. package/TRUNK_FLOW_PLAN.md +0 -401
  116. package/assets/logo_banner.png +0 -0
  117. package/assets/logo_banner.webp +0 -0
  118. package/docs/USER_JOURNEY_ERRORS.md +0 -352
  119. package/eslint.config.js +0 -77
  120. package/examples/basic-config.json +0 -41
  121. package/examples/monorepo-config.json +0 -49
  122. package/examples/usage.md +0 -289
  123. package/scripts/debug-workflows.sh +0 -339
  124. package/scripts/parse-pipeline.js +0 -29
  125. package/scripts/test-job-order.cjs +0 -140
  126. package/scripts/validate-pipeline.cjs +0 -379
  127. package/scripts/verify-job-order.sh +0 -30
  128. package/src/cli/index.ts +0 -414
  129. package/src/generators/init.tpl.ts +0 -126
  130. package/src/generators/workflows.tpl.ts +0 -80
  131. package/src/templates/actions/calculate-version.yml.tpl.ts +0 -124
  132. package/src/templates/actions/create-pr.yml.tpl.ts +0 -147
  133. package/src/templates/actions/create-tag.yml.tpl.ts +0 -112
  134. package/src/templates/actions/detect-changes.yml.tpl.ts +0 -87
  135. package/src/templates/actions/manage-branch.yml.tpl.ts +0 -132
  136. package/src/templates/actions/promote-branch.yml.tpl.ts +0 -364
  137. package/src/templates/workflows/pipeline-path-based.yml.tpl.ts +0 -706
  138. package/src/types/index.ts +0 -64
  139. package/src/utils/README-ast-path-operations.md +0 -390
  140. package/src/utils/ast-path-operations.ts +0 -581
  141. package/src/utils/config.ts +0 -64
  142. package/src/utils/github-setup.ts +0 -558
  143. package/src/utils/idempotency.ts +0 -215
  144. package/src/utils/preflight.ts +0 -306
  145. package/src/utils/versioning.ts +0 -244
  146. package/tests/README.md +0 -229
  147. package/tests/TEST_STRUCTURE.md +0 -256
  148. package/tests/act/run-act-tests.sh +0 -345
  149. package/tests/debugging/debug-utils.ts +0 -538
  150. package/tests/debugging/debug-workflow.test.ts +0 -339
  151. package/tests/debugging/debug-workflows.sh +0 -339
  152. package/tests/debugging/iterative-debug.ts +0 -652
  153. package/tests/debugging/run-debug-tests.sh +0 -431
  154. package/tests/fixtures/basic-config.json +0 -51
  155. package/tests/fixtures/invalid-config.json +0 -9
  156. package/tests/fixtures/pipeline-generated.yml +0 -235
  157. package/tests/fixtures/pipeline-preserve-comments.yml +0 -245
  158. package/tests/fixtures/pipeline-user-modified.yml +0 -245
  159. package/tests/fixtures/test-config.json +0 -58
  160. package/tests/github-live/README.md +0 -250
  161. package/tests/github-local/JOB_WORKFLOW_TESTS.md +0 -296
  162. package/tests/github-local/README.md +0 -240
  163. package/tests/github-local/run-all-tests.sh +0 -422
  164. package/tests/github-local/test-job-workflows.sh +0 -631
  165. package/tests/github-local/test-pipeline-workflow.sh +0 -440
  166. package/tests/integration/generators.test.ts +0 -578
  167. package/tests/integration/path-based-template.test.ts +0 -510
  168. package/tests/integration/simple-path-based.test.ts +0 -415
  169. package/tests/setup.ts +0 -56
  170. package/tests/unit/ast-path-operations-extended.test.ts +0 -302
  171. package/tests/unit/cli.test.ts +0 -541
  172. package/tests/unit/config-extended.test.ts +0 -412
  173. package/tests/unit/config.test.ts +0 -152
  174. package/tests/unit/github-setup.test.ts +0 -189
  175. package/tests/unit/idempotency-isolated.test.ts +0 -297
  176. package/tests/unit/job-order.test.ts +0 -157
  177. package/tests/unit/pipeline-path-based.test.ts +0 -511
  178. package/tests/unit/validate-pipeline.test.ts +0 -384
  179. package/tests/unit/versioning-extended.test.ts +0 -279
  180. package/tests/unit/versioning.test.ts +0 -241
  181. package/tsconfig.debug.json +0 -17
  182. package/tsconfig.json +0 -28
  183. package/vitest.config.ts +0 -51
@@ -1,140 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Test script to compare job order between original and generated pipelines
5
- * This ensures that Pipecraft jobs maintain their original positions
6
- */
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
- const yaml = require('yaml');
11
-
12
- /**
13
- * Extract job names in order from a YAML file
14
- */
15
- function getJobOrder(filePath) {
16
- if (!fs.existsSync(filePath)) {
17
- console.error(`āŒ File not found: ${filePath}`);
18
- return [];
19
- }
20
-
21
- try {
22
- const content = fs.readFileSync(filePath, 'utf8');
23
- const doc = yaml.parseDocument(content);
24
-
25
- if (!doc.contents || !doc.contents.get('jobs')) {
26
- console.error(`āŒ No jobs section found in ${filePath}`);
27
- return [];
28
- }
29
-
30
- const jobsNode = doc.contents.get('jobs');
31
- if (!jobsNode.items) {
32
- console.error(`āŒ No job items found in ${filePath}`);
33
- return [];
34
- }
35
-
36
- return jobsNode.items.map(item => item.key.value);
37
- } catch (error) {
38
- console.error(`āŒ Error parsing ${filePath}:`, error.message);
39
- return [];
40
- }
41
- }
42
-
43
- /**
44
- * Compare two job orders and report differences
45
- */
46
- function compareJobOrders(original, generated, testName) {
47
- console.log(`\nšŸ” Testing: ${testName}`);
48
- console.log(`šŸ“‹ Original order: [${original.join(', ')}]`);
49
- console.log(`šŸ“‹ Generated order: [${generated.join(', ')}]`);
50
-
51
- if (original.length !== generated.length) {
52
- console.log(`āŒ Length mismatch: original has ${original.length} jobs, generated has ${generated.length}`);
53
- return false;
54
- }
55
-
56
- let matches = 0;
57
- let differences = [];
58
-
59
- for (let i = 0; i < original.length; i++) {
60
- if (original[i] === generated[i]) {
61
- matches++;
62
- } else {
63
- differences.push({
64
- position: i,
65
- original: original[i],
66
- generated: generated[i]
67
- });
68
- }
69
- }
70
-
71
- if (matches === original.length) {
72
- console.log(`āœ… Perfect match! All ${matches} jobs in correct order`);
73
- return true;
74
- } else {
75
- console.log(`āŒ ${differences.length} position(s) differ:`);
76
- differences.forEach(diff => {
77
- console.log(` Position ${diff.position}: expected "${diff.original}", got "${diff.generated}"`);
78
- });
79
- return false;
80
- }
81
- }
82
-
83
- /**
84
- * Main test function
85
- */
86
- function runJobOrderTests() {
87
- console.log('🧪 Pipecraft Job Order Test Suite');
88
- console.log('=====================================');
89
-
90
- const testCases = [
91
- {
92
- name: 'User Modified Pipeline',
93
- original: '.github/workflows/pipeline-user-modified.yml',
94
- generated: '.github/workflows/pipeline-test-order-restored.yml'
95
- },
96
- {
97
- name: 'Final Test Pipeline',
98
- original: '.github/workflows/pipeline-user-modified.yml',
99
- generated: '.github/workflows/pipeline-test-final.yml'
100
- }
101
- ];
102
-
103
- let allPassed = true;
104
-
105
- for (const testCase of testCases) {
106
- const originalOrder = getJobOrder(testCase.original);
107
- const generatedOrder = getJobOrder(testCase.generated);
108
-
109
- if (originalOrder.length === 0 || generatedOrder.length === 0) {
110
- console.log(`āŒ Skipping ${testCase.name} - failed to parse files`);
111
- allPassed = false;
112
- continue;
113
- }
114
-
115
- const passed = compareJobOrders(originalOrder, generatedOrder, testCase.name);
116
- if (!passed) {
117
- allPassed = false;
118
- }
119
- }
120
-
121
- console.log('\nšŸ“Š Test Results');
122
- console.log('================');
123
- if (allPassed) {
124
- console.log('āœ… All job order tests PASSED!');
125
- console.log('šŸŽ‰ Pipecraft jobs are maintaining their original positions');
126
- } else {
127
- console.log('āŒ Some job order tests FAILED!');
128
- console.log('šŸ”§ Job positions are not being preserved correctly');
129
- }
130
-
131
- return allPassed;
132
- }
133
-
134
- // Run the tests if this script is executed directly
135
- if (require.main === module) {
136
- const success = runJobOrderTests();
137
- process.exit(success ? 0 : 1);
138
- }
139
-
140
- module.exports = { getJobOrder, compareJobOrders, runJobOrderTests };
@@ -1,379 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Pipeline Validation Script
5
- *
6
- * Validates GitHub Actions workflows and composite actions to catch common errors:
7
- * - Missing git config in actions that perform git operations
8
- * - Secrets usage in composite actions (should use inputs)
9
- * - Local actions used without checkout
10
- * - Missing token inputs for actions that need authentication
11
- * - Version format issues
12
- */
13
-
14
- const fs = require('fs');
15
- const path = require('path');
16
- const yaml = require('yaml');
17
-
18
- // ANSI color codes for pretty output
19
- const colors = {
20
- reset: '\x1b[0m',
21
- red: '\x1b[31m',
22
- green: '\x1b[32m',
23
- yellow: '\x1b[33m',
24
- blue: '\x1b[34m',
25
- cyan: '\x1b[36m',
26
- };
27
-
28
- function log(message, color = colors.reset) {
29
- console.log(`${color}${message}${colors.reset}`);
30
- }
31
-
32
- function error(message) {
33
- log(`āŒ ${message}`, colors.red);
34
- }
35
-
36
- function warning(message) {
37
- log(`āš ļø ${message}`, colors.yellow);
38
- }
39
-
40
- function success(message) {
41
- log(`āœ… ${message}`, colors.green);
42
- }
43
-
44
- function info(message) {
45
- log(`ā„¹ļø ${message}`, colors.cyan);
46
- }
47
-
48
- /**
49
- * Validate that git operations have git config set
50
- */
51
- function validateGitConfig(actionPath) {
52
- const content = fs.readFileSync(actionPath, 'utf8');
53
- // Only check for write operations that require git config
54
- // Exclude read-only operations like 'git tag --points-at'
55
- const gitWriteOps = [
56
- /git commit(?!\s+--points-at)/,
57
- /git tag(?!\s+(--points-at|--list))/,
58
- /git push/,
59
- /git merge/
60
- ];
61
-
62
- const hasGitWriteOps = gitWriteOps.some(op => op.test(content));
63
- const hasGitConfig = content.includes('git config user.name') ||
64
- content.includes('git config --global user.name');
65
-
66
- if (hasGitWriteOps && !hasGitConfig) {
67
- return `Git operations without git config (user.name/email)`;
68
- }
69
- return null;
70
- }
71
-
72
- /**
73
- * Validate that composite actions don't access secrets directly
74
- */
75
- function validateTokenUsage(actionPath) {
76
- const content = fs.readFileSync(actionPath, 'utf8');
77
-
78
- try {
79
- const action = yaml.parse(content);
80
-
81
- // Check if composite action tries to use secrets
82
- if (action.runs?.using === 'composite' && content.includes('secrets.')) {
83
- return `Composite action accessing secrets directly (use inputs instead)`;
84
- }
85
-
86
- // Check if action uses gh/hub CLI without token input
87
- if (content.includes('gh ') || content.includes('hub ') || content.includes('GITHUB_TOKEN')) {
88
- const hasTokenInput = action.inputs?.token !== undefined;
89
- if (!hasTokenInput && content.includes('secrets.GITHUB_TOKEN')) {
90
- return `Uses GITHUB_TOKEN via secrets instead of input parameter`;
91
- }
92
- }
93
- } catch (err) {
94
- return `Failed to parse YAML: ${err.message}`;
95
- }
96
-
97
- return null;
98
- }
99
-
100
- /**
101
- * Validate version format handling
102
- */
103
- function validateVersionFormat(actionPath) {
104
- const content = fs.readFileSync(actionPath, 'utf8');
105
-
106
- // Check if action uses version input
107
- if (content.includes('${{ inputs.version }}') || content.includes('${VERSION}')) {
108
- // Check if it handles both v-prefixed and non-prefixed versions
109
- const hasStripPrefix = content.includes('VERSION="${VERSION#v}"') ||
110
- content.includes('strip v prefix') ||
111
- content.includes('VERSION#v');
112
-
113
- // Only warn if it looks like it's validating version format without stripping
114
- const hasVersionValidation = content.includes('=~ ^[0-9]+') ||
115
- content.includes('version format');
116
-
117
- if (hasVersionValidation && !hasStripPrefix) {
118
- return `Validates version format but doesn't strip 'v' prefix (may reject v1.0.0)`;
119
- }
120
- }
121
-
122
- return null;
123
- }
124
-
125
- /**
126
- * Validate error handling in scripts
127
- */
128
- function validateErrorHandling(actionPath) {
129
- const content = fs.readFileSync(actionPath, 'utf8');
130
- const warnings = [];
131
-
132
- // Check for suppressed error output (2>/dev/null) with critical commands
133
- const criticalCommands = ['gh pr create', 'gh pr merge', 'gh release create', 'git push', 'git tag'];
134
-
135
- for (const cmd of criticalCommands) {
136
- // Match command followed by 2>/dev/null
137
- const regex = new RegExp(`${cmd.replace(/\s+/g, '\\s+')}[^\\n]*2>/dev/null`, 'g');
138
- if (regex.test(content)) {
139
- warnings.push(`Suppresses errors for '${cmd}' (2>/dev/null hides useful error messages)`);
140
- }
141
- }
142
-
143
- return warnings.length > 0 ? warnings.join('; ') : null;
144
- }
145
-
146
- /**
147
- * Validate GitHub CLI usage has GH_TOKEN set
148
- * Note: For composite actions, GH_TOKEN should be set at the workflow job level,
149
- * not in the action itself. This validator just warns if gh CLI is used.
150
- */
151
- function validateGhToken(actionPath) {
152
- const content = fs.readFileSync(actionPath, 'utf8');
153
-
154
- // Check if action uses gh CLI commands
155
- const ghCommands = ['gh pr', 'gh release', 'gh issue', 'gh workflow'];
156
- const usesGhCli = ghCommands.some(cmd => content.includes(cmd));
157
-
158
- if (!usesGhCli) {
159
- return null;
160
- }
161
-
162
- // For composite actions, this is just a warning reminder
163
- // The GH_TOKEN should be set at the job level in the workflow
164
- return null; // No error - workflow should handle this
165
- }
166
-
167
- /**
168
- * Validate workflow jobs
169
- */
170
- function validateWorkflow(workflowPath) {
171
- const errors = [];
172
- const warnings = [];
173
-
174
- try {
175
- const content = fs.readFileSync(workflowPath, 'utf8');
176
- const workflow = yaml.parse(content);
177
-
178
- // Check for local actions without checkout
179
- for (const [jobName, job] of Object.entries(workflow.jobs || {})) {
180
- const steps = job.steps || [];
181
- let hasCheckout = false;
182
-
183
- for (let i = 0; i < steps.length; i++) {
184
- const step = steps[i];
185
-
186
- if (step.uses === 'actions/checkout@v4' || step.uses?.startsWith('actions/checkout@')) {
187
- hasCheckout = true;
188
- }
189
-
190
- // Check for local action usage
191
- if (step.uses && step.uses.startsWith('./')) {
192
- if (!hasCheckout) {
193
- // Check if the action itself has a checkout step
194
- const actionPath = path.join('.github', 'actions', path.basename(step.uses), 'action.yml');
195
- let actionHasCheckout = false;
196
-
197
- try {
198
- if (fs.existsSync(actionPath)) {
199
- const actionContent = fs.readFileSync(actionPath, 'utf8');
200
- actionHasCheckout = actionContent.includes('actions/checkout');
201
- }
202
- } catch (e) {
203
- // Ignore errors reading action file
204
- }
205
-
206
- if (!actionHasCheckout) {
207
- errors.push(`Job '${jobName}' step ${i + 1}: Local action '${step.uses}' used before checkout`);
208
- }
209
- }
210
- }
211
- }
212
- }
213
-
214
- // Check for pnpm cache before pnpm install
215
- for (const [jobName, job] of Object.entries(workflow.jobs || {})) {
216
- const steps = job.steps || [];
217
- let setupNodeWithPnpmCache = false;
218
- let pnpmInstalled = false;
219
-
220
- for (const step of steps) {
221
- if (step.uses?.includes('actions/setup-node') && step.with?.cache === 'pnpm') {
222
- setupNodeWithPnpmCache = true;
223
- if (!pnpmInstalled) {
224
- warnings.push(`Job '${jobName}': setup-node with pnpm cache before pnpm is installed`);
225
- }
226
- }
227
-
228
- if (step.uses?.includes('pnpm/action-setup')) {
229
- pnpmInstalled = true;
230
- }
231
- }
232
- }
233
-
234
- } catch (err) {
235
- errors.push(`Failed to parse workflow: ${err.message}`);
236
- }
237
-
238
- return { errors, warnings };
239
- }
240
-
241
- /**
242
- * Validate composite action
243
- */
244
- function validateCompositeAction(actionPath) {
245
- const errors = [];
246
- const warnings = [];
247
- const fileName = path.basename(path.dirname(actionPath));
248
-
249
- // Git config validation
250
- const gitConfigError = validateGitConfig(actionPath);
251
- if (gitConfigError) {
252
- errors.push(`${fileName}: ${gitConfigError}`);
253
- }
254
-
255
- // Token usage validation
256
- const tokenError = validateTokenUsage(actionPath);
257
- if (tokenError) {
258
- errors.push(`${fileName}: ${tokenError}`);
259
- }
260
-
261
- // Version format validation
262
- const versionWarning = validateVersionFormat(actionPath);
263
- if (versionWarning) {
264
- warnings.push(`${fileName}: ${versionWarning}`);
265
- }
266
-
267
- // Error handling validation
268
- const errorHandlingWarning = validateErrorHandling(actionPath);
269
- if (errorHandlingWarning) {
270
- warnings.push(`${fileName}: ${errorHandlingWarning}`);
271
- }
272
-
273
- // GH_TOKEN validation
274
- const ghTokenError = validateGhToken(actionPath);
275
- if (ghTokenError) {
276
- errors.push(`${fileName}: ${ghTokenError}`);
277
- }
278
-
279
- return { errors, warnings };
280
- }
281
-
282
- /**
283
- * Main validation function
284
- */
285
- function main() {
286
- info('šŸ” Validating PipeCraft Pipeline...\n');
287
-
288
- let allErrors = [];
289
- let allWarnings = [];
290
-
291
- // Validate workflows
292
- const workflowsDir = path.join(process.cwd(), '.github/workflows');
293
- if (fs.existsSync(workflowsDir)) {
294
- const workflowFiles = fs.readdirSync(workflowsDir)
295
- .filter(f => f.endsWith('.yml') || f.endsWith('.yaml'))
296
- .map(f => path.join(workflowsDir, f));
297
-
298
- log(`šŸ“„ Validating ${workflowFiles.length} workflow(s)...`, colors.blue);
299
-
300
- for (const workflowPath of workflowFiles) {
301
- const fileName = path.basename(workflowPath);
302
- const { errors, warnings } = validateWorkflow(workflowPath);
303
-
304
- if (errors.length === 0 && warnings.length === 0) {
305
- success(` ${fileName}`);
306
- } else {
307
- if (errors.length > 0) {
308
- error(` ${fileName}`);
309
- errors.forEach(err => error(` ${err}`));
310
- }
311
- if (warnings.length > 0) {
312
- warning(` ${fileName}`);
313
- warnings.forEach(warn => warning(` ${warn}`));
314
- }
315
- }
316
-
317
- allErrors = allErrors.concat(errors);
318
- allWarnings = allWarnings.concat(warnings);
319
- }
320
- }
321
-
322
- console.log();
323
-
324
- // Validate composite actions
325
- const actionsDir = path.join(process.cwd(), '.github/actions');
326
- if (fs.existsSync(actionsDir)) {
327
- const actionDirs = fs.readdirSync(actionsDir)
328
- .map(dir => path.join(actionsDir, dir, 'action.yml'))
329
- .filter(p => fs.existsSync(p));
330
-
331
- log(`šŸŽ¬ Validating ${actionDirs.length} action(s)...`, colors.blue);
332
-
333
- for (const actionPath of actionDirs) {
334
- const actionName = path.basename(path.dirname(actionPath));
335
- const { errors, warnings } = validateCompositeAction(actionPath);
336
-
337
- if (errors.length === 0 && warnings.length === 0) {
338
- success(` ${actionName}`);
339
- } else {
340
- if (errors.length > 0) {
341
- error(` ${actionName}`);
342
- errors.forEach(err => error(` ${err}`));
343
- }
344
- if (warnings.length > 0) {
345
- warning(` ${actionName}`);
346
- warnings.forEach(warn => warning(` ${warn}`));
347
- }
348
- }
349
-
350
- allErrors = allErrors.concat(errors);
351
- allWarnings = allWarnings.concat(warnings);
352
- }
353
- }
354
-
355
- console.log();
356
-
357
- // Summary
358
- if (allErrors.length === 0 && allWarnings.length === 0) {
359
- success('✨ Pipeline validation passed! No issues found.');
360
- return 0;
361
- }
362
-
363
- if (allErrors.length > 0) {
364
- error(`\nāŒ Found ${allErrors.length} error(s)`);
365
- return 1;
366
- }
367
-
368
- if (allWarnings.length > 0) {
369
- warning(`\nāš ļø Found ${allWarnings.length} warning(s)`);
370
- warning('Warnings do not fail validation but should be reviewed.');
371
- return 0;
372
- }
373
-
374
- return 0;
375
- }
376
-
377
- // Run validation
378
- const exitCode = main();
379
- process.exit(exitCode);
@@ -1,30 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Quick job order verification script
4
- # Usage: ./scripts/verify-job-order.sh <original-file> <generated-file>
5
-
6
- set -e
7
-
8
- ORIGINAL_FILE=${1:-".github/workflows/pipeline-user-modified.yml"}
9
- GENERATED_FILE=${2:-".github/workflows/pipeline-test-order-restored.yml"}
10
-
11
- echo "šŸ” Verifying job order preservation..."
12
- echo "šŸ“‹ Original: $ORIGINAL_FILE"
13
- echo "šŸ“‹ Generated: $GENERATED_FILE"
14
-
15
- # Extract job names from both files (only from jobs section)
16
- ORIGINAL_JOBS=$(sed -n '/^jobs:/,/^[^ ]/p' "$ORIGINAL_FILE" | grep "^ [a-zA-Z]" | sed 's/://' | tr '\n' ' ')
17
- GENERATED_JOBS=$(sed -n '/^jobs:/,/^[^ ]/p' "$GENERATED_FILE" | grep "^ [a-zA-Z]" | sed 's/://' | tr '\n' ' ')
18
-
19
- echo "šŸ“‹ Original jobs: $ORIGINAL_JOBS"
20
- echo "šŸ“‹ Generated jobs: $GENERATED_JOBS"
21
-
22
- # Compare the job orders
23
- if [ "$ORIGINAL_JOBS" = "$GENERATED_JOBS" ]; then
24
- echo "āœ… Job order preserved correctly!"
25
- exit 0
26
- else
27
- echo "āŒ Job order differs!"
28
- echo "šŸ”§ Pipecraft jobs may have moved to incorrect positions"
29
- exit 1
30
- fi