Repository navigation
197 lines (186 loc) · 8.45 KB
/
Copy pathdev.yml
File metadata and controls
197 lines (186 loc) · 8.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
on:
push:
branches:
- main
# No branches filter: a stacked PR targets the PR below it, not main, and
# would never get its pr-N image. Forks are kept out by the job's `if`.
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
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@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Login to DockerHub
uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the Container registry
uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.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@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.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@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.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