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,352 @@
1
+ name: 'Promote Branch'
2
+ description: 'Promote code from source to target branch via temporary branch + PR'
3
+ author: 'PipeCraft'
4
+
5
+ inputs:
6
+ sourceBranch:
7
+ description: 'Source branch to promote from (defaults to github.ref_name)'
8
+ required: false
9
+ default: ${{ github.ref_name }}
10
+ version:
11
+ description: 'Version being promoted (e.g., v1.2.3, auto-detected from git tags if not provided)'
12
+ required: false
13
+ default: ''
14
+ run_number:
15
+ description: 'Original run number from develop branch to track across promotions'
16
+ required: false
17
+ default: ''
18
+ configPath:
19
+ description: 'Path to .pipecraftrc.json config file'
20
+ required: false
21
+ default: '.pipecraftrc.json'
22
+ tempBranchPattern:
23
+ description: 'Pattern for temp branch name'
24
+ required: false
25
+ default: 'release/{source}-to-{target}-{version}'
26
+ token:
27
+ description: 'GitHub token for authentication'
28
+ required: false
29
+ default: ${{ github.token }}
30
+
31
+ outputs:
32
+ prNumber:
33
+ description: 'The created PR number'
34
+ value: ${{ steps.create-pr.outputs.prNumber }}
35
+ prUrl:
36
+ description: 'The created PR URL'
37
+ value: ${{ steps.create-pr.outputs.prUrl }}
38
+ tempBranch:
39
+ description: 'The temporary branch name'
40
+ value: ${{ steps.create-temp.outputs.tempBranch }}
41
+ targetBranch:
42
+ description: 'The target branch determined from config'
43
+ value: ${{ steps.read-config.outputs.targetBranch }}
44
+ autoMerge:
45
+ description: 'The autoMerge setting for the target branch'
46
+ value: ${{ steps.read-config.outputs.autoMerge }}
47
+
48
+ runs:
49
+ using: 'composite'
50
+ steps:
51
+ - name: Checkout Code
52
+ uses: actions/checkout@v4
53
+ with:
54
+ fetch-depth: 0
55
+ token: ${{ inputs.token }}
56
+
57
+ - name: Read Config and Determine Target Branch
58
+ id: read-config
59
+ shell: bash
60
+ run: |
61
+ SOURCE="${{ inputs.sourceBranch }}"
62
+ CONFIG_PATH="${{ inputs.configPath }}"
63
+
64
+ echo "📖 Reading config from $CONFIG_PATH"
65
+
66
+ # Check if config file exists
67
+ if [ ! -f "$CONFIG_PATH" ]; then
68
+ echo "❌ Config file not found: $CONFIG_PATH"
69
+ exit 1
70
+ fi
71
+
72
+ # Read branchFlow array from config
73
+ BRANCH_FLOW=$(cat "$CONFIG_PATH" | jq -r '.branchFlow | join(" ")')
74
+ echo "🔍 Branch flow: $BRANCH_FLOW"
75
+
76
+ # Convert to array
77
+ IFS=' ' read -ra BRANCHES <<< "$BRANCH_FLOW"
78
+
79
+ # Find current branch index and determine target
80
+ TARGET=""
81
+ for i in "${!BRANCHES[@]}"; do
82
+ if [ "${BRANCHES[$i]}" == "$SOURCE" ]; then
83
+ # Get next branch in flow
84
+ NEXT_INDEX=$((i + 1))
85
+ if [ $NEXT_INDEX -lt ${#BRANCHES[@]} ]; then
86
+ TARGET="${BRANCHES[$NEXT_INDEX]}"
87
+ fi
88
+ break
89
+ fi
90
+ done
91
+
92
+ if [ -z "$TARGET" ]; then
93
+ echo "❌ Could not determine target branch for source: $SOURCE"
94
+ echo " Branch flow: $BRANCH_FLOW"
95
+ exit 1
96
+ fi
97
+
98
+ echo "✅ Target branch: $TARGET"
99
+ echo "targetBranch=$TARGET" >> $GITHUB_OUTPUT
100
+
101
+ # Determine autoMerge setting for target branch
102
+ AUTO_MERGE=$(cat "$CONFIG_PATH" | jq -r ".autoMerge.\"$TARGET\" // false")
103
+ echo "🔍 Auto-merge for $TARGET: $AUTO_MERGE"
104
+ echo "autoMerge=$AUTO_MERGE" >> $GITHUB_OUTPUT
105
+
106
+ - name: Get Version
107
+ id: get-version
108
+ shell: bash
109
+ run: |
110
+ VERSION_INPUT="${{ inputs.version }}"
111
+
112
+ if [ -n "$VERSION_INPUT" ]; then
113
+ VERSION="$VERSION_INPUT"
114
+ echo "📌 Using provided version: $VERSION"
115
+ else
116
+ # Auto-detect from git tags
117
+ VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
118
+ echo "🏷️ Auto-detected version from tags: $VERSION"
119
+ fi
120
+
121
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
122
+
123
+ - name: Create Temporary Branch
124
+ id: create-temp
125
+ shell: bash
126
+ run: |
127
+ SOURCE="${{ inputs.sourceBranch }}"
128
+ TARGET="${{ steps.read-config.outputs.targetBranch }}"
129
+ VERSION="${{ steps.get-version.outputs.version }}"
130
+ PATTERN="${{ inputs.tempBranchPattern }}"
131
+
132
+ # Remove 'v' prefix from version for branch name
133
+ VERSION_CLEAN="${VERSION#v}"
134
+
135
+ # Replace placeholders in pattern
136
+ TEMP_BRANCH="$PATTERN"
137
+ TEMP_BRANCH="${TEMP_BRANCH//{source\}/$SOURCE}"
138
+ TEMP_BRANCH="${TEMP_BRANCH//{target\}/$TARGET}"
139
+ TEMP_BRANCH="${TEMP_BRANCH//{version\}/$VERSION_CLEAN}"
140
+
141
+ echo "🌿 Preparing temporary branch: $TEMP_BRANCH"
142
+ echo " From: $SOURCE"
143
+ echo " To: $TARGET"
144
+ echo " Version: $VERSION"
145
+
146
+ # Ensure we're on the source branch
147
+ git checkout "$SOURCE"
148
+
149
+ # Check if temp branch already exists locally or remotely
150
+ if git show-ref --verify --quiet refs/heads/"$TEMP_BRANCH" || git ls-remote --heads origin "$TEMP_BRANCH" | grep -q "$TEMP_BRANCH"; then
151
+ echo "⚠️ Temp branch $TEMP_BRANCH already exists, using existing branch"
152
+ # Fetch and checkout existing branch
153
+ git fetch origin "$TEMP_BRANCH" 2>/dev/null || true
154
+ git checkout "$TEMP_BRANCH" 2>/dev/null || git checkout -b "$TEMP_BRANCH" origin/"$TEMP_BRANCH"
155
+ else
156
+ echo "📝 Creating new temp branch $TEMP_BRANCH"
157
+ # Create temp branch from current commit
158
+ git checkout -b "$TEMP_BRANCH"
159
+ # Push temp branch to remote
160
+ git push origin "$TEMP_BRANCH"
161
+ fi
162
+
163
+ echo "tempBranch=$TEMP_BRANCH" >> $GITHUB_OUTPUT
164
+ echo "✅ Temporary branch ready"
165
+
166
+ - name: Check for Existing PR
167
+ id: check-pr
168
+ shell: bash
169
+ env:
170
+ GH_TOKEN: ${{ inputs.token }}
171
+ run: |
172
+ TEMP_BRANCH="${{ steps.create-temp.outputs.tempBranch }}"
173
+ TARGET="${{ steps.read-config.outputs.targetBranch }}"
174
+
175
+ # Check if PR already exists from temp branch to target
176
+ EXISTING_PR=$(gh pr list --head "$TEMP_BRANCH" --base "$TARGET" --json number --jq '.[0].number' 2>/dev/null || echo "")
177
+
178
+ if [ -n "$EXISTING_PR" ]; then
179
+ echo "exists=true" >> $GITHUB_OUTPUT
180
+ echo "prNumber=$EXISTING_PR" >> $GITHUB_OUTPUT
181
+ echo "⚠️ PR already exists: #$EXISTING_PR"
182
+
183
+ # Get PR URL
184
+ PR_URL=$(gh pr view "$EXISTING_PR" --json url --jq '.url')
185
+ echo "prUrl=$PR_URL" >> $GITHUB_OUTPUT
186
+ else
187
+ echo "exists=false" >> $GITHUB_OUTPUT
188
+ echo "✅ No existing PR found"
189
+ fi
190
+
191
+ - name: Create Pull Request
192
+ id: create-pr
193
+ if: steps.check-pr.outputs.exists == 'false'
194
+ shell: bash
195
+ env:
196
+ GH_TOKEN: ${{ inputs.token }}
197
+ run: |
198
+ TEMP_BRANCH="${{ steps.create-temp.outputs.tempBranch }}"
199
+ TARGET="${{ steps.read-config.outputs.targetBranch }}"
200
+ SOURCE="${{ inputs.sourceBranch }}"
201
+ VERSION="${{ steps.get-version.outputs.version }}"
202
+ AUTO_MERGE="${{ steps.read-config.outputs.autoMerge }}"
203
+
204
+ TITLE="🚀 Release $VERSION to $TARGET"
205
+
206
+ # Determine merge behavior text
207
+ if [ "$AUTO_MERGE" == "true" ]; then
208
+ MERGE_TEXT="merge automatically"
209
+ else
210
+ MERGE_TEXT="require manual approval"
211
+ fi
212
+
213
+ # Disable error exit to capture gh pr create output even on failure
214
+ set +e
215
+
216
+ # Create the PR - gh pr create returns the PR URL on success
217
+ PR_URL=$(gh pr create \--title "$TITLE" \--body "Release $VERSION from $SOURCE to $TARGET" \--head "$TEMP_BRANCH" \--base "$TARGET" 2>&1)
218
+ PR_EXIT_CODE=$?
219
+
220
+ # Re-enable error exit
221
+ set -e
222
+
223
+ if [ $PR_EXIT_CODE -eq 0 ] && [[ "$PR_URL" =~ ^https://github.com/.*/pull/[0-9]+$ ]]; then
224
+ # Extract PR number from URL (e.g., https://github.com/owner/repo/pull/123)
225
+ PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
226
+ echo "prNumber=$PR_NUMBER" >> $GITHUB_OUTPUT
227
+ echo "prUrl=$PR_URL" >> $GITHUB_OUTPUT
228
+ echo "✅ Created PR #$PR_NUMBER"
229
+ echo "🔗 URL: $PR_URL"
230
+ else
231
+ echo "❌ Failed to create PR"
232
+ echo "Error output:"
233
+ echo "$PR_URL"
234
+ exit 1
235
+ fi
236
+
237
+ - name: Enable PR Auto-Merge with Rebase (Manual Approval Required)
238
+ if: steps.read-config.outputs.autoMerge == 'false'
239
+ shell: bash
240
+ env:
241
+ GH_TOKEN: ${{ inputs.token }}
242
+ run: |
243
+ PR_NUMBER="${{ steps.check-pr.outputs.prNumber || steps.create-pr.outputs.prNumber }}"
244
+ TARGET="${{ steps.read-config.outputs.targetBranch }}"
245
+
246
+ echo "🔐 Enabling auto-merge with rebase for PR #$PR_NUMBER (requires approval)"
247
+ echo " This will fast-forward $TARGET when approved, maintaining linear history"
248
+
249
+ # Enable auto-merge with rebase method (fast-forward, no merge commit)
250
+ gh pr merge "$PR_NUMBER" --auto --rebase
251
+
252
+ echo "✅ Auto-merge enabled - PR will fast-forward merge when approved"
253
+ echo "⚠️ Manual approval required before merge completes"
254
+
255
+ - name: Fast-Forward Merge (Immediate Auto-Merge)
256
+ if: steps.read-config.outputs.autoMerge == 'true'
257
+ shell: bash
258
+ run: |
259
+ SOURCE="${{ inputs.sourceBranch }}"
260
+ TARGET="${{ steps.read-config.outputs.targetBranch }}"
261
+ VERSION="${{ steps.get-version.outputs.version }}"
262
+
263
+ echo "🚀 Fast-forwarding $TARGET to $SOURCE (maintaining linear history)"
264
+
265
+ # Get the commit SHA from source branch
266
+ COMMIT_SHA=$(git rev-parse $SOURCE)
267
+ echo "Source commit: $COMMIT_SHA"
268
+
269
+ # Fetch target branch
270
+ git fetch origin $TARGET:$TARGET 2>/dev/null || true
271
+
272
+ # Checkout target branch
273
+ git checkout $TARGET
274
+
275
+ # Verify we can fast-forward (no divergent commits)
276
+ if git merge-base --is-ancestor $TARGET $SOURCE; then
277
+ echo "✅ Fast-forward is possible (linear history maintained)"
278
+ else
279
+ echo "❌ Cannot fast-forward - branches have diverged"
280
+ echo "Target branch has commits not in source branch"
281
+ exit 1
282
+ fi
283
+
284
+ # Fast-forward merge (no merge commit)
285
+ git merge --ff-only $SOURCE
286
+
287
+ # Push to remote
288
+ git push origin $TARGET
289
+
290
+ echo "✅ Fast-forwarded $TARGET to $COMMIT_SHA"
291
+ echo "📝 PR will be closed automatically for audit trail"
292
+
293
+ - name: Trigger Pipeline Workflow on Target Branch
294
+ if: steps.read-config.outputs.autoMerge == 'true'
295
+ shell: bash
296
+ env:
297
+ GH_TOKEN: ${{ inputs.token }}
298
+ run: |
299
+ TARGET="${{ steps.read-config.outputs.targetBranch }}"
300
+ VERSION="${{ steps.get-version.outputs.version }}"
301
+ RUN_NUMBER="${{ inputs.run_number }}"
302
+
303
+ echo "🔄 Triggering pipeline workflow on $TARGET branch with version $VERSION"
304
+
305
+ if [ -n "$RUN_NUMBER" ]; then
306
+ echo " Tracking original run number: $RUN_NUMBER"
307
+ fi
308
+
309
+ # Trigger the pipeline workflow on the target branch
310
+ # This is necessary because GITHUB_TOKEN pushes don't trigger workflows by default
311
+ # Pass the version and run_number so runs can be tracked across promotions
312
+ if [ -n "$RUN_NUMBER" ]; then
313
+ gh workflow run pipeline.yml --ref "$TARGET" --field version="$VERSION" --field run_number="$RUN_NUMBER"
314
+ else
315
+ gh workflow run pipeline.yml --ref "$TARGET" --field version="$VERSION"
316
+ fi
317
+
318
+ echo "✅ Pipeline workflow triggered on $TARGET with version $VERSION"
319
+
320
+ - name: Close PR and Delete Branch
321
+ if: steps.read-config.outputs.autoMerge == 'true'
322
+ shell: bash
323
+ env:
324
+ GH_TOKEN: ${{ inputs.token }}
325
+ run: |
326
+ TEMP_BRANCH="${{ steps.create-temp.outputs.tempBranch }}"
327
+ PR_NUMBER="${{ steps.check-pr.outputs.prNumber || steps.create-pr.outputs.prNumber }}"
328
+
329
+ echo "🧹 Cleaning up temporary branch: $TEMP_BRANCH"
330
+
331
+ # Close the PR (it's already been fast-forward merged)
332
+ if [ -n "$PR_NUMBER" ]; then
333
+ gh pr close "$PR_NUMBER" --comment "Automatically merged via fast-forward. Closing PR and cleaning up release branch." || true
334
+ echo "✅ Closed PR #$PR_NUMBER"
335
+ fi
336
+
337
+ # Delete the temporary branch from remote
338
+ git push origin --delete "$TEMP_BRANCH" || echo "⚠️ Branch may already be deleted"
339
+
340
+ echo "✅ Deleted temporary branch $TEMP_BRANCH"
341
+
342
+ - name: Use Existing PR
343
+ if: steps.check-pr.outputs.exists == 'true'
344
+ shell: bash
345
+ run: |
346
+ PR_NUMBER="${{ steps.check-pr.outputs.prNumber }}"
347
+ PR_URL="${{ steps.check-pr.outputs.prUrl }}"
348
+
349
+ echo "ℹ️ Using existing PR #$PR_NUMBER"
350
+ echo "🔗 URL: $PR_URL"
351
+
352
+ # Note: Fast-forward merge happens in separate step if autoMerge is enabled
@@ -0,0 +1,26 @@
1
+ name: CodeQL Analysis
2
+
3
+ on:
4
+ workflow_call:
5
+ workflow_dispatch:
6
+
7
+ jobs:
8
+ analyze:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ actions: read
12
+ contents: read
13
+ security-events: write
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ language: [javascript, typescript]
18
+ steps:
19
+ - name: Checkout repository
20
+ uses: actions/checkout@v4
21
+ # - name: Initialize CodeQL
22
+ # uses: github/codeql-action/init@v3
23
+ # with:
24
+ # languages: ${{ matrix.language }}
25
+ # - name: Perform CodeQL Analysis
26
+ # uses: github/codeql-action/analyze@v3
@@ -0,0 +1,32 @@
1
+ name: Trivy Security Scan
2
+
3
+ on:
4
+ workflow_call:
5
+ workflow_dispatch:
6
+
7
+ jobs:
8
+ analyze:
9
+ runs-on: ubuntu-latest
10
+ container: aquasec/trivy:latest # 🔹 Use Trivy's Docker image directly
11
+ steps:
12
+ - name: Checkout Code
13
+ uses: actions/checkout@v4
14
+
15
+ # ✅ Scan all directories containing a Dockerfile for misconfigurations
16
+ - name: Scan Dockerfile Misconfigurations
17
+ run: |
18
+ echo "Scanning directories containing Dockerfiles..."
19
+ for dir in $(find . -name "Dockerfile" -exec dirname {} \; | sort -u); do
20
+ echo "Scanning $dir..."
21
+ trivy config --severity HIGH,CRITICAL --exit-code 1 "$dir"
22
+ done
23
+
24
+ # ✅ Scan images listed in Docker Compose files
25
+ - name: Scan Docker Compose Images
26
+ run: |
27
+ echo "Extracting images from docker-compose.yml files..."
28
+ images=$(grep -hPo '(?<=image: )\S+' **/docker-compose.yml | sort -u || true)
29
+ for image in $images; do
30
+ echo "Scanning $image..."
31
+ trivy image --exit-code 1 --severity HIGH,CRITICAL "$image"
32
+ done
@@ -0,0 +1,127 @@
1
+ name: "API:Job:Deploy"
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ version:
7
+ description: 'The version to deploy'
8
+ required: true
9
+ type: string
10
+ project_id:
11
+ description: 'The project ID for the environment'
12
+ required: true
13
+ type: string
14
+
15
+ jobs:
16
+ deploy:
17
+ environment: ${{ github.ref_name == 'main' && 'production' || github.ref_name }}
18
+ env:
19
+ ENV: ${{ github.ref_name == 'main' && 'production' || github.ref_name }}
20
+ API_DIR: "apps/api"
21
+ PROJECT_ID: ${{ inputs.project_id }}
22
+ REGION: "us-east4"
23
+
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - name: Checkout
27
+ uses: actions/checkout@v4
28
+ with:
29
+ fetch-tags: true
30
+
31
+ - name: Debug Environment Variables
32
+ run: |
33
+ echo "PROJECT_ID from env: $PROJECT_ID"
34
+ echo "Input project_id: ${{ inputs.project_id }}"
35
+ echo "All environment variables:"
36
+ env | sort
37
+
38
+
39
+ - name: Get SHA for "migration" tag
40
+ id: get-migration-sha
41
+ run: |
42
+ if git rev-parse refs/tags/migration >/dev/null 2>&1; then
43
+ echo "MIGRATION_SHA=$(git rev-parse refs/tags/_migration-${{ env.ENV }})"
44
+ echo "MIGRATION_SHA=$(git rev-parse refs/tags/_migration-${{ env.ENV }})" >> $GITHUB_ENV
45
+ else
46
+ echo "MIGRATION_SHA=${GITHUB_SHA}" >> $GITHUB_ENV
47
+ fi
48
+
49
+ - name: Get changes in migrations folder
50
+ id: changed-files-migrations
51
+ uses: tj-actions/changed-files@v42
52
+ with:
53
+ files: apps/api/migrations/**
54
+ sha: ${{ env.MIGRATION_SHA }}
55
+
56
+
57
+ - name: Authenticate with Google
58
+ uses: 'google-github-actions/auth@v2'
59
+ with:
60
+ project_id: ${{ env.PROJECT_ID }}
61
+ credentials_json: ${{ secrets.GCP_CREDENTIALS }}
62
+
63
+ - name: Build and Push Container
64
+ uses: whoan/docker-build-with-cache-action@v5
65
+ with:
66
+ username: _json_key
67
+ password: ${{ secrets.GCP_CREDENTIALS }}
68
+ registry: ${{ vars.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ vars.AR_REPO }}
69
+ image_name: api-${{ env.PROJECT_ID }}
70
+ image_tag: ${{ github.sha }}
71
+ context: .
72
+ dockerfile: ${{ env.API_DIR }}/Dockerfile
73
+
74
+ # - name: Deploy to Cloud Run with Migration
75
+ # id: migration
76
+ # if: steps.changed-files-migrations.outputs.any_changed == 'true' || github.event.inputs.migration_input == true
77
+ # uses: google-github-actions/deploy-cloudrun@v2.0.0
78
+ # with:
79
+ # project_id: ${{ env.PROJECT_ID }}
80
+ # service: api-${{ env.PROJECT_ID }}
81
+ # region: ${{ env.REGION }}
82
+ # image: ${{ vars.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ vars.AR_REPO }}/api-${{ env.PROJECT_ID }}:${{ github.sha }}
83
+ # env_vars: |
84
+ # MIGRATION=${{ toJson(github.event.inputs.migration_input) || steps.changed-files-migrations.outputs.any_changed }}
85
+ # NODE_CONFIG_ENV=${{ env.ENV }}
86
+ # SEED=false
87
+ # no_traffic: true
88
+
89
+ # - name: Create Migration tag
90
+ # if: steps.migration.outcome == 'success'
91
+ # uses: actions/github-script@v7
92
+ # with:
93
+ # script: require('./libs/cicd/upsertTag.cjs')({ github, context, tag:"_migration-${{ env.ENV }}" })
94
+
95
+ # - name: Seed Data to Instance
96
+ # id: seed
97
+ # if: steps.changed-files-migrations.outputs.any_changed == 'true' || github.event.inputs.migration_input == true
98
+ # uses: google-github-actions/deploy-cloudrun@v2.0.0
99
+ # with:
100
+ # project_id: ${{ env.PROJECT_ID }}
101
+ # service: api-${{ env.PROJECT_ID }}
102
+ # region: ${{ env.REGION }}
103
+ # image: ${{ vars.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ vars.AR_REPO }}/api-${{ env.PROJECT_ID }}:${{ github.sha }}
104
+ # env_vars: |
105
+ # MIGRATION=false
106
+ # SEED=${{ github.ref_name == 'main' && 'false' || toJson(github.event.inputs.migration_input) || 'true' }}
107
+ # NODE_CONFIG_ENV=${{ env.ENV }}
108
+ # no_traffic: true
109
+
110
+ - name: Deploy to Cloud Run
111
+ uses: google-github-actions/deploy-cloudrun@v2.6.1
112
+ with:
113
+ project_id: ${{ env.PROJECT_ID }}
114
+ service: api-${{ env.PROJECT_ID }}
115
+ region: ${{ env.REGION }}
116
+ image: ${{ vars.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ vars.AR_REPO }}/api-${{ env.PROJECT_ID }}:${{ github.sha }}
117
+ env_vars: |
118
+ MIGRATION=false
119
+ SEED=false
120
+ NODE_CONFIG_ENV=${{ env.ENV }}
121
+
122
+
123
+ # - name: Create Deployed Tag
124
+ # uses: actions/github-script@v7
125
+ # with:
126
+ # script: require('./libs/cicd/upsertTag.cjs')({ github, context, tag:"_deployed-${{ env.ENV }}" })
127
+
@@ -0,0 +1,102 @@
1
+ on:
2
+ workflow_call:
3
+ workflow_dispatch:
4
+
5
+ env:
6
+ API_DIR: apps/api
7
+ DOCKER_REGISTRY: https://ghcr.io # the github registry
8
+ COMPOSE_FILE: apps/api/test/contracts/contract.docker-compose.yml
9
+
10
+ jobs:
11
+ blueprint:
12
+ env:
13
+ CONTRACT_TYPE: "blueprint"
14
+ runs-on: ubuntu-latest
15
+ timeout-minutes: 7
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@v4
19
+ with:
20
+ fetch-tags: true
21
+
22
+ - name: Login to GitHub Container Registry
23
+ uses: docker/login-action@v3
24
+ with:
25
+ registry: ${{ env.DOCKER_REGISTRY }}
26
+ username: ${{ github.repository_owner }}
27
+ password: ${{ secrets.GITHUB_TOKEN }}
28
+
29
+ - name: Start stack
30
+ shell: bash
31
+ run: |
32
+ echo OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }} > ${{ env.API_DIR }}/.env
33
+ echo CONTRACT_TYPE=${{ env.CONTRACT_TYPE }} >> ${{ env.API_DIR }}/.env
34
+ docker compose --env-file ${{ env.API_DIR }}/.env -f ${{ env.COMPOSE_FILE }} up --exit-code-from newman
35
+
36
+ - name: Stop stacks
37
+ if: always()
38
+ shell: bash
39
+ run: |
40
+ docker compose -f ${{ env.COMPOSE_FILE }} down
41
+
42
+ plugin-loader:
43
+ env:
44
+ CONTRACT_TYPE: "plugin-loader"
45
+ runs-on: ubuntu-latest
46
+ timeout-minutes: 7
47
+ steps:
48
+ - name: Checkout
49
+ uses: actions/checkout@v4
50
+ with:
51
+ fetch-tags: true
52
+
53
+ - name: Login to GitHub Container Registry
54
+ uses: docker/login-action@v3
55
+ with:
56
+ registry: ${{ env.DOCKER_REGISTRY }}
57
+ username: ${{ github.repository_owner }}
58
+ password: ${{ secrets.GITHUB_TOKEN }}
59
+
60
+ - name: Start stack
61
+ shell: bash
62
+ run: |
63
+ echo OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }} > ${{ env.API_DIR }}/.env
64
+ echo CONTRACT_TYPE=${{ env.CONTRACT_TYPE }} >> ${{ env.API_DIR }}/.env
65
+ docker compose --env-file ${{ env.API_DIR }}/.env -f ${{ env.COMPOSE_FILE }} up --exit-code-from newman
66
+
67
+ - name: Stop stacks
68
+ if: always()
69
+ shell: bash
70
+ run: |
71
+ docker compose -f ${{ env.COMPOSE_FILE }} down
72
+
73
+ raw-parser:
74
+ env:
75
+ CONTRACT_TYPE: "raw-parser"
76
+ runs-on: ubuntu-latest
77
+ timeout-minutes: 7
78
+ steps:
79
+ - name: Checkout
80
+ uses: actions/checkout@v4
81
+ with:
82
+ fetch-tags: true
83
+
84
+ - name: Login to GitHub Container Registry
85
+ uses: docker/login-action@v3
86
+ with:
87
+ registry: ${{ env.DOCKER_REGISTRY }}
88
+ username: ${{ github.repository_owner }}
89
+ password: ${{ secrets.GITHUB_TOKEN }}
90
+
91
+ - name: Start stack
92
+ shell: bash
93
+ run: |
94
+ echo CONTRACT_TYPE=${{ env.CONTRACT_TYPE }} > ${{ env.API_DIR }}/.env
95
+ docker compose --env-file ${{ env.API_DIR }}/.env -f ${{ env.COMPOSE_FILE }} up parser newman --exit-code-from newman
96
+
97
+ - name: Stop stacks
98
+ if: always()
99
+ shell: bash
100
+ run: |
101
+ docker compose -f ${{ env.COMPOSE_FILE }} down
102
+
@@ -0,0 +1,40 @@
1
+ name: "Docs:Deploy"
2
+
3
+
4
+ on:
5
+ workflow_dispatch:
6
+ push:
7
+ branches:
8
+ - main
9
+ paths:
10
+ - apps/docs/**/*
11
+ # Review gh actions docs if you want to further define triggers, paths, etc
12
+ # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
13
+ workflow_call:
14
+ inputs:
15
+ version:
16
+ description: 'The version to deploy'
17
+ required: true
18
+ type: string
19
+
20
+ jobs:
21
+ deploy:
22
+ # Grant GITHUB_TOKEN the permissions required to make a Pages deployment
23
+ permissions:
24
+ pages: write # to deploy to Pages
25
+ id-token: write # to verify the deployment originates from an appropriate source
26
+ if: github.ref == 'refs/heads/main'
27
+ name: Deploy to GitHub Pages
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - name: Checkout code
31
+ uses: actions/checkout@v4
32
+
33
+ - name: Download Build Artifact
34
+ uses: actions/download-artifact@v3
35
+ with:
36
+ name: docs-build
37
+
38
+ - name: Deploy to GitHub Pages
39
+ id: deployment
40
+ uses: actions/deploy-pages@v4