22#
33# WORKFLOW:
44# - Push to 'development' branch → Deploys to DEVELOPMENT automatically
5- # - Push to 'main' branch → Creates GitHub release with semantic version → Deploys to PRODUCTION
5+ # - Push to 'main' branch → Calculates version → Deploys to PRODUCTION → Creates GitHub release (only on success)
66#
77# VERSION CONTROL:
88# Use commit message tags to control version bumps:
@@ -21,24 +21,21 @@ name: Build and Deploy
2121on :
2222 push :
2323 branches :
24- - main # Creates release and deploys to production
24+ - main # Calculates version, deploys to production, then creates release
2525 - development # Development environment
26- release :
27- types : [published] # Production deployment trigger
2826
2927permissions :
3028 id-token : write
3129 contents : read
3230
3331jobs :
34- # Create release when pushing to main
35- create-release :
32+ # Calculate the next version number — deployment and release creation happen in subsequent jobs
33+ calculate-version :
3634 runs-on : ubuntu-latest
37- permissions :
38- contents : write
3935 if : " github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip deploy]') && !contains(github.event.head_commit.message, '[no deploy]')"
4036 outputs :
41- release-tag : ${{ steps.create_release.outputs.tag_name }}
37+ new_version : ${{ steps.new_version.outputs.new_version }}
38+ current_version : ${{ steps.version_bump.outputs.current_version }}
4239 steps :
4340 - uses : actions/checkout@v4
4441 with :
@@ -115,39 +112,9 @@ jobs:
115112 echo "New version: $NEW_VERSION"
116113 echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
117114
118- - name : Create Release
119- id : create_release
120- run : |
121- NOTES_FILE="$(mktemp)"
122-
123- cat > "$NOTES_FILE" <<'EOF'
124- ## What's Changed
125-
126- Auto-generated release from main branch.
127-
128- **Commits included:**
129- ```
130- __COMMITS_HERE__
131- ```
132-
133- **Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_bump.outputs.current_version }}...${{ steps.new_version.outputs.new_version }}
134- EOF
135-
136- # Replace placeholder with the commit message safely (no shell expansion)
137- perl -0777 -pe 's/__COMMITS_HERE__/\Q$ENV{COMMIT_MESSAGE}\E/g' -i "$NOTES_FILE"
138-
139- gh release create "${{ steps.new_version.outputs.new_version }}" \
140- --title "Release ${{ steps.new_version.outputs.new_version }}" \
141- --notes-file "$NOTES_FILE"
142-
143- echo "tag_name=${{ steps.new_version.outputs.new_version }}" >> "$GITHUB_OUTPUT"
144- env :
145- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
146- COMMIT_MESSAGE : ${{ github.event.head_commit.message }}
147-
148- # Production deployment (runs after release creation)
115+ # Production deployment (runs after version is calculated)
149116 build-and-deploy-production :
150- needs : create-release
117+ needs : calculate-version
151118 runs-on : ubuntu-latest
152119 environment : production
153120 if : " github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip deploy]') && !contains(github.event.head_commit.message, '[no deploy]')"
@@ -245,6 +212,45 @@ jobs:
245212 --port 22 \
246213 --cidr ${{ steps.ip.outputs.ipv4 }}/32
247214
215+ # Create release only after a successful production deployment
216+ create-release :
217+ needs : [calculate-version, build-and-deploy-production]
218+ runs-on : ubuntu-latest
219+ permissions :
220+ contents : write
221+ if : " github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip deploy]') && !contains(github.event.head_commit.message, '[no deploy]')"
222+ steps :
223+ - uses : actions/checkout@v4
224+ with :
225+ fetch-depth : 0
226+
227+ - name : Create Release
228+ run : |
229+ NOTES_FILE="$(mktemp)"
230+
231+ cat > "$NOTES_FILE" <<'EOF'
232+ ## What's Changed
233+
234+ Auto-generated release from main branch.
235+
236+ **Commits included:**
237+ ```
238+ __COMMITS_HERE__
239+ ```
240+
241+ **Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ needs.calculate-version.outputs.current_version }}...${{ needs.calculate-version.outputs.new_version }}
242+ EOF
243+
244+ # Replace placeholder with the commit message safely (no shell expansion)
245+ perl -0777 -pe 's/__COMMITS_HERE__/\Q$ENV{COMMIT_MESSAGE}\E/g' -i "$NOTES_FILE"
246+
247+ gh release create "${{ needs.calculate-version.outputs.new_version }}" \
248+ --title "Release ${{ needs.calculate-version.outputs.new_version }}" \
249+ --notes-file "$NOTES_FILE"
250+ env :
251+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
252+ COMMIT_MESSAGE : ${{ github.event.head_commit.message }}
253+
248254 build-and-deploy-development :
249255 runs-on : ubuntu-latest
250256 environment : development
0 commit comments