Files
dozzle/.github/workflows/dev.yml
T
2026-09-06 07:21:21 -07:00

198 lines
8.3 KiB
YAML

on:
push:
branches:
- master
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
branches:
- master
name: Push container
permissions:
contents: read
packages: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
buildx:
name: Push branches and PRs
runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && !github.event.pull_request.head.repo.fork && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == 'amir20/dozzle') }}
steps:
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
- name: Login to DockerHub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the Container registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: |
amir20/dozzle
ghcr.io/amir20/dozzle
- name: Docker meta for alpine variant
id: meta-alpine
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: |
amir20/dozzle
ghcr.io/amir20/dozzle
flavor: |
suffix=-alpine,onlatest=true
- name: Short SHA
id: sha
run: echo "short=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
- name: Writing certs to file
run: |
echo "${{ secrets.TTL_KEY }}" > shared_key.pem
echo "${{ secrets.TTL_CERT }}" > shared_cert.pem
# BuildKit cache mounts live in the builder's local state and cache-to:gha
# does not export them. Every run got a fresh builder, so /go/pkg/mod and
# /root/.cache/go-build started empty and the whole dependency tree was
# recompiled from scratch for both platforms, ~135s of a 3m30s build. Note
# `go mod download` looks CACHED in the log while leaving its mount empty,
# so the modules were re-fetched inside `go build` too.
#
# Both mounts default to id=<target>, so the amd64 and arm64 builds share
# one copy and a single injection reaches both. That is safe for Go (GOCACHE
# keys entries by GOOS/GOARCH, the module cache is just source) and would
# not be for apt/apk, which is why those need id=...-${TARGETARCH}.
- name: Restore Go caches
id: go-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
go-build-cache
go-mod-cache
# Keyed on go.sum with no run-unique suffix so a hit skips the save.
# Dependencies are the expensive part and they only move when go.sum
# does; recompiling dozzle's own packages each run costs a few seconds.
# A per-SHA key would instead upload the whole cache on every build.
key: go-docker-${{ hashFiles('go.sum') }}
restore-keys: go-docker-
- name: Inject Go caches into the builder
uses: reproducible-containers/buildkit-cache-dance@5422eac04292c961a382e0f584ea0f03ad9da723 # v3.4.0
with:
builder: ${{ steps.buildx.outputs.name }}
cache-map: |
{
"go-build-cache": "/root/.cache/go-build",
"go-mod-cache": "/go/pkg/mod"
}
# On a hit the mounts would be written back unchanged, so skip the
# extraction post-step that tars them back out of the builder.
skip-extraction: ${{ steps.go-cache.outputs.cache-hit == 'true' }}
- name: Build and push
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
push: true
platforms: linux/amd64,linux/arm64/v8
tags: ${{ steps.meta.outputs.tags }}
build-args: TAG=${{ steps.meta.outputs.version }}-${{ steps.sha.outputs.short }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push alpine variant
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
push: true
target: alpine
platforms: linux/amd64,linux/arm64/v8
tags: ${{ steps.meta-alpine.outputs.tags }}
build-args: TAG=${{ steps.meta.outputs.version }}-${{ steps.sha.outputs.short }}
labels: ${{ steps.meta-alpine.outputs.labels }}
# No cache-to. See the note in deploy.yml.
cache-from: type=gha
comment:
name: Comment image tag
needs: buildx
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Post pr-${{ github.event.pull_request.number }} image tag
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
IS_DRAFT: ${{ github.event.pull_request.draft }}
run: |
set -euo pipefail
short_sha="${HEAD_SHA:0:7}"
pr_marker="<!-- dozzle-pr-image -->"
issue_marker="<!-- dozzle-pr-image:${PR} -->"
run_cmd="docker run -v /var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle:pr-${PR}"
pr_body=$(printf '%s\n%s\n\n```\n%s\n```\n\n%s\n' \
"$pr_marker" \
"Try this PR without waiting for a release:" \
"$run_cmd" \
"Also on \`ghcr.io/amir20/dozzle:pr-${PR}\` and as \`amir20/dozzle:pr-${PR}-alpine\`. Rebuilt on every push, currently \`${short_sha}\`.")
# Sticky comment on the PR itself.
existing=$(gh api "repos/${REPO}/issues/${PR}/comments" --paginate \
--jq ".[] | select(.body != null and (.body | contains(\"${pr_marker}\"))) | .id" | head -1)
if [ -n "$existing" ]; then
gh api -X PATCH "repos/${REPO}/issues/comments/${existing}" -f body="$pr_body" > /dev/null
else
gh api -X POST "repos/${REPO}/issues/${PR}/comments" -f body="$pr_body" > /dev/null
fi
if [ "$IS_DRAFT" = "true" ]; then
echo "Draft PR, skipping linked issues."
exit 0
fi
# One-time comment on every issue this PR closes.
linked=$(gh api "repos/${REPO}/pulls/${PR}" --jq '.body // ""' \
| grep -oiE '(close[sd]?|fix(e[sd])?|resolve[sd]?)[[:space:]]*:?[[:space:]]+#[0-9]+' \
| grep -oE '[0-9]+' | sort -u || true)
for issue in $linked; do
if [ "$issue" = "$PR" ]; then
continue
fi
kind=$(gh api "repos/${REPO}/issues/${issue}" --jq 'if .pull_request then "pr" else "issue" end' 2>/dev/null || echo "missing")
if [ "$kind" != "issue" ]; then
echo "#${issue} is not an issue, skipping."
continue
fi
already=$(gh api "repos/${REPO}/issues/${issue}/comments" --paginate \
--jq ".[] | select(.body != null and (.body | contains(\"${issue_marker}\"))) | .id" | head -1)
if [ -n "$already" ]; then
echo "#${issue} already has a pr-${PR} comment, skipping."
continue
fi
issue_body=$(printf '%s\n%s\n\n```\n%s\n```\n\n%s\n' \
"$issue_marker" \
"A fix for this is in #${PR}. CI builds an image per PR, so you can test it before the next release:" \
"$run_cmd" \
"Report back on #${PR} if it still happens.")
gh api -X POST "repos/${REPO}/issues/${issue}/comments" -f body="$issue_body" > /dev/null
echo "Commented on #${issue}."
done