Skip to content
Merged
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
21 changes: 21 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,13 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

# Full history so the release-tag step can diff against the
# before-sha of the push; the repo is small enough for this to be
# cheap.
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Set up Docker buildx
uses: docker/setup-buildx-action@v4
Expand Down Expand Up @@ -164,3 +169,19 @@ jobs:
- name: Build and push manifest
run: |
./scripts/build-image.sh push-manifest

# A release tag is what downstream Dependabot configs watch. Mint one
# only for main builds that changed the image or how it is built, so
# docs/CI-only merges don't open no-op bump PRs in every downstream
# repo. An unknown before-sha (force push, newly created branch) errs
# on the side of releasing.
- name: Publish release tag
if: ${{ github.ref == 'refs/heads/main' }}
run: |
before='${{ github.event.before }}'
if git cat-file -e "$before" 2>/dev/null &&
! git diff --name-only "$before" HEAD | grep -qE '^(Dockerfile|tool-versions\.env|image/|scripts/build-image\.sh)'; then
echo "No image-affecting changes since $before; skipping release tag"
else
./scripts/build-image.sh push-release-tag
fi
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,63 @@ and dhi.io logins fail on every Dependabot PR. The same two secrets also
let Dependabot authenticate to dhi.io for base image digest updates (see
`.github/dependabot.yaml`).

# How to build this for release
# Image tags and releases

Once you push to GitHub (either on a branch or main), the GitHub workflow
will build a multi-architecture version of the image and publish it to the
GitHub Container Registry. From there it can be used as a base for other
GitHub Container Registry. From there it can be used as a base for other
images or directly in projects that require no further tools to be installed.

The registry holds three kinds of tags:

- `debian-ghc-X.Y.Z-<sha7>` — per-commit tags, published for every push on
every branch. Use these to try out a not-yet-merged image.
- `debian-ghc-X.Y.Z-build-YYYY.M.D.N` — release tags, minted automatically
from main whenever a push changed the image or how it is built
(`Dockerfile`, `tool-versions.env`, `image/`, or
`scripts/build-image.sh`). Docs- and CI-only merges don't mint one.
These are the tags downstream repositories should pin. The date is the
commit date and `N` is the workflow run number; month and day are
unpadded (`2026.8.6`, not `2026.08.06`) because Dependabot compares
version segments numerically.
- `buildcache-*` — registry-hosted layer caches, internal to CI; never pin
these.

A release tag is a digest-identical re-tag of the same commit's sha
manifest, created after the Trivy scan (so a blocking scan configuration
also blocks releases). The tag is derived from the commit date and run
number, so re-running a main workflow recreates the same tag rather than
minting a new one.

To try a candidate image downstream before merging: push your haskell-tools
branch, pin the resulting `debian-ghc-X.Y.Z-<sha7>` tag on a branch of the
downstream repository, and iterate. Once your change merges here, Dependabot
opens the release-tag bump PR in each downstream repository — discard the
sha-tag test pin rather than merging it.

# For Flipstone Developers

Update all our repositories that use this image, to the latest, when
a new image is published. This list can be found in the codex.
Repositories that use this image should pin a release tag
(`debian-ghc-X.Y.Z-build-YYYY.M.D.N`) and carry a `.github/dependabot.yaml`
so new releases arrive as bump PRs automatically:
Comment thread
Copilot marked this conversation as resolved.

```yaml
version: 2
updates:
- package-ecosystem: "docker-compose" # image: lines in compose files
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "docker" # FROM lines in Dockerfiles
directory: "/"
schedule:
interval: "weekly"
```

Dependabot keeps `tag@sha256:...` pins working too — it updates the tag and
digest together. It only proposes updates within the currently pinned GHC
version: the GHC version sits in the part of the tag Dependabot treats as an
opaque prefix, so a GHC upgrade is a deliberate, one-time manual pin edit in
each downstream repository, made alongside the code and resolver changes the
upgrade requires anyway. The list of repositories using this image can be
found in the codex.
28 changes: 27 additions & 1 deletion scripts/build-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,32 @@ case $COMMAND in
docker buildx imagetools create --tag "$TAG_ROOT" "$AMD_TAG" "$ARM_TAG"
;;

push-release-tag)
set_tag_and_arch_variables
if [ -z "$GITHUB_RUN_NUMBER" ]; then
echo "GITHUB_RUN_NUMBER must be set (this command is meant to run in CI)"
exit 1
fi
if [ "$COMMIT_SHA" = "uncommitted" ]; then
echo "Refusing to publish a release tag from a dirty tree"
exit 1
fi
# Release tags are what downstream Dependabot configs watch, so the
# version must stay within a single dependabot-core tag format class.
# A bare run number breaks at 1000 (dependabot-core#11198); leading
# with the 4-digit year avoids that for good. Month and day are
# unpadded on purpose: Dependabot compares segments numerically.
#
# The date is the commit's, not today's: a wall-clock date would let a
# re-run of an old workflow mint a tag that sorts above newer releases
# while pointing at an older image. With the commit date, a re-run
# recreates the identical tag.
COMMIT_DATE=$(TZ=UTC git show -s --format=%cd --date=format-local:%Y.%-m.%-d HEAD)
RELEASE_TAG="ghcr.io/flipstone/haskell-tools:debian-ghc-$GHC_VERSION-build-$COMMIT_DATE.$GITHUB_RUN_NUMBER"
echo "Publishing release tag $RELEASE_TAG (re-tag of $TAG_ROOT)"
docker buildx imagetools create --tag "$RELEASE_TAG" "$TAG_ROOT"
;;

scan-local-beta)
mkdir -p trivy-reports
docker compose run --rm trivy image haskell-tools-beta | tee trivy-reports/haskell-tools-beta.txt
Expand All @@ -101,6 +127,6 @@ case $COMMAND in
docker compose run --rm trivy image "$AMD_TAG" | tee trivy-reports/amd64.txt
;;
*)
echo "usage: ./scripts/build-image.sh build-local-beta|build-arch-tag|build-and-push-arch-tag|push-manifest|scan-local-beta|scan-amd64-tag"
echo "usage: ./scripts/build-image.sh build-local-beta|build-arch-tag|build-and-push-arch-tag|push-manifest|push-release-tag|scan-local-beta|scan-amd64-tag"
exit 1
esac;