Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ on:

env:
PREK_BASE_IMG: ghcr.io/${{ github.repository_owner }}/prek
PREK_ALPINE_IMG: ghcr.io/${{ github.repository_owner }}/prek-alpine

permissions:
contents: read
packages: write # zizmor: ignore[excessive-permissions]

jobs:
docker-build:
name: Build Docker image ghcr.io/j178/prek for ${{ matrix.platform }}
name: Build Docker image (${{ matrix.image }}, ${{ matrix.platform }})
runs-on: ubuntu-latest
environment:
name: release
Expand All @@ -35,6 +36,9 @@ jobs:
platform:
- linux/amd64
- linux/arm64
image:
- minimal
- alpine
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand Down Expand Up @@ -69,7 +73,7 @@ jobs:
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
with:
images: ${{ env.PREK_BASE_IMG }}
images: ${{ matrix.image == 'alpine' && env.PREK_ALPINE_IMG || env.PREK_BASE_IMG }}
# Defining this makes sure the org.opencontainers.image.version OCI label becomes the actual release version and not the branch name
tags: |
type=raw,value=dry-run,enable=${{ inputs.plan == '' || fromJson(inputs.plan).announcement_tag_is_implicit }}
Expand All @@ -87,10 +91,11 @@ jobs:
with:
context: .
platforms: ${{ matrix.platform }}
cache-from: type=gha,scope=prek-${{ env.PLATFORM_TUPLE }}
cache-to: type=gha,mode=min,scope=prek-${{ env.PLATFORM_TUPLE }}
target: ${{ matrix.image }}
cache-from: type=gha,scope=prek-${{ matrix.image }}-${{ env.PLATFORM_TUPLE }}
cache-to: type=gha,mode=min,scope=prek-${{ matrix.image }}-${{ env.PLATFORM_TUPLE }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.PREK_BASE_IMG }},push-by-digest=true,name-canonical=true,push=${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
outputs: type=image,name=${{ matrix.image == 'alpine' && env.PREK_ALPINE_IMG || env.PREK_BASE_IMG }},push-by-digest=true,name-canonical=true,push=${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}

- name: Export digests
env:
Expand All @@ -102,13 +107,13 @@ jobs:
- name: Upload digests
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: digests-${{ env.PLATFORM_TUPLE }}
name: digests-${{ matrix.image }}-${{ env.PLATFORM_TUPLE }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1

docker-publish:
name: Publish Docker image (ghcr.io/j178/prek)
name: Publish Docker image (${{ matrix.image }})
runs-on: ubuntu-latest
environment:
name: release
Expand All @@ -120,12 +125,18 @@ jobs:
id-token: write
attestations: write
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
strategy:
fail-fast: false
matrix:
image:
- minimal
- alpine
steps:
- name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: /tmp/digests
pattern: digests-*
pattern: digests-${{ matrix.image }}-*
merge-multiple: true

- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
Expand All @@ -136,7 +147,7 @@ jobs:
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
with:
images: ${{ env.PREK_BASE_IMG }}
images: ${{ matrix.image == 'alpine' && env.PREK_ALPINE_IMG || env.PREK_BASE_IMG }}
# Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version
tags: |
type=raw,value=${{ fromJson(inputs.plan).announcement_tag }}
Expand All @@ -151,27 +162,26 @@ jobs:
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
- name: Create manifest list and push
working-directory: /tmp/digests
# The jq command expands the docker/metadata json "tags" array entry to `-t tag1 -t tag2 ...` for each tag in the array
# The printf will expand the base image with the `<PREK_BASE_IMG>@sha256:<sha256> ...` for each sha256 in the directory
# The final command becomes `docker buildx imagetools create -t tag1 -t tag2 ... <PREK_BASE_IMG>@sha256:<sha256_1> <PREK_BASE_IMG>@sha256:<sha256_2> ...`
env:
IMAGE_NAME: ${{ matrix.image == 'alpine' && env.PREK_ALPINE_IMG || env.PREK_BASE_IMG }}
run: |
# shellcheck disable=SC2046
readarray -t lines <<< "$DOCKER_METADATA_OUTPUT_ANNOTATIONS"; annotations=(); for line in "${lines[@]}"; do annotations+=(--annotation "$line"); done

docker buildx imagetools create \
"${annotations[@]}" \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf "${PREK_BASE_IMG}@sha256:%s " *)
$(printf "${IMAGE_NAME}@sha256:%s " *)

- name: Export manifest digest
id: manifest-digest
env:
IMAGE: ${{ env.PREK_BASE_IMG }}
IMAGE_NAME: ${{ matrix.image == 'alpine' && env.PREK_ALPINE_IMG || env.PREK_BASE_IMG }}
VERSION: ${{ steps.meta.outputs.version }}
run: |
digest="$(
docker buildx imagetools inspect \
"${IMAGE}:${VERSION}" \
"${IMAGE_NAME}:${VERSION}" \
--format '{{json .Manifest}}' \
| jq -r '.digest'
)"
Expand All @@ -180,5 +190,5 @@ jobs:
- name: Generate artifact attestation
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
with:
subject-name: ${{ env.PREK_BASE_IMG }}
subject-name: ${{ matrix.image == 'alpine' && env.PREK_ALPINE_IMG || env.PREK_BASE_IMG }}
subject-digest: ${{ steps.manifest-digest.outputs.digest }}
14 changes: 13 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,19 @@ RUN cp target/$(cat rust_target.txt)/dist/prek /prek
# TODO: Optimize binary size, with a version that also works when cross compiling
# RUN strip --strip-all /prek

FROM scratch
FROM alpine:3.23 AS alpine
RUN apk add --no-cache \
ca-certificates \
git \
nodejs \
npm \
python3 \
py3-pip
COPY --from=build /prek /usr/local/bin/prek
WORKDIR /io
ENTRYPOINT ["prek"]

FROM scratch AS minimal
COPY --from=build /prek /
WORKDIR /io
ENTRYPOINT ["/prek"]
21 changes: 17 additions & 4 deletions docs/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ This page documents common ways to integrate prek into CI and container workflow

## Docker

prek is published as a distroless container image at:
prek publishes two container images:

- `ghcr.io/j178/prek`
| Image | Base | Description |
| -- | -- | -- |
| `ghcr.io/j178/prek` | `scratch` | Minimal distroless image containing only the prek binary |
| `ghcr.io/j178/prek-alpine` | `alpine:3.23` | Alpine image with common hook dependencies pre-installed |

The image is based on `scratch` (no shell, no package manager). It contains the prek binary at `/prek`.
### Minimal (scratch)

The default image is based on `scratch` (no shell, no package manager). It contains the prek binary at `/prek`.

A common pattern is to copy the binary into your own image:

Expand All @@ -23,9 +28,17 @@ If you prefer, you can also run the distroless image directly:
docker run --rm ghcr.io/j178/prek:v0.3.9 --version
```

### Alpine

The Alpine image includes `git`, `nodejs`, `npm`, `python3`, and `py3-pip`, covering the most common hook runtimes.

```bash
docker run --rm ghcr.io/j178/prek-alpine:v0.3.9 --version
```

### Verifying Images

Docker images are signed with
Both images are signed with
[GitHub Attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations)
to verify they were built by official prek workflows. Verify using the
[GitHub CLI](https://cli.github.com/):
Expand Down
Loading