Repository: shssoichiro/oxipng (language: rust)

Issue:
Add docker release images
It will be good to build and push on each release an image to dockerhub.


Discussion / hints:
Any public registry will make us happy I guess.
I prefer ghcr over docker hub personally.
Hi @ilya-hontarau, sorry for the late reply. We're just trying to sort out the current release and then we can look into this.

Out of interest, may I ask what is your use case?
@andrews05 we are using precommit with an oxipng hook. a few of our team members are too lazy to download precommit, and just manually run oxipng instead. particularly, manually run oxipng via Docker.

some dude posted an oxipng Docker image a year ago but that's outdated now. https://hub.docker.com/r/videah/oxipng
Just as a test, I've published an image under my own user: https://github.com/andrews05/oxipng/pkgs/container/oxipng
If this is helpful we can look at setting up a workflow (don't rely on this one to stick around forever).

I guess I'm a little puzzled by the need to use docker to run a single binary with no dependencies. Wouldn't it be easier for your team members to just install oxipng directly?
> I guess I'm a little puzzled by the need to use docker to run a single binary with no dependencies. Wouldn't it be easier for your team members to just install oxipng directly?

Yeah, that puzzles me quite a bit too. I've also received requests to release another single-binary, no-setup Rust application as a Docker image, and, to be honest, I'm still grappling with the value proposition of Docker in such scenarios.

OxiPNG isn't really designed for deployment or orchestration like archetypical web application services. Besides, most sysadmins and DevOps professionals I've seen can already leverage artifacts like Debian packages for automating provisioning.

The most compelling use case for Docker images in these cases I've found so far is to (ab)use them as some sort of "action steps" for GitLab CI workflows. However, if you're concerned about keeping the image lightweight and go distroless, which entails removing the shell and other basic tools from the image, the image can get quite cumbersome to use with GitLab.

Maybe I haven't delved deep enough into the Docker ecosystem to grasp the usefulness of this suggestion fully, but I'm left wondering: what problems does a OxiPNG Docker image actually solve?
@AlexTMjugador 

In general people like to _not_ download stuff unless they need to. That's how I optimize screenshots in our documentation repo:

```
docker run --rm -v $PWD:/workspace author/oxping /workspace/assets/screenshot_01.png
```

CLI tools having Docker images aren't unheard of at all, this request usually doesn't come as a surprise to maintainers.
Thanks for the clarification! I also try to avoid downloading stuff I don't need, but Docker images have to be pulled (i.e., downloaded) before use anyway. A Docker image is, for this purpose, just another way of downloading the binary, but with extra overhead for the image metadata and such. It doesn't really get more portable, or require less system modifications to set up.

However, I'm thinking that a Docker image could be useful if you're looking into scaling with Kubernetes, so you can spawn OxiPNG containers to handle incoming requests as required. That integration is, frankly, something that cannot be achieved well without Docker images. What do you think about this?

Fix patch:
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..136b3eca
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+scripts
+.github
+.editorconfig
+.pre-commit-hooks.yaml
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
new file mode 100644
index 00000000..2635f59c
--- /dev/null
+++ b/.github/workflows/docker.yml
@@ -0,0 +1,92 @@
+name: docker
+on:
+  push:
+    branches:
+      - 'master'
+    tags:
+      - 'v*.*.*'
+  pull_request:
+    types:
+      - opened
+      - synchronize
+  workflow_dispatch:
+    inputs:
+      use_cache:
+        description: "Use build cache"
+        required: true
+        type: boolean
+        default: true
+
+env:
+  REGISTRY: ghcr.io
+  # ghcr.io/OWNER/REPO
+  IMAGE_NAME: ${{ github.repository }}
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+      packages: write
+      id-token: write
+      attestations: write
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+
+      # Workaround: https://github.com/docker/build-push-action/issues/461
+      - name: Setup Docker buildx
+        uses: docker/setup-buildx-action@v3
+
+      # Login against a Docker registry except on PR
+      # https://github.com/docker/login-action
+      - name: Log into registry ${{ env.REGISTRY }}
+        if: github.event_name != 'pull_request'
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      # Extract metadata (tags, labels) for Docker
+      # For some reason the title have to be set manually
+      # https://github.com/docker/metadata-action
+      - name: Extract Docker metadata
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+          labels: |
+            org.opencontainers.image.title=Oxipng
+          annotations: |
+            org.opencontainers.image.title=Oxipng
+
+      # Build and push Docker image with Buildx (don't push on PR)
+      # Cache isn't used for tags and on workflow_dispatch if specified
+      # https://github.com/docker/build-push-action
+      - name: Build and push Docker image
+        id: build-and-push
+        uses: docker/build-push-action@v6
+        with:
+          context: .
+          platforms: linux/amd64,linux/arm64
+          push: ${{ github.event_name != 'pull_request' }}
+          tags: ${{ steps.meta.outputs.tags }}
+          labels: ${{ steps.meta.outputs.labels }}
+          cache-from: type=gha
+          cache-to: type=gha,mode=max
+          no-cache: ${{ (github.event_name == 'workflow_dispatch' && !inputs.use_cache) || startsWith(github.ref, 'refs/tags/') }}
+
+      # Attest the build provenance
+      # TODO: enable push to registry when referrers API will be supported by ghcr.io
+      - name: Attest Build Provenance
+        if: github.event_name != 'pull_request'
+        uses: actions/attest-build-provenance@v1
+        with:
+          subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+          subject-digest: ${{ steps.build-and-push.outputs.digest }}
+          push-to-registry: false
diff --git a/Dockerfile b/Dockerfile
index 9eeeaa1a..636c5835 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,20 +1,45 @@
-FROM rust:alpine as base
+# syntax=docker/dockerfile:1
+FROM --platform=$BUILDPLATFORM tonistiigi/xx AS xx
 
-COPY . /src
+FROM --platform=$BUILDPLATFORM rust:1.74-alpine AS base
 
-RUN rustup update 1.74 && rustup default 1.74
-
-RUN apk update \
-    && apk add \
+RUN apk update && \
+    apk add \
         gcc \
-        g++
+        g++ \
+        clang
 
-RUN cd /src && cargo build --release
+COPY --from=xx / /
 
-FROM alpine as tool
+ARG TARGETPLATFORM
+RUN xx-info env
 
-COPY --from=base /src/target/release/oxipng /usr/local/bin
+RUN xx-apk add \
+    gcc \
+    musl-dev \
+    libdeflate
 
 WORKDIR /src
+
+COPY . .
+
+RUN --mount=type=cache,target=/root/.cargo/git/db \
+    --mount=type=cache,target=/root/.cargo/registry/cache \
+    --mount=type=cache,target=/root/.cargo/registry/index \
+    xx-cargo build --release && \
+    xx-verify /src/target/$(xx-cargo --print-target-triple)/release/oxipng && \
+    cp /src/target/$(xx-cargo --print-target-triple)/release/oxipng /src/target/oxipng
+
+FROM alpine AS tool
+
+LABEL org.opencontainers.image.title="Oxipng"
+LABEL org.opencontainers.image.description="Multithreaded PNG optimizer written in Rust"
+LABEL org.opencontainers.image.authors="Joshua Holmer <jholmer.in@gmail.com>"
+LABEL org.opencontainers.image.licenses="MIT"
+LABEL org.opencontainers.image.source="https://github.com/shssoichiro/oxipng"
+
+COPY --from=base /src/target/oxipng /usr/local/bin
+
+WORKDIR /work
 ENTRYPOINT [ "oxipng" ]
 CMD [ "--help" ]
diff --git a/README.md b/README.md
index d7a6367a..d9d1965a 100644
--- a/README.md
+++ b/README.md
@@ -133,6 +133,16 @@ trunk actions enable trunk-fmt-pre-commit
 
 [trunk]: https://docs.trunk.io
 
+## Docker
+
+A Docker image is availlable at `ghcr.io/shssoichiro/oxipng` for `linux/amd64` and `linux/arm64`.
+
+You can use it the following way:
+
+```bash
+docker run --rm -v $(pwd):/work ghcr.io/shssoichiro/oxipng -o 4 /work/file.png
+```
+
 ## Library Usage
 
 Although originally intended to be used as an executable, oxipng can also be used as a library in


Test patch: