From ef64dd07685867abe5b9c500c6a6a8e2c7c7a846 Mon Sep 17 00:00:00 2001 From: AFCMS Date: Sun, 24 Nov 2024 14:08:11 +0100 Subject: [PATCH] Improved Docker image + publish on `ghcr.io` (#642) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #610 - [x] Native cross-compilation support in Dockerfile. Only `linux/amd64` and `linux/arm64` supported and tested (Tier 1 platform support from Rust) - [x] Cache support for cargo downloads and compilation results in Dockerfile - [x] Open Container's [annotations](https://github.com/opencontainers/image-spec/blob/main/annotations.md) in Dockerfile - [x] GitHub Actions workflow to build the image for both platforms, publishing to ghcr.io on tags and master branch pushes. - [x] Disable use of GitHub Actions cache for tags build, allow manually triggering the workflow with or without cache. - [x] [Attestation artifacts](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) for builds - [x] Add basic informations about the use of the Docker image in README I also changed the WORKDIR from `/src` to `/work`, because if got me confused with the use of the src folder for the project source in the first stage of the Dockerfile. It doesn't impact anything anyways since you can mount the file where you want and have the program options point to it. **How to test?** _After checking out the branch and making sure you have QEMU installed to build/test_ ```shell # Docker sadly doesn't have yet the way to have locally a tag with multiple platforms # despite being able to pass multiple platforms to the build command, so we have to use two distinct tags. docker build --platform=linux/amd64 --tag test-oxipng-amd:latest --load . docker build --platform=linux/arm64 --tag test-oxipng-arm:latest --load . docker run --rm test-oxipng-amd:latest --version docker run --rm test-oxipng-arm:latest --version # Run on some files docker run --rm -it -v $(pwd):/work test-oxipng-amd:latest -a /work/tests/files/apng_file.png docker run --rm -it -v $(pwd):/work test-oxipng-arm:latest -a /work/tests/files/apng_file.png # Remove the images docker image rm test-oxipng-amd:latest docker image rm test-oxipng-arm:latest ``` For the workflow, see the GitHub Actions logs. If you want to test the ghcr.io publishing you can merge the branch into a fork and see the result. --------- Co-authored-by: Alejandro González --- .dockerignore | 4 ++ .github/workflows/docker.yml | 92 ++++++++++++++++++++++++++++++++++++ Dockerfile | 45 ++++++++++++++---- README.md | 10 ++++ 4 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker.yml 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 " +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