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,124 @@
1
+ import { PinionContext, toFile, renderTemplate } from '@featherscloud/pinion'
2
+ import fs from 'fs'
3
+
4
+ // Template for the Version Calculation GitHub Action
5
+ const versionActionTemplate = (ctx: any) => {
6
+ return `name: 'Calculate Version'
7
+ description: 'Calculate semantic version using release-it and conventional commits'
8
+ author: 'Pipecraft'
9
+
10
+ inputs:
11
+ baseRef:
12
+ description: 'Base reference for version calculation'
13
+ required: false
14
+ default: 'main'
15
+
16
+ outputs:
17
+ version:
18
+ description: 'The determined version'
19
+ value: \${{ steps.set_version.outputs.version }}
20
+
21
+ runs:
22
+ using: 'composite'
23
+ steps:
24
+ - name: Checkout Code
25
+ uses: actions/checkout@v4
26
+ with:
27
+ fetch-depth: 0
28
+
29
+ - name: Install Node.js
30
+ uses: actions/setup-node@v4
31
+ with:
32
+ node-version: 22
33
+
34
+ - name: Configure Git
35
+ shell: bash
36
+ run: |
37
+ git config --global user.name "github-actions[bot]"
38
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
39
+
40
+ - name: Get existing version tag
41
+ id: get_version_old
42
+ shell: bash
43
+ run: |
44
+ set -x # Enable debugging output
45
+ # Get the current commit hash
46
+ git rev-parse HEAD
47
+ COMMIT_HASH=$(git rev-parse HEAD)
48
+ echo "Current commit hash: $COMMIT_HASH"
49
+
50
+ # Check for tags on the current commit
51
+ TAG=$(git tag --points-at $COMMIT_HASH | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+\\$' || true)
52
+ echo "Tags found: $TAG"
53
+
54
+ if [ -z "$TAG" ]; then
55
+ echo "No existing tag found on the current commit"
56
+ else
57
+ echo "Found existing tag: $TAG"
58
+ echo "version=$TAG" >> $GITHUB_OUTPUT
59
+ fi
60
+
61
+ - name: Set up minimal package.json
62
+ shell: bash
63
+ run: |
64
+ echo '{
65
+ "name": "temporary-lib",
66
+ "version": "0.0.0-release-it"
67
+ }' > package.json
68
+
69
+ - name: Install dependencies
70
+ shell: bash
71
+ run: npm install @release-it/conventional-changelog conventional-changelog-angular
72
+
73
+ - name: Get version
74
+ if: steps.get_version_old.outputs.version == ''
75
+ id: get_version_new
76
+ shell: bash
77
+ run: |
78
+ # Get version and filter out warnings
79
+ RAW_OUTPUT=$(npx release-it --ci --release-version 2>&1 || echo "")
80
+ # Extract just the version number (last line that looks like a version)
81
+ VERSION=$(echo "$RAW_OUTPUT" | grep -E '^[0-9]+\\.[0-9]+\\.[0-9]+' | tail -1 || true)
82
+
83
+ if [[ "$RAW_OUTPUT" == *"No new version to release"* || -z "$VERSION" ]]; then
84
+ echo "No new version to release"
85
+ echo "VERSION=" >> $GITHUB_ENV
86
+ echo "version=" >> $GITHUB_OUTPUT
87
+ else
88
+ echo "New version to release: $VERSION"
89
+ echo "VERSION=v$VERSION" >> $GITHUB_ENV
90
+ echo "version=v$VERSION" >> $GITHUB_OUTPUT
91
+ fi
92
+
93
+ - name: Set version output
94
+ id: set_version
95
+ shell: bash
96
+ run: |
97
+ if [ -n "\${{ steps.get_version_old.outputs.version }}" ]; then
98
+ echo "Using old version: \${{ steps.get_version_old.outputs.version }}"
99
+ echo "version=\${{ steps.get_version_old.outputs.version }}" >> $GITHUB_OUTPUT
100
+ elif [ -n "\${{ steps.get_version_new.outputs.version }}" ]; then
101
+ echo "Using new version: \${{ steps.get_version_new.outputs.version }}"
102
+ echo "version=\${{ steps.get_version_new.outputs.version }}" >> $GITHUB_OUTPUT
103
+ else
104
+ echo "No version determined"
105
+ echo "version=" >> $GITHUB_OUTPUT
106
+ fi
107
+
108
+ - name: Output version
109
+ shell: bash
110
+ run: |
111
+ echo "Version determined: \${{ steps.set_version.outputs.version }}"`
112
+ }
113
+
114
+ export const generate = (ctx: PinionContext) =>
115
+ Promise.resolve(ctx)
116
+ .then((ctx) => {
117
+ // Check if file exists to determine merge status
118
+ const filePath = '.github/actions/calculate-version/action.yml'
119
+ const exists = fs.existsSync(filePath)
120
+ const status = exists ? '🔄 Merged with existing' : '📝 Created new'
121
+ console.log(`${status} ${filePath}`)
122
+ return ctx
123
+ })
124
+ .then(renderTemplate(versionActionTemplate, toFile('.github/actions/calculate-version/action.yml')))
@@ -0,0 +1,147 @@
1
+ import { PinionContext, toFile, renderTemplate } from '@featherscloud/pinion'
2
+ import fs from 'fs'
3
+ import dedent from 'dedent'
4
+
5
+ // Template for the Create PR GitHub Action
6
+ const createprActionTemplate = (ctx: any) => {
7
+ return dedent`name: 'Create Pull Request'
8
+ description: 'Create a pull request between source and target branches'
9
+ author: 'Pipecraft'
10
+
11
+ inputs:
12
+ sourceBranch:
13
+ description: 'Source branch for the PR'
14
+ required: true
15
+ targetBranch:
16
+ description: 'Target branch for the PR'
17
+ required: true
18
+ title:
19
+ description: 'Title for the PR'
20
+ required: true
21
+ body:
22
+ description: 'Body/description for the PR'
23
+ required: false
24
+ labels:
25
+ description: 'Comma-separated list of labels'
26
+ required: false
27
+ token:
28
+ description: 'GitHub token for authentication'
29
+ required: false
30
+ default: \${{ github.token }}
31
+
32
+ outputs:
33
+ prNumber:
34
+ description: 'The created PR number'
35
+ value: \${{ steps.create-pr.outputs.prNumber }}
36
+ prUrl:
37
+ description: 'The created PR URL'
38
+ value: \${{ steps.create-pr.outputs.prUrl }}
39
+
40
+ runs:
41
+ using: 'composite'
42
+ steps:
43
+ - name: Checkout Code
44
+ uses: actions/checkout@v4
45
+ with:
46
+ fetch-depth: 0
47
+ token: \${{ inputs.token }}
48
+
49
+ - name: Check if PR Already Exists
50
+ id: check-existing-pr
51
+ shell: bash
52
+ run: |
53
+ SOURCE="\${{ inputs.sourceBranch }}"
54
+ TARGET="\${{ inputs.targetBranch }}"
55
+
56
+ # Check if PR already exists
57
+ EXISTING_PR=$(gh pr list --head "\$SOURCE" --base "\$TARGET" --json number --jq '.[0].number' 2>/dev/null || echo "")
58
+
59
+ if [ -n "\$EXISTING_PR" ]; then
60
+ echo "exists=true" >> \$GITHUB_OUTPUT
61
+ echo "pr_number=\$EXISTING_PR" >> \$GITHUB_OUTPUT
62
+ echo "⚠️ PR already exists: #\$EXISTING_PR"
63
+ else
64
+ echo "exists=false" >> \$GITHUB_OUTPUT
65
+ echo "✅ No existing PR found"
66
+ fi
67
+
68
+ - name: Create Pull Request
69
+ if: steps.check-existing-pr.outputs.exists == 'false'
70
+ id: create-pr
71
+ shell: bash
72
+ run: |
73
+ SOURCE="\${{ inputs.sourceBranch }}"
74
+ TARGET="\${{ inputs.targetBranch }}"
75
+ TITLE="\${{ inputs.title }}"
76
+ BODY="\${{ inputs.body }}"
77
+ LABELS="\${{ inputs.labels }}"
78
+
79
+ # Prepare labels array
80
+ LABEL_ARGS=""
81
+ if [ -n "\$LABELS" ]; then
82
+ IFS=',' read -ra LABEL_ARRAY <<< "\$LABELS"
83
+ for label in "\${LABEL_ARRAY[@]}"; do
84
+ LABEL_ARGS="\$LABEL_ARGS --label \"\${label// /}\""
85
+ done
86
+ fi
87
+
88
+ # Create the PR
89
+ PR_OUTPUT=$(gh pr create \\
90
+ --title "\$TITLE" \\
91
+ --body "\$BODY" \\
92
+ --head "\$SOURCE" \\
93
+ --base "\$TARGET" \\
94
+ \$LABEL_ARGS \\
95
+ --json number,url --jq '.number,.url' 2>&1)
96
+
97
+ if echo "\$PR_OUTPUT" | grep -q '"number":'; then
98
+ PR_NUMBER=$(echo "\$PR_OUTPUT" | jq -r '.number')
99
+ PR_URL=$(echo "\$PR_OUTPUT" | jq -r '.url')
100
+ echo "prNumber=\$PR_NUMBER" >> \$GITHUB_OUTPUT
101
+ echo "prUrl=\$PR_URL" >> \$GITHUB_OUTPUT
102
+ echo "✅ Created PR #\$PR_NUMBER"
103
+ echo "🔗 URL: \$PR_URL"
104
+ else
105
+ echo "❌ Failed to create PR"
106
+ echo "Error output:"
107
+ echo "\$PR_OUTPUT"
108
+ exit 1
109
+ fi
110
+
111
+ - name: Use Existing PR
112
+ if: steps.check-existing-pr.outputs.exists == 'true'
113
+ id: use-existing-pr
114
+ shell: bash
115
+ run: |
116
+ PR_NUMBER="\${{ steps.check-existing-pr.outputs.pr_number }}"
117
+ PR_URL="https://github.com/\${{ github.repository }}/pull/\$PR_NUMBER"
118
+ echo "prNumber=\$PR_NUMBER" >> \$GITHUB_OUTPUT
119
+ echo "prUrl=\$PR_URL" >> \$GITHUB_OUTPUT
120
+ echo "✅ Using existing PR #\$PR_NUMBER"
121
+ echo "🔗 URL: \$PR_URL"
122
+
123
+ - name: Action Summary
124
+ shell: bash
125
+ run: |
126
+ if [ "\${{ steps.create-pr.outputs.prNumber }}" != "" ]; then
127
+ echo "✅ Successfully created PR #\${{ steps.create-pr.outputs.prNumber }}"
128
+ echo "🔗 URL: \${{ steps.create-pr.outputs.prUrl }}"
129
+ elif [ "\${{ steps.use-existing-pr.outputs.prNumber }}" != "" ]; then
130
+ echo "ℹ️ Using existing PR #\${{ steps.use-existing-pr.outputs.prNumber }}"
131
+ echo "🔗 URL: \${{ steps.use-existing-pr.outputs.prUrl }}"
132
+ else
133
+ echo "❌ No PR created or found"
134
+ fi`
135
+ };
136
+
137
+ export const generate = (ctx: PinionContext) =>
138
+ Promise.resolve(ctx)
139
+ .then((ctx) => {
140
+ // Check if file exists to determine merge status
141
+ const filePath = '.github/actions/create-pr/action.yml'
142
+ const exists = fs.existsSync(filePath)
143
+ const status = exists ? '🔄 Merged with existing' : '📝 Created new'
144
+ console.log(`${status} ${filePath}`)
145
+ return ctx
146
+ })
147
+ .then(renderTemplate(createprActionTemplate, toFile('.github/actions/create-pr/action.yml')))
@@ -0,0 +1,112 @@
1
+ import { PinionContext, toFile, renderTemplate } from '@featherscloud/pinion'
2
+ import fs from 'fs'
3
+ import dedent from 'dedent'
4
+ // Template for the Tag GitHub Action
5
+ const tagActionTemplate = (ctx: any) => {
6
+ return dedent`name: 'Tag Version'
7
+ description: 'Create and push a Git tag for a given version'
8
+ author: 'Pipecraft'
9
+
10
+ inputs:
11
+ version:
12
+ description: 'Version to tag'
13
+ required: true
14
+ tag_prefix:
15
+ description: 'Prefix for the tag (e.g., v)'
16
+ required: false
17
+ default: 'v'
18
+ push:
19
+ description: 'Whether to push the tag to remote'
20
+ required: false
21
+ default: 'true'
22
+
23
+ outputs:
24
+ tag_name:
25
+ description: 'The created tag name'
26
+ value: \${{ steps.tag.outputs.tag_name }}
27
+ success:
28
+ description: 'Whether the tag was created successfully'
29
+ value: \${{ steps.tag.outputs.success }}
30
+
31
+ runs:
32
+ using: 'composite'
33
+ steps:
34
+ - name: Validate Inputs
35
+ id: validate
36
+ shell: bash
37
+ run: |
38
+ if [ -z "\${{ inputs.version }}" ]; then
39
+ echo "❌ Version is required"
40
+ exit 1
41
+ fi
42
+
43
+ # Strip v prefix if present and validate version format
44
+ VERSION="\${{ inputs.version }}"
45
+ VERSION="\${VERSION#v}" # Remove v prefix
46
+
47
+ if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
48
+ echo "❌ Version must be in format [v]x.y.z (e.g., 1.0.0 or v1.0.0)"
49
+ exit 1
50
+ fi
51
+
52
+ echo "✅ Version format is valid: $VERSION"
53
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
54
+
55
+ - name: Configure Git
56
+ shell: bash
57
+ run: |
58
+ git config user.name "github-actions[bot]"
59
+ git config user.email "github-actions[bot]@users.noreply.github.com"
60
+
61
+ - name: Create Tag
62
+ id: tag
63
+ shell: bash
64
+ run: |
65
+ VERSION="\${{ steps.validate.outputs.version }}"
66
+ PREFIX="\${{ inputs.tag_prefix }}"
67
+ TAG_NAME="\${PREFIX}\${VERSION}"
68
+
69
+ # Check if tag already exists
70
+ if git tag -l | grep -q "^\${TAG_NAME}$"; then
71
+ echo "⚠️ Tag \${TAG_NAME} already exists"
72
+ echo "tag_name=\${TAG_NAME}" >> \$GITHUB_OUTPUT
73
+ echo "success=false" >> \$GITHUB_OUTPUT
74
+ exit 0
75
+ fi
76
+
77
+ # Create the tag
78
+ git tag -a "\${TAG_NAME}" -m "Release \${TAG_NAME}"
79
+ echo "✅ Created tag: \${TAG_NAME}"
80
+
81
+ echo "tag_name=\${TAG_NAME}" >> \$GITHUB_OUTPUT
82
+ echo "success=true" >> \$GITHUB_OUTPUT
83
+
84
+ - name: Push Tag
85
+ if: steps.tag.outputs.success == 'true' && inputs.push == 'true'
86
+ shell: bash
87
+ run: |
88
+ TAG_NAME="\${{ steps.tag.outputs.tag_name }}"
89
+ git push origin "\${TAG_NAME}"
90
+ echo "✅ Pushed tag \${TAG_NAME} to remote"
91
+
92
+ - name: Action Summary
93
+ shell: bash
94
+ run: |
95
+ if [ "\${{ steps.tag.outputs.success }}" == "true" ]; then
96
+ echo "✅ Successfully created and pushed tag: \${{ steps.tag.outputs.tag_name }}"
97
+ else
98
+ echo "❌ Failed to create tag: \${{ steps.tag.outputs.tag_name }}"
99
+ fi`
100
+ };
101
+
102
+ export const generate = (ctx: PinionContext) =>
103
+ Promise.resolve(ctx)
104
+ .then((ctx) => {
105
+ // Check if file exists to determine merge status
106
+ const filePath = '.github/actions/create-tag/action.yml'
107
+ const exists = fs.existsSync(filePath)
108
+ const status = exists ? '🔄 Merged with existing' : '📝 Created new'
109
+ console.log(`${status} ${filePath}`)
110
+ return ctx
111
+ })
112
+ .then(renderTemplate(tagActionTemplate, toFile('.github/actions/create-tag/action.yml')))
@@ -0,0 +1,87 @@
1
+ import { PinionContext, toFile, renderTemplate } from '@featherscloud/pinion'
2
+ import fs from 'fs'
3
+ import dedent from 'dedent'
4
+ import { DomainConfig } from '../../types/index.js'
5
+
6
+ // Template for the Changes Detection GitHub Action
7
+ const changesActionTemplate = (ctx: any) => {
8
+ const domainOutputs = Object.entries(ctx.domains).map((entry) => {
9
+ const [domainName, domainConfig] = entry as [string, DomainConfig];
10
+ return ` ${domainName}:
11
+ description: 'Whether ${domainName} domain has changes'
12
+ value: \${{ steps.merge.outputs.${domainName} }}`;
13
+ }).join('\n');
14
+
15
+ const domainFilters = Object.entries(ctx.domains).map((entry) => {
16
+ const [domainName, domainConfig] = entry as [string, DomainConfig];
17
+ return ` ${domainName}:\n - '${domainConfig.paths.join("'\\n - '")}'`;
18
+ }).join('\n');
19
+
20
+ const domainEchoes = Object.entries(ctx.domains).map((entry) => {
21
+ const [domainName, domainConfig] = entry as [string, DomainConfig];
22
+ return ` echo "${domainName}=\${{ contains(steps.filter.outputs.changes, '${domainName}') }}" >> $GITHUB_OUTPUT`
23
+ }).join('\n');
24
+
25
+ const domainResults = Object.entries(ctx.domains).map((entry) => {
26
+ const [domainName, domainConfig] = entry as [string, DomainConfig];
27
+ return ` echo " ${domainName}: \${{ contains(steps.filter.outputs.changes, '${domainName}') }}"`
28
+ }).join('\n');
29
+
30
+ return `name: 'Detect Changes'
31
+ description: 'Detect changes in different application domains based on file paths'
32
+ author: 'Pipecraft'
33
+
34
+ inputs:
35
+ baseRef:
36
+ description: 'Base reference to compare against'
37
+ required: false
38
+ default: 'main'
39
+
40
+ outputs:
41
+ ${domainOutputs}
42
+
43
+ runs:
44
+ using: 'composite'
45
+ steps:
46
+ - name: Checkout Code
47
+ uses: actions/checkout@v4
48
+ with:
49
+ fetch-depth: 0
50
+
51
+ - name: Set Base Branch
52
+ id: set-base
53
+ shell: bash
54
+ run: |
55
+ base_branch=\${{ inputs.baseRef || 'main' }}
56
+ echo "base_branch=$base_branch" >> $GITHUB_OUTPUT
57
+ echo "base_branch=$base_branch" >> $GITHUB_ENV
58
+
59
+ - name: Detect Changes
60
+ uses: dorny/paths-filter@v3
61
+ id: filter
62
+ with:
63
+ base: \${{ steps.set-base.outputs.base_branch }}
64
+ filters: |
65
+ ${domainFilters}
66
+
67
+ - name: Merge filter outputs
68
+ id: merge
69
+ shell: bash
70
+ run: |
71
+ ${domainEchoes}
72
+
73
+ echo "📋 Change Detection Results:"
74
+ ${domainResults}`
75
+ }
76
+
77
+ export const generate = (ctx: PinionContext) =>
78
+ Promise.resolve(ctx)
79
+ .then((ctx) => {
80
+ // Check if file exists to determine merge status
81
+ const filePath = '.github/actions/detect-changes/action.yml'
82
+ const exists = fs.existsSync(filePath)
83
+ const status = exists ? '🔄 Merged with existing' : '📝 Created new'
84
+ console.log(`${status} ${filePath}`)
85
+ return ctx
86
+ })
87
+ .then(renderTemplate(changesActionTemplate, toFile('.github/actions/detect-changes/action.yml')))
@@ -0,0 +1,132 @@
1
+ import { PinionContext, toFile, renderTemplate } from '@featherscloud/pinion'
2
+ import fs from 'fs'
3
+ import dedent from 'dedent'
4
+
5
+ // Template for the Branch Management GitHub Action
6
+ const branchActionTemplate = (ctx: any) => {
7
+ return dedent`name: 'Branch Management'
8
+ description: 'Manage branches (fast-forward, create, delete)'
9
+ author: 'Pipecraft'
10
+
11
+ inputs:
12
+ action:
13
+ description: 'Action to perform (fast-forward, create, delete)'
14
+ required: true
15
+ targetBranch:
16
+ description: 'Target branch for the action'
17
+ required: true
18
+ sourceBranch:
19
+ description: 'Source branch for the action'
20
+ required: false
21
+ branchName:
22
+ description: 'Name for new branch (for create action)'
23
+ required: false
24
+
25
+ outputs:
26
+ success:
27
+ description: 'Whether the action was successful'
28
+ value: \${{ steps.branch-action.outputs.success }}
29
+ message:
30
+ description: 'Result message'
31
+ value: \${{ steps.branch-action.outputs.message }}
32
+
33
+ runs:
34
+ using: 'composite'
35
+ steps:
36
+ - name: Checkout Code
37
+ uses: actions/checkout@v4
38
+ with:
39
+ fetch-depth: 0
40
+ token: \${{ inputs.token }}
41
+
42
+ - name: Validate Inputs
43
+ id: validate
44
+ shell: bash
45
+ run: |
46
+ ACTION="\${{ inputs.action }}"
47
+ TARGET="\${{ inputs.targetBranch }}"
48
+ SOURCE="\${{ inputs.sourceBranch }}"
49
+ BRANCH_NAME="\${{ inputs.branchName }}"
50
+
51
+ case "\$ACTION" in
52
+ "fast-forward")
53
+ if [ -z "\$SOURCE" ]; then
54
+ echo "❌ Source branch is required for fast-forward"
55
+ exit 1
56
+ fi
57
+ ;;
58
+ "create")
59
+ if [ -z "\$BRANCH_NAME" ]; then
60
+ echo "❌ Branch name is required for create action"
61
+ exit 1
62
+ fi
63
+ ;;
64
+ "delete")
65
+ if [ -z "\$TARGET" ]; then
66
+ echo "❌ Target branch is required for delete action"
67
+ exit 1
68
+ fi
69
+ ;;
70
+ *)
71
+ echo "❌ Invalid action: \$ACTION. Must be fast-forward, create, or delete"
72
+ exit 1
73
+ ;;
74
+ esac
75
+
76
+ echo "✅ Input validation passed"
77
+
78
+ - name: Perform Branch Action
79
+ id: branch-action
80
+ shell: bash
81
+ run: |
82
+ ACTION="\${{ inputs.action }}"
83
+ TARGET="\${{ inputs.targetBranch }}"
84
+ SOURCE="\${{ inputs.sourceBranch }}"
85
+ BRANCH_NAME="\${{ inputs.branchName }}"
86
+
87
+ case "\$ACTION" in
88
+ "fast-forward")
89
+ echo "🔄 Fast-forwarding \$TARGET to \$SOURCE"
90
+ git checkout "\$TARGET"
91
+ git merge --ff-only "\$SOURCE"
92
+ git push origin "\$TARGET"
93
+ echo "success=true" >> \$GITHUB_OUTPUT
94
+ echo "message=Successfully fast-forwarded \$TARGET to \$SOURCE" >> \$GITHUB_OUTPUT
95
+ ;;
96
+ "create")
97
+ echo "🌱 Creating branch \$BRANCH_NAME from \$TARGET"
98
+ git checkout "\$TARGET"
99
+ git checkout -b "\$BRANCH_NAME"
100
+ git push -u origin "\$BRANCH_NAME"
101
+ echo "success=true" >> \$GITHUB_OUTPUT
102
+ echo "message=Successfully created branch \$BRANCH_NAME" >> \$GITHUB_OUTPUT
103
+ ;;
104
+ "delete")
105
+ echo "🗑️ Deleting branch \$TARGET"
106
+ git push origin --delete "\$TARGET"
107
+ echo "success=true" >> \$GITHUB_OUTPUT
108
+ echo "message=Successfully deleted branch \$TARGET" >> \$GITHUB_OUTPUT
109
+ ;;
110
+ esac
111
+
112
+ - name: Action Summary
113
+ shell: bash
114
+ run: |
115
+ if [ "\${{ steps.branch-action.outputs.success }}" == "true" ]; then
116
+ echo "✅ \${{ steps.branch-action.outputs.message }}"
117
+ else
118
+ echo "❌ \${{ steps.branch-action.outputs.message }}"
119
+ fi`
120
+ };
121
+
122
+ export const generate = (ctx: PinionContext) =>
123
+ Promise.resolve(ctx)
124
+ .then((ctx) => {
125
+ // Check if file exists to determine merge status
126
+ const filePath = '.github/actions/manage-branch/action.yml'
127
+ const exists = fs.existsSync(filePath)
128
+ const status = exists ? '🔄 Merged with existing' : '📝 Created new'
129
+ console.log(`${status} ${filePath}`)
130
+ return ctx
131
+ })
132
+ .then(renderTemplate(branchActionTemplate, toFile('.github/actions/manage-branch/action.yml')))