diff --git a/docker-publish/action.yml b/docker-publish/action.yml index 793fc92..0de4a96 100644 --- a/docker-publish/action.yml +++ b/docker-publish/action.yml @@ -1,9 +1,15 @@ name: Build & publish Docker image description: >- - Build a Docker image and push it to this Forgejo instance's container - registry. Reimplements docker/login-action + docker/metadata-action + - docker/build-push-action in plain shell using the docker CLI — no - marketplace actions are fetched or run. + Build a Docker image with BuildKit (buildx) and push it to this Forgejo + instance's container registry. Reimplements docker/login-action + + docker/metadata-action + docker/setup-buildx-action + docker/build-push-action + in plain shell using the docker CLI and a statically-installed buildx plugin — + no marketplace actions are fetched or run. + + BuildKit (not the legacy builder) is required: the runners share one host + Docker daemon across replicas, and the legacy builder corrupts layers under + that concurrency ("failed to get layer ... layer does not exist"). buildx + builds in an isolated buildkitd container, which is robust against it. inputs: context: @@ -61,6 +67,30 @@ runs: fi docker --version + # --- Ensure the buildx plugin exists (static binary, not a marketplace action) --- + if ! docker buildx version >/dev/null 2>&1; then + case "$(uname -m)" in + x86_64) BX_ARCH=amd64 ;; + aarch64|arm64) BX_ARCH=arm64 ;; + *) BX_ARCH="$(uname -m)" ;; + esac + mkdir -p /usr/local/lib/docker/cli-plugins + curl -fsSL --retry 3 --retry-connrefused \ + "https://github.com/docker/buildx/releases/download/v0.19.3/buildx-v0.19.3.linux-${BX_ARCH}" \ + -o /usr/local/lib/docker/cli-plugins/docker-buildx + chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx + fi + docker buildx version + + # A host-networked buildkit builder so it can reach the registry on 127.0.0.1 + # (buildkit treats loopback registries as insecure/HTTP automatically). A fixed + # name lets concurrent jobs share one builder and its cache; the inspect||create + # guard tolerates the create race between simultaneous jobs. + docker buildx inspect ci >/dev/null 2>&1 \ + || docker buildx create --name ci --driver docker-container --driver-opt network=host >/dev/null 2>&1 \ + || true + docker buildx use ci + # --- Resolve the registry host (canonical form of the block copy-pasted across repos) --- host="${INPUT_REGISTRY_HOST:-${CONTAINER_REGISTRY_HOST:-$SERVER_URL}}" host="${host#https://}"; host="${host#http://}"; host="${host%/}" @@ -91,16 +121,19 @@ runs: [ -n "$a" ] && extra+=( --build-arg "$a" ) done <<< "${INPUT_BUILD_ARGS}" - # --- Build --- - docker build "${extra[@]}" \ + # --- Log in before building so buildx can push directly (skipped on PRs) --- + if [ "${INPUT_PUSH}" = "true" ]; then + printf '%s' "${INPUT_TOKEN}" | docker login "$host" -u "$OWNER" --password-stdin + output=( --push ) + else + # Pull-request builds validate the Dockerfile without producing/pushing an image. + output=() + fi + + # --- Build (and push when requested) with BuildKit --- + docker buildx build "${extra[@]}" "${output[@]}" \ --label "org.opencontainers.image.source=${SERVER_URL}/${REPO}" \ --label "org.opencontainers.image.revision=${SHA}" \ --label "org.opencontainers.image.created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \ "${tags[@]}" \ "${INPUT_CONTEXT}" - - # --- Push (skipped on pull requests by passing push: false) --- - if [ "${INPUT_PUSH}" = "true" ]; then - printf '%s' "${INPUT_TOKEN}" | docker login "$host" -u "$OWNER" --password-stdin - docker push --all-tags "$image" - fi