Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference
#
# NOTE: Dependabot groups cannot span ecosystems, so the Docker `golang` image
# and the go.mod `go`/`toolchain` directives are updated by separate PRs and can
# drift apart. The risk is one-directional: if go.mod asks for a newer Go than
# the builder image ships, the build pulls a toolchain over the network at image
# build time, and fails outright when offline or under GOTOOLCHAIN=local. The
# reverse (image newer than go.mod) is fine, since a newer toolchain builds an
# older `go` directive. `make verify-go-version` enforces that ordering.
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 10
labels:
- dependencies
groups:
k8s.io:
patterns:
- "k8s.io/*"
sigs.k8s.io:
patterns:
- "sigs.k8s.io/*"

# Keeps the `golang` builder image current.
- package-ecosystem: docker
directory: /
schedule:
interval: weekly
labels:
- dependencies

# Dockerfile for the mkdocs image used to build the docs site.
- package-ecosystem: docker
directory: /hack/mkdocs/image
schedule:
interval: monthly
labels:
- dependencies

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
labels:
- dependencies
groups:
github-actions:
patterns:
- "*"
88 changes: 0 additions & 88 deletions .github/renovate-config.js

This file was deleted.

35 changes: 0 additions & 35 deletions .github/workflows/renovate.yml

This file was deleted.

10 changes: 10 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,13 @@ jobs:

- name: Verify generated code is up-to-date
run: make verify

verify-go-version:
name: Verify Go version alignment
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
Comment on lines +29 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- workflow ---'
cat -n .github/workflows/verify.yml

printf '%s\n' '--- referenced verification files ---'
for f in Makefile hack/verify-go-version.sh; do
  if [ -f "$f" ]; then
    printf '\n--- %s ---\n' "$f"
    cat -n "$f"
  fi
done

printf '%s\n' '--- workflow-related references ---'
rg -n --hidden -S \
  'verify-go-version|persist-credentials|permissions:|pull_request|pull_request_target|actions/checkout@' \
  .github Makefile hack 2>/dev/null || true

printf '%s\n' '--- checkout pin metadata ---'
git ls-remote --tags https://github.com/actions/checkout.git \
  'refs/tags/v6.0.3' 'refs/tags/v6.0.3^{}' 2>/dev/null || true

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 25010


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

path = Path(".github/workflows/verify.yml")
lines = path.read_text().splitlines()

# Read the workflow as data and report the security-relevant fields without
# executing YAML, Make, or repository scripts.
print("--- pull_request trigger and permissions ---")
for i, line in enumerate(lines, 1):
    if re.match(r"^(on:|  pull_request:|permissions:|  contents:)", line):
        print(f"{i}: {line}")

print("--- checkout steps and following inputs ---")
for i, line in enumerate(lines):
    if re.match(r"^\s+uses:\s+actions/checkout@", line):
        job = next(
            (lines[j].strip() for j in range(i - 1, -1, -1)
             if re.match(r"^  [A-Za-z0-9_-]+:$", lines[j])),
            "<unknown job>",
        )
        print(f"line {i+1}, job {job}, {line.strip()}")
        j = i + 1
        inputs = []
        while j < len(lines) and (lines[j].startswith(" ") or not lines[j].strip()):
            if re.match(r"^\s{10}\S", lines[j]) or re.match(r"^\s{8}with:$", lines[j]):
                inputs.append(f"{j+1}: {lines[j]}")
            j += 1
        for item in inputs:
            print(item)
        if not any("persist-credentials:" in item for item in inputs):
            print("persist-credentials: <action default applies>")

print("--- commands after checkout ---")
for i, line in enumerate(lines, 1):
    if re.match(r"^\s+run:\s+", line):
        print(f"{i}: {line.strip()}")
PY

printf '%s\n' '--- pinned checkout action default ---'
curl -fsSL \
  https://raw.githubusercontent.com/actions/checkout/df4cb1c069e1874edd31b4311f1884172cec0e10/action.yml |
  grep -n -A3 -B2 'persist-credentials'

printf '%s\n' '--- other verification workflow permissions and checkout settings ---'
for f in .github/workflows/test.yml .github/workflows/lint.yml \
         .github/workflows/test-e2e.yml .github/workflows/govulncheck.yml \
         .github/workflows/build-mcp-everything.yml; do
  if [ -f "$f" ]; then
    printf '\n--- %s ---\n' "$f"
    rg -n -A3 -B2 'permissions:|contents:|actions/checkout@|persist-credentials:|run:' "$f"
  fi
done

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 4294


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- checkout credential handling at the pinned commit ---'
curl -fsSL \
  https://raw.githubusercontent.com/actions/checkout/df4cb1c069e1874edd31b4311f1884172cec0e10/src/git-auth-helper.ts |
  rg -n -A8 -B8 \
    'persist-credentials|extraheader|configureAuth|removeGitConfigAuth'

printf '%s\n' '--- workflow permission keys ---'
python3 - <<'PY'
from pathlib import Path
import re

lines = Path(".github/workflows/verify.yml").read_text().splitlines()
start = next(i for i, line in enumerate(lines) if line == "permissions:")
end = next((i for i in range(start + 1, len(lines))
            if lines[i] and not lines[i].startswith(" ")), len(lines))
print("\n".join(f"{i+1}: {lines[i]}" for i in range(start, end)))

print("--- checkout count and explicit credential settings ---")
checkouts = [i + 1 for i, line in enumerate(lines)
             if "uses: actions/checkout@" in line]
explicit = [i + 1 for i, line in enumerate(lines)
            if "persist-credentials:" in line]
print("checkout_lines=", checkouts)
print("explicit_persist_credentials_lines=", explicit)
PY

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 2280


Disable persisted checkout credentials in both jobs.

Both actions/checkout steps use the default persist-credentials: true, while pull_request jobs execute repository-controlled Make targets. Set persist-credentials: false on both steps. The workflow already grants only contents: read, which is sufficient for checkout.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 29-30: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/verify.yml around lines 29 - 30, Update both
actions/checkout steps in the workflow jobs to explicitly set
persist-credentials to false, while preserving the existing checkout
configuration and contents: read permissions.

Sources: MCP tools, Linters/SAST tools


- name: Verify the Dockerfile is not older than go.mod
run: make verify-go-version
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# https://github.com/kubernetes-sigs/kubebuilder/blob/v4.11.1/pkg/plugins/golang/v4/scaffolds/internal/templates/dockerfile.go

# Build the manager binary
FROM --platform=${BUILDPLATFORM} golang:1.26.4 AS builder
FROM --platform=${BUILDPLATFORM} golang:1.26.5 AS builder
ARG TARGETOS
ARG TARGETARCH

Expand All @@ -25,7 +25,7 @@ COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager ./cmd

# Build the manager binary with debug symbols
FROM golang:1.26.4 AS debug-builder
FROM golang:1.26.5 AS debug-builder
ARG TARGETOS
ARG TARGETARCH

Expand All @@ -39,7 +39,7 @@ COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -gcflags="all=-N -l" -o manager ./cmd

# Debug image with Delve
FROM golang:1.26.4 AS debug
FROM golang:1.26.5 AS debug
RUN go install github.com/go-delve/delve/cmd/dlv@v1.26.3
WORKDIR /
COPY --from=debug-builder /workspace/manager .
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ verify: manifests generate fmt ## Verify generated code and formatting are up-to
echo "Generated code and formatting are up-to-date."; \
fi

.PHONY: verify-go-version
verify-go-version: ## Verify the Dockerfile's Go version is not older than go.mod requires.
./hack/verify-go-version.sh

.PHONY: lint
lint: golangci-lint ## Run golangci-lint linter
"$(GOLANGCI_LINT)" run
Expand Down
87 changes: 87 additions & 0 deletions hack/verify-go-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
# Verifies that the Go version in the Dockerfile is not older than the version
# go.mod asks for.
#
# A newer toolchain builds an older `go` directive, so the builder image running
# ahead of go.mod is fine. The reverse is not: if go.mod asks for a newer Go than
# the image ships, `go build` downloads a toolchain over the network mid-build,
# and fails outright when offline or under GOTOOLCHAIN=local.

set -o errexit
set -o nounset
set -o pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DOCKERFILE="${REPO_ROOT}/Dockerfile"
GO_MOD="${REPO_ROOT}/go.mod"

# Every `FROM golang:<version>` in the Dockerfile must agree, otherwise "the"
# image version is ambiguous and the stages would build against different Gos.
mapfile -t image_versions < <(grep -oE '^FROM.* golang:[0-9]+(\.[0-9]+)*' "${DOCKERFILE}" |
sed -E 's/.*golang://' | sort -u)
Comment on lines +20 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- script ---'
cat -n hack/verify-go-version.sh

printf '%s\n' '--- related files ---'
git ls-files | rg '(^|/)(Dockerfile|.*Dockerfile.*|verify-go-version\.sh)$' || true

printf '%s\n' '--- references ---'
rg -n --glob '!node_modules' --glob '!dist' 'verify-go-version|golang:' . || true

printf '%s\n' '--- regex behavior ---'
python3 - <<'PY'
import re

text = """FROM golang:1.25.4 AS build
 from golang:1.26.4 AS second
FROM alpine:3.20
FROM golang:latest AS latest
FROM golang:1.27 AS numeric
\tFrOm\tgolang:1.28.1 AS mixed
FROM golang:1.29.0@sha256:abc AS digest
"""

patterns = {
    "current": r"^FROM.* golang:[0-9]+(\.[0-9]+)*",
    "proposed": r"^[ \t]*FROM[ \t]+.*golang:[0-9]+(\.[0-9]+)*",
}
for name, pattern in patterns.items():
    flags = re.I if name == "proposed" else 0
    print(f"{name}:")
    for line in text.splitlines():
        match = re.search(pattern, line, flags)
        if match:
            print(f"  {line!r} -> {match.group(0)!r}")
PY

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 5756


🏁 Script executed:

#!/bin/bash
set -eu

cat -n hack/verify-go-version.sh
printf '\n--- references ---\n'
rg -n 'verify-go-version|golang:' . --glob '!node_modules' --glob '!dist' || true

printf '\n--- regex behavior ---\n'
python3 - <<'PY'
import re

lines = [
    "FROM golang:1.25.4 AS build",
    " from golang:1.26.4 AS second",
    "FROM golang:latest AS latest",
    "\tFrOm\tgolang:1.28.1 AS mixed",
]

patterns = {
    "current": (r"^FROM.* golang:[0-9]+(\.[0-9]+)*", 0),
    "proposed": (r"^[ \t]*FROM[ \t]+.*golang:[0-9]+(\.[0-9]+)*", re.I),
}

for name, (pattern, flags) in patterns.items():
    print(name)
    for line in lines:
        match = re.search(pattern, line, flags)
        if match:
            print(" ", repr(line), "=>", repr(match.group(0)))
PY

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 5409


🏁 Script executed:

#!/bin/bash
set -eu

input='
FROM golang:1.26.5 AS builder
 from golang:1.26.4 AS whitespace
FrOm golang:1.26.3 AS mixed
FROM golang:latest AS unpinned
FROM golang:${GO_VERSION} AS variable
FROM golang:1.26.5@sha256:deadbeef AS digest
FROM alpine:3.20
'

printf '%s\n' '--- current extraction ---'
printf '%s\n' "$input" |
  grep -oE '^FROM.* golang:[0-9]+(\.[0-9]+)*' |
  sed -E 's/.*golang://' |
  sort -u || true

printf '%s\n' '--- proposed extraction ---'
printf '%s\n' "$input" |
  grep -Eio '^[[:space:]]*FROM[[:space:]]+.*golang:[0-9]+(\.[0-9]+)*' |
  sed -E 's/.*golang://' |
  sort -u || true

printf '%s\n' '--- golang references on FROM lines ---'
printf '%s\n' "$input" |
  grep -Ein '^[[:space:]]*FROM[[:space:]].*golang:' || true

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 517


🏁 Script executed:

#!/bin/bash
set -eu

input='
FROM golang:1.26.5 AS builder
 from golang:1.26.4 AS whitespace
FrOm golang:1.26.3 AS mixed
FROM golang:latest AS unpinned
FROM golang:${GO_VERSION} AS variable
FROM golang:1.26.5@sha256:deadbeef AS digest
FROM alpine:3.20
'

printf '%s\n' "$input" |
  grep -oE '^FROM.* golang:[0-9]+(\.[0-9]+)*' |
  sed -E 's/.*golang://' |
  sort -u || true

printf '%s\n' "$input" |
  grep -Eio '^[[:space:]]*FROM[[:space:]]+.*golang:[0-9]+(\.[0-9]+)*' |
  sed -E 's/.*golang://' |
  sort -u || true

printf '%s\n' "$input" |
  grep -Ein '^[[:space:]]*FROM[[:space:]].*golang:' || true

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 422


Scan and validate every FROM golang: stage.

The current expression skips lowercase instructions and instructions with leading whitespace. It also ignores unpinned or unsupported tags such as latest and ${GO_VERSION}. Inspect every golang: reference and fail when its tag is not a supported pinned version.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/verify-go-version.sh` around lines 20 - 21, Update the image_versions
extraction in the verification script to inspect every case-insensitive,
whitespace-tolerant FROM instruction referencing golang, including tags like
latest and ${GO_VERSION}. Validate each extracted tag as a supported pinned
version and fail the verification when any tag is unpinned or unsupported, while
retaining deduplication for valid versions.

Source: MCP tools


if [[ "${#image_versions[@]}" -eq 0 ]]; then
echo "ERROR: found no 'FROM golang:<version>' lines in ${DOCKERFILE}." >&2
exit 1
fi

if [[ "${#image_versions[@]}" -gt 1 ]]; then
echo "ERROR: Dockerfile pins more than one golang version: ${image_versions[*]}" >&2
echo " All 'FROM golang:<version>' stages must use the same version." >&2
exit 1
fi

image_version="${image_versions[0]}"

# `go 1.26.0` -> 1.26.0. Required; a bare `go 1.26` is normalised to 1.26.0 so
# that it compares correctly against a three-component image tag.
go_directive="$(sed -nE 's/^go[[:space:]]+([0-9]+(\.[0-9]+)*).*/\1/p' "${GO_MOD}" | head -n1)"
if [[ -z "${go_directive}" ]]; then
echo "ERROR: could not find a 'go' directive in ${GO_MOD}." >&2
exit 1
fi

# `toolchain go1.26.5` -> 1.26.5. Optional.
toolchain="$(sed -nE 's/^toolchain[[:space:]]+go([0-9]+(\.[0-9]+)*).*/\1/p' "${GO_MOD}" | head -n1)"

normalise() {
local v="$1"
while [[ "$(tr -dc '.' <<<"${v}" | wc -c)" -lt 2 ]]; do
v="${v}.0"
done
printf '%s' "${v}"
}

# Highest of the two is what go.mod effectively demands of the toolchain.
required="${go_directive}"
required_from="go directive"
if [[ -n "${toolchain}" ]]; then
highest="$(printf '%s\n%s\n' "$(normalise "${go_directive}")" "$(normalise "${toolchain}")" |
sort -V | tail -n1)"
if [[ "${highest}" == "$(normalise "${toolchain}")" && "${toolchain}" != "${go_directive}" ]]; then
required="${toolchain}"
required_from="toolchain directive"
fi
fi
Comment on lines +55 to +65

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target script ---'
cat -n hack/verify-go-version.sh | sed -n '1,120p'
printf '%s\n' '--- repository Go declarations ---'
find . -name go.mod -type f -print -exec sed -n '1,20p' {} \;
printf '%s\n' '--- relevant references ---'
rg -n 'GOTOOLCHAIN|verify-go-version|toolchain directive|automatic toolchain|bundled toolchain' . --glob '!vendor/**' --glob '!node_modules/**' | head -200
printf '%s\n' '--- local Go availability ---'
if command -v go >/dev/null 2>&1; then go version; else echo 'go: unavailable'; fi

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 5031


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- Go toolchain documentation ---'
go help toolchain 2>&1 | sed -n '1,220p' || true
printf '%s\n' '--- Go environment ---'
go env GOTOOLCHAIN GOROOT GOVERSION
printf '%s\n' '--- Go source references ---'
goroot="$(go env GOROOT)"
rg -n 'GOTOOLCHAIN|toolchain.*directive|local toolchain|Newer Go version|go directive' \
  "${goroot}/src/cmd/go" "${goroot}/src/cmd/go/internal" 2>/dev/null | head -240

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 28599


🌐 Web query:

Go official documentation GOTOOLCHAIN=local toolchain directive go.mod suggested toolchain bundled toolchain

💡 Result:

The GOTOOLCHAIN environment variable and the toolchain directive in go.mod provide a mechanism for managing and automatically switching Go toolchain versions [1][2][3]. GOTOOLCHAIN Environment Variable The GOTOOLCHAIN environment variable controls which Go toolchain is used when you run the go command [1][4]. - Default: For standard Go releases, the default is GOTOOLCHAIN=auto [1][2][4]. - GOTOOLCHAIN=local: Directs the go command to always use the bundled toolchain (the one currently running) [4][3][5]. If this bundled toolchain is older than what the project requires (specified in go.mod), the go command will fail rather than attempt to switch [1][4][5]. - GOTOOLCHAIN=auto: Allows the go command to automatically switch to a newer toolchain if the current project (via go.mod or go.work) requests a version newer than the bundled toolchain [1][2][4]. - GOTOOLCHAIN=: For example, GOTOOLCHAIN=go1.23.0. The go command will always use that specific version [4][5]. It will look in the system PATH first; if not found, it will download and verify the requested version [4][5]. Toolchain Directive in go.mod The toolchain directive is used in the main module's go.mod (or in go.work) to suggest a specific Go toolchain version [6][5]. - Purpose: It allows a project to specify a minimum toolchain version that is distinct from the go directive, which primarily specifies language semantics [2][5]. - Interaction: If the toolchain directive is present and suggests a version newer than the default (bundled) toolchain, the go command (provided GOTOOLCHAIN=auto or similar) will switch to that newer toolchain [1][6][4]. - Writing: The go command automatically updates the toolchain directive in go.mod whenever it updates the go version, ensuring reproducibility [6]. Bundled Toolchain The bundled toolchain refers to the specific Go distribution that you currently have installed and are executing [4][5]. When GOTOOLCHAIN=local is set, the system is constrained to only use this specific binary, disabling any automatic downloads or switching functionality [4][3][5]. In summary, these features work together to provide "extended forwards compatibility," allowing developers to specify required toolchains in their code while the go command manages the underlying binaries automatically, unless explicitly restricted by the user [2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
goroot="$(go env GOROOT)"
printf '%s\n' '--- toolchain selection implementation ---'
cat -n "${goroot}/src/cmd/go/internal/toolchain/select.go" | sed -n '175,280p'
printf '%s\n' '--- local-policy test cases ---'
cat -n "${goroot}/src/cmd/go/testdata/script/gotoolchain_local.txt" | sed -n '45,115p'
printf '%s\n' '--- local-policy failure test cases ---'
cat -n "${goroot}/src/cmd/go/testdata/script/gotoolchain_path.txt" | sed -n '20,70p'
printf '%s\n' '--- relevant official documentation source ---'
cat -n "${goroot}/src/cmd/go/alldocs.go" | sed -n '2440,2475p'

Repository: kubernetes-sigs/mcp-lifecycle-operator

Length of output: 11743


Correct the GOTOOLCHAIN=local diagnostic.

When GOTOOLCHAIN=local is selected, Go uses the bundled toolchain and enforces the go directive. It does not switch to a newer toolchain suggestion. If the bundled version satisfies the go directive but is below the toolchain suggestion, local mode can still build. Limit the failure message to automatic switching or compare against the selected GOTOOLCHAIN policy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/verify-go-version.sh` around lines 55 - 65, The required-version
selection in the verification logic must account for GOTOOLCHAIN=local: use only
the go directive when local mode is selected, rather than elevating the
requirement to the toolchain directive. Keep the higher toolchain requirement
for automatic switching modes, and ensure the failure diagnostic reflects the
selected GOTOOLCHAIN policy.

Source: MCP tools


# Ordering holds when the lower of {required, image} is `required`.
lowest="$(printf '%s\n%s\n' "$(normalise "${required}")" "$(normalise "${image_version}")" |
sort -V | head -n1)"

if [[ "${lowest}" != "$(normalise "${required}")" ]]; then
cat >&2 <<-EOF
ERROR: the Dockerfile's Go version is older than go.mod requires.

Dockerfile FROM golang:${image_version}
go.mod ${required} (${required_from})

Building this image would download a Go toolchain over the network, and
would fail offline or under GOTOOLCHAIN=local.

Fix by bumping the 'FROM golang:' stages in Dockerfile to at least
${required}, or by lowering the go.mod ${required_from}.
EOF
exit 1
fi

echo "Dockerfile Go version (${image_version}) satisfies go.mod (${required} from ${required_from})."
Loading