From b7878ac26de583e4149c4e685afea78e2eb63daa Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Mon, 14 Sep 2026 07:24:49 -0400 Subject: [PATCH 1/2] =?UTF-8?q?fix(compose):=20point=20MinIO=20at=20quay.i?= =?UTF-8?q?o=20=E2=80=94=20the=20Docker=20Hub=20repository=20is=20gone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MinIO removed minio/minio from Docker Hub. As of 2026-09-14 the Hub API answers {"message":"object not found"} for the repository and every tag returns 401 — including RELEASE.2024-01-16T16-07-38Z, the pin nself build has been emitting. Authenticating does not help; the repository does not exist, it is not gated. Every generated stack with MINIO_ENABLED=true therefore fails to pull its storage image: pull access denied for minio/minio, repository does not exist or may require 'docker login' quay.io/minio/minio is MinIO's own registry and serves the same tags anonymously (:latest, RELEASE.2024-01-16T16-07-38Z and RELEASE.2024-10-02T17-50-41Z all 200). Both places that name the image now build from one MinioImagePath constant so they cannot drift apart, and two tests fail if either reverts to an unqualified name. Also corrects nhost/functions:0.3.7, found by the same sweep: that tag has never existed (nhost's 0.x line stops at 0.1.9; tags are now -). Nothing broke because the entry is unreachable — FUNCTIONS_VERSION defaults to "latest", so ResolveImage never falls back to the pin — and "latest" is what users actually run, so naming it changes no emitted compose file. Adds the gate that would have caught both: a daily workflow resolving every DefaultImageVersions entry with docker manifest inspect. The list comes from the map itself via tools/imagelist, so new pins are covered automatically. A manifest lookup, not a pull: no credentials, no layer cache to hide a dead pin. Verified locally — all 12 images resolve; reintroducing minio/minio makes the gate exit 1 and both new tests fail. --- .github/wiki/Config-Custom-Services.md | 2 +- .github/wiki/Core-Services.md | 2 +- .github/wiki/Service-Graph.md | 2 +- .github/workflows/default-images-pullable.yml | 82 +++++++++++++++++++ .../compose/custom_service_extras_test.go | 2 +- internal/compose/images.go | 45 ++++++++-- internal/compose/images_test.go | 54 ++++++++++++ internal/compose/optional_services.go | 4 +- .../custom_services_image_env_volumes_test.go | 4 +- internal/config/types_ops_plugins.go | 2 +- tools/imagelist/main.go | 43 ++++++++++ 11 files changed, 227 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/default-images-pullable.yml create mode 100644 tools/imagelist/main.go diff --git a/.github/wiki/Config-Custom-Services.md b/.github/wiki/Config-Custom-Services.md index 1668c20ec..a01b8c6f2 100644 --- a/.github/wiki/Config-Custom-Services.md +++ b/.github/wiki/Config-Custom-Services.md @@ -84,7 +84,7 @@ All variables use the pattern `CS_N_*` where `N` is the slot number (1–10). Va | `CS_N_ENV_PASSTHROUGH` | string | *(empty)* | Comma-separated allowlist of project `.env` var names to forward into this container in addition to the fixed core set. `CS_N_ENV` still wins on a name conflict. | | `CS_N_ENV_FILE` | string | *(empty)* | Project-relative path to a dotenv-format file whose `KEY=VALUE` lines are injected into this container. Applied after `CS_N_ENV_PASSTHROUGH`, before `CS_N_ENV`. Use this instead of `CS_N_ENV` when a value itself contains a comma (e.g. some SMTP passwords) or when there are too many vars for one line. A missing file fails `nself build` rather than silently starting the service without those vars. | | `CS_N_ENV` | string | *(empty)* | Additional env vars to inject, in `KEY=VALUE,KEY=VALUE` format. Always applied last — overrides the fixed core set, `CS_N_ENV_PASSTHROUGH`, and `CS_N_ENV_FILE`. | -| `CS_N_IMAGE` | string | *(empty)* | Run a pre-built image instead of building from a Dockerfile — e.g. `minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:...` to pin an exact digest. Mutually exclusive with `CS_N_PATH`; when set, no `build:` block is emitted at all. | +| `CS_N_IMAGE` | string | *(empty)* | Run a pre-built image instead of building from a Dockerfile — e.g. `quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:...` to pin an exact digest. Mutually exclusive with `CS_N_PATH`; when set, no `build:` block is emitted at all. | | `CS_N_VOLUMES` | string | *(empty)* | Comma-separated extra bind mounts in `host:container[:mode]` form, e.g. `./email-templates:/app/templates:ro`. Appended to the service's generated volume list. | All `CS_*` variables are automatically exempt from "unknown env var" warnings. diff --git a/.github/wiki/Core-Services.md b/.github/wiki/Core-Services.md index b4e4a8a75..534d863e7 100644 --- a/.github/wiki/Core-Services.md +++ b/.github/wiki/Core-Services.md @@ -28,7 +28,7 @@ Added to the stack only when their enabling variable is `true`. | `functions` | Serverless function runtime | `FUNCTIONS_ENABLED=true` | `FUNCTIONS_VERSION` | `nhost/functions:0.3.7` | | `mailpit` | Development SMTP catcher — captures outbound mail instead of sending it | `MAILPIT_ENABLED=true` | `MAILPIT_VERSION` | `axllent/mailpit:v1.15` | | `meilisearch` | Full-text search index (SEARCH_ENGINE=meilisearch, the default) | `SEARCH_ENABLED=true` | `MEILISEARCH_VERSION` | `getmeili/meilisearch:v1.6` | -| `minio` | S3-compatible object storage | `MINIO_ENABLED=true` | `MINIO_VERSION` | `minio/minio:RELEASE.2024-01-16T16-07-38Z` | +| `minio` | S3-compatible object storage | `MINIO_ENABLED=true` | `MINIO_VERSION` | `quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z` | | `typesense` | Full-text search index (SEARCH_ENGINE=typesense alternative) | `SEARCH_ENABLED=true` | `TYPESENSE_VERSION` | `typesense/typesense:0.25.2` | | `redis` | Cache and queue backend | `REDIS_ENABLED=true` | `REDIS_VERSION` | `redis:7.2-alpine` | diff --git a/.github/wiki/Service-Graph.md b/.github/wiki/Service-Graph.md index fa30dd4b4..73c979c30 100644 --- a/.github/wiki/Service-Graph.md +++ b/.github/wiki/Service-Graph.md @@ -51,7 +51,7 @@ Optional services are enabled by setting the corresponding environment variable | Service | Toggle | Image | Internal Port | Purpose | |---------|--------|-------|---------------|---------| | Redis | `REDIS_ENABLED=true` | `redis:7-alpine` | 6379 | Caching, session storage, job queues | -| Storage (MinIO) | `MINIO_ENABLED=true` | `minio/minio:latest` | 9000 (API), 9001 (console) | S3-compatible object storage | +| Storage (MinIO) | `MINIO_ENABLED=true` | `quay.io/minio/minio:latest` | 9000 (API), 9001 (console) | S3-compatible object storage | | Email | `MAILPIT_ENABLED=true` | `axllent/mailpit:latest` | 1025 (SMTP), 8025 (UI) | Email testing in development , not for production use | | Search | `SEARCH_ENABLED=true` | varies | 7700 (MeiliSearch) or 8108 (Typesense) | Full-text search indexing and querying | | Functions | `FUNCTIONS_ENABLED=true` | `nhost/functions:latest` | 3008 | Serverless function runtime | diff --git a/.github/workflows/default-images-pullable.yml b/.github/workflows/default-images-pullable.yml new file mode 100644 index 000000000..85d9e9767 --- /dev/null +++ b/.github/workflows/default-images-pullable.yml @@ -0,0 +1,82 @@ +name: Default images pullable + +# Every image `nself build` can emit must still exist in its registry. +# +# Added 2026-09-14 after MinIO deleted the `minio/minio` repository from Docker +# Hub. Every generated stack with MINIO_ENABLED=true stopped being able to pull +# its storage image, and nothing in this repo noticed: the pin was valid the day +# it was written, and no test or gate re-checks a pin after it lands. The same +# sweep found nhost/functions:0.3.7, a tag that never existed at all. +# +# This gate closes that class of failure. It is deliberately a manifest lookup, +# not a pull: it costs a few HTTP requests, needs no registry credentials, and +# cannot be defeated by a warm layer cache on the runner. +# +# It runs on a schedule because the failure is external — a pin that passed +# yesterday can break today with no commit in this repo — and on pushes that +# touch the image list so a bad pin is caught before it ships. + +on: + schedule: + - cron: '0 5 * * *' + push: + branches: [main] + paths: + - 'internal/compose/images.go' + - 'tools/imagelist/**' + - '.github/workflows/default-images-pullable.yml' + pull_request: + paths: + - 'internal/compose/images.go' + - 'tools/imagelist/**' + - '.github/workflows/default-images-pullable.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + check: + name: Every default image resolves + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + cache: false + + - name: Check every default image manifest resolves + run: | + set -uo pipefail + + # The list comes from compose.DefaultImageVersions itself, so a new + # pin is covered the moment it lands and there is no second list. + go run ./tools/imagelist > /tmp/default-images.tsv + if [ ! -s /tmp/default-images.tsv ]; then + echo "::error::imagelist produced no images — the generator or the map is broken" + exit 1 + fi + + failed=0 + while IFS="$(printf '\t')" read -r service image; do + [ -n "${image}" ] || continue + if err=$(docker manifest inspect "${image}" 2>&1 >/dev/null); then + echo "ok ${service} ${image}" + else + failed=1 + echo "FAIL ${service} ${image}" + echo "::error title=${service} image is not pullable::${image} — ${err}" + fi + done < /tmp/default-images.tsv + + echo + if [ "${failed}" -ne 0 ]; then + echo "One or more images in internal/compose/images.go no longer resolve." + echo "Fix the pin (a registry move needs the new registry-qualified path," + echo "e.g. quay.io/minio/minio), do NOT delete the entry to make this pass." + exit 1 + fi + echo "All default images resolve." diff --git a/internal/compose/custom_service_extras_test.go b/internal/compose/custom_service_extras_test.go index 957be73cc..4ad4befc6 100644 --- a/internal/compose/custom_service_extras_test.go +++ b/internal/compose/custom_service_extras_test.go @@ -24,7 +24,7 @@ func TestBuildCustomService_ImageSkipsBuild(t *testing.T) { cfg := minimalConfigWithCS() g := NewGenerator(cfg) cs := testCS() - cs.Image = "minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123" + cs.Image = "quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123" svc, err := g.buildCustomService(cs) if err != nil { diff --git a/internal/compose/images.go b/internal/compose/images.go index 7462339a8..3a8def9b2 100644 --- a/internal/compose/images.go +++ b/internal/compose/images.go @@ -16,16 +16,47 @@ import ( // Intentionally referencing nself/nself-admin (Docker Hub) — never github.com/nself-org/ paths. const AdminImagePath = "nself/nself-admin" +// MinioImagePath is the registry path for the MinIO object-storage image. +// +// It is REGISTRY-QUALIFIED and must stay that way. MinIO removed the +// `minio/minio` repository from Docker Hub: as of 2026-09-14 the Hub API +// returns `{"message":"object not found"}` for it and every tag — including +// long-published pins such as RELEASE.2024-01-16T16-07-38Z — answers 401 to +// both anonymous and authenticated manifest requests. A `docker pull` of it +// fails with "pull access denied for minio/minio, repository does not exist +// or may require 'docker login'", so every generated stack with +// MINIO_ENABLED=true could no longer start. Authenticating does NOT help; +// the repository is gone, not gated. +// +// quay.io/minio/minio is MinIO's own registry and serves the same tags +// anonymously (verified 2026-09-14: :latest, RELEASE.2024-01-16T16-07-38Z +// and RELEASE.2024-10-02T17-50-41Z all return 200). +// +// Both the DefaultImageVersions pin below and buildMinioService's +// MINIO_VERSION path must build from this constant so the two cannot drift +// back to an unqualified Docker Hub name. +const MinioImagePath = "quay.io/minio/minio" + // DefaultImageVersions maps service name to pinned image:tag. // Update with each nSelf release. var DefaultImageVersions = map[string]string{ - "postgres": "pgvector/pgvector:pg16", - "hasura": "hasura/graphql-engine:v2.44.0", - "auth": "nhost/hasura-auth:0.36.0", - "nginx": "nginx:1.25-alpine", - "redis": "redis:7.2-alpine", - "minio": "minio/minio:RELEASE.2024-01-16T16-07-38Z", - "functions": "nhost/functions:0.3.7", + "postgres": "pgvector/pgvector:pg16", + "hasura": "hasura/graphql-engine:v2.44.0", + "auth": "nhost/hasura-auth:0.36.0", + "nginx": "nginx:1.25-alpine", + "redis": "redis:7.2-alpine", + "minio": MinioImagePath + ":RELEASE.2024-01-16T16-07-38Z", + // nhost/functions:0.3.7 never existed. nhost's 0.x line stops at 0.1.9 and + // the repository now tags as - (22-2.2.0, 26-2.2.0); + // `docker manifest inspect nhost/functions:0.3.7` answers "no such + // manifest". Nothing broke because this entry is unreachable in practice: + // applyDefaultsFunctions sets FUNCTIONS_VERSION to "latest" when unset, so + // buildFunctionsService always passes a non-empty image and ResolveImage + // never falls back to this pin. "latest" is therefore what users actually + // run, and naming it here makes the pin honest without changing any + // emitted compose file. Choosing a real pinned tag is an upgrade decision + // (latest is not any of the current 2.2.0 tags), not a drive-by edit. + "functions": "nhost/functions:latest", "mailpit": "axllent/mailpit:v1.15", "meilisearch": "getmeili/meilisearch:v1.6", "typesense": "typesense/typesense:0.25.2", diff --git a/internal/compose/images_test.go b/internal/compose/images_test.go index 25f2e7396..b74a1472e 100644 --- a/internal/compose/images_test.go +++ b/internal/compose/images_test.go @@ -140,3 +140,57 @@ func TestDefaultImageVersions_NoGoModulePaths(t *testing.T) { } } } + +// TestMinioImageIsRegistryQualified guards the fix for the 2026-09-14 storage +// outage: MinIO deleted the `minio/minio` repository from Docker Hub, so an +// unqualified reference resolves to a repository that no longer exists and +// every generated stack with MINIO_ENABLED=true failed to pull. The Hub API +// returns "object not found" and every tag answers 401, to anonymous and +// authenticated requests alike, so this is not something a docker login fixes. +// +// Both places that name the image must stay pointed at MinIO's own registry: +// the DefaultImageVersions pin (used when no MINIO_VERSION is set) and +// buildMinioService's MINIO_VERSION path. A bare "minio/minio:..." in either +// is the regression this test exists to catch. +func TestMinioImageIsRegistryQualified(t *testing.T) { + const wantPrefix = "quay.io/minio/minio:" + + if !strings.HasPrefix(MinioImagePath+":", wantPrefix) { + t.Fatalf("MinioImagePath = %q, want %q without the tag", MinioImagePath, "quay.io/minio/minio") + } + + pinned, ok := DefaultImageVersions["minio"] + if !ok { + t.Fatal(`DefaultImageVersions has no "minio" entry`) + } + if !strings.HasPrefix(pinned, wantPrefix) { + t.Errorf("DefaultImageVersions[\"minio\"] = %q, want prefix %q", pinned, wantPrefix) + } +} + +// TestBuildMinioService_UsesQuayRegistry covers the MINIO_VERSION path, which +// formats its own image string and so can drift away from the pin above +// independently. Both an explicit version and the empty-version default are +// checked, because the empty case builds "…:latest" rather than falling +// through to DefaultImageVersions. +func TestBuildMinioService_UsesQuayRegistry(t *testing.T) { + for _, tc := range []struct { + name string + version string + want string + }{ + {"explicit version", "RELEASE.2024-10-02T17-50-41Z", "quay.io/minio/minio:RELEASE.2024-10-02T17-50-41Z"}, + {"empty version defaults to latest", "", "quay.io/minio/minio:latest"}, + } { + t.Run(tc.name, func(t *testing.T) { + g := &Generator{cfg: &config.Config{ + ProjectName: "testproject", + DockerNetwork: "testproject_network", + Minio: config.MinioConfig{Enabled: true, Version: tc.version}, + }} + if got := g.buildMinioService().Image; got != tc.want { + t.Errorf("buildMinioService().Image = %q, want %q", got, tc.want) + } + }) + } +} diff --git a/internal/compose/optional_services.go b/internal/compose/optional_services.go index b54e9c89c..8636e6b2d 100644 --- a/internal/compose/optional_services.go +++ b/internal/compose/optional_services.go @@ -91,7 +91,9 @@ func (g *Generator) buildMinioService() ServiceConfig { } return ServiceConfig{ - Image: ResolveImage("minio", fmt.Sprintf("minio/minio:%s", version)), + // MinioImagePath, never a bare "minio/minio" — that Docker Hub + // repository no longer exists. See the constant's doc comment. + Image: ResolveImage("minio", fmt.Sprintf("%s:%s", MinioImagePath, version)), ContainerName: fmt.Sprintf("%s_minio", g.cfg.ProjectName), Restart: "unless-stopped", Networks: []string{g.cfg.DockerNetwork}, diff --git a/internal/config/custom_services_image_env_volumes_test.go b/internal/config/custom_services_image_env_volumes_test.go index 8b5860aad..c49e3c4cf 100644 --- a/internal/config/custom_services_image_env_volumes_test.go +++ b/internal/config/custom_services_image_env_volumes_test.go @@ -25,7 +25,7 @@ func clearOtherCSSlots(t *testing.T, keep int) { // pinned minio image (G-013 evidence row 1). func TestCustomServicesImage_Valid(t *testing.T) { t.Setenv("CS_1", "email-storage:go") - t.Setenv("CS_1_IMAGE", "minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123") + t.Setenv("CS_1_IMAGE", "quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123") clearOtherCSSlots(t, 1) services, err := parseCustomServices() @@ -35,7 +35,7 @@ func TestCustomServicesImage_Valid(t *testing.T) { if len(services) == 0 { t.Fatal("expected at least one custom service") } - want := "minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123" + want := "quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123" if got := services[0].Image; got != want { t.Errorf("Image = %q, want %q", got, want) } diff --git a/internal/config/types_ops_plugins.go b/internal/config/types_ops_plugins.go index b7aa08ec9..c18a4696b 100644 --- a/internal/config/types_ops_plugins.go +++ b/internal/config/types_ops_plugins.go @@ -163,7 +163,7 @@ type CustomService struct { EnvPassthrough string // Image is CS_N_IMAGE: a pre-built image reference (optionally digest-pinned, - // e.g. "minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:...") to run instead + // e.g. "quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:...") to run instead // of building from a Dockerfile. When set, the compose generator emits // `image:` and omits `build:` entirely — mutually exclusive with // CS_N_PATH (G-013: closes the gap where a pinned third-party image had diff --git a/tools/imagelist/main.go b/tools/imagelist/main.go new file mode 100644 index 000000000..2139f265d --- /dev/null +++ b/tools/imagelist/main.go @@ -0,0 +1,43 @@ +// Command imagelist prints every default service image `nself build` can emit. +// +// Purpose: give CI a single, authoritative list to check for pullability +// without restating the images anywhere. It reads them straight from +// compose.DefaultImageVersions, so a new or changed pin is covered the moment +// it lands — there is no second list to forget to update. +// +// Inputs: none. +// +// Outputs: one "\t" line per service on stdout, sorted by +// service name so the output is stable and diffable. +// +// Constraints: prints only the DEFAULT pins. Images a user selects through env +// (POSTGRES_IMAGE, a custom CS_N_IMAGE) are that operator's choice and are +// deliberately out of scope. +// +// SPORT: consumed by .github/workflows/default-images-pullable.yml +package main + +import ( + "fmt" + "os" + "sort" + + "github.com/nself-org/cli/internal/compose" +) + +func main() { + services := make([]string, 0, len(compose.DefaultImageVersions)) + for service := range compose.DefaultImageVersions { + services = append(services, service) + } + sort.Strings(services) + + for _, service := range services { + fmt.Printf("%s\t%s\n", service, compose.DefaultImageVersions[service]) + } + + if len(services) == 0 { + fmt.Fprintln(os.Stderr, "imagelist: DefaultImageVersions is empty") + os.Exit(1) + } +} From f82f5f5605cd969653f2260c726d5e73da989954 Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Mon, 14 Sep 2026 07:29:42 -0400 Subject: [PATCH 2/2] chore: regenerate Core-Services.md; drop the cosmetic internal/config edits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Core-Services.md is generated (scripts/sport/generate-core-services.sh) and internal/repoqa asserts it is current; hand-editing the minio row made TestCoreServicesPageIsCurrent fail. Regenerated with `make core-services`, which also picked up the functions row I had missed. The two internal/config changes are reverted. They only swapped a dead image out of a doc comment and a test literal, neither of which is an env-var change — but doc-sync keys off any path under internal/config/ and demanded an env-var doc update. Editing Config-Env-Vars.md to satisfy that would be a docs edit with nothing true to say, so the cosmetic changes go instead. The user-facing CS_N_IMAGE example in the wiki is still corrected. --- .github/wiki/Core-Services.md | 2 +- internal/config/custom_services_image_env_volumes_test.go | 4 ++-- internal/config/types_ops_plugins.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/wiki/Core-Services.md b/.github/wiki/Core-Services.md index 534d863e7..2f7cca517 100644 --- a/.github/wiki/Core-Services.md +++ b/.github/wiki/Core-Services.md @@ -25,7 +25,7 @@ Added to the stack only when their enabling variable is `true`. | Service | Purpose | Enable with | Version override | Default image | |---|---|---|---|---| | `nself-admin` | nSelf Admin web UI (localhost only; never deployed) | `NSELF_ADMIN_ENABLED=true` | `NSELF_ADMIN_VERSION` | `nself/nself-admin:latest` | -| `functions` | Serverless function runtime | `FUNCTIONS_ENABLED=true` | `FUNCTIONS_VERSION` | `nhost/functions:0.3.7` | +| `functions` | Serverless function runtime | `FUNCTIONS_ENABLED=true` | `FUNCTIONS_VERSION` | `nhost/functions:latest` | | `mailpit` | Development SMTP catcher — captures outbound mail instead of sending it | `MAILPIT_ENABLED=true` | `MAILPIT_VERSION` | `axllent/mailpit:v1.15` | | `meilisearch` | Full-text search index (SEARCH_ENGINE=meilisearch, the default) | `SEARCH_ENABLED=true` | `MEILISEARCH_VERSION` | `getmeili/meilisearch:v1.6` | | `minio` | S3-compatible object storage | `MINIO_ENABLED=true` | `MINIO_VERSION` | `quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z` | diff --git a/internal/config/custom_services_image_env_volumes_test.go b/internal/config/custom_services_image_env_volumes_test.go index c49e3c4cf..8b5860aad 100644 --- a/internal/config/custom_services_image_env_volumes_test.go +++ b/internal/config/custom_services_image_env_volumes_test.go @@ -25,7 +25,7 @@ func clearOtherCSSlots(t *testing.T, keep int) { // pinned minio image (G-013 evidence row 1). func TestCustomServicesImage_Valid(t *testing.T) { t.Setenv("CS_1", "email-storage:go") - t.Setenv("CS_1_IMAGE", "quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123") + t.Setenv("CS_1_IMAGE", "minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123") clearOtherCSSlots(t, 1) services, err := parseCustomServices() @@ -35,7 +35,7 @@ func TestCustomServicesImage_Valid(t *testing.T) { if len(services) == 0 { t.Fatal("expected at least one custom service") } - want := "quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123" + want := "minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:abc123" if got := services[0].Image; got != want { t.Errorf("Image = %q, want %q", got, want) } diff --git a/internal/config/types_ops_plugins.go b/internal/config/types_ops_plugins.go index c18a4696b..b7aa08ec9 100644 --- a/internal/config/types_ops_plugins.go +++ b/internal/config/types_ops_plugins.go @@ -163,7 +163,7 @@ type CustomService struct { EnvPassthrough string // Image is CS_N_IMAGE: a pre-built image reference (optionally digest-pinned, - // e.g. "quay.io/minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:...") to run instead + // e.g. "minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:...") to run instead // of building from a Dockerfile. When set, the compose generator emits // `image:` and omits `build:` entirely — mutually exclusive with // CS_N_PATH (G-013: closes the gap where a pinned third-party image had