diff --git a/.ko.yaml b/.ko.yaml index fd8e859684..9e7ee93e75 100644 --- a/.ko.yaml +++ b/.ko.yaml @@ -12,15 +12,24 @@ # See the License for the specific language governing permissions and # limitations under the License. -defaultBaseImage: gcr.io/distroless/static-debian13 +# Every base image is pinned by digest so that two builds of the same commit +# produce the same images regardless of when or where they run. The tag is +# kept next to the digest for readability and so a bump can re-resolve it: +# the digest is what ko uses. hack/verify/ko-base-images.sh fails a ref +# without a digest. Bump by re-resolving each tag (`crane digest `) +# and replacing the digest; a change of tag (a new major) is a deliberate +# edit, not a bump. + +defaultBaseImage: gcr.io/distroless/static-debian13:latest@sha256:f2ea2709ac8db56323cbd7d014277f32cb572d9ea124b0076f7aafe5980678fe defaultPlatforms: - linux/amd64 - linux/arm64 baseImageOverrides: - github.com/agent-substrate/substrate/demos/sandbox: alpine + github.com/agent-substrate/substrate/demos/sandbox: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b # ateom-microvm needs glibc (for the fetched cloud-hypervisor binary) and mount/umount - # (to bind the image into the virtiofsd shared dir) — both in debian:stable-slim but - # not in the distroless static default. - github.com/agent-substrate/substrate/cmd/ateom-microvm: debian:stable-slim + # (to bind the image into the virtiofsd shared dir) — both in debian slim but + # not in the distroless static default. Pinned to an explicit Debian major + # rather than stable-slim, which silently moves when Debian's stable does. + github.com/agent-substrate/substrate/cmd/ateom-microvm: debian:13-slim@sha256:d7e12182ce18b85b93007c1dedf31f2d29e01ccf3182cc4017c709b6259bc132 diff --git a/hack/verify/ko-base-images.sh b/hack/verify/ko-base-images.sh new file mode 100755 index 0000000000..d59b7d6c0b --- /dev/null +++ b/hack/verify/ko-base-images.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Fails if any base image in .ko.yaml is not pinned by digest. A tag-only base +# makes the build depend on when it runs: the same commit built a week apart +# would produce different images. See the header comment in .ko.yaml. + +set -o errexit -o nounset -o pipefail + +ROOT="$(git rev-parse --show-toplevel)" +cd "${ROOT}" + +# Image refs are the values of defaultBaseImage and of every entry under +# baseImageOverrides. Both are `key: value` lines; comments and blank lines +# are dropped. defaultPlatforms entries are list items and never match. +refs="$(sed -e 's/#.*$//' .ko.yaml \ + | awk ' + /^defaultBaseImage:/ { print $2; next } + /^baseImageOverrides:/ { in_overrides = 1; next } + /^[^[:space:]]/ { in_overrides = 0 } + in_overrides && NF >= 2 { print $NF } + ')" + +if [[ -z "${refs}" ]]; then + echo "error: no base image refs found in .ko.yaml" >&2 + exit 1 +fi + +rc=0 +while IFS= read -r ref; do + if [[ ! "${ref}" =~ @sha256:[0-9a-f]{64}$ ]]; then + echo "error: .ko.yaml base image is not pinned by digest: ${ref}" >&2 + rc=1 + fi +done <<< "${refs}" + +if [[ "${rc}" -ne 0 ]]; then + echo "Pin every base image as :@sha256: (see .ko.yaml)." >&2 +fi +exit "${rc}"