-
Notifications
You must be signed in to change notification settings - Fork 71
162 lines (142 loc) · 6.54 KB
/
Copy pathdocker.yml
File metadata and controls
162 lines (142 loc) · 6.54 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
name: Docker
on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
sha:
description: "Commit SHA or ref to build (e.g. a release tag's commit). Lets you re-release a signed image from an exact source commit without moving the git tag. Leave blank to build the branch you dispatched from."
required: false
default: ''
image_tag:
description: 'Image tag to publish as (e.g. 0.10.5). Leave blank to tag with the branch name (or sha-<short> when a sha is given).'
required: false
default: ''
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
concurrency:
group: docker-${{ github.ref }}-${{ inputs.image_tag || inputs.sha || github.sha }}
cancel-in-progress: true
jobs:
# Build each architecture on its OWN native runner — free arm64 hosted
# runners (ubuntu-24.04-arm) are GA for public repos — in parallel, pushing
# each as an untagged per-arch image BY DIGEST. Native beats QEMU here: the
# image runs `bun install` plus two Vite/TanStack production builds, which are
# far too slow under arm64 emulation (and risk job timeouts).
build:
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- name: Prepare platform pair
env:
PLATFORM: ${{ matrix.platform }}
run: echo "PLATFORM_PAIR=${PLATFORM//\//-}" >> "$GITHUB_ENV"
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
# On workflow_dispatch, build the requested sha/ref (re-release of an
# exact source commit); otherwise build the pushed ref as usual.
ref: ${{ github.event.inputs.sha || github.ref }}
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
- name: Resolve lowercase image ref
run: echo "IMAGE=${REGISTRY}/$(printf '%s' "$IMAGE_NAME" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
with:
context: .
file: ./apps/web/Dockerfile
platforms: ${{ matrix.platform }}
# No tags here — the manifest list (and its tags) is assembled in the
# merge job; each leg pushes an untagged per-arch image by digest.
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
# Per-arch cache scope so the two runners don't clobber each other.
cache-from: type=gha,scope=${{ env.PLATFORM_PAIR }}
cache-to: type=gha,mode=max,scope=${{ env.PLATFORM_PAIR }}
- name: Export digest
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
mkdir -p /tmp/digests
touch "/tmp/digests/${DIGEST#sha256:}"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
# Assemble the multi-arch manifest list (OCI image index) from the per-arch
# digests, apply the real tags, then keyless-sign the index digest.
merge:
runs-on: ubuntu-latest
needs: [build]
permissions:
contents: read
packages: write
id-token: write # keyless cosign signing via GitHub OIDC
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
- name: Resolve lowercase image ref
run: echo "IMAGE=${REGISTRY}/$(printf '%s' "$IMAGE_NAME" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
id: meta
with:
images: ${{ env.IMAGE }}
# The branch-name tag covers branch pushes AND manual dispatches that
# build the dispatched branch itself (no sha/image_tag override).
tags: |
type=ref,event=branch,enable=${{ github.event.inputs.sha == '' && github.event.inputs.image_tag == '' }}
type=semver,pattern={{version}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=raw,value=${{ github.event.inputs.image_tag }},enable=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tag != '' }}
type=sha,enable=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tag == '' && github.event.inputs.sha != '' }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf "${IMAGE}@sha256:%s " *)
- name: Capture image index digest
id: index
env:
VERSION: ${{ steps.meta.outputs.version }}
run: |
DIGEST=$(docker buildx imagetools inspect "${IMAGE}:${VERSION}" --format '{{json .Manifest.Digest}}' | tr -d '"')
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
- name: Install cosign
uses: sigstore/cosign-installer@f713795cb21599bc4e5c4b58cbad1da852d7eeb9 # v3
# Keyless cosign signature over the pushed index digest, required by the
# cluster's Kyverno verify-images ClusterPolicy (keyless, GitHub OIDC
# issuer + QuackbackIO workflow subject). --recursive signs the OCI index
# AND each per-arch child manifest, so policy can verify either the index
# or a resolved per-arch digest.
- name: Sign image index (keyless)
env:
IMAGE_DIGEST: ${{ steps.index.outputs.digest }}
run: cosign sign --yes --recursive "${IMAGE}@${IMAGE_DIGEST}"