Docker refresh #274
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 refresh | |
| on: | |
| push: | |
| branches: | |
| - master | |
| paths: | |
| - .github/workflows/docker-refresh.yml | |
| - scripts/ci/docker/Dockerfile* | |
| - scripts/docker/build/*/Dockerfile.ci | |
| - debian/control | |
| - redhat/freeradius.spec | |
| workflow_dispatch: | |
| schedule: | |
| - cron: '0 1 * * *' | |
| concurrency: | |
| # | |
| # Only one refresh at a time. Lets in-flight runs finish rather than | |
| # having a push trigger a cancel: an interrupted refresh leaves the | |
| # registry partially updated, whereas a queued second run just | |
| # re-publishes everything cleanly after the first one finishes. | |
| # | |
| group: docker-refresh | |
| cancel-in-progress: false | |
| env: | |
| DOCKER_REGISTRY: "docker.internal.networkradius.com" | |
| jobs: | |
| # | |
| # Stage 1: rebuild the runner-host base and the dind sidecar image | |
| # from upstream. These can't run inside our own dind/cli container | |
| # because that container is built on top of them. | |
| # | |
| process-host-bases: | |
| timeout-minutes: 20 | |
| runs-on: self-hosted | |
| if: github.repository_owner == 'FreeRADIUS' || github.event_name == 'workflow_dispatch' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: | |
| - base_image: ubuntu:24.04 | |
| display: self-hosted-ubuntu24 | |
| image_name: docker.internal.networkradius.com/self-hosted-ubuntu24 | |
| extra_tags: | |
| - docker.internal.networkradius.com/self-hosted | |
| dockerfile: scripts/ci/docker/Dockerfile | |
| needs_build_deps: true | |
| # | |
| # Custom dind sidecar image used by the dind-based | |
| # workflows (docker-service.yml, docker-crossbuild.yml, multi-server-tests). | |
| # Bakes in registry-mirror -> internal cache and trusts the | |
| # internal CA, so per-job apt-installing and daemon.json | |
| # wiring goes away. | |
| # | |
| - base_image: docker:24-dind | |
| display: self-hosted-docker-dind | |
| image_name: docker.internal.networkradius.com/self-hosted-docker-dind | |
| dockerfile: scripts/ci/docker/Dockerfile.docker-dind | |
| needs_internal_ca: true | |
| name: "${{ matrix.os.display }}" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| lfs: false | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ vars.DOCKERHUB_READ_USER }} | |
| password: ${{ secrets.DOCKERHUB_READ_KEY }} | |
| - name: Fetch and label image | |
| env: | |
| DOCKER_BASE_IMAGE: ${{ matrix.os.base_image }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker pull "$DOCKER_BASE_IMAGE" && break | |
| if [ "$attempt" = 3 ]; then exit 1; fi | |
| echo "Pull attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| echo "FROM $DOCKER_BASE_IMAGE" | docker build --label preserve=true -t "$DOCKER_REGISTRY/$DOCKER_BASE_IMAGE" - | |
| - name: Stage internal CA into build context | |
| if: ${{ matrix.os.needs_internal_ca }} | |
| run: | | |
| cp /usr/local/share/ca-certificates/networkradius.com.crt \ | |
| scripts/ci/networkradius.com.crt | |
| - name: Create build dependency package | |
| if: ${{ matrix.os.needs_build_deps }} | |
| env: | |
| DOCKER_BASE_IMAGE: ${{ matrix.os.base_image }} | |
| run: | | |
| cat > build-dep-pkg.sh << EOF | |
| apt-get update | |
| export DEBIAN_FRONTEND=noninteractive | |
| apt-get install -y --no-install-recommends build-essential devscripts equivs quilt | |
| cd /work | |
| debian/rules debian/control | |
| mk-build-deps debian/control | |
| mv freeradius-build-deps_*.deb freeradius-build-deps.deb | |
| mk-build-deps scripts/ci/extra-packages.debian.control | |
| mv freeradius-build-deps_1*.deb freeradius-build-deps-extra.deb | |
| chown $(stat -c'%u:%g' .git) * | |
| EOF | |
| chmod 755 build-dep-pkg.sh | |
| docker run \ | |
| -v ${{ github.workspace }}:/work \ | |
| -w /work \ | |
| --rm \ | |
| "$DOCKER_BASE_IMAGE" \ | |
| sh -c './build-dep-pkg.sh' | |
| - name: Build main Docker image | |
| env: | |
| DOCKER_IMAGE_NAME: ${{ matrix.os.image_name }} | |
| DOCKERFILE: ${{ matrix.os.dockerfile }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker build --no-cache -f "$DOCKERFILE" -t "$DOCKER_IMAGE_NAME" --label preserve=true . && exit 0 | |
| echo "Build attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| - name: Login to internal Docker registry for push | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKER_REPO_UPDATE_USERNAME }} | |
| password: ${{ secrets.DOCKER_REPO_UPDATE_PASSWORD }} | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| - name: Push base image to local registry | |
| env: | |
| DOCKER_BASE_IMAGE: ${{ matrix.os.base_image }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker push "$DOCKER_REGISTRY/$DOCKER_BASE_IMAGE" && exit 0 | |
| echo "Push attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| - name: Push main image to local registry | |
| env: | |
| DOCKER_IMAGE_NAME: ${{ matrix.os.image_name }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker push "$DOCKER_IMAGE_NAME" && exit 0 | |
| echo "Push attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| - name: Create and push alternative tags | |
| if: ${{ matrix.os.extra_tags }} | |
| env: | |
| DOCKER_IMAGE_NAME: ${{ matrix.os.image_name }} | |
| DOCKER_TAGS: ${{ join(matrix.os.extra_tags, ' ') }} | |
| run: | | |
| for TAG in $DOCKER_TAGS; do | |
| docker image tag "$DOCKER_IMAGE_NAME" "$TAG" | |
| for attempt in 1 2 3; do | |
| docker push "$TAG" && break | |
| if [ "$attempt" = "3" ]; then exit 1; fi | |
| echo "Push of $TAG attempt $attempt failed; retrying..." | |
| sleep 10 | |
| done | |
| done | |
| # | |
| # Stage 2: build the docker-cli image, which FROMs self-hosted (built | |
| # in stage 1). Stages 3+ use this as their job container, so it has | |
| # to be fresh before they start. | |
| # | |
| process-docker-cli: | |
| needs: process-host-bases | |
| timeout-minutes: 15 | |
| runs-on: self-hosted | |
| if: github.repository_owner == 'FreeRADIUS' || github.event_name == 'workflow_dispatch' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: | |
| - parent_image: docker.internal.networkradius.com/self-hosted | |
| display: self-hosted-docker-cli | |
| image_name: docker.internal.networkradius.com/self-hosted-docker-cli | |
| dockerfile: scripts/ci/docker/Dockerfile.docker-cli | |
| name: "${{ matrix.os.display }}" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| lfs: false | |
| - name: Login to internal Docker registry (read) | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKER_REPO_USERNAME }} | |
| password: ${{ secrets.DOCKER_REPO_PASSWORD }} | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| - name: Pull parent image | |
| env: | |
| PARENT_IMAGE: ${{ matrix.os.parent_image }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker pull "$PARENT_IMAGE" && exit 0 | |
| echo "Pull attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| - name: Build derived image | |
| env: | |
| DOCKER_IMAGE_NAME: ${{ matrix.os.image_name }} | |
| DOCKERFILE: ${{ matrix.os.dockerfile }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker build --no-cache -f "$DOCKERFILE" -t "$DOCKER_IMAGE_NAME" --label preserve=true . && exit 0 | |
| echo "Build attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| - name: Login to internal Docker registry for push | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKER_REPO_UPDATE_USERNAME }} | |
| password: ${{ secrets.DOCKER_REPO_UPDATE_PASSWORD }} | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| - name: Push derived image | |
| env: | |
| DOCKER_IMAGE_NAME: ${{ matrix.os.image_name }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker push "$DOCKER_IMAGE_NAME" && exit 0 | |
| echo "Push attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| # | |
| # Stage 3: refresh the remaining CI base images and the pull-through | |
| # cache mirrors. Runs inside dind + the freshly-built docker-cli | |
| # container. | |
| # | |
| process-ci-images: | |
| needs: process-docker-cli | |
| timeout-minutes: 20 | |
| runs-on: self-hosted | |
| if: github.repository_owner == 'FreeRADIUS' || github.event_name == 'workflow_dispatch' | |
| services: | |
| dind: | |
| image: docker.internal.networkradius.com/self-hosted-docker-dind | |
| options: --privileged | |
| env: | |
| DOCKER_TLS_CERTDIR: "" | |
| NO_PROXY: "*.networkradius.com,127.0.0.1" | |
| volumes: | |
| - ${{ github.workspace }}:/workspace | |
| container: | |
| image: docker.internal.networkradius.com/self-hosted-docker-cli | |
| env: | |
| DOCKER_HOST: tcp://dind:2375 | |
| NO_PROXY: dind,*.networkradius.com,127.0.0.1 | |
| volumes: | |
| - /usr/local/share/ca-certificates/networkradius.com.crt:/usr/local/share/ca-certificates/networkradius.com.crt:ro | |
| - ${{ github.workspace }}:/workspace | |
| defaults: | |
| run: | |
| shell: bash | |
| working-directory: /workspace | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: | |
| # | |
| # Slim build bases for ci-rpm.yml / ci-deb.yml. Generated from | |
| # scripts/docker/m4/ci.{rpm,deb}.m4 - regenerate with | |
| # `make dockerfile.ci` after editing the m4. The | |
| # generated Dockerfile.ci hardcodes the per-distro FROM, so | |
| # no build_args are needed here. | |
| # | |
| - base_image: rockylinux/rockylinux:9 | |
| display: self-hosted-ci-rocky9 | |
| image_name: docker.internal.networkradius.com/self-hosted-ci-rocky9 | |
| dockerfile: scripts/docker/build/rocky9/Dockerfile.ci | |
| - base_image: rockylinux/rockylinux:10 | |
| display: self-hosted-ci-rocky10 | |
| image_name: docker.internal.networkradius.com/self-hosted-ci-rocky10 | |
| dockerfile: scripts/docker/build/rocky10/Dockerfile.ci | |
| - base_image: debian:bookworm | |
| display: self-hosted-ci-debian12 | |
| image_name: docker.internal.networkradius.com/self-hosted-ci-debian12 | |
| dockerfile: scripts/docker/build/debian12/Dockerfile.ci | |
| - base_image: debian:trixie | |
| display: self-hosted-ci-debian13 | |
| image_name: docker.internal.networkradius.com/self-hosted-ci-debian13 | |
| dockerfile: scripts/docker/build/debian13/Dockerfile.ci | |
| - base_image: debian:sid | |
| display: self-hosted-ci-debiansid | |
| image_name: docker.internal.networkradius.com/self-hosted-ci-debiansid | |
| dockerfile: scripts/docker/build/debiansid/Dockerfile.ci | |
| - base_image: ubuntu:22.04 | |
| display: self-hosted-ci-ubuntu22 | |
| image_name: docker.internal.networkradius.com/self-hosted-ci-ubuntu22 | |
| dockerfile: scripts/docker/build/ubuntu22/Dockerfile.ci | |
| - base_image: ubuntu:24.04 | |
| display: self-hosted-ci-ubuntu24 | |
| image_name: docker.internal.networkradius.com/self-hosted-ci-ubuntu24 | |
| dockerfile: scripts/docker/build/ubuntu24/Dockerfile.ci | |
| - base_image: ubuntu:26.04 | |
| display: self-hosted-ci-ubuntu26 | |
| image_name: docker.internal.networkradius.com/self-hosted-ci-ubuntu26 | |
| dockerfile: scripts/docker/build/ubuntu26/Dockerfile.ci | |
| - base_image: mariadb | |
| display: mariadb | |
| - base_image: postgres | |
| display: postgres | |
| - base_image: 4teamwork/389ds | |
| display: 4teamwork/389ds | |
| name: "${{ matrix.os.display }}" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| lfs: false | |
| - name: setup-dind | |
| uses: ./.github/actions/setup-dind | |
| # | |
| # Authenticate dind to Docker Hub up-front so every subsequent | |
| # `docker pull` (and the implicit FROM-pulls triggered by | |
| # `docker build`) uses the higher per-user rate limit instead | |
| # of anonymous 100/6h. Skipped on parent_image-only entries | |
| # since they pull from the internal registry instead. | |
| # | |
| - name: Login to Docker Hub (via dind) | |
| if: ${{ matrix.os.base_image }} | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ vars.DOCKERHUB_READ_USER }} | |
| password: ${{ secrets.DOCKERHUB_READ_KEY }} | |
| # | |
| # Read-only login to the internal registry. Needed up front so | |
| # parent_image entries (cli) can pull their parent before the | |
| # build step. | |
| # | |
| - name: Login to internal Docker registry (read, via dind) | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKER_REPO_USERNAME }} | |
| password: ${{ secrets.DOCKER_REPO_PASSWORD }} | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| # | |
| # Pull the source image into dind. base_image entries pull from | |
| # Docker Hub then re-tag to the internal registry namespace so | |
| # pull-through cache consumers see the same name. parent_image | |
| # entries pull from the internal registry directly. | |
| # | |
| - name: Pull source image | |
| env: | |
| DOCKER_BASE_IMAGE: ${{ matrix.os.base_image }} | |
| PARENT_IMAGE: ${{ matrix.os.parent_image }} | |
| run: | | |
| if [ -n "$PARENT_IMAGE" ]; then | |
| for attempt in 1 2 3; do | |
| docker pull "$PARENT_IMAGE" && exit 0 | |
| echo "Pull attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| fi | |
| for attempt in 1 2 3; do | |
| docker pull "$DOCKER_BASE_IMAGE" && break | |
| if [ "$attempt" = 3 ]; then exit 1; fi | |
| echo "Pull attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| echo "FROM $DOCKER_BASE_IMAGE" | docker build --label preserve=true -t "$DOCKER_REGISTRY/$DOCKER_BASE_IMAGE" - | |
| # | |
| # Generate freeradius-build-deps*.deb so the slim CI base images | |
| # can apt-install the package list without pulling the full | |
| # build-essential / devscripts toolchain at every CI run. Only | |
| # meaningful for the deb-family CI bases that build FreeRADIUS; | |
| # pull-through DB caches, dind, and cli skip this. | |
| # | |
| - name: Create build dependency package | |
| if: ${{ matrix.os.image_name && matrix.os.dockerfile && matrix.os.base_image }} | |
| env: | |
| DOCKER_BASE_IMAGE: ${{ matrix.os.base_image }} | |
| run: | | |
| cat > build-dep-pkg.sh << EOF | |
| apt-get update | |
| export DEBIAN_FRONTEND=noninteractive | |
| apt-get install -y --no-install-recommends build-essential devscripts equivs quilt | |
| cd /work | |
| debian/rules debian/control | |
| mk-build-deps debian/control | |
| mv freeradius-build-deps_*.deb freeradius-build-deps.deb | |
| mk-build-deps scripts/ci/extra-packages.debian.control | |
| mv freeradius-build-deps_1*.deb freeradius-build-deps-extra.deb | |
| chown $(stat -c'%u:%g' .git) * | |
| EOF | |
| chmod 755 build-dep-pkg.sh | |
| docker run \ | |
| -v /workspace:/work \ | |
| -w /work \ | |
| --rm \ | |
| "$DOCKER_BASE_IMAGE" \ | |
| sh -c './build-dep-pkg.sh' | |
| # | |
| # Stage the internal CA cert into the build context for any | |
| # matrix entry that asks for it (currently the dind sidecar | |
| # image, which needs to trust docker.internal.networkradius.com). | |
| # | |
| - name: Stage internal CA into build context | |
| if: ${{ matrix.os.needs_internal_ca }} | |
| run: | | |
| cp /usr/local/share/ca-certificates/networkradius.com.crt \ | |
| scripts/ci/networkradius.com.crt | |
| # | |
| # docker build's "exporting to image" phase occasionally trips a | |
| # containerd snapshot-store lock race (transient, ~ms-scale). | |
| # Three attempts with a short sleep is enough to absorb this | |
| # without masking real failures. | |
| # | |
| - name: Build main Docker image | |
| if: ${{ matrix.os.image_name && matrix.os.dockerfile }} | |
| env: | |
| DOCKER_IMAGE_NAME: ${{ matrix.os.image_name }} | |
| DOCKERFILE: ${{ matrix.os.dockerfile }} | |
| BUILD_ARGS_JSON: ${{ toJson(matrix.os.build_args) }} | |
| run: | | |
| build_args=() | |
| while IFS=$'\t' read -r key value; do | |
| [ -n "$key" ] && build_args+=(--build-arg "${key}=${value}") | |
| done < <(printf '%s' "$BUILD_ARGS_JSON" | jq -r 'to_entries[]? | [.key, .value] | @tsv') | |
| for attempt in 1 2 3; do | |
| docker build --no-cache -f "$DOCKERFILE" -t "$DOCKER_IMAGE_NAME" --label preserve=true "${build_args[@]}" . && exit 0 | |
| echo "Build attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| # | |
| # Switch to push-capable creds and publish. | |
| # | |
| - name: Login to internal Docker registry for push (via dind) | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKER_REPO_UPDATE_USERNAME }} | |
| password: ${{ secrets.DOCKER_REPO_UPDATE_PASSWORD }} | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| - name: Push base image to local registry | |
| if: ${{ matrix.os.base_image }} | |
| env: | |
| DOCKER_BASE_IMAGE: ${{ matrix.os.base_image }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker push "$DOCKER_REGISTRY/$DOCKER_BASE_IMAGE" && exit 0 | |
| echo "Push attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| - name: Push main image to local registry | |
| if: ${{ matrix.os.image_name && matrix.os.dockerfile }} | |
| env: | |
| DOCKER_IMAGE_NAME: ${{ matrix.os.image_name }} | |
| run: | | |
| for attempt in 1 2 3; do | |
| docker push "$DOCKER_IMAGE_NAME" && exit 0 | |
| echo "Push attempt $attempt failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| - name: Create and push alternative tags | |
| if: ${{ matrix.os.image_name && matrix.os.extra_tags }} | |
| env: | |
| DOCKER_IMAGE_NAME: ${{ matrix.os.image_name }} | |
| DOCKER_TAGS: ${{ join(matrix.os.extra_tags, ' ') }} | |
| run: | | |
| for TAG in $DOCKER_TAGS; do | |
| docker image tag "$DOCKER_IMAGE_NAME" "$TAG" | |
| for attempt in 1 2 3; do | |
| docker push "$TAG" && break | |
| if [ "$attempt" = "3" ]; then exit 1; fi | |
| echo "Push of $TAG attempt $attempt failed; retrying..." | |
| sleep 10 | |
| done | |
| done | |
| # | |
| # Periodic refresh of the FreeRADIUS base image chain in the | |
| # internal registry. Builds for each supported distro in parallel: | |
| # | |
| # crossbuild FROM upstream OS base, full toolchain + FR | |
| # build-deps | |
| # profiling-deps FROM crossbuild:latest, toolchain layer | |
| # (valgrind / kcachegrind / dbgsym / FlameGraph / | |
| # inferno) | |
| # service FROM the same upstream OS base as crossbuild | |
| # (NOT from crossbuild) - the FR debian/rpm | |
| # package build does its own apt-install of | |
| # build deps; layering on crossbuild would | |
| # bloat the runtime image with toolchain | |
| # artefacts. | |
| # | |
| # Tags pushed: <prefix>-<type>/<image>:latest for each of those | |
| # three. | |
| # | |
| # The `profiling` layer (FR compiled with callgrind-friendly CFLAGS | |
| # on top of profiling-deps:latest) is deliberately NOT published | |
| # here. It's per-commit by design - we want to profile specific | |
| # commits, not whatever master HEAD was at refresh time. Multi- | |
| # server profiling tests pull profiling-deps:latest and build the | |
| # profiling layer locally per commit. | |
| # | |
| process-freeradius-images: | |
| needs: process-docker-cli | |
| timeout-minutes: 90 | |
| runs-on: self-hosted | |
| if: github.repository_owner == 'FreeRADIUS' || github.event_name == 'workflow_dispatch' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu22, ubuntu24, ubuntu26, debian12, debian13, debiansid, rocky9, rocky10] | |
| services: | |
| dind: | |
| image: docker.internal.networkradius.com/self-hosted-docker-dind | |
| options: --privileged | |
| env: | |
| DOCKER_TLS_CERTDIR: "" | |
| NO_PROXY: "*.networkradius.com,127.0.0.1" | |
| volumes: | |
| - ${{ github.workspace }}:/workspace | |
| container: | |
| image: docker.internal.networkradius.com/self-hosted-docker-cli | |
| env: | |
| DOCKER_HOST: tcp://dind:2375 | |
| NO_PROXY: dind,*.networkradius.com,127.0.0.1 | |
| volumes: | |
| - /usr/local/share/ca-certificates/networkradius.com.crt:/usr/local/share/ca-certificates/networkradius.com.crt:ro | |
| - ${{ github.workspace }}:/workspace | |
| defaults: | |
| run: | |
| shell: bash | |
| working-directory: /workspace | |
| name: "freeradius4-{crossbuild,profiling-deps,service}-${{ matrix.os }}" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| lfs: false | |
| - name: setup-dind | |
| uses: ./.github/actions/setup-dind | |
| - name: Login to Docker Hub (via dind) | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ vars.DOCKERHUB_READ_USER }} | |
| password: ${{ secrets.DOCKERHUB_READ_KEY }} | |
| # | |
| # Read-only login for any DOCKER_REGISTRY pull-through misses | |
| # during the build (crossbuild itself FROMs upstream OS, not | |
| # the internal registry). | |
| # | |
| - name: Login to internal Docker registry (read, via dind) | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKER_REPO_USERNAME }} | |
| password: ${{ secrets.DOCKER_REPO_PASSWORD }} | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| # | |
| # Build crossbuild + profiling-deps + service. crossbuild and | |
| # service both FROM the upstream OS base; profiling-deps FROMs | |
| # crossbuild:latest which DOCKER_BUILD writes locally as part | |
| # of the crossbuild build, so the chain resolves in order | |
| # without a registry pull between layers. | |
| # | |
| - name: Build crossbuild + profiling-deps + service images | |
| env: | |
| OS: ${{ matrix.os }} | |
| run: | | |
| make dockerfile.check | |
| make docker.crossbuild.$OS | |
| make docker.profiling-deps.$OS | |
| make docker.service.$OS | |
| - name: Show docker build logs | |
| if: failure() | |
| env: | |
| OS: ${{ matrix.os }} | |
| run: | | |
| for type in crossbuild profiling-deps service; do | |
| log="build/docker/build.$OS.$type" | |
| if [ -f "$log" ]; then | |
| echo "===== $log =====" | |
| cat "$log" | |
| fi | |
| done | |
| # | |
| # Switch to push-capable creds and publish the three :latest | |
| # tags. The per-commit `profiling` layer stays local. | |
| # | |
| - name: Login to internal Docker registry for push (via dind) | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKER_REPO_UPDATE_USERNAME }} | |
| password: ${{ secrets.DOCKER_REPO_UPDATE_PASSWORD }} | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| - name: Push freeradius4-{crossbuild,profiling-deps,service}:latest | |
| env: | |
| OS: ${{ matrix.os }} | |
| REGISTRY: docker.internal.networkradius.com | |
| run: | | |
| for type in crossbuild profiling-deps service; do | |
| local_tag="freeradius4-${type}/${OS}:latest" | |
| remote_tag="${REGISTRY}/${local_tag}" | |
| docker tag "${local_tag}" "${remote_tag}" | |
| for attempt in 1 2 3; do | |
| docker push "${remote_tag}" && break | |
| echo "Push attempt $attempt for ${remote_tag} failed; retrying after backoff..." | |
| sleep 10 | |
| done | |
| done |