From 20323839575fd32a53cb2268db4f0d0209ddbd57 Mon Sep 17 00:00:00 2001 From: Eric Greer Date: Mon, 8 Jun 2026 20:29:39 -0700 Subject: [PATCH 1/4] chore: automate multi-arch releases Co-authored-by: Codex Signed-off-by: Eric Greer --- .github/workflows/publish-tag.yaml | 55 ++++++++++++++++++--- .github/workflows/publish.yaml | 18 +++++-- .github/workflows/validate-image-build.yaml | 37 ++++++++++++++ Containerfile | 6 ++- RELEASE.md | 27 ++++++++++ 5 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/validate-image-build.yaml create mode 100644 RELEASE.md diff --git a/.github/workflows/publish-tag.yaml b/.github/workflows/publish-tag.yaml index da81dce..f1d58a6 100644 --- a/.github/workflows/publish-tag.yaml +++ b/.github/workflows/publish-tag.yaml @@ -9,12 +9,19 @@ jobs: publish: runs-on: ubuntu-latest permissions: - contents: read + contents: write packages: write steps: - name: Checkout uses: actions/checkout@v4 + - name: Validate release tag + run: | + if [[ ! "${GITHUB_REF_NAME}" =~ ^v[0-9]+[.][0-9]+[.][0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "Release tags must use semver like v1.2.3 or v1.2.3-rc.1" + exit 1 + fi + - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -29,21 +36,57 @@ jobs: - name: Set image tag run: | - IMAGE_TAG="kuberhealthy/http-check:${GITHUB_REF_NAME}" - echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV + IMAGE_REPO="kuberhealthy/http-check" + IMAGE_TAG="${IMAGE_REPO}:${GITHUB_REF_NAME}" + IMAGE_URL="https://hub.docker.com/r/${IMAGE_REPO}/tags?name=${GITHUB_REF_NAME}" + echo "IMAGE_REPO=${IMAGE_REPO}" >> "$GITHUB_ENV" + echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" + echo "IMAGE_URL=${IMAGE_URL}" >> "$GITHUB_ENV" - name: Build and push uses: docker/build-push-action@v6 with: context: . file: ./Containerfile + platforms: linux/amd64,linux/arm64 push: true tags: ${{ env.IMAGE_TAG }} + + - name: Create release notes + run: | + cat > release-images.txt < release-notes.md </dev/null 2>&1; then + gh release edit "${GITHUB_REF_NAME}" --title "${GITHUB_REF_NAME}" --notes-file release-notes.md + else + gh release create "${GITHUB_REF_NAME}" --title "${GITHUB_REF_NAME}" --notes-file release-notes.md --verify-tag + fi + gh release upload "${GITHUB_REF_NAME}" release-images.txt --clobber + - name: Publish summary run: | - TAG="${IMAGE_TAG#*:}" - IMAGE_REPO="${IMAGE_TAG%:*}" - IMAGE_URL="https://hub.docker.com/r/${IMAGE_REPO}/tags?name=${TAG}" + echo "Release created:" >> "$GITHUB_STEP_SUMMARY" + echo "- ${GITHUB_REF_NAME}" >> "$GITHUB_STEP_SUMMARY" echo "Images pushed:" >> "$GITHUB_STEP_SUMMARY" echo "- ${IMAGE_TAG}" >> "$GITHUB_STEP_SUMMARY" echo "- ${IMAGE_URL}" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index bbecd99..f153de5 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -30,21 +30,31 @@ jobs: - name: Set image tag run: | SHORT_SHA=$(git rev-parse --short HEAD) - IMAGE_TAG="kuberhealthy/http-check:${SHORT_SHA}" - echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV + IMAGE_REPO="kuberhealthy/http-check" + IMAGE_TAG="${IMAGE_REPO}:${SHORT_SHA}" + LATEST_IMAGE_TAG="${IMAGE_REPO}:latest" + echo "IMAGE_REPO=${IMAGE_REPO}" >> "$GITHUB_ENV" + echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" + echo "LATEST_IMAGE_TAG=${LATEST_IMAGE_TAG}" >> "$GITHUB_ENV" - name: Build and push uses: docker/build-push-action@v6 with: context: . file: ./Containerfile + platforms: linux/amd64,linux/arm64 push: true - tags: ${{ env.IMAGE_TAG }} + tags: | + ${{ env.IMAGE_TAG }} + ${{ env.LATEST_IMAGE_TAG }} + - name: Publish summary run: | TAG="${IMAGE_TAG#*:}" - IMAGE_REPO="${IMAGE_TAG%:*}" IMAGE_URL="https://hub.docker.com/r/${IMAGE_REPO}/tags?name=${TAG}" + LATEST_IMAGE_URL="https://hub.docker.com/r/${IMAGE_REPO}/tags?name=latest" echo "Images pushed:" >> "$GITHUB_STEP_SUMMARY" echo "- ${IMAGE_TAG}" >> "$GITHUB_STEP_SUMMARY" echo "- ${IMAGE_URL}" >> "$GITHUB_STEP_SUMMARY" + echo "- ${LATEST_IMAGE_TAG}" >> "$GITHUB_STEP_SUMMARY" + echo "- ${LATEST_IMAGE_URL}" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/validate-image-build.yaml b/.github/workflows/validate-image-build.yaml new file mode 100644 index 0000000..140954b --- /dev/null +++ b/.github/workflows/validate-image-build.yaml @@ -0,0 +1,37 @@ +name: Validate image build + +on: + pull_request: + branches: + - main + paths: + - "Containerfile" + - "go.mod" + - "go.sum" + - "cmd/**" + - ".github/workflows/validate-image-build.yaml" + - ".github/workflows/publish-tag.yaml" + - ".github/workflows/publish.yaml" + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Containerfile + platforms: linux/amd64,linux/arm64 + push: false diff --git a/Containerfile b/Containerfile index 83dad5a..e3ebae2 100644 --- a/Containerfile +++ b/Containerfile @@ -1,4 +1,6 @@ -FROM golang:1.24 AS builder +FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.24 AS builder +ARG TARGETOS +ARG TARGETARCH WORKDIR /build # Cache module downloads. @@ -8,7 +10,7 @@ RUN go mod download # Copy source and build. COPY . /build ENV CGO_ENABLED=0 -RUN go build -v -o /build/bin/http-check ./cmd/http-check +RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -v -o /build/bin/http-check ./cmd/http-check # Create a non-root user. RUN groupadd -g 999 user && \ diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..fdcdac0 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,27 @@ +# Release + +Releases are automated from semver git tags. + +To make a release, create and push a semver tag from the commit you want to release: + +```sh +git tag v1.2.3 +git push origin v1.2.3 +``` + +The `Publish tag` GitHub Actions workflow runs on `v*` tags and validates the tag format before publishing. Use `vMAJOR.MINOR.PATCH`, such as `v1.2.3`, or a prerelease tag like `v1.2.3-rc.1`. + +The workflow publishes this multi-arch image: + +```text +docker.io/kuberhealthy/http-check: +``` + +The image manifest includes: + +- `linux/amd64` +- `linux/arm64` + +After the image is pushed, the workflow creates or updates a GitHub release with the same semver as the tag. The release notes link to the Docker Hub image, and the release includes a `release-images.txt` asset listing the image and supported platforms. + +Docker image tags do not support `+`, so do not use semver build metadata in release tags. From 42bc96bf11e8f82c93d4f16f125034aad6130c07 Mon Sep 17 00:00:00 2001 From: Eric Greer Date: Mon, 8 Jun 2026 20:41:01 -0700 Subject: [PATCH 2/4] chore: publish check images to ghcr Co-authored-by: Codex Signed-off-by: Eric Greer --- .github/workflows/publish-tag.yaml | 28 +++++++++++++++------------- .github/workflows/publish.yaml | 18 +++++++++--------- RELEASE.md | 6 +++--- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/.github/workflows/publish-tag.yaml b/.github/workflows/publish-tag.yaml index f1d58a6..5e2cf46 100644 --- a/.github/workflows/publish-tag.yaml +++ b/.github/workflows/publish-tag.yaml @@ -28,20 +28,21 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Log in to Docker Hub + - name: Log in to GHCR uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_PAT }} + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} - name: Set image tag run: | - IMAGE_REPO="kuberhealthy/http-check" + IMAGE_REPO="ghcr.io/kuberhealthy/http-check" IMAGE_TAG="${IMAGE_REPO}:${GITHUB_REF_NAME}" - IMAGE_URL="https://hub.docker.com/r/${IMAGE_REPO}/tags?name=${GITHUB_REF_NAME}" + PACKAGE_URL="https://github.com/orgs/kuberhealthy/packages/container/package/http-check" echo "IMAGE_REPO=${IMAGE_REPO}" >> "$GITHUB_ENV" echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" - echo "IMAGE_URL=${IMAGE_URL}" >> "$GITHUB_ENV" + echo "PACKAGE_URL=${PACKAGE_URL}" >> "$GITHUB_ENV" - name: Build and push uses: docker/build-push-action@v6 @@ -55,20 +56,20 @@ jobs: - name: Create release notes run: | cat > release-images.txt < release-notes.md <> "$GITHUB_STEP_SUMMARY" echo "- ${GITHUB_REF_NAME}" >> "$GITHUB_STEP_SUMMARY" - echo "Images pushed:" >> "$GITHUB_STEP_SUMMARY" + echo "Image pushed:" >> "$GITHUB_STEP_SUMMARY" echo "- ${IMAGE_TAG}" >> "$GITHUB_STEP_SUMMARY" - echo "- ${IMAGE_URL}" >> "$GITHUB_STEP_SUMMARY" + echo "Package:" >> "$GITHUB_STEP_SUMMARY" + echo "- ${PACKAGE_URL}" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index f153de5..4a4715d 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -21,21 +21,24 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Log in to Docker Hub + - name: Log in to GHCR uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_PAT }} + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} - name: Set image tag run: | SHORT_SHA=$(git rev-parse --short HEAD) - IMAGE_REPO="kuberhealthy/http-check" + IMAGE_REPO="ghcr.io/kuberhealthy/http-check" IMAGE_TAG="${IMAGE_REPO}:${SHORT_SHA}" LATEST_IMAGE_TAG="${IMAGE_REPO}:latest" + PACKAGE_URL="https://github.com/orgs/kuberhealthy/packages/container/package/http-check" echo "IMAGE_REPO=${IMAGE_REPO}" >> "$GITHUB_ENV" echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" echo "LATEST_IMAGE_TAG=${LATEST_IMAGE_TAG}" >> "$GITHUB_ENV" + echo "PACKAGE_URL=${PACKAGE_URL}" >> "$GITHUB_ENV" - name: Build and push uses: docker/build-push-action@v6 @@ -50,11 +53,8 @@ jobs: - name: Publish summary run: | - TAG="${IMAGE_TAG#*:}" - IMAGE_URL="https://hub.docker.com/r/${IMAGE_REPO}/tags?name=${TAG}" - LATEST_IMAGE_URL="https://hub.docker.com/r/${IMAGE_REPO}/tags?name=latest" echo "Images pushed:" >> "$GITHUB_STEP_SUMMARY" echo "- ${IMAGE_TAG}" >> "$GITHUB_STEP_SUMMARY" - echo "- ${IMAGE_URL}" >> "$GITHUB_STEP_SUMMARY" echo "- ${LATEST_IMAGE_TAG}" >> "$GITHUB_STEP_SUMMARY" - echo "- ${LATEST_IMAGE_URL}" >> "$GITHUB_STEP_SUMMARY" + echo "Package:" >> "$GITHUB_STEP_SUMMARY" + echo "- ${PACKAGE_URL}" >> "$GITHUB_STEP_SUMMARY" diff --git a/RELEASE.md b/RELEASE.md index fdcdac0..ab450ad 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -11,10 +11,10 @@ git push origin v1.2.3 The `Publish tag` GitHub Actions workflow runs on `v*` tags and validates the tag format before publishing. Use `vMAJOR.MINOR.PATCH`, such as `v1.2.3`, or a prerelease tag like `v1.2.3-rc.1`. -The workflow publishes this multi-arch image: +The workflow publishes this multi-arch image to GitHub Container Registry: ```text -docker.io/kuberhealthy/http-check: +ghcr.io/kuberhealthy/http-check: ``` The image manifest includes: @@ -22,6 +22,6 @@ The image manifest includes: - `linux/amd64` - `linux/arm64` -After the image is pushed, the workflow creates or updates a GitHub release with the same semver as the tag. The release notes link to the Docker Hub image, and the release includes a `release-images.txt` asset listing the image and supported platforms. +After the image is pushed, the workflow creates or updates a GitHub release with the same semver as the tag. The release notes link to the GitHub package, and the release includes a `release-images.txt` asset listing the image and supported platforms. Docker image tags do not support `+`, so do not use semver build metadata in release tags. From cf2a21ac33e07f05b5ace3b75c666872a6c75445 Mon Sep 17 00:00:00 2001 From: Eric Greer Date: Mon, 8 Jun 2026 21:11:23 -0700 Subject: [PATCH 3/4] chore: update healthcheck images on release Co-authored-by: Codex Signed-off-by: Eric Greer --- .github/workflows/publish-tag.yaml | 35 +++++++++++++++++++++++++++++- RELEASE.md | 4 +++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-tag.yaml b/.github/workflows/publish-tag.yaml index 5e2cf46..3d8633a 100644 --- a/.github/workflows/publish-tag.yaml +++ b/.github/workflows/publish-tag.yaml @@ -12,7 +12,7 @@ jobs: contents: write packages: write steps: - - name: Checkout + - name: Checkout tag uses: actions/checkout@v4 - name: Validate release tag @@ -53,6 +53,37 @@ jobs: push: true tags: ${{ env.IMAGE_TAG }} + - name: Update healthcheck examples + env: + GH_TOKEN: ${{ github.token }} + run: | + git fetch origin main + git checkout main + git pull --ff-only origin main + + ruby <<'RUBY' + image_tag = ENV.fetch("IMAGE_TAG") + repo = "http-check" + files = Dir.glob("*.yaml") + Dir.glob("*.yml") + files.each do |path| + text = File.read(path) + updated = text.gsub(/(image: +)(?:S*/)?http\-check:[^ ]+/, "\1#{image_tag}") + next if updated == text + File.write(path, updated) + end + RUBY + + if git diff --quiet; then + echo "No healthcheck image references needed updates." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add '*.yaml' '*.yml' + git commit -m "chore: update healthcheck image for ${GITHUB_REF_NAME}" + git push origin main + - name: Create release notes run: | cat > release-images.txt < Date: Mon, 8 Jun 2026 21:24:26 -0700 Subject: [PATCH 4/4] fix: correct release healthcheck updater Co-authored-by: Codex Signed-off-by: Eric Greer --- .github/workflows/publish-tag.yaml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/publish-tag.yaml b/.github/workflows/publish-tag.yaml index 3d8633a..26bc57d 100644 --- a/.github/workflows/publish-tag.yaml +++ b/.github/workflows/publish-tag.yaml @@ -60,18 +60,7 @@ jobs: git fetch origin main git checkout main git pull --ff-only origin main - - ruby <<'RUBY' - image_tag = ENV.fetch("IMAGE_TAG") - repo = "http-check" - files = Dir.glob("*.yaml") + Dir.glob("*.yml") - files.each do |path| - text = File.read(path) - updated = text.gsub(/(image: +)(?:S*/)?http\-check:[^ ]+/, "\1#{image_tag}") - next if updated == text - File.write(path, updated) - end - RUBY + ruby -e 'image_tag = ENV.fetch("IMAGE_TAG"); files = Dir.glob("*.yaml") + Dir.glob("*.yml"); files.each do |path| text = File.read(path); updated = text.gsub(/(image:\s+)(?:\S*\/)?http\-check:[^\s]+/) { "#{$1}#{image_tag}" }; File.write(path, updated) if updated != text; end' if git diff --quiet; then echo "No healthcheck image references needed updates."