Merge pull request #868 from MODSetter/dev #5
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 Images | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - dev | |
| paths: | |
| - 'surfsense_backend/**' | |
| - 'surfsense_web/**' | |
| workflow_dispatch: | |
| inputs: | |
| branch: | |
| description: 'Branch to build from (leave empty for default branch)' | |
| required: false | |
| default: '' | |
| concurrency: | |
| group: docker-build | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| packages: write | |
| jobs: | |
| tag_release: | |
| runs-on: ubuntu-latest | |
| if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) || github.event_name == 'workflow_dispatch' | |
| 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: Read app version and calculate next Docker build version | |
| id: tag_version | |
| run: | | |
| APP_VERSION=$(grep -E '^version = ' surfsense_backend/pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| echo "App version from pyproject.toml: $APP_VERSION" | |
| if [ -z "$APP_VERSION" ]; then | |
| echo "Error: Could not read version from surfsense_backend/pyproject.toml" | |
| exit 1 | |
| fi | |
| git fetch --tags | |
| LATEST_BUILD_TAG=$(git tag --list "${APP_VERSION}.*" --sort='-v:refname' | head -n 1) | |
| if [ -z "$LATEST_BUILD_TAG" ]; then | |
| echo "No previous Docker build tag found for version ${APP_VERSION}. Starting with ${APP_VERSION}.1" | |
| NEXT_VERSION="${APP_VERSION}.1" | |
| else | |
| echo "Latest Docker build tag found: $LATEST_BUILD_TAG" | |
| BUILD_NUMBER=$(echo "$LATEST_BUILD_TAG" | rev | cut -d. -f1 | rev) | |
| NEXT_BUILD=$((BUILD_NUMBER + 1)) | |
| NEXT_VERSION="${APP_VERSION}.${NEXT_BUILD}" | |
| fi | |
| echo "Calculated next Docker 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 "Docker build $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: | |
| needs: tag_release | |
| if: always() && (needs.tag_release.result == 'success' || needs.tag_release.result == 'skipped') | |
| runs-on: ${{ matrix.os }} | |
| permissions: | |
| packages: write | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: [linux/amd64, linux/arm64] | |
| image: [backend, web] | |
| include: | |
| - platform: linux/amd64 | |
| suffix: amd64 | |
| os: ubuntu-latest | |
| - platform: linux/arm64 | |
| suffix: arm64 | |
| os: ubuntu-24.04-arm | |
| - image: backend | |
| name: surfsense-backend | |
| context: ./surfsense_backend | |
| file: ./surfsense_backend/Dockerfile | |
| - image: web | |
| name: surfsense-web | |
| context: ./surfsense_web | |
| file: ./surfsense_web/Dockerfile | |
| env: | |
| REGISTRY_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.name }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set lowercase image name | |
| id: image | |
| run: echo "name=${REGISTRY_IMAGE,,}" >> $GITHUB_OUTPUT | |
| - name: Docker meta | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ steps.image.outputs.name }} | |
| - 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 by digest ${{ matrix.name }} (${{ matrix.suffix }}) | |
| id: build | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: ${{ matrix.context }} | |
| file: ${{ matrix.file }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| tags: ${{ steps.image.outputs.name }} | |
| outputs: type=image,push-by-digest=true,name-canonical=true,push=true | |
| platforms: ${{ matrix.platform }} | |
| cache-from: type=gha,scope=${{ matrix.image }}-${{ matrix.suffix }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.image }}-${{ matrix.suffix }} | |
| provenance: false | |
| build-args: | | |
| ${{ matrix.image == 'web' && 'NEXT_PUBLIC_FASTAPI_BACKEND_URL=__NEXT_PUBLIC_FASTAPI_BACKEND_URL__' || '' }} | |
| ${{ matrix.image == 'web' && 'NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=__NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE__' || '' }} | |
| ${{ matrix.image == 'web' && 'NEXT_PUBLIC_ETL_SERVICE=__NEXT_PUBLIC_ETL_SERVICE__' || '' }} | |
| ${{ matrix.image == 'web' && 'NEXT_PUBLIC_ELECTRIC_URL=__NEXT_PUBLIC_ELECTRIC_URL__' || '' }} | |
| ${{ matrix.image == 'web' && 'NEXT_PUBLIC_ELECTRIC_AUTH_MODE=__NEXT_PUBLIC_ELECTRIC_AUTH_MODE__' || '' }} | |
| ${{ matrix.image == 'web' && 'NEXT_PUBLIC_DEPLOYMENT_MODE=__NEXT_PUBLIC_DEPLOYMENT_MODE__' || '' }} | |
| - name: Export digest | |
| run: | | |
| mkdir -p /tmp/digests | |
| digest="${{ steps.build.outputs.digest }}" | |
| touch "/tmp/digests/${digest#sha256:}" | |
| - name: Upload digest | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: digests-${{ matrix.image }}-${{ matrix.suffix }} | |
| path: /tmp/digests/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| create_manifest: | |
| runs-on: ubuntu-latest | |
| needs: [tag_release, build] | |
| if: always() && needs.build.result == 'success' | |
| permissions: | |
| packages: write | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: surfsense-backend | |
| image: backend | |
| - name: surfsense-web | |
| image: web | |
| env: | |
| REGISTRY_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.name }} | |
| steps: | |
| - name: Set lowercase image name | |
| id: image | |
| run: echo "name=${REGISTRY_IMAGE,,}" >> $GITHUB_OUTPUT | |
| - name: Download amd64 digest | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: digests-${{ matrix.image }}-amd64 | |
| path: /tmp/digests | |
| - name: Download arm64 digest | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: digests-${{ matrix.image }}-arm64 | |
| path: /tmp/digests | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Compute app version | |
| id: appver | |
| run: | | |
| VERSION_TAG="${{ needs.tag_release.outputs.new_tag }}" | |
| if [ -n "$VERSION_TAG" ]; then | |
| APP_VERSION=$(echo "$VERSION_TAG" | rev | cut -d. -f2- | rev) | |
| else | |
| APP_VERSION="" | |
| fi | |
| echo "app_version=$APP_VERSION" >> $GITHUB_OUTPUT | |
| - name: Docker meta | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ steps.image.outputs.name }} | |
| tags: | | |
| type=raw,value=${{ needs.tag_release.outputs.new_tag }},enable=${{ needs.tag_release.outputs.new_tag != '' }} | |
| type=raw,value=${{ steps.appver.outputs.app_version }},enable=${{ needs.tag_release.outputs.new_tag != '' && (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) || github.event.inputs.branch == github.event.repository.default_branch) }} | |
| type=ref,event=branch | |
| type=sha,prefix=git- | |
| flavor: | | |
| latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) || github.event.inputs.branch == github.event.repository.default_branch }} | |
| - name: Create manifest list and push | |
| working-directory: /tmp/digests | |
| run: | | |
| docker buildx imagetools create \ | |
| $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ | |
| $(printf '${{ steps.image.outputs.name }}@sha256:%s ' *) | |
| - name: Inspect image | |
| run: | | |
| docker buildx imagetools inspect ${{ steps.image.outputs.name }}:${{ steps.meta.outputs.version }} | |
| - name: Summary | |
| run: | | |
| echo "Multi-arch manifest created for ${{ matrix.name }}!" | |
| echo "Tags: $(jq -cr '.tags | join(", ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")" |