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,364 @@
1
+ import { PinionContext, toFile, renderTemplate } from '@featherscloud/pinion'
2
+ import fs from 'fs'
3
+ import dedent from 'dedent'
4
+
5
+ // Template for the Promote Branch GitHub Action
6
+ // This action handles branch promotions via temporary branches + PRs
7
+ const promoteBranchActionTemplate = (ctx: any) => {
8
+ return dedent`name: 'Promote Branch'
9
+ description: 'Promote code from source to target branch via temporary branch + PR'
10
+ author: 'PipeCraft'
11
+
12
+ inputs:
13
+ sourceBranch:
14
+ description: 'Source branch to promote from (defaults to github.ref_name)'
15
+ required: false
16
+ default: \${{ github.ref_name }}
17
+ version:
18
+ description: 'Version being promoted (e.g., v1.2.3, auto-detected from git tags if not provided)'
19
+ required: false
20
+ default: ''
21
+ configPath:
22
+ description: 'Path to .pipecraftrc.json config file'
23
+ required: false
24
+ default: '.pipecraftrc.json'
25
+ tempBranchPattern:
26
+ description: 'Pattern for temp branch name'
27
+ required: false
28
+ default: 'release/{source}-to-{target}-{version}'
29
+ token:
30
+ description: 'GitHub token for authentication'
31
+ required: false
32
+ default: \${{ github.token }}
33
+
34
+ outputs:
35
+ prNumber:
36
+ description: 'The created PR number'
37
+ value: \${{ steps.create-pr.outputs.prNumber }}
38
+ prUrl:
39
+ description: 'The created PR URL'
40
+ value: \${{ steps.create-pr.outputs.prUrl }}
41
+ tempBranch:
42
+ description: 'The temporary branch name'
43
+ value: \${{ steps.create-temp.outputs.tempBranch }}
44
+ targetBranch:
45
+ description: 'The target branch determined from config'
46
+ value: \${{ steps.read-config.outputs.targetBranch }}
47
+ autoMerge:
48
+ description: 'The autoMerge setting for the target branch'
49
+ value: \${{ steps.read-config.outputs.autoMerge }}
50
+
51
+ runs:
52
+ using: 'composite'
53
+ steps:
54
+ - name: Checkout Code
55
+ uses: actions/checkout@v4
56
+ with:
57
+ fetch-depth: 0
58
+ token: \${{ inputs.token }}
59
+
60
+ - name: Read Config and Determine Target Branch
61
+ id: read-config
62
+ shell: bash
63
+ run: |
64
+ SOURCE="\${{ inputs.sourceBranch }}"
65
+ CONFIG_PATH="\${{ inputs.configPath }}"
66
+
67
+ echo "๐Ÿ“– Reading config from \$CONFIG_PATH"
68
+
69
+ # Check if config file exists
70
+ if [ ! -f "\$CONFIG_PATH" ]; then
71
+ echo "โŒ Config file not found: \$CONFIG_PATH"
72
+ exit 1
73
+ fi
74
+
75
+ # Read branchFlow array from config
76
+ BRANCH_FLOW=\$(cat "\$CONFIG_PATH" | jq -r '.branchFlow | join(" ")')
77
+ echo "๐Ÿ” Branch flow: \$BRANCH_FLOW"
78
+
79
+ # Convert to array
80
+ IFS=' ' read -ra BRANCHES <<< "\$BRANCH_FLOW"
81
+
82
+ # Find current branch index and determine target
83
+ TARGET=""
84
+ for i in "\${!BRANCHES[@]}"; do
85
+ if [ "\${BRANCHES[\$i]}" == "\$SOURCE" ]; then
86
+ # Get next branch in flow
87
+ NEXT_INDEX=\$((i + 1))
88
+ if [ \$NEXT_INDEX -lt \${#BRANCHES[@]} ]; then
89
+ TARGET="\${BRANCHES[\$NEXT_INDEX]}"
90
+ fi
91
+ break
92
+ fi
93
+ done
94
+
95
+ if [ -z "\$TARGET" ]; then
96
+ echo "โŒ Could not determine target branch for source: \$SOURCE"
97
+ echo " Branch flow: \$BRANCH_FLOW"
98
+ exit 1
99
+ fi
100
+
101
+ echo "โœ… Target branch: \$TARGET"
102
+ echo "targetBranch=\$TARGET" >> \$GITHUB_OUTPUT
103
+
104
+ # Determine autoMerge setting for target branch
105
+ AUTO_MERGE=\$(cat "\$CONFIG_PATH" | jq -r ".autoMerge.\"\$TARGET\" // false")
106
+ echo "๐Ÿ” Auto-merge for \$TARGET: \$AUTO_MERGE"
107
+ echo "autoMerge=\$AUTO_MERGE" >> \$GITHUB_OUTPUT
108
+
109
+ - name: Get Version
110
+ id: get-version
111
+ shell: bash
112
+ run: |
113
+ VERSION_INPUT="\${{ inputs.version }}"
114
+
115
+ if [ -n "\$VERSION_INPUT" ]; then
116
+ VERSION="\$VERSION_INPUT"
117
+ echo "๐Ÿ“Œ Using provided version: \$VERSION"
118
+ else
119
+ # Auto-detect from git tags
120
+ VERSION=\$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
121
+ echo "๐Ÿท๏ธ Auto-detected version from tags: \$VERSION"
122
+ fi
123
+
124
+ echo "version=\$VERSION" >> \$GITHUB_OUTPUT
125
+
126
+ - name: Create Temporary Branch
127
+ id: create-temp
128
+ shell: bash
129
+ run: |
130
+ SOURCE="\${{ inputs.sourceBranch }}"
131
+ TARGET="\${{ steps.read-config.outputs.targetBranch }}"
132
+ VERSION="\${{ steps.get-version.outputs.version }}"
133
+ PATTERN="\${{ inputs.tempBranchPattern }}"
134
+
135
+ # Remove 'v' prefix from version for branch name
136
+ VERSION_CLEAN="\${VERSION#v}"
137
+
138
+ # Replace placeholders in pattern
139
+ TEMP_BRANCH="\$PATTERN"
140
+ TEMP_BRANCH="\${TEMP_BRANCH//\{source\}/\$SOURCE}"
141
+ TEMP_BRANCH="\${TEMP_BRANCH//\{target\}/\$TARGET}"
142
+ TEMP_BRANCH="\${TEMP_BRANCH//\{version\}/\$VERSION_CLEAN}"
143
+
144
+ echo "๐ŸŒฟ Preparing temporary branch: \$TEMP_BRANCH"
145
+ echo " From: \$SOURCE"
146
+ echo " To: \$TARGET"
147
+ echo " Version: \$VERSION"
148
+
149
+ # Ensure we're on the source branch
150
+ git checkout "\$SOURCE"
151
+
152
+ # Check if temp branch already exists locally or remotely
153
+ if git show-ref --verify --quiet refs/heads/"\$TEMP_BRANCH" || git ls-remote --heads origin "\$TEMP_BRANCH" | grep -q "\$TEMP_BRANCH"; then
154
+ echo "โš ๏ธ Temp branch \$TEMP_BRANCH already exists, using existing branch"
155
+ # Fetch and checkout existing branch
156
+ git fetch origin "\$TEMP_BRANCH" 2>/dev/null || true
157
+ git checkout "\$TEMP_BRANCH" 2>/dev/null || git checkout -b "\$TEMP_BRANCH" origin/"\$TEMP_BRANCH"
158
+ else
159
+ echo "๐Ÿ“ Creating new temp branch \$TEMP_BRANCH"
160
+ # Create temp branch from current commit
161
+ git checkout -b "\$TEMP_BRANCH"
162
+ # Push temp branch to remote
163
+ git push origin "\$TEMP_BRANCH"
164
+ fi
165
+
166
+ echo "tempBranch=\$TEMP_BRANCH" >> \$GITHUB_OUTPUT
167
+ echo "โœ… Temporary branch ready"
168
+
169
+ - name: Check for Existing PR
170
+ id: check-pr
171
+ shell: bash
172
+ env:
173
+ GH_TOKEN: \${{ inputs.token }}
174
+ run: |
175
+ TEMP_BRANCH="\${{ steps.create-temp.outputs.tempBranch }}"
176
+ TARGET="\${{ steps.read-config.outputs.targetBranch }}"
177
+
178
+ # Check if PR already exists from temp branch to target
179
+ EXISTING_PR=\$(gh pr list --head "\$TEMP_BRANCH" --base "\$TARGET" --json number --jq '.[0].number' 2>/dev/null || echo "")
180
+
181
+ if [ -n "\$EXISTING_PR" ]; then
182
+ echo "exists=true" >> \$GITHUB_OUTPUT
183
+ echo "prNumber=\$EXISTING_PR" >> \$GITHUB_OUTPUT
184
+ echo "โš ๏ธ PR already exists: #\$EXISTING_PR"
185
+
186
+ # Get PR URL
187
+ PR_URL=\$(gh pr view "\$EXISTING_PR" --json url --jq '.url')
188
+ echo "prUrl=\$PR_URL" >> \$GITHUB_OUTPUT
189
+ else
190
+ echo "exists=false" >> \$GITHUB_OUTPUT
191
+ echo "โœ… No existing PR found"
192
+ fi
193
+
194
+ - name: Create Pull Request
195
+ id: create-pr
196
+ if: steps.check-pr.outputs.exists == 'false'
197
+ shell: bash
198
+ env:
199
+ GH_TOKEN: \${{ inputs.token }}
200
+ run: |
201
+ TEMP_BRANCH="\${{ steps.create-temp.outputs.tempBranch }}"
202
+ TARGET="\${{ steps.read-config.outputs.targetBranch }}"
203
+ SOURCE="\${{ inputs.sourceBranch }}"
204
+ VERSION="\${{ steps.get-version.outputs.version }}"
205
+ AUTO_MERGE="\${{ steps.read-config.outputs.autoMerge }}"
206
+
207
+ TITLE="๐Ÿš€ Release \$VERSION to \$TARGET"
208
+
209
+ # Determine merge behavior text
210
+ if [ "\$AUTO_MERGE" == "true" ]; then
211
+ MERGE_TEXT="merge automatically"
212
+ else
213
+ MERGE_TEXT="require manual approval"
214
+ fi
215
+
216
+ # Disable error exit to capture gh pr create output even on failure
217
+ set +e
218
+
219
+ # Create the PR - gh pr create returns the PR URL on success
220
+ PR_URL=\$(gh pr create \\
221
+ --title "\$TITLE" \\
222
+ --body "Release \$VERSION from \$SOURCE to \$TARGET" \\
223
+ --head "\$TEMP_BRANCH" \\
224
+ --base "\$TARGET" 2>&1)
225
+ PR_EXIT_CODE=\$?
226
+
227
+ # Re-enable error exit
228
+ set -e
229
+
230
+ if [ \$PR_EXIT_CODE -eq 0 ] && [[ "\$PR_URL" =~ ^https://github.com/.*/pull/[0-9]+$ ]]; then
231
+ # Extract PR number from URL (e.g., https://github.com/owner/repo/pull/123)
232
+ PR_NUMBER=\$(echo "\$PR_URL" | grep -oE '[0-9]+$')
233
+ echo "prNumber=\$PR_NUMBER" >> \$GITHUB_OUTPUT
234
+ echo "prUrl=\$PR_URL" >> \$GITHUB_OUTPUT
235
+ echo "โœ… Created PR #\$PR_NUMBER"
236
+ echo "๐Ÿ”— URL: \$PR_URL"
237
+ else
238
+ echo "โŒ Failed to create PR"
239
+ echo "Error output:"
240
+ echo "\$PR_URL"
241
+ exit 1
242
+ fi
243
+
244
+ - name: Enable PR Auto-Merge with Rebase (Manual Approval Required)
245
+ if: steps.read-config.outputs.autoMerge == 'false'
246
+ shell: bash
247
+ env:
248
+ GH_TOKEN: \${{ inputs.token }}
249
+ run: |
250
+ PR_NUMBER="\${{ steps.check-pr.outputs.prNumber || steps.create-pr.outputs.prNumber }}"
251
+ TARGET="\${{ steps.read-config.outputs.targetBranch }}"
252
+
253
+ echo "๐Ÿ” Enabling auto-merge with rebase for PR #\$PR_NUMBER (requires approval)"
254
+ echo " This will fast-forward \$TARGET when approved, maintaining linear history"
255
+
256
+ # Enable auto-merge with rebase method (fast-forward, no merge commit)
257
+ gh pr merge "\$PR_NUMBER" --auto --rebase
258
+
259
+ echo "โœ… Auto-merge enabled - PR will fast-forward merge when approved"
260
+ echo "โš ๏ธ Manual approval required before merge completes"
261
+
262
+ - name: Fast-Forward Merge (Immediate Auto-Merge)
263
+ if: steps.read-config.outputs.autoMerge == 'true'
264
+ shell: bash
265
+ run: |
266
+ SOURCE="\${{ inputs.sourceBranch }}"
267
+ TARGET="\${{ steps.read-config.outputs.targetBranch }}"
268
+ VERSION="\${{ steps.get-version.outputs.version }}"
269
+
270
+ echo "๐Ÿš€ Fast-forwarding \$TARGET to \$SOURCE (maintaining linear history)"
271
+
272
+ # Get the commit SHA from source branch
273
+ COMMIT_SHA=\$(git rev-parse \$SOURCE)
274
+ echo "Source commit: \$COMMIT_SHA"
275
+
276
+ # Fetch target branch
277
+ git fetch origin \$TARGET:\$TARGET 2>/dev/null || true
278
+
279
+ # Checkout target branch
280
+ git checkout \$TARGET
281
+
282
+ # Verify we can fast-forward (no divergent commits)
283
+ if git merge-base --is-ancestor \$TARGET \$SOURCE; then
284
+ echo "โœ… Fast-forward is possible (linear history maintained)"
285
+ else
286
+ echo "โŒ Cannot fast-forward - branches have diverged"
287
+ echo "Target branch has commits not in source branch"
288
+ exit 1
289
+ fi
290
+
291
+ # Fast-forward merge (no merge commit)
292
+ git merge --ff-only \$SOURCE
293
+
294
+ # Push to remote
295
+ git push origin \$TARGET
296
+
297
+ echo "โœ… Fast-forwarded \$TARGET to \$COMMIT_SHA"
298
+ echo "๐Ÿ“ PR will be closed automatically for audit trail"
299
+
300
+ - name: Trigger Pipeline Workflow on Target Branch
301
+ if: steps.read-config.outputs.autoMerge == 'true'
302
+ shell: bash
303
+ env:
304
+ GH_TOKEN: \${{ inputs.token }}
305
+ run: |
306
+ TARGET="\${{ steps.read-config.outputs.targetBranch }}"
307
+ VERSION="\${{ steps.get-version.outputs.version }}"
308
+
309
+ echo "๐Ÿ”„ Triggering pipeline workflow on \$TARGET branch with version \$VERSION"
310
+
311
+ # Trigger the pipeline workflow on the target branch
312
+ # This is necessary because GITHUB_TOKEN pushes don't trigger workflows by default
313
+ # Pass the version so the run name is descriptive
314
+ gh workflow run pipeline.yml --ref "\$TARGET" --field version="\$VERSION"
315
+
316
+ echo "โœ… Pipeline workflow triggered on \$TARGET with version \$VERSION"
317
+
318
+ - name: Close PR and Delete Branch
319
+ if: steps.read-config.outputs.autoMerge == 'true'
320
+ shell: bash
321
+ env:
322
+ GH_TOKEN: \${{ inputs.token }}
323
+ run: |
324
+ TEMP_BRANCH="\${{ steps.create-temp.outputs.tempBranch }}"
325
+ PR_NUMBER="\${{ steps.check-pr.outputs.prNumber || steps.create-pr.outputs.prNumber }}"
326
+
327
+ echo "๐Ÿงน Cleaning up temporary branch: \$TEMP_BRANCH"
328
+
329
+ # Close the PR (it's already been fast-forward merged)
330
+ if [ -n "\$PR_NUMBER" ]; then
331
+ gh pr close "\$PR_NUMBER" --comment "Automatically merged via fast-forward. Closing PR and cleaning up release branch." || true
332
+ echo "โœ… Closed PR #\$PR_NUMBER"
333
+ fi
334
+
335
+ # Delete the temporary branch from remote
336
+ git push origin --delete "\$TEMP_BRANCH" || echo "โš ๏ธ Branch may already be deleted"
337
+
338
+ echo "โœ… Deleted temporary branch \$TEMP_BRANCH"
339
+
340
+ - name: Use Existing PR
341
+ if: steps.check-pr.outputs.exists == 'true'
342
+ shell: bash
343
+ run: |
344
+ PR_NUMBER="\${{ steps.check-pr.outputs.prNumber }}"
345
+ PR_URL="\${{ steps.check-pr.outputs.prUrl }}"
346
+
347
+ echo "โ„น๏ธ Using existing PR #\$PR_NUMBER"
348
+ echo "๐Ÿ”— URL: \$PR_URL"
349
+
350
+ # Note: Fast-forward merge happens in separate step if autoMerge is enabled
351
+ `
352
+ }
353
+
354
+ export const generate = (ctx: PinionContext) =>
355
+ Promise.resolve(ctx)
356
+ .then((ctx) => {
357
+ // Check if file exists to determine merge status
358
+ const filePath = '.github/actions/promote-branch/action.yml'
359
+ const exists = fs.existsSync(filePath)
360
+ const status = exists ? '๐Ÿ”„ Merged with existing' : '๐Ÿ“ Created new'
361
+ console.log(`${status} ${filePath}`)
362
+ return ctx
363
+ })
364
+ .then(renderTemplate(promoteBranchActionTemplate, toFile('.github/actions/promote-branch/action.yml')))