Build and Push Docker Image #101
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Push Docker Image | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| bump_type: | |
| description: 'Version bump type (patch, minor, major)' | |
| required: true | |
| default: 'patch' | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| branch: | |
| description: 'Branch to tag (leave empty for default branch)' | |
| required: false | |
| default: '' | |
| permissions: | |
| contents: write | |
| packages: write | |
| jobs: | |
| tag_release: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| new_tag: ${{ steps.tag_version.outputs.next_version }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.inputs.branch }} | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Get latest SemVer tag and calculate next version | |
| id: tag_version | |
| run: | | |
| git fetch --tags | |
| LATEST_TAG=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort='v:refname' | tail -n 1) | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "No previous SemVer tag found. Starting with v0.1.0" | |
| case "${{ github.event.inputs.bump_type }}" in | |
| patch|minor) | |
| NEXT_VERSION="v0.1.0" | |
| ;; | |
| major) | |
| NEXT_VERSION="v1.0.0" | |
| ;; | |
| *) | |
| echo "Invalid bump type: ${{ github.event.inputs.bump_type }}" | |
| exit 1 | |
| ;; | |
| esac | |
| else | |
| echo "Latest tag found: $LATEST_TAG" | |
| VERSION=${LATEST_TAG#v} | |
| MAJOR=$(echo $VERSION | cut -d. -f1) | |
| MINOR=$(echo $VERSION | cut -d. -f2) | |
| PATCH=$(echo $VERSION | cut -d. -f3) | |
| case "${{ github.event.inputs.bump_type }}" in | |
| patch) | |
| PATCH=$((PATCH + 1)) | |
| ;; | |
| minor) | |
| MINOR=$((MINOR + 1)) | |
| PATCH=0 | |
| ;; | |
| major) | |
| MAJOR=$((MAJOR + 1)) | |
| MINOR=0 | |
| PATCH=0 | |
| ;; | |
| *) | |
| echo "Invalid bump type: ${{ github.event.inputs.bump_type }}" | |
| exit 1 | |
| ;; | |
| esac | |
| NEXT_VERSION="v${MAJOR}.${MINOR}.${PATCH}" | |
| fi | |
| echo "Calculated next version: $NEXT_VERSION" | |
| echo "next_version=$NEXT_VERSION" >> $GITHUB_OUTPUT | |
| - name: Create and Push Tag | |
| run: | | |
| git config --global user.name 'github-actions[bot]' | |
| git config --global user.email 'github-actions[bot]@users.noreply.github.com' | |
| NEXT_TAG="${{ steps.tag_version.outputs.next_version }}" | |
| COMMIT_SHA=$(git rev-parse HEAD) | |
| echo "Tagging commit $COMMIT_SHA with $NEXT_TAG" | |
| git tag -a "$NEXT_TAG" -m "Release $NEXT_TAG" | |
| echo "Pushing tag $NEXT_TAG to origin" | |
| git push origin "$NEXT_TAG" | |
| - name: Verify Tag Push | |
| run: | | |
| echo "Checking if tag ${{ steps.tag_version.outputs.next_version }} exists remotely..." | |
| sleep 5 | |
| git ls-remote --tags origin | grep "refs/tags/${{ steps.tag_version.outputs.next_version }}" || (echo "Tag push verification failed!" && exit 1) | |
| echo "Tag successfully pushed." | |
| # Build for AMD64 on native x64 runner | |
| build_amd64: | |
| runs-on: ubuntu-latest | |
| needs: tag_release | |
| permissions: | |
| packages: write | |
| contents: read | |
| outputs: | |
| digest: ${{ steps.build.outputs.digest }} | |
| env: | |
| REGISTRY_IMAGE: ghcr.io/${{ github.repository_owner }}/surfsense | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set lowercase image name | |
| id: image | |
| run: echo "name=${REGISTRY_IMAGE,,}" >> $GITHUB_OUTPUT | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Free up disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf /opt/ghc | |
| sudo rm -rf /usr/local/share/boost | |
| sudo rm -rf "$AGENT_TOOLSDIRECTORY" | |
| docker system prune -af | |
| - name: Build and push AMD64 image | |
| id: build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./Dockerfile.allinone | |
| push: true | |
| tags: ${{ steps.image.outputs.name }}:${{ needs.tag_release.outputs.new_tag }}-amd64 | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=amd64 | |
| cache-to: type=gha,mode=max,scope=amd64 | |
| provenance: false | |
| # Build for ARM64 on native arm64 runner (no QEMU emulation!) | |
| build_arm64: | |
| runs-on: ubuntu-24.04-arm | |
| needs: tag_release | |
| permissions: | |
| packages: write | |
| contents: read | |
| outputs: | |
| digest: ${{ steps.build.outputs.digest }} | |
| env: | |
| REGISTRY_IMAGE: ghcr.io/${{ github.repository_owner }}/surfsense | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set lowercase image name | |
| id: image | |
| run: echo "name=${REGISTRY_IMAGE,,}" >> $GITHUB_OUTPUT | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Free up disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf /opt/ghc | |
| sudo rm -rf /usr/local/share/boost | |
| sudo rm -rf "$AGENT_TOOLSDIRECTORY" || true | |
| docker system prune -af | |
| - name: Build and push ARM64 image | |
| id: build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./Dockerfile.allinone | |
| push: true | |
| tags: ${{ steps.image.outputs.name }}:${{ needs.tag_release.outputs.new_tag }}-arm64 | |
| platforms: linux/arm64 | |
| cache-from: type=gha,scope=arm64 | |
| cache-to: type=gha,mode=max,scope=arm64 | |
| provenance: false | |
| # Create multi-arch manifest combining both platform images | |
| create_manifest: | |
| runs-on: ubuntu-latest | |
| needs: [tag_release, build_amd64, build_arm64] | |
| permissions: | |
| packages: write | |
| contents: read | |
| env: | |
| REGISTRY_IMAGE: ghcr.io/${{ github.repository_owner }}/surfsense | |
| steps: | |
| - name: Set lowercase image name | |
| id: image | |
| run: echo "name=${REGISTRY_IMAGE,,}" >> $GITHUB_OUTPUT | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create and push multi-arch manifest | |
| run: | | |
| VERSION_TAG="${{ needs.tag_release.outputs.new_tag }}" | |
| IMAGE="${{ steps.image.outputs.name }}" | |
| # Create manifest for version tag | |
| docker manifest create ${IMAGE}:${VERSION_TAG} \ | |
| ${IMAGE}:${VERSION_TAG}-amd64 \ | |
| ${IMAGE}:${VERSION_TAG}-arm64 | |
| docker manifest push ${IMAGE}:${VERSION_TAG} | |
| # Create/update latest tag if on default branch | |
| if [[ "${{ github.ref }}" == "refs/heads/${{ github.event.repository.default_branch }}" ]] || [[ "${{ github.event.inputs.branch }}" == "${{ github.event.repository.default_branch }}" ]]; then | |
| docker manifest create ${IMAGE}:latest \ | |
| ${IMAGE}:${VERSION_TAG}-amd64 \ | |
| ${IMAGE}:${VERSION_TAG}-arm64 | |
| docker manifest push ${IMAGE}:latest | |
| fi | |
| - name: Clean up architecture-specific tags (optional) | |
| continue-on-error: true | |
| run: | | |
| # Note: GHCR doesn't support tag deletion via API easily | |
| # The arch-specific tags will remain but users should use the main tags | |
| echo "Multi-arch manifest created successfully!" | |
| echo "Users should pull: ${{ steps.image.outputs.name }}:${{ needs.tag_release.outputs.new_tag }}" | |
| echo "Or for latest: ${{ steps.image.outputs.name }}:latest" |