88 - ' Dockerfile'
99 - ' scripts/**'
1010 - ' .github/workflows/release.yml'
11+ - ' package.json' # Trigger on version changes
1112 pull_request :
1213 types : [opened, synchronize, reopened]
1314 workflow_dispatch :
@@ -38,13 +39,22 @@ jobs:
3839 docker-changed : ${{ steps.changes.outputs.docker }}
3940 scripts-changed : ${{ steps.changes.outputs.scripts }}
4041 workflow-changed : ${{ steps.changes.outputs.workflow }}
42+ package-changed : ${{ steps.changes.outputs.package }}
4143 should-build : ${{ steps.should-build.outputs.result }}
44+ version : ${{ steps.version.outputs.version }}
4245
4346 steps :
4447 - uses : actions/checkout@v4
4548 with :
4649 fetch-depth : 2
4750
51+ - name : Get version from package.json
52+ id : version
53+ run : |
54+ VERSION=$(node -p "require('./package.json').version")
55+ echo "version=$VERSION" >> $GITHUB_OUTPUT
56+ echo "Detected version: $VERSION"
57+
4858 - name : Detect changes
4959 id : changes
5060 run : |
@@ -80,15 +90,25 @@ jobs:
8090 echo "workflow=false" >> $GITHUB_OUTPUT
8191 fi
8292
93+ # Check for package.json changes (version bump)
94+ if echo "$CHANGED_FILES" | grep -qE '^package\.json$'; then
95+ echo "package=true" >> $GITHUB_OUTPUT
96+ else
97+ echo "package=false" >> $GITHUB_OUTPUT
98+ fi
99+
83100 - name : Determine if build is needed
84101 id : should-build
85102 run : |
86103 if [ "${{ github.event.inputs.force_build }}" = "true" ]; then
87104 echo "result=true" >> $GITHUB_OUTPUT
105+ echo "Build triggered by: force_build input"
88106 elif [ "${{ steps.changes.outputs.docker }}" = "true" ] || [ "${{ steps.changes.outputs.scripts }}" = "true" ]; then
89107 echo "result=true" >> $GITHUB_OUTPUT
108+ echo "Build triggered by: Dockerfile or scripts changes"
90109 else
91110 echo "result=false" >> $GITHUB_OUTPUT
111+ echo "No build needed: no relevant changes detected"
92112 fi
93113
94114 # === BUILD AND TEST DOCKER IMAGE (PR) ===
@@ -173,11 +193,18 @@ jobs:
173193 docker-build-push :
174194 runs-on : ubuntu-latest
175195 needs : [detect-changes]
176- if : github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.detect-changes.outputs.should-build == 'true'
196+ # Run on push to main with changes, OR on workflow_dispatch with force_build
197+ if : |
198+ (github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.detect-changes.outputs.should-build == 'true') ||
199+ (github.event_name == 'workflow_dispatch' && github.event.inputs.force_build == 'true')
177200 permissions :
178201 contents : read
179202 packages : write
180203
204+ outputs :
205+ version : ${{ needs.detect-changes.outputs.version }}
206+ digest : ${{ steps.build.outputs.digest }}
207+
181208 steps :
182209 - name : Checkout repository
183210 uses : actions/checkout@v4
@@ -208,17 +235,20 @@ jobs:
208235 ${{ env.DOCKERHUB_IMAGE_NAME }}
209236 tags : |
210237 type=raw,value=latest
238+ type=raw,value=${{ needs.detect-changes.outputs.version }}
211239 type=sha,prefix=
212240 type=raw,value={{date 'YYYYMMDD'}}
213241
214242 - name : Build and push Docker image (amd64)
243+ id : build
215244 uses : docker/build-push-action@v5
216245 with :
217246 context : .
218247 platforms : linux/amd64
219248 push : true
220249 tags : ${{ steps.meta.outputs.tags }}
221250 labels : ${{ steps.meta.outputs.labels }}
251+ provenance : false # Prevents unknown/unknown platform in registry
222252 cache-from : type=gha
223253 cache-to : type=gha,mode=max
224254
@@ -230,11 +260,17 @@ jobs:
230260 runs-on : ubuntu-24.04-arm # Native ARM64 runner (free for public repos since Jan 2025)
231261 timeout-minutes : 120 # Safety timeout to prevent runaway builds
232262 needs : [detect-changes, docker-build-push]
233- if : github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.detect-changes.outputs.should-build == 'true'
263+ # Run on push to main with changes, OR on workflow_dispatch with force_build
264+ if : |
265+ (github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.detect-changes.outputs.should-build == 'true') ||
266+ (github.event_name == 'workflow_dispatch' && github.event.inputs.force_build == 'true')
234267 permissions :
235268 contents : read
236269 packages : write
237270
271+ outputs :
272+ digest : ${{ steps.build.outputs.digest }}
273+
238274 steps :
239275 - name : Checkout repository
240276 uses : actions/checkout@v4
@@ -267,25 +303,30 @@ jobs:
267303 suffix=-arm64
268304 tags : |
269305 type=raw,value=latest
306+ type=raw,value=${{ needs.detect-changes.outputs.version }}
270307 type=sha,prefix=
271308 type=raw,value={{date 'YYYYMMDD'}}
272309
273310 - name : Build and push Docker image (arm64)
311+ id : build
274312 uses : docker/build-push-action@v5
275313 with :
276314 context : .
277315 platforms : linux/arm64
278316 push : true
279317 tags : ${{ steps.meta.outputs.tags }}
280318 labels : ${{ steps.meta.outputs.labels }}
319+ provenance : false # Prevents unknown/unknown platform in registry
281320 cache-from : type=gha
282321 cache-to : type=gha,mode=max
283322
284323 # === CREATE MULTI-ARCH MANIFEST ===
285324 docker-manifest :
286325 runs-on : ubuntu-latest
287- needs : [docker-build-push, docker-build-push-arm64]
288- if : github.event_name == 'push' && github.ref == 'refs/heads/main'
326+ needs : [detect-changes, docker-build-push, docker-build-push-arm64]
327+ if : |
328+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
329+ (github.event_name == 'workflow_dispatch' && github.event.inputs.force_build == 'true')
289330 permissions :
290331 contents : read
291332 packages : write
@@ -307,22 +348,103 @@ jobs:
307348
308349 - name : Create and push multi-arch manifest (GHCR)
309350 run : |
351+ VERSION="${{ needs.detect-changes.outputs.version }}"
352+
310353 # Create manifest for latest tag on GHCR
311354 docker manifest create ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest \
312355 --amend ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest \
313356 --amend ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest-arm64
314357
315358 docker manifest push ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest
316359
317- echo "GHCR multi-arch manifest created and pushed successfully"
360+ # Create manifest for version tag on GHCR
361+ docker manifest create ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION} \
362+ --amend ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION} \
363+ --amend ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}-arm64
364+
365+ docker manifest push ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}
366+
367+ echo "GHCR multi-arch manifest created and pushed successfully for latest and ${VERSION}"
318368
319369 - name : Create and push multi-arch manifest (Docker Hub)
320370 run : |
371+ VERSION="${{ needs.detect-changes.outputs.version }}"
372+
321373 # Create manifest for latest tag on Docker Hub
322374 docker manifest create ${{ env.DOCKERHUB_IMAGE_NAME }}:latest \
323375 --amend ${{ env.DOCKERHUB_IMAGE_NAME }}:latest \
324376 --amend ${{ env.DOCKERHUB_IMAGE_NAME }}:latest-arm64
325377
326378 docker manifest push ${{ env.DOCKERHUB_IMAGE_NAME }}:latest
327379
328- echo "Docker Hub multi-arch manifest created and pushed successfully"
380+ # Create manifest for version tag on Docker Hub
381+ docker manifest create ${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION} \
382+ --amend ${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION} \
383+ --amend ${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}-arm64
384+
385+ docker manifest push ${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}
386+
387+ echo "Docker Hub multi-arch manifest created and pushed successfully for latest and ${VERSION}"
388+
389+ # === CREATE GITHUB RELEASE ===
390+ create-release :
391+ runs-on : ubuntu-latest
392+ needs : [detect-changes, docker-manifest]
393+ if : |
394+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
395+ (github.event_name == 'workflow_dispatch' && github.event.inputs.force_build == 'true')
396+ permissions :
397+ contents : write
398+
399+ steps :
400+ - name : Checkout repository
401+ uses : actions/checkout@v4
402+
403+ - name : Create GitHub Release
404+ env :
405+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
406+ GHCR_IMAGE : ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}
407+ DOCKERHUB_IMAGE : ${{ env.DOCKERHUB_IMAGE_NAME }}
408+ run : |
409+ VERSION="${{ needs.detect-changes.outputs.version }}"
410+ DATE=$(date +%Y-%m-%d)
411+
412+ # Create release notes file
413+ cat > /tmp/release-notes.md << ENDOFNOTES
414+ ## Docker Images
415+
416+ ### GitHub Container Registry (GHCR)
417+ - ${GHCR_IMAGE}:${VERSION} (multi-arch)
418+ - ${GHCR_IMAGE}:${VERSION}-arm64 (ARM64)
419+ - ${GHCR_IMAGE}:latest (multi-arch)
420+
421+ ### Docker Hub
422+ - ${DOCKERHUB_IMAGE}:${VERSION} (multi-arch)
423+ - ${DOCKERHUB_IMAGE}:${VERSION}-arm64 (ARM64)
424+ - ${DOCKERHUB_IMAGE}:latest (multi-arch)
425+
426+ ## Quick Start
427+
428+ Pull from GHCR:
429+ docker pull ${GHCR_IMAGE}:${VERSION}
430+
431+ Pull from Docker Hub:
432+ docker pull ${DOCKERHUB_IMAGE}:${VERSION}
433+
434+ ## Links
435+ - [GHCR Package](https://github.com/${{ github.repository }}/pkgs/container/sandbox)
436+ - [Docker Hub](https://hub.docker.com/r/${DOCKERHUB_IMAGE})
437+
438+ Released on ${DATE}
439+ ENDOFNOTES
440+
441+ # Check if release already exists
442+ if gh release view "v${VERSION}" &>/dev/null; then
443+ echo "Release v${VERSION} already exists, updating..."
444+ gh release edit "v${VERSION}" --title "v${VERSION}" --notes-file /tmp/release-notes.md
445+ else
446+ echo "Creating new release v${VERSION}..."
447+ gh release create "v${VERSION}" --title "v${VERSION}" --notes-file /tmp/release-notes.md
448+ fi
449+
450+ echo "GitHub Release v${VERSION} created/updated successfully"
0 commit comments