Skip to content

Commit 2895e4f

Browse files
authored
Merge branch 'main' into samejr/modal-close-button-audit
2 parents b4dc346 + 802d238 commit 2895e4f

54 files changed

Lines changed: 1922 additions & 1730 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/base-images.yml

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
name: 🐳 Deploy base images
2+
3+
# Publishes the deploy base images (see base-images/README.md) to Docker Hub.
4+
# Tags are mutable and rebuilt in place; the CLI pins digests, so consumers
5+
# only move when a release bumps its pins.
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
debian_snapshot:
11+
description: "Debian snapshot timestamp (YYYYMMDDTHHMMSSZ). Defaults to yesterday 00:00 UTC."
12+
required: false
13+
type: string
14+
push:
15+
branches: [main]
16+
paths:
17+
- "base-images/**"
18+
- ".github/workflows/base-images.yml"
19+
pull_request:
20+
paths:
21+
- "base-images/**"
22+
- ".github/workflows/base-images.yml"
23+
24+
concurrency:
25+
group: base-images-${{ github.ref }}
26+
cancel-in-progress: false
27+
28+
permissions: {}
29+
30+
jobs:
31+
setup:
32+
runs-on: ubuntu-latest
33+
timeout-minutes: 10
34+
permissions:
35+
contents: read
36+
outputs:
37+
images: ${{ steps.config.outputs.images }}
38+
packages: ${{ steps.config.outputs.packages }}
39+
build_packages: ${{ steps.config.outputs.build_packages }}
40+
suite: ${{ steps.config.outputs.suite }}
41+
snapshot: ${{ steps.config.outputs.snapshot }}
42+
source_date_epoch: ${{ steps.config.outputs.source_date_epoch }}
43+
push: ${{ steps.config.outputs.push }}
44+
steps:
45+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
46+
with:
47+
persist-credentials: false
48+
49+
- name: Read image matrix and resolve snapshot
50+
id: config
51+
env:
52+
SNAPSHOT_INPUT: ${{ inputs.debian_snapshot }}
53+
EVENT_NAME: ${{ github.event_name }}
54+
REF: ${{ github.ref }}
55+
run: |
56+
PACKAGES="$(jq -er '.packages' base-images/images.json)"
57+
BUILD_PACKAGES="$(jq -er '.buildPackages' base-images/images.json)"
58+
SUITE="$(jq -er '.suite' base-images/images.json)"
59+
60+
# Values land in build args and shell lines; keep them boring.
61+
# NUL-delimited whole-record match so multi-line values can't sneak through
62+
printf '%s\0' "$PACKAGES" | grep -zqxE '[a-z0-9][a-z0-9 .+:=~-]*' || { echo "invalid packages value"; exit 1; }
63+
printf '%s\0' "$BUILD_PACKAGES" | grep -zqxE '[a-z0-9][a-z0-9 .+:=~-]*' || { echo "invalid buildPackages value"; exit 1; }
64+
printf '%s\0' "$SUITE" | grep -zqxE '[a-z]+' || { echo "invalid suite value"; exit 1; }
65+
jq -e '.images | length > 0 and all((.repo | test("^[a-z0-9-]+$")) and (.tag | test("^[a-z0-9.-]+$")) and (.base | test("^[a-zA-Z0-9./:@-]+$")))' base-images/images.json > /dev/null \
66+
|| { echo "invalid images entries"; exit 1; }
67+
68+
SNAPSHOT="$SNAPSHOT_INPUT"
69+
if [ -z "$SNAPSHOT" ]; then
70+
SNAPSHOT="$(date -u -d yesterday +%Y%m%dT000000Z)"
71+
fi
72+
printf '%s\0' "$SNAPSHOT" | grep -zqxE '[0-9]{8}T[0-9]{6}Z' || { echo "invalid debian_snapshot: $SNAPSHOT"; exit 1; }
73+
74+
# Snapshot-derived timestamps: reproducible, with a real created date
75+
EPOCH="$(date -u -d "${SNAPSHOT:0:4}-${SNAPSHOT:4:2}-${SNAPSHOT:6:2} ${SNAPSHOT:9:2}:${SNAPSHOT:11:2}:${SNAPSHOT:13:2}Z" +%s)"
76+
# Future snapshots resolve to "latest" and break mtime normalization
77+
[ "$EPOCH" -le "$(date -u +%s)" ] || { echo "debian_snapshot is in the future: $SNAPSHOT"; exit 1; }
78+
79+
# Pull requests and branch dispatches build without pushing
80+
if [ "$EVENT_NAME" = "pull_request" ] || [ "$REF" != "refs/heads/main" ]; then
81+
PUSH=false
82+
else
83+
PUSH=true
84+
fi
85+
86+
{
87+
echo "images=$(jq -c '.images' base-images/images.json)"
88+
echo "packages=$PACKAGES"
89+
echo "build_packages=$BUILD_PACKAGES"
90+
echo "suite=$SUITE"
91+
echo "snapshot=$SNAPSHOT"
92+
echo "source_date_epoch=$EPOCH"
93+
echo "push=$PUSH"
94+
} >> "$GITHUB_OUTPUT"
95+
96+
publish:
97+
needs: setup
98+
runs-on: ubuntu-latest
99+
timeout-minutes: 60
100+
permissions:
101+
contents: read
102+
id-token: write
103+
attestations: write
104+
strategy:
105+
fail-fast: false
106+
matrix:
107+
image: ${{ fromJSON(needs.setup.outputs.images) }}
108+
env:
109+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
110+
DOCKER_BUILD_SUMMARY: "false"
111+
DOCKER_BUILD_RECORD_UPLOAD: "false"
112+
steps:
113+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
114+
with:
115+
persist-credentials: false
116+
117+
# Before any pull so rate limits are authenticated; fork PRs skip (no secrets)
118+
- name: 🐳 Login to Docker Hub
119+
if: env.DOCKERHUB_USERNAME != ''
120+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
121+
with:
122+
username: ${{ secrets.DOCKERHUB_USERNAME }}
123+
password: ${{ secrets.DOCKERHUB_TOKEN }}
124+
125+
- name: 🐳 Set up QEMU
126+
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
127+
with:
128+
image: docker.io/tonistiigi/binfmt:latest@sha256:400a4873b838d1b89194d982c45e5fb3cda4593fbfd7e08a02e76b03b21166f0
129+
130+
- name: 🐳 Set up Docker Buildx
131+
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
132+
133+
# Build both targets before pushing either so the tag pair can't skew
134+
- name: 🐳 Build both targets (no push)
135+
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
136+
with:
137+
context: base-images
138+
file: base-images/Dockerfile
139+
target: build
140+
platforms: linux/amd64,linux/arm64
141+
provenance: false
142+
outputs: type=image,push=false,rewrite-timestamp=true
143+
tags: triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-build
144+
build-args: |
145+
BASE_IMAGE=${{ matrix.image.base }}
146+
DEBIAN_SNAPSHOT=${{ needs.setup.outputs.snapshot }}
147+
DEBIAN_SUITE=${{ needs.setup.outputs.suite }}
148+
PACKAGES=${{ needs.setup.outputs.packages }}
149+
BUILD_PACKAGES=${{ needs.setup.outputs.build_packages }}
150+
SOURCE_DATE_EPOCH=${{ needs.setup.outputs.source_date_epoch }}
151+
labels: |
152+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
153+
org.opencontainers.image.revision=${{ github.sha }}
154+
dev.trigger.debian-snapshot=${{ needs.setup.outputs.snapshot }}
155+
156+
- name: 🐳 Push runtime image
157+
id: build_runtime
158+
if: needs.setup.outputs.push == 'true'
159+
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
160+
with:
161+
context: base-images
162+
file: base-images/Dockerfile
163+
target: runtime
164+
platforms: linux/amd64,linux/arm64
165+
provenance: false
166+
outputs: type=image,push=true,rewrite-timestamp=true
167+
tags: triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}
168+
build-args: |
169+
BASE_IMAGE=${{ matrix.image.base }}
170+
DEBIAN_SNAPSHOT=${{ needs.setup.outputs.snapshot }}
171+
DEBIAN_SUITE=${{ needs.setup.outputs.suite }}
172+
PACKAGES=${{ needs.setup.outputs.packages }}
173+
SOURCE_DATE_EPOCH=${{ needs.setup.outputs.source_date_epoch }}
174+
labels: |
175+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
176+
org.opencontainers.image.revision=${{ github.sha }}
177+
dev.trigger.debian-snapshot=${{ needs.setup.outputs.snapshot }}
178+
179+
- name: 🐳 Push build-variant image
180+
id: build_toolchain
181+
if: needs.setup.outputs.push == 'true'
182+
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
183+
with:
184+
context: base-images
185+
file: base-images/Dockerfile
186+
target: build
187+
platforms: linux/amd64,linux/arm64
188+
provenance: false
189+
outputs: type=image,push=true,rewrite-timestamp=true
190+
tags: triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-build
191+
build-args: |
192+
BASE_IMAGE=${{ matrix.image.base }}
193+
DEBIAN_SNAPSHOT=${{ needs.setup.outputs.snapshot }}
194+
DEBIAN_SUITE=${{ needs.setup.outputs.suite }}
195+
PACKAGES=${{ needs.setup.outputs.packages }}
196+
BUILD_PACKAGES=${{ needs.setup.outputs.build_packages }}
197+
SOURCE_DATE_EPOCH=${{ needs.setup.outputs.source_date_epoch }}
198+
labels: |
199+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
200+
org.opencontainers.image.revision=${{ github.sha }}
201+
dev.trigger.debian-snapshot=${{ needs.setup.outputs.snapshot }}
202+
203+
# An auto-created private repo would publish green while customer pulls fail
204+
- name: 🔎 Verify anonymous pullability
205+
if: needs.setup.outputs.push == 'true'
206+
env:
207+
IMAGE_REPO: ${{ matrix.image.repo }}
208+
RUNTIME_DIGEST: ${{ steps.build_runtime.outputs.digest }}
209+
BUILD_DIGEST: ${{ steps.build_toolchain.outputs.digest }}
210+
run: |
211+
for digest in "$RUNTIME_DIGEST" "$BUILD_DIGEST"; do
212+
TOKEN="$(curl -fsS --connect-timeout 10 --max-time 60 "https://auth.docker.io/token?service=registry.docker.io&scope=repository:triggerdotdev/$IMAGE_REPO:pull" | jq -r .token)"
213+
curl -fsS --connect-timeout 10 --max-time 60 -o /dev/null -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json" "https://registry-1.docker.io/v2/triggerdotdev/$IMAGE_REPO/manifests/$digest" || { echo "triggerdotdev/$IMAGE_REPO@$digest is not anonymously pullable; is the repo private?"; exit 1; }
214+
done
215+
216+
# Builds are reproducible, so re-running a red publish re-pushes the
217+
# same digests and re-attests them
218+
- name: 🔏 Attest runtime image provenance
219+
if: needs.setup.outputs.push == 'true'
220+
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
221+
with:
222+
subject-name: index.docker.io/triggerdotdev/${{ matrix.image.repo }}
223+
subject-digest: ${{ steps.build_runtime.outputs.digest }}
224+
push-to-registry: false
225+
226+
- name: 🔏 Attest build-variant image provenance
227+
if: needs.setup.outputs.push == 'true'
228+
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
229+
with:
230+
subject-name: index.docker.io/triggerdotdev/${{ matrix.image.repo }}
231+
subject-digest: ${{ steps.build_toolchain.outputs.digest }}
232+
push-to-registry: false
233+
234+
- name: 📋 Record digests
235+
if: needs.setup.outputs.push == 'true'
236+
env:
237+
IMAGE_REPO: ${{ matrix.image.repo }}
238+
IMAGE_TAG: ${{ matrix.image.tag }}
239+
RUNTIME_DIGEST: ${{ steps.build_runtime.outputs.digest }}
240+
BUILD_DIGEST: ${{ steps.build_toolchain.outputs.digest }}
241+
SNAPSHOT: ${{ needs.setup.outputs.snapshot }}
242+
run: |
243+
{
244+
echo "### triggerdotdev/$IMAGE_REPO:$IMAGE_TAG"
245+
echo '```'
246+
echo "runtime: $RUNTIME_DIGEST"
247+
echo "build: $BUILD_DIGEST"
248+
echo "debian snapshot: $SNAPSHOT"
249+
echo '```'
250+
} >> "$GITHUB_STEP_SUMMARY"
251+
252+
results:
253+
needs: [publish]
254+
if: always()
255+
runs-on: ubuntu-latest
256+
timeout-minutes: 5
257+
permissions: {}
258+
steps:
259+
- name: Fail if any image build failed
260+
env:
261+
RESULT: ${{ needs.publish.result }}
262+
run: |
263+
[ "$RESULT" = "success" ] || { echo "one or more image builds failed: $RESULT"; exit 1; }
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: improvement
4+
---
5+
6+
Routine cleanup of old dashboard agent data now runs on its own schedule.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Renaming a project now keeps you on the project settings page and tells you what happened, instead of silently moving you to the tasks page or clearing the form with no explanation.

0 commit comments

Comments
 (0)