From 461fc2d4f3944291ad39d549f28bf0333137d841 Mon Sep 17 00:00:00 2001 From: Timo Derstappen Date: Sat, 19 Sep 2026 09:47:18 +0200 Subject: [PATCH 1/2] ci: publish through the orb's stock jobs with decoupled versions The six images are one architect/push-to-registries job each, built from .circleci/Dockerfile (ko's result: the component at /ko-app/ on the distroless base, CGO_ENABLED=0 -trimpath from the vendored modules) for linux/amd64 and linux/arm64, signed, with provenance and SBOM, annotated with the upstream pin. The version is the orb's, from the git tags: a release tag vX.Y.Z publishes X.Y.Z, 1.0.0 first; a push to giantswarm a dev build; other branches build with push: false. scan runs Trivy from its image on the remote engine and records image-refs.txt; charts reads .build_version. The sync workflow derives the pin from the stable tags upstream also has. FORK.md states the scheme. Signed-off-by: Timo Derstappen --- .circleci/Dockerfile | 65 ++++++ .circleci/config.yml | 309 ++++++++++++++------------- .github/workflows/sync-upstream.yaml | 11 +- FORK.md | 75 ++++--- 4 files changed, 278 insertions(+), 182 deletions(-) create mode 100644 .circleci/Dockerfile diff --git a/.circleci/Dockerfile b/.circleci/Dockerfile new file mode 100644 index 0000000000..b0f89e3ba6 --- /dev/null +++ b/.circleci/Dockerfile @@ -0,0 +1,65 @@ +# check=skip=InvalidDefaultArgInFrom +# Copyright 2026 The Agent Substrate Authors +# +# 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. + +# Fork (giantswarm/substrate): the image of one component of the Giant Swarm +# line, built by the architect orb's push-to-registries job for linux/amd64 and +# linux/arm64 (FORK.md, "Publishing"). It reproduces what ko builds for +# upstream: the binary of ./cmd/, compiled with CGO_ENABLED=0 and +# -trimpath from the vendored modules (the VCS revision embedded from the +# checkout, as ko does), at /ko-app/ on the distroless base +# .ko.yaml pins, /ko-app on PATH and the binary as the ENTRYPOINT. The builder +# runs on the build host's own architecture and cross-compiles — nothing is +# emulated; the runtime stage only copies. An ENTRYPOINT in exec form cannot +# take a build argument, so the last FROM selects one of the six runtime +# stages by COMPONENT — which has no default on purpose: a build without it +# fails instead of quietly producing some component (the directive on the +# first line stops the linter from asking for one). +# +# docker build --build-arg COMPONENT=ateapi -f .circleci/Dockerfile . + +ARG COMPONENT + +FROM --platform=$BUILDPLATFORM golang:1.27 AS builder +ARG COMPONENT +ARG TARGETOS +ARG TARGETARCH +WORKDIR /src +COPY . . +RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -trimpath -mod=vendor -o /ko-app/${COMPONENT} ./cmd/${COMPONENT} + +FROM gcr.io/distroless/static-debian13:latest@sha256:f2ea2709ac8db56323cbd7d014277f32cb572d9ea124b0076f7aafe5980678fe AS runtime +ARG COMPONENT +COPY --from=builder /ko-app/${COMPONENT} /ko-app/${COMPONENT} +ENV PATH=$PATH:/ko-app + +FROM runtime AS ateapi +ENTRYPOINT ["/ko-app/ateapi"] + +FROM runtime AS atecontroller +ENTRYPOINT ["/ko-app/atecontroller"] + +FROM runtime AS atelet +ENTRYPOINT ["/ko-app/atelet"] + +FROM runtime AS atenet +ENTRYPOINT ["/ko-app/atenet"] + +FROM runtime AS podcertcontroller +ENTRYPOINT ["/ko-app/podcertcontroller"] + +FROM runtime AS ateom-gvisor +ENTRYPOINT ["/ko-app/ateom-gvisor"] + +FROM ${COMPONENT} diff --git a/.circleci/config.yml b/.circleci/config.yml index 2de7608152..259a7f9167 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,30 +13,29 @@ # limitations under the License. # Fork (giantswarm/substrate): publishes the Giant Swarm line of Agent -# Substrate to gsoci.azurecr.io/giantswarm/substrate — every push to a branch -# as a dev build, every `v*` tag as a release (FORK.md, "Publishing"). What is -# published: the six images the agent platform runs (ateapi, atecontroller, -# atelet, atenet, podcertcontroller, ateom-gvisor; linux/amd64 + linux/arm64, -# built with ko like upstream's release.yaml, each index signed with the -# organisation's CircleCI identity through the architect orb) and the charts -# substrate-crds and substrate with their image defaults stamped to this -# registry — including the agentgateway image atenet-router and atenet-egress -# run, which comes from the Giant Swarm line of agentgateway (pinned below) -# instead of upstream's build. Every own image is scanned before the charts -# that reference it are pushed. Hand-written: the repository carries upstream's -# workflows and its own, nothing devctl-generated. +# Substrate to gsoci.azurecr.io/giantswarm/substrate through the architect +# orb — every push to `giantswarm` as a dev build, every release tag `vX.Y.Z` +# (or `-rc.N`) as the release `X.Y.Z` (FORK.md, "Publishing"). The six images +# the agent platform runs (ateapi, atecontroller, atelet, atenet, +# podcertcontroller, ateom-gvisor) are one architect/push-to-registries job +# each: built from .circleci/Dockerfile for linux/amd64 and linux/arm64, +# signed with the organisation's CircleCI identity, with provenance and an +# SBOM, the index annotated with the upstream pin. The version is the orb's +# (gitsemver, from the git tags) — nothing here computes one. Every own image +# is scanned before the charts that reference it are pushed: substrate-crds +# and substrate with their image defaults stamped to this registry, including +# the agentgateway image atenet-router and atenet-egress run, which comes from +# the Giant Swarm line of agentgateway (pinned below) instead of upstream's +# build. Every other branch builds the six images without pushing, so a +# Dockerfile regression surfaces on the pull request. Hand-written: the +# repository carries upstream's workflows and its own, nothing +# devctl-generated. version: 2.1 orbs: - architect: giantswarm/architect@10.5.0 + architect: giantswarm/architect@10.6.0 parameters: - version: - description: > - The version to publish (a one-off through the API). Empty: the tag - without "v", or a dev version derived from the commit. - type: string - default: "" agentgateway-image: description: > The agentgateway build atenet-router and atenet-egress run: a release of @@ -48,150 +47,70 @@ parameters: default: gsoci.azurecr.io/giantswarm/agentgateway:v1.5.1-gs.4 jobs: - images: + scan: executor: architect/architect - resource_class: large + resource_class: small # The line's registry path: images at REGISTRY/, charts at - # oci://REGISTRY/helm. + # oci://REGISTRY/helm. The version is the one the push jobs computed + # (.build_version, from the workspace). environment: &publish-environment REGISTRY: gsoci.azurecr.io/giantswarm/substrate COMPONENTS: ateapi atecontroller atelet atenet podcertcontroller ateom-gvisor AGENTGATEWAY_IMAGE: << pipeline.parameters.agentgateway-image >> - INPUT_VERSION: << pipeline.parameters.version >> - steps: - - checkout - - run: - name: Resolve the version - command: | - set -o errexit -o nounset -o pipefail - if [ -n "${INPUT_VERSION}" ]; then - version="${INPUT_VERSION}" - elif [ -n "${CIRCLE_TAG:-}" ]; then - version="${CIRCLE_TAG#v}" - else - # Dev version in the schema giantswarm's gitsemver emits today: - # -dev....h - # base = the next patch of the pin, the nearest upstream release tag - # reachable from the branch (the line's own tags carry a pre-release - # suffix and are excluded). Consumers select the channel with a Flux - # semverFilter on the branch part (`.*-dev\.giantswarm\..*`); the - # timestamp keeps semver order across rebuilds. - git fetch --quiet https://github.com/kagent-dev/substrate.git 'refs/tags/v*:refs/tags/v*' - pin=$(git describe --tags --abbrev=0 --match 'v[0-9]*' --exclude '*-*' HEAD) - base=$(printf '%s' "${pin#v}" | awk -F. '{ printf "%d.%d.%d", $1, $2, $3 + 1 }') - branch=$(printf '%s' "${CIRCLE_BRANCH}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g') - stamp=$(TZ=UTC git show -s --format=%cd --date=format-local:%Y-%m-%d.%H-%M-%S "${CIRCLE_SHA1}") - version="${base}-dev.${branch}.${stamp}.h${CIRCLE_SHA1:0:7}" - echo "pin ${pin} -> dev base ${base}" - fi - if ! [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then - echo "ERROR: '${version}' is not a semver version" >&2 - exit 1 - fi - printf '%s' "${version}" | tee .version - echo - echo "export VERSION=$(cat .version)" >> "${BASH_ENV}" - - architect/go-cache-restore - - run: - name: Log in to the registry - # ko, oras and cosign read ~/.docker/config.json; the docker CLI's - # login needs no daemon but the file is all that is needed. - command: | - set -o errexit -o nounset -o pipefail - mkdir -p ~/.docker - auth=$(printf '%s:%s' "${ACR_GSOCI_USERNAME}" "${ACR_GSOCI_PASSWORD}" | base64 -w0) - printf '{"auths":{"gsoci.azurecr.io":{"auth":"%s"}}}' "${auth}" > ~/.docker/config.json - chmod 600 ~/.docker/config.json - - run: - name: Build and push the component images - # ko cross-compiles both platforms (no QEMU) onto the distroless base - # .ko.yaml pins and pushes one index per component under the version - # tag. Go itself is the version go.mod names (the toolchain is fetched - # into the module cache when the executor's is older). - no_output_timeout: 20m - command: | - set -o errexit -o nounset -o pipefail - go version - for component in ${COMPONENTS}; do - KO_DOCKER_REPO="${REGISTRY}/${component}" ./hack/run-tool.sh ko build \ - --bare --platform linux/amd64,linux/arm64 \ - --tags "${VERSION}" \ - "./cmd/${component}" - done - - run: - name: Resolve the pushed indexes and the agentgateway image - # image-refs.txt: ` @` per own image - # (the index the tag points to, checked to carry both platforms) and - # for the agentgateway image the chart deploys — the scan, the - # signing and the ledger read it. The own indexes are staged for - # signing. - command: | - set -o errexit -o nounset -o pipefail - : > image-refs.txt - : > /tmp/.cosign_refs - for component in ${COMPONENTS}; do - ref="${REGISTRY}/${component}:${VERSION}" - digest=$(oras manifest fetch --descriptor "${ref}" | jq -r '.digest') - platforms=$(oras manifest fetch "${ref}" | jq -r '[.manifests[] | select(.platform.os == "linux" and .platform.architecture != "unknown") | .platform.architecture] | sort | join(",")') - if [ "${platforms}" != "amd64,arm64" ]; then - echo "ERROR: ${ref} is not an index of linux/amd64 and linux/arm64 (found: ${platforms:-none})" >&2 - exit 1 - fi - echo "${component} ${REGISTRY}/${component}@${digest}" >> image-refs.txt - echo "${REGISTRY}/${component}@${digest}" >> /tmp/.cosign_refs - done - # atenet-router and atenet-egress run the Giant Swarm line's - # agentgateway (AGENTGATEWAY_IMAGE), not upstream's build; recorded - # by digest with the own images so the scan and the ledger see it. - digest=$(oras manifest fetch --descriptor "${AGENTGATEWAY_IMAGE}" | jq -r '.digest') - echo "agentgateway ${AGENTGATEWAY_IMAGE%:*}@${digest}" >> image-refs.txt - echo "Images ${VERSION}" - cat image-refs.txt - - architect/cosign-sign-verify - - architect/go-cache-save - - store_artifacts: - path: image-refs.txt - - persist_to_workspace: - root: . - paths: - - .version - - image-refs.txt - - .trivyignore - - scan: - docker: - - image: gsoci.azurecr.io/giantswarm/trivy:0.74.0 - resource_class: small - shell: /bin/sh - environment: + TRIVY_IMAGE: gsoci.azurecr.io/giantswarm/trivy:0.74.0 # The trivy database mirrors, tried in order (ghcr rate-limits anonymous pulls). TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db:2,ghcr.io/aquasecurity/trivy-db:2 steps: + - checkout - attach_workspace: at: . + - setup_remote_docker - run: - name: Scan every image by digest + name: Scan every image # Own images gate the charts: a fixable HIGH or CRITICAL finding # fails the publish (fix it — bump the module, or time-box it in # .trivyignore with an expiry when upstream has no fix). The # agentgateway image is the Giant Swarm line's build and reports # only here: its scan gates its own publish in # giantswarm/agentgateway-upstream, and a finding is fixed there. + # Trivy runs from its image on the remote docker engine, one + # container per image sharing one database cache; a bind mount does + # not reach a remote engine, so .trivyignore is copied in. + # image-refs.txt (` @`) records what was + # scanned, by digest, as the run's artifact. no_output_timeout: 20m command: | - set -eu + set -o errexit -o nounset -o pipefail + VERSION=$(cat .build_version) + refs="" + for component in ${COMPONENTS}; do + refs="${refs} ${component}=${REGISTRY}/${component}:${VERSION}" + done + refs="${refs} agentgateway=${AGENTGATEWAY_IMAGE}" + : > image-refs.txt failed=0 - while read -r name ref; do - [ -n "${name}" ] || continue + for entry in ${refs}; do + name="${entry%%=*}" + ref="${entry#*=}" + digest=$(oras manifest fetch --descriptor "${ref}" | jq -r '.digest') + echo "${name} ${ref%:*}@${digest}" >> image-refs.txt if [ "${name}" = agentgateway ]; then exit_code=0; else exit_code=1; fi - echo "==> ${name}: ${ref}" - if ! trivy image --severity CRITICAL,HIGH --ignore-unfixed --ignorefile .trivyignore \ - --exit-code "${exit_code}" --format table "${ref}"; then + echo "==> ${name}: ${ref} (${digest})" + docker create --name "trivy-${name}" \ + -e TRIVY_DB_REPOSITORY -e TRIVY_CACHE_DIR=/cache -v trivy-cache:/cache \ + "${TRIVY_IMAGE}" image --severity CRITICAL,HIGH --ignore-unfixed \ + --ignorefile /.trivyignore --exit-code "${exit_code}" --format table "${ref}" > /dev/null + docker cp .trivyignore "trivy-${name}:/.trivyignore" + if ! docker start --attach "trivy-${name}"; then echo "ERROR: ${name} carries a fixable HIGH or CRITICAL finding" >&2 failed=1 fi - done < image-refs.txt + done + echo "Images ${VERSION}" + cat image-refs.txt exit "${failed}" + - store_artifacts: + path: image-refs.txt charts: executor: architect/architect @@ -212,7 +131,7 @@ jobs: # signing. command: | set -o errexit -o nounset -o pipefail - VERSION=$(cat .version) + VERSION=$(cat .build_version) values=charts/substrate/values.yaml # The agentgateway stamp anchors on the key, not on upstream's value: # upstream moves that value between pins. @@ -265,23 +184,117 @@ jobs: path: chart-refs.txt workflows: + # The line's branch and its release tags: build, push, scan, then the charts. + # Exactly one push job persists the version to the workspace (CircleCI refuses + # to attach a path two concurrent jobs persisted); the other five run with + # persist-build-version false. publish: jobs: - - images: + - architect/push-to-registries: &publish-push + name: push-ateapi context: architect - filters: + image: giantswarm/substrate/ateapi + build-args: COMPONENT=ateapi + persist-build-version: true + dockerfile: .circleci/Dockerfile + platforms: linux/amd64,linux/arm64 + resource_class: large + registries-data: public gsoci.azurecr.io ACR_GSOCI_USERNAME ACR_GSOCI_PASSWORD true + # The upstream release this line is built from (FORK.md, "Pin"); moved with the pin. + annotations: index:io.giantswarm.upstream.version=v0.0.29 + filters: &publish-filters + branches: + only: giantswarm tags: - only: /^v.*/ + only: /^v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$/ + - architect/push-to-registries: + <<: *publish-push + name: push-atecontroller + image: giantswarm/substrate/atecontroller + build-args: COMPONENT=atecontroller + persist-build-version: false + - architect/push-to-registries: + <<: *publish-push + name: push-atelet + image: giantswarm/substrate/atelet + build-args: COMPONENT=atelet + persist-build-version: false + - architect/push-to-registries: + <<: *publish-push + name: push-atenet + image: giantswarm/substrate/atenet + build-args: COMPONENT=atenet + persist-build-version: false + - architect/push-to-registries: + <<: *publish-push + name: push-podcertcontroller + image: giantswarm/substrate/podcertcontroller + build-args: COMPONENT=podcertcontroller + persist-build-version: false + - architect/push-to-registries: + <<: *publish-push + name: push-ateom-gvisor + image: giantswarm/substrate/ateom-gvisor + build-args: COMPONENT=ateom-gvisor + persist-build-version: false - scan: requires: - - images - filters: - tags: - only: /^v.*/ + - push-ateapi + - push-atecontroller + - push-atelet + - push-atenet + - push-podcertcontroller + - push-ateom-gvisor + filters: *publish-filters - charts: context: architect requires: - scan - filters: - tags: - only: /^v.*/ + filters: *publish-filters + + # Every other branch: the same six builds for both platforms, nothing pushed. + validate: + jobs: + - architect/push-to-registries: &validate-push + name: build-ateapi + image: giantswarm/substrate/ateapi + build-args: COMPONENT=ateapi + push: false + persist-build-version: true + dockerfile: .circleci/Dockerfile + platforms: linux/amd64,linux/arm64 + resource_class: large + annotations: index:io.giantswarm.upstream.version=v0.0.29 + filters: &validate-filters + branches: + ignore: giantswarm + - architect/push-to-registries: + <<: *validate-push + name: build-atecontroller + image: giantswarm/substrate/atecontroller + build-args: COMPONENT=atecontroller + persist-build-version: false + - architect/push-to-registries: + <<: *validate-push + name: build-atelet + image: giantswarm/substrate/atelet + build-args: COMPONENT=atelet + persist-build-version: false + - architect/push-to-registries: + <<: *validate-push + name: build-atenet + image: giantswarm/substrate/atenet + build-args: COMPONENT=atenet + persist-build-version: false + - architect/push-to-registries: + <<: *validate-push + name: build-podcertcontroller + image: giantswarm/substrate/podcertcontroller + build-args: COMPONENT=podcertcontroller + persist-build-version: false + - architect/push-to-registries: + <<: *validate-push + name: build-ateom-gvisor + image: giantswarm/substrate/ateom-gvisor + build-args: COMPONENT=ateom-gvisor + persist-build-version: false diff --git a/.github/workflows/sync-upstream.yaml b/.github/workflows/sync-upstream.yaml index 5079aad287..a2572f65af 100644 --- a/.github/workflows/sync-upstream.yaml +++ b/.github/workflows/sync-upstream.yaml @@ -134,8 +134,15 @@ jobs: run: | set -o errexit -o nounset -o pipefail # The pin is the nearest upstream release tag reachable from the - # branch; the line's own tags carry a pre-release suffix and are excluded. - current=$(git describe --tags --abbrev=0 --match 'v[0-9]*' --exclude '*-*' HEAD) + # branch. Only upstream's tags count: the line's own tags (v1.0.0, …) + # version the package, not the software (FORK.md, "Versions"), and they + # share the v* namespace — a stable tag reachable from HEAD is the pin + # only when upstream has it. (upstream main is rebased onto + # agent-substrate and has no release tag as an ancestor, so the + # merge-base with it cannot serve.) + upstream_tags=$(git ls-remote --tags --refs upstream 'refs/tags/v[0-9]*' | awk -F/ '{ print $NF }') + current=$(git tag --merged HEAD --list 'v[0-9]*' | grep -v -- - | grep -Fx -f <(printf '%s\n' "${upstream_tags}") | sort -V | tail -1) + test -n "${current}" new="${INPUT_PIN:-${current}}" if ! git rev-parse --verify --quiet "${new}^{commit}" >/dev/null; then echo "::error::pin '${new}' is not a known upstream tag or commit" diff --git a/FORK.md b/FORK.md index 59eaa370c3..13d5946491 100644 --- a/FORK.md +++ b/FORK.md @@ -29,7 +29,7 @@ kagent line built the same way [giantswarm/giantswarm#37010](https://github.com/ | Why this one | `giantswarm/kagent-upstream` pins `github.com/kagent-dev/substrate v0.0.29` in `go/go.mod` (the `replace` of `github.com/agent-substrate/substrate`) since upstream kagent-dev/kagent#2802 (`2d843e37`, "upgrade Substrate to v0.0.29"): the ate-api gRPC contract between kagent's client and Substrate's server must match, and that kagent addresses an actor by the `ate-target-actor` header, which the router knows from v0.0.28 on — a kagent at or past #2802 on a 0.0.27 Substrate is green in CI and fails every turn on a cluster, so the two lines moved together (2026-09-14). | | agentgateway it runs | the `agentgateway-image` parameter of `.circleci/config.yml`, equal to the chart default `images.agentgateway` (the publish refuses a drift — the chart must install unstamped, and its egress config is written for this build's schema): a release of the agentgateway line from its gsoci copy, `gsoci.azurecr.io/giantswarm/agentgateway:vX.Y.Z-gs.N` (retagger's `renamed-agentgateway.yaml` mirrors the line's release tags there, the same digest as `ghcr.io/giantswarm/agentgateway-upstream/agentgateway`) — **`v1.5.1-gs.4`** = upstream agentgateway `main` @ `c1d24607` (2026-09-14, ≥ `9f9744cf`: #3237's CONNECT-time actor authorization, #3318's `substrateEgressActorResolution` frontend policy, #3409's substrate ingress header, #3428) + the line's patch admitting a `RESUMING` actor + its `GRPCRoute` method-match translation. Upstream's chart pins `ghcr.io/agentgateway/agentgateway:v0.0.0-alpha.9f9744cf` at v0.0.29 (kagent-dev/substrate#28, a nightly of that commit); the line runs the same protocol from its own scanned and signed build (the agentgateway line's `FORK.md`, "Convergence with the Substrate line"). | | When it moves | only together with kagent's pin, proven in agentlab first (`agentlab configure --defaults --chart-branch poc/kagent-main && agentlab up` and the proofs) — see "Re-pin". Not on a schedule. | -| Derived how | `git describe --tags --abbrev=0 --match 'v[0-9]*' --exclude '*-*' giantswarm` with upstream's tags fetched; the line's own tags carry a pre-release suffix and are excluded. The workflows compute it, nothing records it twice. | +| Derived how | the highest stable `v*` tag reachable from `giantswarm` that upstream also has (`git tag --merged` against `git ls-remote --tags upstream`): the line's own tags version the package (see "Versions") and share the `v*` namespace, so they never count — and upstream `main`, rebased onto agent-substrate, has no release tag as an ancestor, so a merge-base with it cannot serve. The sync workflow computes it; `.circleci/config.yml` states it once, as the annotation `io.giantswarm.upstream.version` every image carries. | ## Carried patches @@ -181,7 +181,9 @@ merged falls away by itself (`git rebase` drops already-applied patches). It is commit), then `git push --force-with-lease=refs/heads/giantswarm origin HEAD:giantswarm` and close the pull request. **Do not merge it** — the line is a rebased branch; a merge would fold the old pin back in. - `dry_run: true` does everything except the pushes; the run summary shows the outcome. -3. Update this file (pin, carried patches) in a pull request, and the Substrate rows of #37742. +3. In a pull request: this file (pin, carried patches), the pin annotation of the six push jobs in + `.circleci/config.yml` (`index:io.giantswarm.upstream.version=` — every image carries the pin from there), + and the Substrate rows of #37742. 4. Move the consumers to the new dev version (see "Consumers"), prove it in agentlab, then let the meta chart's pin and kagent-upstream follow. @@ -216,15 +218,19 @@ git push --force-with-lease=refs/heads/giantswarm origin HEAD:giantswarm ## Publishing The CircleCI pipeline `.circleci/config.yml` publishes to `gsoci.azurecr.io/giantswarm/substrate` on every push -to a branch and on every `v*` tag — `images` (ko, the six components; each index resolved from its tag and checked -to carry both platforms, then signed) → `scan` (Trivy, per image by digest) → `charts` (stamped, packaged, pushed, -signed). Nothing is ever pushed by hand, nothing publishes from GitHub Actions, and nothing pushes to ghcr.io: the -registry credentials exist in CircleCI's `architect` context only, and the signing identity is the pipeline's own. +to `giantswarm` (a dev build) and on every release tag `vX.Y.Z` or `vX.Y.Z-rc.N` — six `architect/push-to-registries` +jobs of the architect orb, one per component (built from `.circleci/Dockerfile` for linux/amd64 and linux/arm64; +the index signed, with provenance and an SPDX SBOM, annotated `io.giantswarm.upstream.version=`) → `scan` +(Trivy, per image) → `charts` (stamped, packaged, pushed, signed). The version is the orb's, from the git tags +(see "Versions"); the pipeline computes none. Every other branch runs the same six builds with `push: false`: a +pull request validates the Dockerfile for both platforms and nothing leaves the builder. Nothing is ever pushed by +hand, nothing publishes from GitHub Actions, and nothing pushes to ghcr.io: the registry credentials exist in +CircleCI's `architect` context only, and the signing identity is the pipeline's own. | Artifact | Name | |---|---| -| Control plane and node images | `gsoci.azurecr.io/giantswarm/substrate/{ateapi,atecontroller,atelet,atenet,podcertcontroller}:` — linux/amd64 + linux/arm64, built with ko from `./cmd/` on the distroless base `.ko.yaml` pins; the index signed | -| Worker image | `gsoci.azurecr.io/giantswarm/substrate/ateom-gvisor:` — the `WorkerPool.spec.workerImage` of the platform's pool; the index signed | +| Control plane and node images | `gsoci.azurecr.io/giantswarm/substrate/{ateapi,atecontroller,atelet,atenet,podcertcontroller}:` — linux/amd64 + linux/arm64, `.circleci/Dockerfile` with `COMPONENT=`: `./cmd/` compiled as ko compiles it (`CGO_ENABLED=0`, `-trimpath`, the vendored modules, the VCS revision embedded) at `/ko-app/` on the distroless base `.ko.yaml` pins, `/ko-app` on `PATH`, the binary the ENTRYPOINT; the index signed, with provenance and SBOM | +| Worker image | `gsoci.azurecr.io/giantswarm/substrate/ateom-gvisor:` — the `WorkerPool.spec.workerImage` of the platform's pool, built the same way; the index signed | | agentgateway | not built or mirrored here any more: `images.agentgateway` is stamped to a **release of the Giant Swarm line of agentgateway** from its gsoci copy — `gsoci.azurecr.io/giantswarm/agentgateway:vX.Y.Z-gs.N` (how it gets there is that line's `FORK.md`), the `agentgateway-image` parameter of `.circleci/config.yml` — which atenet-router and atenet-egress run ([giantswarm/agentgateway-upstream `FORK.md`](https://github.com/giantswarm/agentgateway-upstream/blob/giantswarm/FORK.md), tracking [giantswarm/giantswarm#37758](https://github.com/giantswarm/giantswarm/issues/37758)) | | Charts | `oci://gsoci.azurecr.io/giantswarm/substrate/helm/substrate-crds:`, `oci://gsoci.azurecr.io/giantswarm/substrate/helm/substrate:` — `image.registry` and `image.tag` stamped to this registry, `images.agentgateway` to the agentgateway line's release; `version` = `appVersion` = the image tag; each chart signed | @@ -234,33 +240,33 @@ tags and digests — under their `gsoci.azurecr.io/giantswarm/…` copies (`post agentgateway line's release; the carried patch above), except `amazon/aws-cli`, which stays the Docker Hub short name until the bucket-init Job can be recreated. -**Versions.** - -- Image tags of this line carry **no `v`** (ko's convention; upstream's do). The sibling agentgateway line keeps upstream's - `v` on its image tags (`v1.5.1-gs.1`) because its consumers and the retagger rules carry it — two deliberate choices, do not - "fix" one to match the other. -- Dev build, on every push to a branch: `-dev....h` - (for the pin v0.0.29: `0.0.30-dev.giantswarm.…`), the schema the kagent line uses — base = the pin's patch + 1, - branch lowercased to `[a-z0-9-]`, committer date in UTC, so a rebuild of the same commit yields the same version - and versions sort chronologically within the branch. Consumers that follow the channel use a Flux - `OCIRepository` with `semver: ">=0.0.30-0 <0.1.0-0"` and `semverFilter: ".*-dev\.giantswarm\..*"`; exact pins - name the full string. -- Release, on a tag `vX.Y.Z-gs.N` where `X.Y.Z` is upstream's **next** version (the dev base) and `N` counts the - line's releases of that pin: `v0.0.30-gs.1`. Ordering by semver: `0.0.30-dev.… < 0.0.30-gs.1 < 0.0.30`, so a dev - build never outranks a release, a fork release never outranks the upstream version it anticipates, and the - switch to an upstream tag one day is a range change, not a rename. A fleet consumer follows - `semverFilter: ".*-gs\..*"`. -- A pipeline triggered through the API with the `version` parameter publishes that string (for a one-off). +**Versions.** The line's versions are its own, decoupled from upstream's, as the company's semver rules for +packaged upstream software require (RFC "Semantic Versioning of Upstream Software"): the upstream release a +version is built from is documented here ("Pin") and carried by every image as the index annotation +`io.giantswarm.upstream.version`, never encoded in the version. + +- Release: a git tag `vX.Y.Z` (`vX.Y.Z-rc.N` for a candidate) publishes the images and both charts as `X.Y.Z` + (the tag without `v`; `appVersion` the same). **`1.0.0`** is the first release of the scheme — the next major + above the `0.0.30-gs.N` releases, which were coupled to upstream's next patch and are superseded. Consumers + follow a Flux `OCIRepository` range, `semver: ">=1.0.0 <2.0.0"`. +- Dev build, on every push to `giantswarm`: gitsemver's dev version — the next patch above the last release and a + pre-release identifier from the branch, the commit's committer time (UTC) and its short hash — so a rebuild of a + commit yields the same version, dev builds sort chronologically within the branch and below the release they + anticipate. The dev channel is selected with `semverFilter` on that pre-release shape; exact pins name the full + string. +- Image tags carry no `v`, as every Giant Swarm image tag does. **Signatures.** Every index and chart is signed keyless with cosign under the pipeline's CircleCI OIDC identity -(issuer `https://oidc.circleci.com`, subject `https://circleci.com/api/v2/projects//pipeline-definitions/`), -through the architect orb's `cosign-sign-verify` — the identity the platform's image policy trusts. To check one: +(issuer `https://oidc.circleci.com`, subject `https://circleci.com/api/v2/projects//pipeline-definitions/`) +— the images and their SBOM attestations by the orb's push job, the charts through the orb's `cosign-sign-verify` — +the identity the platform's image policy trusts. To check one: `cosign verify --certificate-oidc-issuer https://oidc.circleci.com --certificate-identity-regexp '^https://circleci\.com/api/v2/projects/[a-f0-9-]+/pipeline-definitions/[a-f0-9-]+$' @`. -**Digests.** Every pipeline prints `Images ` and `Charts ` with the digest of each pushed artifact -and stores them as the artifacts `image-refs.txt` (` @`, the agentgateway image included) -and `chart-refs.txt` of the `images` and `charts` jobs; the platform pins by tag and verifies by digest and signature -from there. Release digests are recorded here: +**Digests.** Every push job prints the digest of the index it pushed; the pipeline prints `Images ` and +`Charts ` with the digest of each artifact and stores them as the artifacts `image-refs.txt` +(` @`, the agentgateway image included) of the `scan` job and `chart-refs.txt` of the +`charts` job; the platform pins by tag and verifies by digest and signature from there. Release digests are recorded +here: | Release | Pin | Images and charts | |---|---|---| @@ -287,10 +293,15 @@ request and weekly. ## Consumers +Each consumer moves to the decoupled versions (`1.0.0` and up, "Versions") in its own change: the agent-platform +meta chart's ranges (giantswarm/agent-platform#580), the kagent line's `SUBSTRATE_VERSION` at its re-pin +(giantswarm/giantswarm#37872), agentlab's defaults (giantswarm/agentlab#229), and retagger's +`renamed-substrate.yaml` entries, retired (giantswarm/retagger#1238). + | Consumer | Where the pin lives | Selects | |---|---|---| | [agentlab](https://github.com/giantswarm/agentlab) | `internal/lab/substrate.go` (`substrateChartsRepo`, `substrateImageRegistry`, `substrateVersion`) | an exact dev version or release; installs `substrate-crds` + `substrate` and preloads the worker image | -| agent-platform meta chart 4.0 (`components.substrate` / `components.substrate-crds`, the `substrate:` values block; [giantswarm/agent-platform#342](https://github.com/giantswarm/agent-platform/issues/342)) | `helm/agent-platform/values.yaml`: the two components' version pins and `kagent.substrateWorkerPool.workerImage` | the `substrate-crds` + `substrate` charts at an exact dev version or release and the `ateom-gvisor` image at the same version (the `WorkerPool` the kagent chart renders) | +| agent-platform meta chart 4.0 (`components.substrate` / `components.substrate-crds`, the `substrate:` values block; [giantswarm/agent-platform#342](https://github.com/giantswarm/agent-platform/issues/342)) | `helm/agent-platform/values.yaml`: the two components' version ranges and `kagent.substrateWorkerPool.workerImage` | the `substrate-crds` + `substrate` charts by release range (`>=1.0.0 <2.0.0`) or at an exact dev version, and the `ateom-gvisor` image at the same version (the `WorkerPool` the kagent chart renders) | | [giantswarm/kagent-upstream](https://github.com/giantswarm/kagent-upstream) | `Makefile` `SUBSTRATE_REPO ?= oci://gsoci.azurecr.io/giantswarm/substrate/helm` (the kagent line follows the path with its own move to gsoci, giantswarm/giantswarm#37872), `SUBSTRATE_VERSION` | the `substrate`/`substrate-crds` chart dependencies of the kagent charts (off in the platform, which installs Substrate as cluster infrastructure) | ## Assets that are not images From 5364bd66dbcee5406d5737c2c853ade380d6bc64 Mon Sep 17 00:00:00 2001 From: Timo Derstappen Date: Sat, 19 Sep 2026 15:02:47 +0200 Subject: [PATCH 2/2] chart: run the agentgateway line's 2.0.0, its first decoupled release on gsoci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit images.agentgateway and the agentgateway-image pipeline parameter move from the retagger mirror gsoci.azurecr.io/giantswarm/agentgateway:v1.5.1-gs.4 to the line's own gsoci path, gsoci.azurecr.io/giantswarm/agentgateway-upstream/ agentgateway:2.0.0 (index sha256:63deaa67…, amd64 + arm64, signed; the same upstream build, main@c1d24607). The chart test asserts the new path and a stable version; FORK.md records the pin and the digest. Signed-off-by: Timo Derstappen --- .circleci/config.yml | 2 +- FORK.md | 6 +++--- charts/substrate/tests/images_test.yaml | 8 ++++---- charts/substrate/values.yaml | 12 ++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 259a7f9167..198871cf3a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,7 +44,7 @@ parameters: images. Moves only together with the chart default, and only with the pin (FORK.md, "Re-pin"); never bumped by Renovate on its own. type: string - default: gsoci.azurecr.io/giantswarm/agentgateway:v1.5.1-gs.4 + default: gsoci.azurecr.io/giantswarm/agentgateway-upstream/agentgateway:2.0.0 jobs: scan: diff --git a/FORK.md b/FORK.md index 13d5946491..d20fc2ca0d 100644 --- a/FORK.md +++ b/FORK.md @@ -27,7 +27,7 @@ kagent line built the same way [giantswarm/giantswarm#37010](https://github.com/ |---|---| | Upstream tag | **v0.0.29** (2026-09-12; tag commit `463f483c`, "feat(chart): configure OTLP export per signal (#37)" — upstream `main` of 2026-09-14). Previous pin: v0.0.26 (2026-09-10 → 2026-09-14, releases `v0.0.27-gs.1`–`gs.9`) | | Why this one | `giantswarm/kagent-upstream` pins `github.com/kagent-dev/substrate v0.0.29` in `go/go.mod` (the `replace` of `github.com/agent-substrate/substrate`) since upstream kagent-dev/kagent#2802 (`2d843e37`, "upgrade Substrate to v0.0.29"): the ate-api gRPC contract between kagent's client and Substrate's server must match, and that kagent addresses an actor by the `ate-target-actor` header, which the router knows from v0.0.28 on — a kagent at or past #2802 on a 0.0.27 Substrate is green in CI and fails every turn on a cluster, so the two lines moved together (2026-09-14). | -| agentgateway it runs | the `agentgateway-image` parameter of `.circleci/config.yml`, equal to the chart default `images.agentgateway` (the publish refuses a drift — the chart must install unstamped, and its egress config is written for this build's schema): a release of the agentgateway line from its gsoci copy, `gsoci.azurecr.io/giantswarm/agentgateway:vX.Y.Z-gs.N` (retagger's `renamed-agentgateway.yaml` mirrors the line's release tags there, the same digest as `ghcr.io/giantswarm/agentgateway-upstream/agentgateway`) — **`v1.5.1-gs.4`** = upstream agentgateway `main` @ `c1d24607` (2026-09-14, ≥ `9f9744cf`: #3237's CONNECT-time actor authorization, #3318's `substrateEgressActorResolution` frontend policy, #3409's substrate ingress header, #3428) + the line's patch admitting a `RESUMING` actor + its `GRPCRoute` method-match translation. Upstream's chart pins `ghcr.io/agentgateway/agentgateway:v0.0.0-alpha.9f9744cf` at v0.0.29 (kagent-dev/substrate#28, a nightly of that commit); the line runs the same protocol from its own scanned and signed build (the agentgateway line's `FORK.md`, "Convergence with the Substrate line"). | +| agentgateway it runs | the `agentgateway-image` parameter of `.circleci/config.yml`, equal to the chart default `images.agentgateway` (the publish refuses a drift — the chart must install unstamped, and its egress config is written for this build's schema): a release of the agentgateway line, published by that line from CircleCI under its own gsoci path and versioned on its own — **`gsoci.azurecr.io/giantswarm/agentgateway-upstream/agentgateway:2.0.0`** (index `sha256:63deaa67438dc7bd9ec5c1cd1521489c783b3345a303f29c70d742559b665339`, linux/amd64 + arm64, signed with the CircleCI identity, annotated `io.giantswarm.upstream.version=main@c1d24607`) = upstream agentgateway `main` @ `c1d24607` (2026-09-14, ≥ `9f9744cf`: #3237's CONNECT-time actor authorization, #3318's `substrateEgressActorResolution` frontend policy, #3409's substrate ingress header, #3428) + the line's patch admitting a `RESUMING` actor + its `GRPCRoute` method-match translation — the build the `v1.5.1-gs.4` mirror (`gsoci.azurecr.io/giantswarm/agentgateway`, retagger's copy of the line's ghcr tag) carried before. Upstream's chart pins `ghcr.io/agentgateway/agentgateway:v0.0.0-alpha.9f9744cf` at v0.0.29 (kagent-dev/substrate#28, a nightly of that commit); the line runs the same protocol from its own scanned and signed build (the agentgateway line's `FORK.md`, "Convergence with the Substrate line"). | | When it moves | only together with kagent's pin, proven in agentlab first (`agentlab configure --defaults --chart-branch poc/kagent-main && agentlab up` and the proofs) — see "Re-pin". Not on a schedule. | | Derived how | the highest stable `v*` tag reachable from `giantswarm` that upstream also has (`git tag --merged` against `git ls-remote --tags upstream`): the line's own tags version the package (see "Versions") and share the `v*` namespace, so they never count — and upstream `main`, rebased onto agent-substrate, has no release tag as an ancestor, so a merge-base with it cannot serve. The sync workflow computes it; `.circleci/config.yml` states it once, as the annotation `io.giantswarm.upstream.version` every image carries. | @@ -57,7 +57,7 @@ Everything on `giantswarm` that is not in the pin (`git log v0.0.29..giantswarm` | Make every pause durable: after `PauseActor` finalizes, ate-api uploads the pause snapshot in the background through the atelet path a paused-origin suspend uses (`UploadPausedCheckpointRequest.keep_local`), records the copy as `external_snapshot` with `source_local_snapshot_name` = the pause snapshot beside `local_snapshot_info`, and a resync every minute finds pauses without a copy; once the copy exists the snapshot's nodes are a placement preference (`scheduling.Constraints.PreferredNodes`) — local restore on the node, the copy (captured scope, memory included) anywhere else; an earlier suspend's snapshot never stands in (`durablePauseCopy`); a suspend of such an actor adopts the copy without dialing the node (the local copy pruned best-effort through the new atelet `PruneLocalCheckpoints`), delete collects both copies; the copy is committed under the actor's lease for two store round trips and `acquireActorLease` retries a held lease for about a second before `Aborted` | a session kagent parks on `input-required`/`auth-required` is paused, not suspended: its only snapshot lived on the worker's node and went with it on a spot interruption or a node roll — the next message failed after 60 s as `ResourceExhausted: no free workers available` for an idle pool and the actor could neither resume nor be suspended or deleted (gazelle 2026-09-15, [giantswarm/giantswarm#37795](https://github.com/giantswarm/giantswarm/issues/37795); [#37742](https://github.com/giantswarm/giantswarm/issues/37742) row 44, the durable-pause half — the fail-fast half is #35). Proven in a two-worker agentlab: the copy lands 60–280 ms after the pause checkpoint; with the pause's node stopped and deleted, the parked turn continues on the other node 4.6–13 s after the approval; an actor paused on the dead node is deletable; the fork's e2e suites caught two lease races before the final cut | `653de64e` ([#34](https://github.com/giantswarm/substrate/pull/34), rebase-merged 2026-09-15; `cmd/ateapi/internal/controlapi/durable_pause.go`, `workflow_{pause,resume,suspend,delete}.go`, `scheduling.go`, `cmd/atelet/main.go`, `ateapi.proto` `ExternalSnapshot.source_local_snapshot_name`, `atelet.proto` `UploadPausedCheckpointRequest.keep_local` + `PruneLocalCheckpoints`; first released as `v0.0.30-gs.3`) | prepared in the fork: branch [`upstream/durable-pause`](https://github.com/giantswarm/substrate/tree/upstream/durable-pause) (mirror `main` @ `463f483c` + the patch, `3f16aaaa`, DCO-signed, no fork references, without the carried lease-context semantics; its message is the pull-request text). agent-substrate/substrate#398 names the node-gone case and defers this remediation to a snapshot-model redesign nobody has opened; #401 (open) is the error message only. To be sent once #37742 has reviewed the list | | Bound the golden boot a workload keeps failing, and carry its cause up (the ActorTemplate reconciler counts the boots the workload itself fails — the failure reason in the workload fault domain, `WORKLOAD_NOT_READY` — in the new `GoldenSnapshotStatus.workload_boot_failures`, persisted before the verdict; at the third it crashes the golden actor, releasing its worker, and fails the template with the new reason `GoldenActorNotReady`, the bound and the last boot's own error; infrastructure failures of the resume stay retried without bound; reentrant across a pass that dies between count, crash and record. ateom keeps each application container's last output lines — `actorlog.OutputTail`, 12 lines cut at 512 bytes, fed by the log pipe both runtimes run — and `readyz` quotes them in the deadline error; the micro-VM runtime starts its log forwarding before the readiness wait) | a golden boot whose workload exits before its readiness probe answers is not a crash: ateom returned the missed deadline, the golden actor stayed `RESUMING` on its claimed worker, the reconciler retried, and the same boot ran once a minute for as long as the template existed with `error_message` never set — kagent reported `Ready=False ActorTemplatePending` indefinitely and the workload's own last lines (`fatal: could not read Username for 'https://github.com'`, the Go ADK's `failed to materialize Agent Plugins … exit status 128`) were only in the worker pod's log, which the agent's author cannot read. On gazelle (2026-09-15) a portal-created agent with three private skills and no credential looped about 120 boots over two hours until its HelmRelease was patched by hand ([giantswarm/giantswarm#37801](https://github.com/giantswarm/giantswarm/issues/37801); [#37742](https://github.com/giantswarm/giantswarm/issues/37742) row 47). Now `Ready=False ActorTemplateFailed` with `GoldenActorNotReady: … last boot: WORKLOAD_NOT_READY: readyz for "kagent" never returned 200 within 30s …; last output of "kagent":` and the lines, in about three minutes; the worker freed | `7a11e479` (as it is on `giantswarm` after the rebase merge; `e684b132` on the pull-request branch), [#39](https://github.com/giantswarm/substrate/pull/39) (rebase-merged 2026-09-15; first release v0.0.30-gs.4; `cmd/ateapi/internal/controlapi/template_reconciler.go`, `service.go`, `internal/actorlog/logger.go`, `internal/readyz/readyz.go`, `cmd/ateom-gvisor/main.go`, `cmd/ateom-microvm/{run,restore}.go`, `ateapi.proto` `GoldenSnapshotStatus.workload_boot_failures`) | to file: the upstream-shaped patch is branch [`upstream/golden-boot-bound`](https://github.com/giantswarm/substrate/tree/upstream/golden-boot-bound) here (`6a9e73f6`, on the mirror `main` @ `463f483c`, DCO-signed, no fork references, without the crash-directive branch of the carried image-pull patch; its message is the pull-request text). No upstream issue or pull request bounds the golden boot or carries the workload's output (searched kagent-dev/substrate and agent-substrate/substrate, 2026-09-15); agent-substrate/substrate#1518 (an actor stays `RESUMING` on a runsc error) is the nearest shape. A team member opens the pull request with DCO sign-off once #37742 has reviewed it | | The chart's Docker Hub image defaults name their registry (`images.postgres`, `images.rustfs`, `images.busybox` → `docker.io/library/postgres:…`, `docker.io/rustfs/rustfs:…`, `docker.io/library/busybox:1.36`; tags and digests unchanged). `images.awsCli` stays a short name: it is the `rustfs-bucket-init` Job's image and a Job's pod template is immutable, so a change fails every existing installation's upgrade with `field is immutable` until the completed Job is deleted by hand — #42 qualified it, [#43](https://github.com/giantswarm/substrate/pull/43) took that back; it moves with a change that lets the Job be recreated (a hook Job, or a template-hash name — the platform's network policies select it by `job-name`) | which registry a short name means is the node runtime's call, and only containerd defaults it to docker.io — CRI-O (short-name mode enforcing, the containers-common alias table only) refuses `rustfs/rustfs`, `postgres` and `amazon/aws-cli` with `ImageInspectError: … returns ambiguous list`. On spidertron (containerd Sparks, CRI-O control planes, no taints) a drain rescheduled rustfs onto a CRI-O node on 2026-09-17 and the snapshot store was gone for 25 min; the bundled postgres and the bucket-init Job carry the same exposure. The installation overrides `substrate.images.*` in the meta chart until a release of the line ships this. On the line the `docker.io` names are superseded by the gsoci copies (next row); this row's upstream-shaped form is what upstream gets | `908df564` ([#42](https://github.com/giantswarm/substrate/pull/42)), [#43](https://github.com/giantswarm/substrate/pull/43) | to file: the upstream-shaped patch is branch [`upstream/qualified-image-refs`](https://github.com/giantswarm/substrate/tree/upstream/qualified-image-refs) here (`27ae1f9`, on the mirror `main`); upstream's `manifests/ate-install/**` carry the same short names (rustfs, aws-cli, postgres; in `kind/` prometheus, otel, jaeger, envoy) and should travel with it | -| The chart's third-party image defaults are their `gsoci.azurecr.io/giantswarm/…` copies — `images.postgres` (`postgres:18.4-alpine@sha256:9a8afca5…`, the digest upstream pins as Docker Hub's floating `18-alpine`, under the versioned tag the mirror publishes it as), `images.rustfs` (`rustfs:1.0.0-beta.3@sha256:378642b0…`), `images.busybox` (`busybox:1.36`, rendered by no template), and `images.agentgateway` = `AGENTGATEWAY_IMAGE` (`agentgateway:v1.5.1-gs.4`, the mirror of the agentgateway line's release tag, `sha256:f3d4b52c…`) — tags and digests unchanged, the same bits. `images.awsCli` stays upstream's short name (the row above: the bucket-init Job's pod template is immutable); its copy `gsoci.azurecr.io/giantswarm/aws-cli:2.17.0` exists for the release that lets the Job be recreated. `charts/substrate/tests/images_test.yaml` pins the rendered images | every Giant Swarm installation pulls from gsoci.azurecr.io and its image policy trusts the Giant Swarm signing identity (giantswarm/agent-platform#575, #580): a chart default off gsoci is pulled from a public registry the fleet does not otherwise reach and no policy admits. Until this release the meta chart forwards the same references in its `substrate.images` block (giantswarm/agent-platform, values.yaml), which becomes redundant with it | [#45](https://github.com/giantswarm/substrate/pull/45) | **ours to keep**: a Giant Swarm registry is not upstream's default; the upstream-shaped change is the row above (`upstream/qualified-image-refs`) | +| The chart's third-party image defaults are their `gsoci.azurecr.io/giantswarm/…` copies — `images.postgres` (`postgres:18.4-alpine@sha256:9a8afca5…`, the digest upstream pins as Docker Hub's floating `18-alpine`, under the versioned tag the mirror publishes it as), `images.rustfs` (`rustfs:1.0.0-beta.3@sha256:378642b0…`), `images.busybox` (`busybox:1.36`, rendered by no template), and `images.agentgateway` = `AGENTGATEWAY_IMAGE` (the agentgateway line's own release on gsoci, `agentgateway-upstream/agentgateway:2.0.0`, `sha256:63deaa67…`; first the mirror `agentgateway:v1.5.1-gs.4`, `sha256:f3d4b52c…`, of the line's release tag — the same upstream build) — tags and digests unchanged, the same bits. `images.awsCli` stays upstream's short name (the row above: the bucket-init Job's pod template is immutable); its copy `gsoci.azurecr.io/giantswarm/aws-cli:2.17.0` exists for the release that lets the Job be recreated. `charts/substrate/tests/images_test.yaml` pins the rendered images | every Giant Swarm installation pulls from gsoci.azurecr.io and its image policy trusts the Giant Swarm signing identity (giantswarm/agent-platform#575, #580): a chart default off gsoci is pulled from a public registry the fleet does not otherwise reach and no policy admits. Until this release the meta chart forwards the same references in its `substrate.images` block (giantswarm/agent-platform, values.yaml), which becomes redundant with it | [#45](https://github.com/giantswarm/substrate/pull/45) | **ours to keep**: a Giant Swarm registry is not upstream's default; the upstream-shaped change is the row above (`upstream/qualified-image-refs`) | | Fork infrastructure: this file, the README pointer, `CODEOWNERS`, `.circleci/config.yml` (the publish to gsoci from CircleCI: the six ko images for both platforms, the Trivy gate, the two charts, everything signed with the organisation's CircleCI identity; with the `agentgateway-image` pin and the drift check against the chart default — nothing publishes from GitHub Actions and nothing pushes to ghcr.io; upstream's dispatch-only `release.yaml` is removed), `.github/workflows/sync-upstream.yaml`, `.trivyignore`, and the branch triggers of `pr-workflow.yaml`, `helm-e2e.yaml`, `govulncheck.yaml` (`main` → `giantswarm` and `sync/**` — a re-pin candidate is a rebased branch, so a pull request from it runs no `pull_request` workflow; the push runs the suites — govulncheck also on pull requests); the chart default `images.agentgateway` and its comment (`b726035c`, the rest of #9 after upstream #28 made the same frontend-policy move) | the line's CI, publishing and sync | the `giantswarm` branch history | not for upstream | Fourteen patches change Substrate ahead of upstream — egress for an actor while it resumes, without which no @@ -231,7 +231,7 @@ CircleCI's `architect` context only, and the signing identity is the pipeline's |---|---| | Control plane and node images | `gsoci.azurecr.io/giantswarm/substrate/{ateapi,atecontroller,atelet,atenet,podcertcontroller}:` — linux/amd64 + linux/arm64, `.circleci/Dockerfile` with `COMPONENT=`: `./cmd/` compiled as ko compiles it (`CGO_ENABLED=0`, `-trimpath`, the vendored modules, the VCS revision embedded) at `/ko-app/` on the distroless base `.ko.yaml` pins, `/ko-app` on `PATH`, the binary the ENTRYPOINT; the index signed, with provenance and SBOM | | Worker image | `gsoci.azurecr.io/giantswarm/substrate/ateom-gvisor:` — the `WorkerPool.spec.workerImage` of the platform's pool, built the same way; the index signed | -| agentgateway | not built or mirrored here any more: `images.agentgateway` is stamped to a **release of the Giant Swarm line of agentgateway** from its gsoci copy — `gsoci.azurecr.io/giantswarm/agentgateway:vX.Y.Z-gs.N` (how it gets there is that line's `FORK.md`), the `agentgateway-image` parameter of `.circleci/config.yml` — which atenet-router and atenet-egress run ([giantswarm/agentgateway-upstream `FORK.md`](https://github.com/giantswarm/agentgateway-upstream/blob/giantswarm/FORK.md), tracking [giantswarm/giantswarm#37758](https://github.com/giantswarm/giantswarm/issues/37758)) | +| agentgateway | not built or mirrored here: `images.agentgateway` is stamped to a **release of the Giant Swarm line of agentgateway**, published by that line from CircleCI under its own gsoci path — `gsoci.azurecr.io/giantswarm/agentgateway-upstream/agentgateway:` (how it gets there is that line's `FORK.md`), the `agentgateway-image` parameter of `.circleci/config.yml` — which atenet-router and atenet-egress run ([giantswarm/agentgateway-upstream `FORK.md`](https://github.com/giantswarm/agentgateway-upstream/blob/giantswarm/FORK.md), tracking [giantswarm/giantswarm#37758](https://github.com/giantswarm/giantswarm/issues/37758)) | | Charts | `oci://gsoci.azurecr.io/giantswarm/substrate/helm/substrate-crds:`, `oci://gsoci.azurecr.io/giantswarm/substrate/helm/substrate:` — `image.registry` and `image.tag` stamped to this registry, `images.agentgateway` to the agentgateway line's release; `version` = `appVersion` = the image tag; each chart signed | Not published from here: `ateom-microvm` and the demo images (the platform runs gVisor workers only), diff --git a/charts/substrate/tests/images_test.yaml b/charts/substrate/tests/images_test.yaml index 8dbf660199..f193890aff 100644 --- a/charts/substrate/tests/images_test.yaml +++ b/charts/substrate/tests/images_test.yaml @@ -68,7 +68,7 @@ tests: path: spec.template.spec.containers[0].image value: amazon/aws-cli:2.17.0@sha256:643507c10ada7964ca6157b3d799f030b90577643da9955d319a77399ed80d73 -- it: runs the atenet router's agentgateway from the gsoci copy of the agentgateway line's release +- it: runs the atenet router's agentgateway from a release of the agentgateway line on gsoci template: atenet-router.yaml documentSelector: path: kind @@ -79,9 +79,9 @@ tests: value: agentgateway - matchRegex: path: spec.template.spec.containers[1].image - pattern: ^gsoci\.azurecr\.io/giantswarm/agentgateway:v[0-9]+\.[0-9]+\.[0-9]+-gs\.[0-9]+$ + pattern: ^gsoci\.azurecr\.io/giantswarm/agentgateway-upstream/agentgateway:[0-9]+\.[0-9]+\.[0-9]+$ -- it: runs the atenet egress's agentgateway from the same gsoci copy +- it: runs the atenet egress's agentgateway from the same release template: atenet-egress.yaml documentSelector: path: kind @@ -92,7 +92,7 @@ tests: value: agentgateway - equal: path: spec.template.spec.containers[0].image - value: gsoci.azurecr.io/giantswarm/agentgateway:v1.5.1-gs.4 + value: gsoci.azurecr.io/giantswarm/agentgateway-upstream/agentgateway:2.0.0 - it: names a registry on every third-party default template: rustfs.yaml diff --git a/charts/substrate/values.yaml b/charts/substrate/values.yaml index f1a484bc21..5f8f72ede6 100644 --- a/charts/substrate/values.yaml +++ b/charts/substrate/values.yaml @@ -170,11 +170,11 @@ images: # is gsoci.azurecr.io/giantswarm/aws-cli:2.17.0, the same digest. awsCli: amazon/aws-cli:2.17.0@sha256:643507c10ada7964ca6157b3d799f030b90577643da9955d319a77399ed80d73 # The agentgateway build atenet-router and atenet-egress run: a release of the - # Giant Swarm line of agentgateway, from its gsoci copy (retagger mirrors the - # line's release tags to gsoci.azurecr.io/giantswarm/agentgateway). Its egress - # config (atenet-egress.yaml) is written for this build's config schema, so - # the two move together; the publish workflow stamps AGENTGATEWAY_IMAGE here - # and refuses a drift. - agentgateway: gsoci.azurecr.io/giantswarm/agentgateway:v1.5.1-gs.4 + # Giant Swarm line of agentgateway, published by that line from CircleCI to + # gsoci under its own path and versioned on its own (decoupled from upstream's + # versions, as this chart's are). Its egress config (atenet-egress.yaml) is + # written for this build's config schema, so the two move together; the + # publish pipeline stamps AGENTGATEWAY_IMAGE here and refuses a drift. + agentgateway: gsoci.azurecr.io/giantswarm/agentgateway-upstream/agentgateway:2.0.0 # Not rendered by any template of this chart; kept as upstream keeps it. busybox: gsoci.azurecr.io/giantswarm/busybox:1.36