Docker Image CI #163
Workflow file for this run
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: Docker Image CI | |
| env: | |
| SKIP_QEMU_SETUP: 'true' | |
| DOCKER_BUILD_RECORD_UPLOAD: false | |
| on: | |
| workflow_dispatch: | |
| pull_request: | |
| types: | |
| - closed | |
| paths-ignore: | |
| - '**/README.md' | |
| - '.github/**' | |
| - '.gitattributes' | |
| - '.gitignore' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| setup: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| VERSION: ${{ steps.setup_env_vars.outputs.VERSION }} | |
| REPO_OWNER_LOWER: ${{ steps.setup_env_vars.outputs.REPO_OWNER_LOWER }} | |
| REPO_NAME: ${{ steps.setup_env_vars.outputs.REPO_NAME }} | |
| BRANCH_NAME: ${{ steps.setup_env_vars.outputs.BRANCH_NAME }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Extract version and set environment variables | |
| id: setup_env_vars | |
| run: | | |
| VERSION=$(grep -E '^version *= *' pyproject.toml | head -n 1 | cut -d '"' -f2) | |
| REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') | |
| REPO_NAME=$(basename "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') | |
| BRANCH_NAME=$(echo "${{ github.ref_name }}" | tr '/' '-') | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| echo "REPO_OWNER_LOWER=$REPO_OWNER_LOWER" >> $GITHUB_OUTPUT | |
| echo "REPO_NAME=$REPO_NAME" >> $GITHUB_OUTPUT | |
| echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT | |
| fetch-latest-tags: | |
| if: > | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.pull_request.merged == true && | |
| startsWith(github.event.pull_request.title, 'chore(master): release ')) | |
| runs-on: ubuntu-latest | |
| outputs: | |
| PGAGENT_TAG: ${{ steps.fetch_pgagent_tag.outputs.PGAGENT_TAG }} | |
| SYS_STATS_TAG: ${{ steps.fetch_sys_stats_tag.outputs.SYS_STATS_TAG }} | |
| ZILEAN_TAG: ${{ steps.fetch_zilean_tag.outputs.ZILEAN_TAG }} | |
| DUMB_FRONTEND_TAG: ${{ steps.fetch_dumb_frontend_tag.outputs.DUMB_FRONTEND_TAG }} | |
| PLEX_DEBRID_TAG: ${{ steps.fetch_plex_debrid_tag.outputs.PLEX_DEBRID_TAG }} | |
| CLI_DEBRID_TAG: ${{ steps.fetch_cli_debrid_tag.outputs.CLI_DEBRID_TAG }} | |
| steps: | |
| - name: Fetch latest pgAgent release tag | |
| id: fetch_pgagent_tag | |
| run: | | |
| PGAGENT_TAG=$(curl -s https://api.github.com/repos/pgadmin-org/pgagent/releases/latest | jq -r .tag_name) | |
| echo "PGAGENT_TAG=$PGAGENT_TAG" >> $GITHUB_OUTPUT | |
| - name: Fetch latest system_stats release tag | |
| id: fetch_sys_stats_tag | |
| run: | | |
| SYS_STATS_TAG=$(curl -s https://api.github.com/repos/EnterpriseDB/system_stats/releases/latest | jq -r .tag_name) | |
| echo "SYS_STATS_TAG=$SYS_STATS_TAG" >> $GITHUB_OUTPUT | |
| - name: Fetch latest zilean release tag | |
| id: fetch_zilean_tag | |
| run: | | |
| ZILEAN_TAG=$(curl -s https://api.github.com/repos/iPromKnight/zilean/releases/latest | jq -r .tag_name) | |
| echo "ZILEAN_TAG=$ZILEAN_TAG" >> $GITHUB_OUTPUT | |
| - name: Fetch latest dmbdb release tag | |
| id: fetch_dumb_frontend_tag | |
| run: | | |
| DUMB_FRONTEND_TAG=$(curl -s https://api.github.com/repos/nicocapalbo/dmbdb/releases/latest | jq -r .tag_name) | |
| echo "DUMB_FRONTEND_TAG=$DUMB_FRONTEND_TAG" >> $GITHUB_OUTPUT | |
| - name: Fetch latest plex_debrid version from source | |
| id: fetch_plex_debrid_tag | |
| run: | | |
| PLEX_DEBRID_TAG=$(curl -s https://raw.githubusercontent.com/elfhosted/plex_debrid/main/ui/ui_settings.py | \ | |
| grep '^version\s*=' | \ | |
| sed -E "s/.*=\s*\[\s*'([^']+)'.*/\1/") | |
| echo "PLEX_DEBRID_TAG=$PLEX_DEBRID_TAG" >> $GITHUB_OUTPUT | |
| - name: Fetch latest cli_debrid release tag | |
| id: fetch_cli_debrid_tag | |
| run: | | |
| CLI_DEBRID_TAG=$(curl -s https://api.github.com/repos/godver3/cli_debrid/releases/latest | jq -r .tag_name) | |
| echo "CLI_DEBRID_TAG=$CLI_DEBRID_TAG" >> $GITHUB_OUTPUT | |
| build-base-arch: | |
| needs: setup | |
| strategy: | |
| matrix: { arch: [ X64, ARM64 ] } | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| PLATFORM: ${{ matrix.arch == 'X64' && 'linux/amd64' || 'linux/arm64' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build base stage (native ${{ env.PLATFORM }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: base | |
| platforms: ${{ env.PLATFORM }} | |
| cache-from: type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base,mode=max | |
| push: false | |
| build-zilean-arch: | |
| needs: [setup, fetch-latest-tags, build-base-arch] | |
| strategy: | |
| matrix: { arch: [ X64, ARM64 ] } | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| PLATFORM: ${{ matrix.arch == 'X64' && 'linux/amd64' || 'linux/arm64' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build zilean-builder (native ${{ env.PLATFORM }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: zilean-builder | |
| platforms: ${{ env.PLATFORM }} | |
| build-args: ZILEAN_TAG=${{ needs.fetch-latest-tags.outputs.ZILEAN_TAG }} | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/zilean | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/zilean,mode=max | |
| push: false | |
| build-pgadmin-arch: | |
| needs: [setup, fetch-latest-tags, build-base-arch] | |
| strategy: | |
| matrix: { arch: [ X64, ARM64 ] } | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| PLATFORM: ${{ matrix.arch == 'X64' && 'linux/amd64' || 'linux/arm64' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build pgadmin-builder (native ${{ env.PLATFORM }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: pgadmin-builder | |
| platforms: ${{ env.PLATFORM }} | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/pgadmin | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/pgadmin,mode=max | |
| push: false | |
| build-systemstats-arch: | |
| needs: [setup, fetch-latest-tags, build-base-arch] | |
| strategy: | |
| matrix: { arch: [ X64, ARM64 ] } | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| PLATFORM: ${{ matrix.arch == 'X64' && 'linux/amd64' || 'linux/arm64' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build systemstats-builder (native ${{ env.PLATFORM }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: systemstats-builder | |
| platforms: ${{ env.PLATFORM }} | |
| build-args: SYS_STATS_TAG=${{ needs.fetch-latest-tags.outputs.SYS_STATS_TAG }} | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/systemstats | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/systemstats,mode=max | |
| push: false | |
| build-dumb-frontend-arch: | |
| needs: [setup, fetch-latest-tags, build-base-arch] | |
| strategy: | |
| matrix: { arch: [ X64, ARM64 ] } | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| PLATFORM: ${{ matrix.arch == 'X64' && 'linux/amd64' || 'linux/arm64' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build dumb-frontend-builder (native ${{ env.PLATFORM }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: dumb-frontend-builder | |
| platforms: ${{ env.PLATFORM }} | |
| build-args: DUMB_FRONTEND_TAG=${{ needs.fetch-latest-tags.outputs.DUMB_FRONTEND_TAG }} | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/dumb-frontend | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/dumb-frontend,mode=max | |
| push: false | |
| build-plex-debrid-arch: | |
| needs: [setup, fetch-latest-tags, build-base-arch] | |
| strategy: | |
| matrix: { arch: [ X64, ARM64 ] } | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| PLATFORM: ${{ matrix.arch == 'X64' && 'linux/amd64' || 'linux/arm64' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build plex_debrid-builder (native ${{ env.PLATFORM }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: plex_debrid-builder | |
| platforms: ${{ env.PLATFORM }} | |
| build-args: PLEX_DEBRID_TAG=${{ needs.fetch-latest-tags.outputs.PLEX_DEBRID_TAG }} | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/plex-debrid | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/plex-debrid,mode=max | |
| push: false | |
| build-cli-debrid-arch: | |
| needs: [setup, fetch-latest-tags, build-base-arch] | |
| strategy: | |
| matrix: { arch: [ X64, ARM64 ] } | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| PLATFORM: ${{ matrix.arch == 'X64' && 'linux/amd64' || 'linux/arm64' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build cli_debrid-builder (native ${{ env.PLATFORM }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: cli_debrid-builder | |
| platforms: ${{ env.PLATFORM }} | |
| build-args: CLI_DEBRID_TAG=${{ needs.fetch-latest-tags.outputs.CLI_DEBRID_TAG }} | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/cli-debrid | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/cli-debrid,mode=max | |
| push: false | |
| build-requirements-arch: | |
| needs: [setup, fetch-latest-tags, build-base-arch] | |
| strategy: | |
| matrix: { arch: [ X64, ARM64 ] } | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| PLATFORM: ${{ matrix.arch == 'X64' && 'linux/amd64' || 'linux/arm64' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build requirements-builder (native ${{ env.PLATFORM }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: requirements-builder | |
| platforms: ${{ env.PLATFORM }} | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/requirements | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/requirements,mode=max | |
| push: false | |
| build-amd64-digest: | |
| name: Build linux/amd64 → Docker Hub (by digest) | |
| needs: [setup, fetch-latest-tags, build-base-arch, build-zilean-arch, build-pgadmin-arch, build-systemstats-arch, build-dumb-frontend-arch, build-plex-debrid-arch, build-cli-debrid-arch, build-requirements-arch] | |
| runs-on: [ self-hosted, Linux, X64 ] | |
| permissions: { contents: read, packages: write } | |
| env: | |
| REPO_NAME: ${{ needs.setup.outputs.REPO_NAME }} | |
| DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} | |
| DOCKER_PASS: ${{ secrets.DOCKER_PASSWORD }} | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| outputs: | |
| digest: ${{ steps.push.outputs.digest }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Login (Docker Hub) | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ env.DOCKER_USER }} | |
| password: ${{ env.DOCKER_PASS }} | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build & push by digest (amd64) with cache | |
| id: push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: final-stage | |
| platforms: linux/amd64 | |
| push: true | |
| outputs: type=registry,name=docker.io/${{ env.DOCKER_USER }}/${{ env.REPO_NAME }},push-by-digest=true,oci-mediatypes=true,compression=zstd,force-compression=true | |
| provenance: true | |
| sbom: true | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/final | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/zilean | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/pgadmin | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/systemstats | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/dumb-frontend | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/plex-debrid | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/cli-debrid | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/requirements | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/final,mode=max | |
| build-args: | | |
| PGAGENT_TAG=${{ needs.fetch-latest-tags.outputs.PGAGENT_TAG }} | |
| SYS_STATS_TAG=${{ needs.fetch-latest-tags.outputs.SYS_STATS_TAG }} | |
| ZILEAN_TAG=${{ needs.fetch-latest-tags.outputs.ZILEAN_TAG }} | |
| DUMB_FRONTEND_TAG=${{ needs.fetch-latest-tags.outputs.DUMB_FRONTEND_TAG }} | |
| PLEX_DEBRID_TAG=${{ needs.fetch-latest-tags.outputs.PLEX_DEBRID_TAG }} | |
| CLI_DEBRID_TAG=${{ needs.fetch-latest-tags.outputs.CLI_DEBRID_TAG }} | |
| build-arm64-digest: | |
| name: Build linux/arm64 → Docker Hub (by digest) | |
| needs: [setup, fetch-latest-tags, build-base-arch, build-zilean-arch, build-pgadmin-arch, build-systemstats-arch, build-dumb-frontend-arch, build-plex-debrid-arch, build-cli-debrid-arch, build-requirements-arch] | |
| runs-on: [ self-hosted, Linux, ARM64 ] | |
| permissions: { contents: read, packages: write } | |
| env: | |
| REPO_NAME: ${{ needs.setup.outputs.REPO_NAME }} | |
| DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} | |
| DOCKER_PASS: ${{ secrets.DOCKER_PASSWORD }} | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| outputs: | |
| digest: ${{ steps.push.outputs.digest }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Login (Docker Hub) | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ env.DOCKER_USER }} | |
| password: ${{ env.DOCKER_PASS }} | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build & push by digest (arm64) with cache | |
| id: push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| target: final-stage | |
| platforms: linux/arm64 | |
| push: true | |
| outputs: type=registry,name=docker.io/${{ env.DOCKER_USER }}/${{ env.REPO_NAME }},push-by-digest=true,oci-mediatypes=true,compression=zstd,force-compression=true | |
| provenance: true | |
| sbom: true | |
| cache-from: | | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/base | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/final | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/zilean | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/pgadmin | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/systemstats | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/dumb-frontend | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/plex-debrid | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/cli-debrid | |
| type=local,src=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/requirements | |
| cache-to: type=local,dest=/home/docker/buildx-cache/${{ github.repository }}/${{ env.BRANCH_NAME }}/final,mode=max | |
| build-args: | | |
| PGAGENT_TAG=${{ needs.fetch-latest-tags.outputs.PGAGENT_TAG }} | |
| SYS_STATS_TAG=${{ needs.fetch-latest-tags.outputs.SYS_STATS_TAG }} | |
| ZILEAN_TAG=${{ needs.fetch-latest-tags.outputs.ZILEAN_TAG }} | |
| DUMB_FRONTEND_TAG=${{ needs.fetch-latest-tags.outputs.DUMB_FRONTEND_TAG }} | |
| PLEX_DEBRID_TAG=${{ needs.fetch-latest-tags.outputs.PLEX_DEBRID_TAG }} | |
| CLI_DEBRID_TAG=${{ needs.fetch-latest-tags.outputs.CLI_DEBRID_TAG }} | |
| build-and-push: | |
| needs: [setup, fetch-latest-tags, build-amd64-digest, build-arm64-digest] | |
| runs-on: ubuntu-latest | |
| env: | |
| REPO_NAME: ${{ needs.setup.outputs.REPO_NAME }} | |
| REPO_OWNER_LOWER: ${{ needs.setup.outputs.REPO_OWNER_LOWER }} | |
| VERSION: ${{ needs.setup.outputs.VERSION }} | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} | |
| DOCKER_PASS: ${{ secrets.DOCKER_PASSWORD }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Determine Docker tags | |
| id: determine_tags | |
| run: | | |
| if [ "${{ github.ref_name }}" = "master" ]; then | |
| echo "DH_VERSION=docker.io/${{ env.DOCKER_USER }}/${{ env.REPO_NAME }}:${{ env.VERSION }}" >> $GITHUB_ENV | |
| echo "DH_LATEST=docker.io/${{ env.DOCKER_USER }}/${{ env.REPO_NAME }}:latest" >> $GITHUB_ENV | |
| echo "GH_VERSION=ghcr.io/${{ env.REPO_OWNER_LOWER }}/${{ env.REPO_NAME }}:${{ env.VERSION }}" >> $GITHUB_ENV | |
| echo "GH_LATEST=ghcr.io/${{ env.REPO_OWNER_LOWER }}/${{ env.REPO_NAME }}:latest" >> $GITHUB_ENV | |
| else | |
| echo "DH_VERSION=docker.io/${{ env.DOCKER_USER }}/${{ env.REPO_NAME }}:${{ env.BRANCH_NAME }}" >> $GITHUB_ENV | |
| echo "DH_LATEST=" >> $GITHUB_ENV | |
| echo "GH_VERSION=ghcr.io/${{ env.REPO_OWNER_LOWER }}/${{ env.REPO_NAME }}:${{ env.BRANCH_NAME }}" >> $GITHUB_ENV | |
| echo "GH_LATEST=" >> $GITHUB_ENV | |
| fi | |
| - name: Login (Docker Hub) | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ env.DOCKER_USER }} | |
| password: ${{ env.DOCKER_PASS }} | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Create multi-arch manifest on Docker Hub | |
| run: | | |
| AMD="docker.io/${{ env.DOCKER_USER }}/${{ env.REPO_NAME }}@${{ needs.build-amd64-digest.outputs.digest }}" | |
| ARM="docker.io/${{ env.DOCKER_USER }}/${{ env.REPO_NAME }}@${{ needs.build-arm64-digest.outputs.digest }}" | |
| # Version tag | |
| docker buildx imagetools create \ | |
| --tag "$DH_VERSION" \ | |
| "$AMD" "$ARM" | |
| # Optional latest on master | |
| if [ -n "$DH_LATEST" ]; then | |
| docker buildx imagetools create \ | |
| --tag "$DH_LATEST" \ | |
| "$AMD" "$ARM" | |
| fi | |
| - name: Login (GHCR) | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Mirror tags to GHCR (from Docker Hub manifest) | |
| run: | | |
| docker buildx imagetools create --tag "$GH_VERSION" "$DH_VERSION" | |
| if [ -n "$GH_LATEST" ]; then | |
| docker buildx imagetools create --tag "$GH_LATEST" "$DH_LATEST" | |
| fi | |
| - name: Select tag for IMAGE_TAG (for inspection) | |
| run: | | |
| echo "IMAGE_TAG=$DH_VERSION" >> "$GITHUB_ENV" | |
| - name: Pull image for version extraction | |
| run: | | |
| docker pull --platform=linux/amd64 "$IMAGE_TAG" | |
| docker tag "$IMAGE_TAG" temp-local-image | |
| - name: Extract Versions from Image | |
| env: | |
| IMAGE_TAG: temp-local-image | |
| run: | | |
| CONTAINER_ID=$(docker run -d --rm --entrypoint /bin/sh $IMAGE_TAG -c "sleep 60") | |
| PSQL_VERSION=$(docker exec $CONTAINER_ID psql --version | awk '{print $3}') | |
| PGADMIN_VERSION=$(docker exec $CONTAINER_ID /pgadmin/venv/bin/python -c "import importlib.metadata; print(importlib.metadata.version('pgadmin4'))" 2>/dev/null || echo "Not Installed") | |
| NODE_VERSION=$(docker exec $CONTAINER_ID node -v 2>/dev/null || echo "Not Installed") | |
| PNPM_VERSION=$(docker exec $CONTAINER_ID pnpm -v 2>/dev/null || echo "Not Installed") | |
| RCLONE_VERSION=$(docker exec $CONTAINER_ID rclone version | head -n 1 | grep -oP 'v[0-9]+\.[0-9]+\.[0-9]+' 2>/dev/null || echo "Not Installed") | |
| echo "PSQL_VERSION=$PSQL_VERSION" >> $GITHUB_ENV | |
| echo "PGADMIN_VERSION=$PGADMIN_VERSION" >> $GITHUB_ENV | |
| echo "NODE_VERSION=$NODE_VERSION" >> $GITHUB_ENV | |
| echo "PNPM_VERSION=$PNPM_VERSION" >> $GITHUB_ENV | |
| echo "RCLONE_VERSION=$RCLONE_VERSION" >> $GITHUB_ENV | |
| docker stop $CONTAINER_ID | |
| docker rmi -f temp-local-image | |
| docker image prune -f | |
| - name: Add Job Summary for Build | |
| env: | |
| DOCKER_TAGS_ALL: ${{ env.DH_VERSION }}${{ env.DH_LATEST && ', ' || '' }}${{ env.DH_LATEST }}${{ env.GH_VERSION && ', ' || '' }}${{ env.GH_VERSION }}${{ env.GH_LATEST && ', ' || '' }}${{ env.GH_LATEST }} | |
| VERSION: ${{ env.VERSION }} | |
| run: | | |
| echo "## Build Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "**Build Version:** \`${{ env.VERSION }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "**Git Branch:** \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "**Docker Tags:**" >> $GITHUB_STEP_SUMMARY | |
| echo "\`${DOCKER_TAGS_ALL}\`" >> $GITHUB_STEP_SUMMARY | |
| cleanup-cache: | |
| name: Prune local buildx cache | |
| needs: [ setup, build-amd64-digest, build-arm64-digest ] | |
| if: always() | |
| strategy: | |
| matrix: | |
| arch: [ X64, ARM64 ] | |
| runs-on: [ "self-hosted", "Linux", "${{ matrix.arch }}" ] | |
| steps: | |
| - name: Ensure tools are present | |
| shell: bash | |
| run: | | |
| if ! command -v jq >/dev/null 2>&1; then | |
| sudo apt-get update -y | |
| sudo apt-get install -y jq | |
| fi | |
| - name: Show cache size → prune → show again | |
| shell: bash | |
| env: | |
| BRANCH_NAME: ${{ needs.setup.outputs.BRANCH_NAME }} | |
| KEEP_MANIFESTS: "1" | |
| MAX_DELETE_RATIO: "0.60" | |
| DRY_RUN: "false" | |
| run: | | |
| set -Eeuo pipefail | |
| shopt -s nullglob | |
| set -x | |
| trap '{ | |
| >&2 echo "❌ Failed at line $LINENO: $BASH_COMMAND"; | |
| if [[ -n "${STAGE_DIR:-}" ]]; then | |
| >&2 echo "📂 Dump of $STAGE_DIR" | |
| ls -la "${STAGE_DIR}" || true | |
| fi | |
| }' ERR | |
| ROOT_BASE="${RUNNER_CACHE_ROOT:-/home/docker/buildx-cache}" | |
| ROOT="${ROOT_BASE}/${GITHUB_REPOSITORY}/${BRANCH_NAME}" | |
| LOCK="${ROOT_BASE}/.prune.lock" | |
| echo "🖥️ Host: $(hostname) | Label: ${{ matrix.arch }}" | |
| echo "📁 Cache root: $ROOT" | |
| [[ -d "$ROOT" ]] || { echo "No cache dir for this branch — nothing to prune."; exit 0; } | |
| echo "📊 Cache *before* prune:" | |
| du -x -h --max-depth=1 "$ROOT" | sort -hr | head -n 20 || true | |
| exec 9>"$LOCK" | |
| flock -x 9 | |
| JQ_SLICE='def clamp0(x): if x < 0 then 0 else x end; | |
| ( .manifests // [] ) as $m | |
| | ($m|length) as $L | |
| | (if $L==0 then [] else $m[(clamp0($L-($n))):$L] end) | |
| | .[]? | (.digest // empty) | sub("^sha256:";"")' | |
| for STAGE_DIR in "$ROOT"/*/ ; do | |
| [[ -f "${STAGE_DIR}index.json" ]] || continue | |
| echo "::group::⏳ Pruning $(basename "$STAGE_DIR")" | |
| BLOBS_DIR="${STAGE_DIR}blobs/sha256" | |
| if [[ ! -d "$BLOBS_DIR" ]]; then | |
| echo "ℹ️ No blobs dir — skip." | |
| echo "::endgroup::" | |
| continue | |
| fi | |
| KEEP="$(mktemp)"; REACH="$(mktemp)" | |
| if ! mapfile -t MANIFESTS < <(jq -r --argjson n "${KEEP_MANIFESTS}" "$JQ_SLICE" "${STAGE_DIR}index.json"); then | |
| echo "⚠️ jq failed parsing index.json — skip stage." | |
| rm -f "$KEEP" "$REACH" | |
| echo "::endgroup::" | |
| continue | |
| fi | |
| if (( ${#MANIFESTS[@]} == 0 )); then | |
| echo "⚠️ No manifests found — skip stage." | |
| rm -f "$KEEP" "$REACH" | |
| echo "::endgroup::" | |
| continue | |
| fi | |
| printf "%s\n" "${MANIFESTS[@]}" > "$KEEP" | |
| for M in "${MANIFESTS[@]}"; do | |
| M_BLOB="${BLOBS_DIR}/${M}" | |
| if [[ ! -f "$M_BLOB" ]]; then | |
| echo "⚠️ Missing manifest blob: $M_BLOB" | |
| continue | |
| fi | |
| jq -r '.. | .digest? | select(type=="string") | sub("^sha256:";"")' "$M_BLOB" | |
| done | sort -u > "$REACH" | |
| cat "$REACH" >> "$KEEP" | |
| sort -u -o "$KEEP" "$KEEP" | |
| grep -E '^[0-9a-f]{64}$' -x "$KEEP" > "${KEEP}.hex" || true | |
| mv -f "${KEEP}.hex" "$KEEP" | |
| if [[ ! -s "$KEEP" ]]; then | |
| echo "⚠️ Empty KEEP set — skip stage." | |
| rm -f "$KEEP" "$REACH" | |
| echo "::endgroup::" | |
| continue | |
| fi | |
| ALL_LIST="$(mktemp)" | |
| find "$BLOBS_DIR" -type f -printf '%f\n' | sort -u > "$ALL_LIST" | |
| if [[ ! -s "$ALL_LIST" ]]; then | |
| echo "ℹ️ No blobs present." | |
| rm -f "$KEEP" "$REACH" "$ALL_LIST" | |
| echo "::endgroup::" | |
| continue | |
| fi | |
| DEL_LIST="$(mktemp)" | |
| comm -23 "$ALL_LIST" "$KEEP" > "$DEL_LIST" || true | |
| TOTAL=$(wc -l < "$ALL_LIST" || echo 0) | |
| TO_DEL=$(wc -l < "$DEL_LIST" || echo 0) | |
| MAX_DELETE_RATIO="${MAX_DELETE_RATIO}" | |
| RATIO=$(awk -v a="$TO_DEL" -v b="$TOTAL" 'BEGIN{ if(b==0) print 0; else printf "%.2f", a/b }') | |
| awk -v r="$RATIO" -v m="$MAX_DELETE_RATIO" 'BEGIN{ exit !(r>m) }' && { | |
| echo "⚠️ Delete ratio $RATIO exceeds max $MAX_DELETE_RATIO — skipping to avoid wipe." | |
| rm -f "$KEEP" "$REACH" "$ALL_LIST" "$DEL_LIST" | |
| echo "::endgroup::" | |
| continue | |
| } | |
| TOTAL_BEFORE=$(du -sBK "$STAGE_DIR" | awk '{print $1}') | |
| if [[ "${DRY_RUN}" == "true" ]]; then | |
| echo "🔎 DRY-RUN: would delete $TO_DEL / $TOTAL blobs (ratio $RATIO)." | |
| else | |
| while IFS= read -r f; do | |
| [[ -n "$f" ]] || continue | |
| rm -f -- "$BLOBS_DIR/$f" 2>/dev/null || true | |
| done < "$DEL_LIST" | |
| fi | |
| rm -f "$KEEP" "$REACH" "$ALL_LIST" "$DEL_LIST" | |
| TOTAL_AFTER=$(du -sBK "$STAGE_DIR" | awk '{print $1}') | |
| echo "🧹 $(basename "$STAGE_DIR"): ${TOTAL_BEFORE} → ${TOTAL_AFTER}" | |
| echo "::endgroup::" | |
| done | |
| echo "📊 Cache *after* prune:" | |
| du -x -h --max-depth=1 "$ROOT" | sort -hr | head -n 20 || true | |
| flock -u 9 || true | |
| release: | |
| needs: [setup, build-and-push] | |
| if: github.ref_name == 'master' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_exists: ${{ steps.check_release.outputs.release_exists }} | |
| env: | |
| VERSION: ${{ needs.setup.outputs.VERSION }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Check if Release Exists | |
| id: check_release | |
| run: | | |
| if gh release view "${{ env.VERSION }}" --repo ${{ github.repository }}; then | |
| echo "Release already exists for version ${{ env.VERSION }}" | |
| echo "release_exists=true" >> $GITHUB_ENV | |
| echo "release_exists=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Release does not exist for version ${{ env.VERSION }}" | |
| echo "release_exists=false" >> $GITHUB_ENV | |
| echo "release_exists=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create Release with CHANGELOG Notes | |
| if: steps.check_release.outputs.release_exists == 'false' | |
| id: create_release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| RELEASE_NOTES=$(sed -n '/^## \[[0-9]\+\.[0-9]\+\.[0-9]\+\](/,$p' CHANGELOG.md | sed -n '1!{/^## \[/q;p}') | |
| gh release create ${{ env.VERSION }} \ | |
| --repo ${{ github.repository }} \ | |
| --title "Release ${{ env.VERSION }}" \ | |
| --notes "$RELEASE_NOTES" \ | |
| --draft=false \ | |
| --prerelease=false | |
| - name: Add Job Summary for Release | |
| run: | | |
| echo "## Release Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "**Release Version:** \`${{ env.VERSION }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "**Release Status:** ${{ steps.check_release.outputs.release_exists == 'false' && '✅ Created' || '⚠️ Skipped (Already Exists)' }}" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ steps.check_release.outputs.release_exists }}" == "false" ]; then | |
| echo "**Release Notes:**" >> $GITHUB_STEP_SUMMARY | |
| sed -n '/^## \[[0-9]\+\.[0-9]\+\.[0-9]\+\](/,$p' CHANGELOG.md | sed -n '1!{/^## \[/q;p}' >> $GITHUB_STEP_SUMMARY | |
| fi | |
| announce: | |
| needs: [release, build-and-push, setup] | |
| if: needs.release.outputs.release_exists == 'false' && github.ref_name == 'master' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Post announcement to Discord | |
| env: | |
| DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} | |
| VERSION: ${{ needs.setup.outputs.VERSION }} | |
| run: | | |
| RELEASE_NOTES=$(sed -n '/^## \[[0-9]\+\.[0-9]\+\.[0-9]\+\](/,$p' CHANGELOG.md | sed -n '1!{/^## \[/q;p}') | |
| ANNOUNCEMENT_BODY="<@&1360241608649605240> 🚀 **New Release: Version [${{ env.VERSION }}]**${RELEASE_NOTES}" | |
| ESCAPED_BODY=$(echo "$ANNOUNCEMENT_BODY" | jq -Rsa .) | |
| curl -H "Content-Type: application/json" \ | |
| -d "{\"content\": $ESCAPED_BODY, \"flags\": 4}" \ | |
| $DISCORD_WEBHOOK_URL | |
| update-pr-label: | |
| needs: release | |
| if: needs.release.outputs.release_exists == 'false' && github.ref_name == 'master' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: "Remove 'autorelease: pending' label from all merged PRs" | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| PR_NUMBERS=$(gh pr list --state merged --base master --json number,labels --jq '[.[] | select(.labels[].name == "autorelease: pending") | .number] | @sh') | |
| if [[ -n "$PR_NUMBERS" ]]; then | |
| for PR_NUMBER in $PR_NUMBERS; do | |
| PR_NUMBER=$(echo $PR_NUMBER | tr -d "'") # Remove quotes from jq output | |
| echo "Updating PR #$PR_NUMBER..." | |
| gh pr edit $PR_NUMBER --remove-label "autorelease: pending" | |
| gh pr edit $PR_NUMBER --add-label "autorelease: tagged" | |
| echo "Updated PR #$PR_NUMBER with 'autorelease: tagged'" | |
| done | |
| else | |
| echo "No merged PRs found with 'autorelease: pending' label." | |
| fi |