# release-tools image # Versions are pinned via ARGs sourced from VERSIONS.md. # Base: Project Hummingbird hi/go (registry.access.redhat.com/hi/go) # TAG-FLOATING RISK: only `latest-builder` is available from Hummingbird as of 2026-05-25. # Switch to `1.26-builder` or `1.26.3-builder` when Hummingbird publishes a specific tag. ARG HI_GO_TAG=latest-builder ARG HI_GO_DIGEST=sha256:e6d96a936f7236394e2424f667925e1756b48bc421b0af65c206033efbd78e18 ARG SYFT_VERSION=v1.44.0 ARG SYFT_SHA256=0e91737aee2b5baf1d255b959630194a302335d848ff97bb07921eb6205b5f5a ARG GORELEASER_VERSION=v2.16.0 ARG GORELEASER_SHA256=eaae05b5eba07533bd0f06846b68c808399504784df00c62eb219541fc04e5e2 ARG COSIGN_VERSION=v3.0.6 ARG GOVULNCHECK_VERSION=latest ARG GO_LICENSES_VERSION=v1.6.0 ARG MCPB_VERSION=2.1.2 # --------------------------------------------------------------------------- # Stage: tools-builder # Compile/fetch syft, goreleaser, cosign. Pre-warm npm cache for mcpb. # --------------------------------------------------------------------------- FROM registry.access.redhat.com/hi/go:${HI_GO_TAG}@${HI_GO_DIGEST} AS tools-builder ARG SYFT_VERSION ARG SYFT_SHA256 ARG GORELEASER_VERSION ARG GORELEASER_SHA256 ARG COSIGN_VERSION ARG GOVULNCHECK_VERSION ARG GO_LICENSES_VERSION ARG MCPB_VERSION # Install build-time deps: tar+gzip+xz for syft install.sh archive extraction; curl for binary fetches RUN dnf install -y nodejs npm curl ca-certificates tar gzip xz && dnf clean all # govulncheck: install via go install (Go module proxy + sumdb integrity) RUN GOBIN=/usr/local/bin go install golang.org/x/vuln/cmd/govulncheck@${GOVULNCHECK_VERSION} # go-licenses: install via go install (pinned semver tag, Go sumdb integrity). # Used by go-ci task to fail PRs that pull in forbidden/restricted licenses (GPL/AGPL/LGPL/MPL). RUN GOBIN=/usr/local/bin go install github.com/google/go-licenses@${GO_LICENSES_VERSION} # goreleaser: download prebuilt tarball + SHA256-verify against publisher checksums.txt. # Avoids go install re-resolving the transitive dep graph from proxy.golang.org each build. # Expected SHA256 is recorded in VERSIONS.md; update both together on version bumps. RUN set -ex \ && curl -sSfLo /tmp/goreleaser_Linux_x86_64.tar.gz \ "https://github.com/goreleaser/goreleaser/releases/download/${GORELEASER_VERSION}/goreleaser_Linux_x86_64.tar.gz" \ && curl -sSfLo /tmp/goreleaser-checksums.txt \ "https://github.com/goreleaser/goreleaser/releases/download/${GORELEASER_VERSION}/checksums.txt" \ && echo "${GORELEASER_SHA256} goreleaser_Linux_x86_64.tar.gz" > /tmp/goreleaser-expected.txt \ && cd /tmp && sha256sum -c goreleaser-expected.txt \ && grep " goreleaser_Linux_x86_64.tar.gz$" /tmp/goreleaser-checksums.txt | sha256sum -c - \ && tar -xzf /tmp/goreleaser_Linux_x86_64.tar.gz -C /usr/local/bin goreleaser \ && chmod +x /usr/local/bin/goreleaser \ && rm /tmp/goreleaser_Linux_x86_64.tar.gz /tmp/goreleaser-checksums.txt /tmp/goreleaser-expected.txt # syft: download prebuilt tarball + SHA256-verify against publisher checksums. # Replaces piped install.sh (no integrity control) with explicit checksum verification. # Expected SHA256 is recorded in VERSIONS.md; update both together on version bumps. RUN set -ex \ && SYFT_VER="${SYFT_VERSION#v}" \ && curl -sSfLo "/tmp/syft_${SYFT_VER}_linux_amd64.tar.gz" \ "https://github.com/anchore/syft/releases/download/${SYFT_VERSION}/syft_${SYFT_VER}_linux_amd64.tar.gz" \ && curl -sSfLo /tmp/syft-checksums.txt \ "https://github.com/anchore/syft/releases/download/${SYFT_VERSION}/syft_${SYFT_VER}_checksums.txt" \ && echo "${SYFT_SHA256} syft_${SYFT_VER}_linux_amd64.tar.gz" > /tmp/syft-expected.txt \ && cd /tmp && sha256sum -c syft-expected.txt \ && grep " syft_${SYFT_VER}_linux_amd64.tar.gz$" /tmp/syft-checksums.txt | sha256sum -c - \ && tar -xzf "/tmp/syft_${SYFT_VER}_linux_amd64.tar.gz" -C /usr/local/bin syft \ && chmod +x /usr/local/bin/syft \ && rm "/tmp/syft_${SYFT_VER}_linux_amd64.tar.gz" /tmp/syft-checksums.txt /tmp/syft-expected.txt # cosign: curl prebuilt linux/amd64 binary + verify against release checksum manifest. # Download to /tmp/cosign-linux-amd64 so sha256sum -c can find the file by its original # name (sha256sum resolves filenames in the checksum file relative to cwd). RUN set -ex \ && curl -sSfLo /tmp/cosign-linux-amd64 \ "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64" \ && curl -sSfLo /tmp/cosign-checksums.txt \ "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign_checksums.txt" \ && cd /tmp && grep " cosign-linux-amd64$" cosign-checksums.txt | sha256sum -c - \ && mv /tmp/cosign-linux-amd64 /usr/local/bin/cosign \ && rm /tmp/cosign-checksums.txt \ && chmod +x /usr/local/bin/cosign # npm packages (mcpb, openspec): install from pinned lockfile into a staging # directory, then copy the integrity-verified tree to global node_modules. # npm ci reads package-lock.json (integrity-hash-pinned); npm install -g does NOT # read the lockfile and re-resolves transitive deps from the registry, defeating # the supply-chain guarantee. Copy instead of re-resolve. COPY npm/package.json npm/package-lock.json /npm-src/ RUN cd /npm-src \ && npm ci --ignore-scripts \ && mkdir -p /usr/local/lib/node_modules \ && cp -r /npm-src/node_modules/. /usr/local/lib/node_modules/ \ && npm cache clean --force # --------------------------------------------------------------------------- # Stage: final # Based on the same Hummingbird builder so Go toolchain is present. # GoReleaser invokes `go build` at release time; a thin runtime variant # would be insufficient. See design D2. # --------------------------------------------------------------------------- FROM registry.access.redhat.com/hi/go:${HI_GO_TAG}@${HI_GO_DIGEST} ARG HI_GO_TAG ARG SYFT_VERSION ARG GORELEASER_VERSION ARG COSIGN_VERSION ARG GOVULNCHECK_VERSION ARG GO_LICENSES_VERSION ARG MCPB_VERSION # Install runtime tools from dnf RUN dnf install -y nodejs npm jq curl ca-certificates && dnf clean all # Copy compiled/fetched binaries from builder stage COPY --from=tools-builder /usr/local/bin/goreleaser /usr/local/bin/goreleaser COPY --from=tools-builder /usr/local/bin/syft /usr/local/bin/syft COPY --from=tools-builder /usr/local/bin/cosign /usr/local/bin/cosign COPY --from=tools-builder /usr/local/bin/govulncheck /usr/local/bin/govulncheck COPY --from=tools-builder /usr/local/bin/go-licenses /usr/local/bin/go-licenses # Copy globally installed mcpb package tree from builder stage. # Do NOT COPY the bin wrapper — buildah dereferences symlinks, producing a plain # script file outside node_modules whose Node import resolution fails. # Recreate the symlink explicitly so Node resolves modules correctly. COPY --from=tools-builder /usr/local/lib/node_modules /usr/local/lib/node_modules RUN ln -sf /usr/local/lib/node_modules/@anthropic-ai/mcpb/dist/cli/cli.js /usr/local/bin/mcpb \ && chmod +x /usr/local/lib/node_modules/@anthropic-ai/mcpb/dist/cli/cli.js RUN ln -sf /usr/local/lib/node_modules/@fission-ai/openspec/bin/openspec.js /usr/local/bin/openspec \ && chmod +x /usr/local/lib/node_modules/@fission-ai/openspec/bin/openspec.js # OCI image labels LABEL org.opencontainers.image.source="https://codeberg.org/goern/forgejo-mcp" \ org.opencontainers.image.version="${GORELEASER_VERSION}" \ org.opencontainers.image.vendor="Operate First, by #B4mad" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.description="Release toolchain image: go, goreleaser, syft, cosign, govulncheck, go-licenses, node, npm, jq, curl, @anthropic-ai/mcpb, @fission-ai/openspec" \ org.opencontainers.image.base.name="registry.access.redhat.com/hi/go:${HI_GO_TAG}" # Smoke-check: every bundled tool must report a version or build fails # npx check runs with --network=host (build network); offline check is in verify.sh RUN go version && \ syft version && \ goreleaser --version && \ cosign version && \ govulncheck -version && \ go-licenses --help >/dev/null && \ jq --version && \ curl --version && \ node --version && \ npm --version && \ mcpb --version && \ openspec --version