track go.work, and build the shipped binary as a shipped binary

The gate failed on `[build failed]` for symbols that are in this tree. Upstream
publishes server/public and pins v0.4.0 of it; we changed server/public, so
anything that reads the pin compiles against a release with no HanzoSettings, no
ServiceHanzo, no UserAuthServiceHanzo. The workspace is what makes ./public mean
this tree, and it was ignored — so `go build`, `go test`, the gate and the image
each had to arrange it privately, and the one that did not simply failed.

Tracking it states the fact once. The image build no longer arranges anything.

Also:

  -tags production. Without it server/public/model selects the DEV service
  environment through a build constraint, and a shipped binary points at dev
  telemetry and licensing while looking entirely normal.

  The runtime tree — data, logs, config, plugins, client/plugins — is made in a
  stage that has a shell, because distroless does not and the server has nowhere
  to write without them.

  The document-extraction binaries are dropped rather than carried. pdftotext
  wants GLIBC_2.38 and libstdc++ on a 2.36 base that has no libstdc++ at all;
  wvText is a shell script and there is no shell. The library COPYs beside them
  read /usr/lib while Ubuntu keeps those under /usr/lib/x86_64-linux-gnu, and a
  --from glob matching nothing is a silent no-op — so upstream's image ships five
  binaries with no libraries and reports success. Restoring extraction needs a
  base that can run it; that is its own decision.

  .dockerignore, because .git alone is 1.2 GiB of context and node_modules is a
  working tree's results standing in for the build's.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-13 23:48:07 -07:00
co-authored by Hanzo Dev
parent dc07a05eb7
commit 61fefb7b8d
6 changed files with 58 additions and 39 deletions
-4
View File
@@ -10,7 +10,3 @@
**/node_modules
webapp/channels/dist
# A local go.work is the same file the server stage writes for itself. Present in
# the context, `go work init` meets one it did not create and fails.
server/go.work
server/go.work.sum
+12 -3
View File
@@ -51,9 +51,18 @@ server/tests/temp.load
# Enterprise & products imports files
imports/imports.go
# go.work file
go.work
go.work.sum
# go.work is TRACKED here, and that is the difference between a fork and the
# project it forks. Upstream publishes server/public as a module and pins a
# released version of it, so a workspace is a developer's local convenience and
# belongs in this file. We changed server/public — the Hanzo model lives there —
# so for this tree the workspace is not a convenience, it is the build
# definition: without it every consumer compiles against the published v0.4.0 and
# cannot see our own types.
#
# Tracking it puts that fact in ONE place. `go build`, `go test`, CI's gate and
# the image build each used to arrange the workspace for themselves, and the one
# that did not — the CI gate — failed with "build failed" on symbols that are
# right here on disk.
#license files
*.license
+26 -29
View File
@@ -18,33 +18,43 @@ RUN npm ci --no-audit --no-fund && npm run build
FROM golang:1.26.4-bookworm AS server
# Declared, or the expansion below is unset on every build and the server reports
# its version as the fallback for the whole life of the image.
# its version as the fallback for the life of the image.
ARG BUILD_NUMBER=dev
WORKDIR /src/server
# go.work comes with this copy and is tracked (see server/.gitignore), so ./public
# resolves to this tree rather than the published v0.4.0, which carries none of
# the Hanzo model. Nothing is arranged here — the tree already states it.
COPY server .
# The workspace makes ./public resolve to this tree rather than the published
# module. server/go.mod requires public v0.4.0 with no replace, and v0.4.0 has
# none of the Hanzo model — HanzoSettings, ServiceHanzo, UserAuthServiceHanzo —
# so without this the compile fails on symbols that are right here on disk.
# .dockerignore drops any go.work a working tree already has, so `init` never
# meets one it did not write.
RUN go work init . ./public && \
CGO_ENABLED=0 go build -trimpath \
# `production` is not decoration: without the tag the !production file in
# server/public/model selects the DEV service environment, and a shipped binary
# then points at dev telemetry and licensing.
RUN CGO_ENABLED=0 go build -trimpath -tags production \
-ldflags "-X github.com/mattermost/mattermost/server/public/model.BuildNumber=${BUILD_NUMBER}" \
-o /out/ ./cmd/mattermost ./cmd/mmctl
# Document extraction needs these at runtime and the final image is distroless,
# so they are lifted out of a distro that has them. This stage also lays out the
# writable directories: distroless has no shell, so a later stage cannot mkdir,
# and the server will not start without config/ and data/.
# The writable tree and the mime table. Distroless has no shell, so directories
# the server creates files in cannot be made in the final stage — and unmade, the
# server has nowhere to write its config or its log and does not start.
FROM ubuntu:noble AS tools
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
ca-certificates media-types mailcap unrtf wv poppler-utils tidy tzdata \
ca-certificates media-types tzdata \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /hanzo/data /hanzo/logs /hanzo/config /hanzo/plugins /hanzo/client/plugins /hanzo/.postgresql \
&& chmod 700 /hanzo/.postgresql \
RUN mkdir -p /hanzo/data /hanzo/logs /hanzo/config /hanzo/plugins /hanzo/client/plugins \
&& chown -R 2000:2000 /hanzo
# Upstream also lifts pdftotext, wvText, wvWare, unrtf and tidy in here for
# attachment text extraction. They are NOT carried, because on this base they
# cannot run and shipping a binary that cannot run is not a feature:
# pdftotext needs GLIBC_2.38 and libstdc++; debian12 is 2.36 and distroless
# `base` has no libstdc++ at any version.
# wvText is a /bin/sh script, and there is no shell here.
# The library COPYs beside them read /usr/lib/libpoppler.so*, while Ubuntu puts
# every one of those under /usr/lib/x86_64-linux-gnu — and a --from glob that
# matches nothing is a silent no-op in BuildKit, so upstream's image has been
# carrying five binaries with no libraries and reporting success. Restoring
# extraction means a base that can run it, which is a different decision than
# this one and should be made on its own.
FROM gcr.io/distroless/base-debian12
ENV PATH="/hanzo/bin:${PATH}"
@@ -52,19 +62,6 @@ ENV MM_SERVICESETTINGS_ENABLELOCALMODE="true"
COPY --from=tools /etc/mime.types /etc/
COPY --from=tools --chown=2000:2000 /etc/ssl/certs /etc/ssl/certs
COPY --from=tools /usr/bin/pdftotext /usr/bin/pdftotext
COPY --from=tools /usr/bin/wvText /usr/bin/wvText
COPY --from=tools /usr/bin/wvWare /usr/bin/wvWare
COPY --from=tools /usr/bin/unrtf /usr/bin/unrtf
COPY --from=tools /usr/bin/tidy /usr/bin/tidy
COPY --from=tools /usr/share/wv /usr/share/wv
COPY --from=tools /usr/lib/libpoppler.so* /usr/lib/
COPY --from=tools /usr/lib/libfreetype.so* /usr/lib/
COPY --from=tools /usr/lib/libpng.so* /usr/lib/
COPY --from=tools /usr/lib/libwv.so* /usr/lib/
COPY --from=tools /usr/lib/libtidy.so* /usr/lib/
COPY --from=tools /usr/lib/libfontconfig.so* /usr/lib/
# The writable tree first, then the read-only content into it.
COPY --from=tools --chown=2000:2000 /hanzo /hanzo
COPY --from=server --chown=2000:2000 /out/ /hanzo/bin/
+2 -3
View File
@@ -22,9 +22,8 @@ config/active.dat
config/logging.json
/plugins
# go.work file
go.work
go.work.sum
# go.work is tracked in this fork — see the root .gitignore, which is where the
# repo-wide rule and the reason for it live.
#license files
*.license
+6
View File
@@ -0,0 +1,6 @@
go 1.26.4
use (
.
./public
)
+12
View File
@@ -0,0 +1,12 @@
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934=