Tag the build server image on its Node version (#38281)

* Tag the build server image on its Node version

mattermost-build-server was published as :${GO_VERSION}, but the image
bakes NODE_VERSION into PATH at build time. Since setup-buildenv skips
the build whenever `docker manifest inspect` finds that tag, bumping the
Dockerfile's Node changed nothing: CI kept pulling the existing image,
and the new Node only appeared once a master merge overwrote the tag in
place -- which also silently changes what release branches pinned to
that tag resolve to.

Tag on both instead, e.g. go1.26.7-node24.11.1. A Node bump now names a
tag nobody has published, so it gets built and published exactly once,
the same way a Go bump does. The "go" prefix keeps these distinct from
the bare Go versions published under the old scheme.

server/build/buildenv-tag.sh is the one place that composes the tag, and
it verifies both Dockerfiles agree on Node since one tag covers both
images. CI resolves it where it already resolved the Go version and
propagates BUILD_SERVER_TAG, so the buildenv actions just take a string.

That leaves nothing needing a threaded Go version except the
actions/setup-go in server-test-template, which reads server/.go-version
via go-version-file. Only two of that template's seven callers shard at
all, so the rest were passing a value nothing read.

Node also moves to 24.11.1 to match .nvmrc, which is what the webapp's
engines constraint has expected for a while.

The buildenv job in server-ci.yml is renamed from "go" to match
server-ci-weekly and server-ci-nightly-race, so its check name changes
from "Compute Go Version" to "Build Environment".

* Fail the step when buildenv-tag.sh fails

`echo BUILD_SERVER_TAG=$(./buildenv-tag.sh)` reports echo's exit status,
not the script's, so under `bash -e` a failing script left the step green
and wrote an empty value:

    $ bash -e -c 'echo "TAG=$(false)"; echo continued'
    TAG=
    continued

That defeated the consistency check the script exists for: on drift it
exits 1, the step passed anyway, and setup-buildenv went on to inspect
`mattermost-build-server:` with no tag -- failing well away from the
cause. Capture into a variable first, which `bash -e` does catch.

Applies to all nine call sites, including the ones already quoted;
quoting doesn't help here.

* Rebuild the buildenv image when a version pin changes

buildenv-tag.sh reads server/.go-version and .nvmrc, so name them in the
paths that trigger a republish.

Redundant in practice -- the script requires the pins and the Dockerfiles
to agree, so a valid pin change always carries a Dockerfile change, which
already triggers this -- but it makes the dependency explicit and fails
here rather than in Server CI.

* Pass buildenv inputs via env, not expression interpolation

GitHub expands `${{ }}` into the script before bash runs it, so
`echo "BUILDENV_TAG=${{ inputs.build-server-tag }}"` executed whatever
the input contained. That was reachable: buildenv-tag.sh reads
ARG NODE_VERSION straight out of a Dockerfile, and a fork PR controls
that file, so `ARG NODE_VERSION=$(...)` would have landed in the tag and
then in this script.

Pass all three inputs through env instead, and reject a tag that isn't
usable as a docker tag anyway.

Also fixes the aggregate's error message, which still named the 'go' job
after it was renamed to 'buildenv'.

* Don't write a literal expression in a shell comment

GitHub parses ${{ ... }} anywhere in the template, including inside a
run: block's comment text, so an empty one failed the action manifest:
"An expression was expected".

* Drop the redundant tag validation

The env mapping is the fix: bash gets the input as a value, never as
script text, so there is nothing left to inject with. The regex only
restated what docker would reject anyway.

* Validate the tag before emitting it

* Pass buildenv values as step outputs, not GITHUB_ENV
This commit is contained in:
Jesse Hallam authored and GitHub committed 2026-09-03 14:32:25 -03:00
1 parent 24e436e7ae
commit 8f6d17d875
13 files changed
+202 -117

No files matched your search

+9 -9
View File
@@ -1,13 +1,13 @@
name: Load build environment
description: >
Loads build-server images uploaded by setup-buildenv into the local Docker
daemon, and resolves the image name from the Go version. Use in jobs that
run the container directly (test jobs). For jobs that only need a shell
inside the container, use run-in-buildenv instead.
daemon, and resolves the image name from the tag. Use in jobs that run the
container directly (test jobs). For jobs that only need a shell inside the
container, use run-in-buildenv instead.
inputs:
go-version:
description: "Go version used to construct the image tag (e.g. 1.26.3)."
build-server-tag:
description: "Build image tag, from server/build/buildenv-tag.sh (e.g. go1.26.3-node24.11.1)"
required: true
fips-enabled:
description: "Set to 'true' to resolve the FIPS build image"
@@ -16,7 +16,7 @@ inputs:
outputs:
image:
description: "Fully-qualified build image name (e.g. mattermost/mattermost-build-server:1.26.3)."
description: "Fully-qualified build image name (e.g. mattermost/mattermost-build-server:go1.26.3-node24.11.1)."
value: ${{ steps.resolve.outputs.image }}
runs:
@@ -57,10 +57,10 @@ runs:
shell: bash
env:
FIPS: ${{ inputs.fips-enabled }}
GO_VERSION: ${{ inputs.go-version }}
TAG: ${{ inputs.build-server-tag }}
run: |
if [[ "${FIPS}" == 'true' ]]; then
echo "image=mattermost/mattermost-build-server-fips:${GO_VERSION}" >> "${GITHUB_OUTPUT}"
echo "image=mattermost/mattermost-build-server-fips:${TAG}" >> "${GITHUB_OUTPUT}"
else
echo "image=mattermost/mattermost-build-server:${GO_VERSION}" >> "${GITHUB_OUTPUT}"
echo "image=mattermost/mattermost-build-server:${TAG}" >> "${GITHUB_OUTPUT}"
fi
+10 -8
View File
@@ -14,8 +14,8 @@ inputs:
run:
description: "Bash script to execute inside the container"
required: true
go-version:
description: "Go version to use (e.g. 1.24.3). Defaults to the version in server/.go-version."
build-server-tag:
description: "Build image tag. Defaults to server/build/buildenv-tag.sh."
required: false
default: ""
fips-enabled:
@@ -30,20 +30,22 @@ inputs:
runs:
using: composite
steps:
- name: Read Go version
- name: Read build image tag
id: tag
shell: bash
env:
GO_VERSION_INPUT: ${{ inputs.go-version }}
TAG_INPUT: ${{ inputs.build-server-tag }}
run: |
if [[ -n "${GO_VERSION_INPUT}" ]]; then
echo "GO_VERSION=${GO_VERSION_INPUT}" >> "${GITHUB_ENV}"
if [[ -n "${TAG_INPUT}" ]]; then
tag="${TAG_INPUT}"
else
echo "GO_VERSION=$(cat server/.go-version)" >> "${GITHUB_ENV}"
tag=$(./server/build/buildenv-tag.sh)
fi
echo "tag=${tag}" >> "${GITHUB_OUTPUT}"
- uses: ./.github/actions/load-buildenv
id: buildenv
with:
go-version: ${{ env.GO_VERSION }}
build-server-tag: ${{ steps.tag.outputs.tag }}
fips-enabled: ${{ inputs.fips-enabled }}
- name: Run in buildenv
shell: bash
+38 -27
View File
@@ -1,12 +1,12 @@
name: Setup build environment
description: >
Builds the mattermost-build-server images if they don't yet exist on Docker
Hub (e.g. during a Go version bump), and uploads them as artifacts for
downstream jobs. Run once per workflow before any build or test jobs.
Hub (e.g. during a Go or Node version bump), and uploads them as artifacts
for downstream jobs. Run once per workflow before any build or test jobs.
inputs:
go-version:
description: "Go version (e.g. 1.26.3)"
build-server-tag:
description: "Build image tag, from server/build/buildenv-tag.sh (e.g. go1.26.3-node24.11.1)"
required: true
dockerfile:
description: "Path to Dockerfile.buildenv (relative to workspace root)"
@@ -21,44 +21,50 @@ runs:
using: composite
steps:
- name: Set constants
id: constants
shell: bash
run: |
echo "BUILDENV_IMAGE=mattermost/mattermost-build-server" >> "${GITHUB_ENV}"
echo "BUILDENV_ARTIFACT=buildenv-image" >> "${GITHUB_ENV}"
echo "BUILDENV_DOCKERFILE=${{ inputs.dockerfile }}" >> "${GITHUB_ENV}"
echo "BUILDENV_FIPS_IMAGE=mattermost/mattermost-build-server-fips" >> "${GITHUB_ENV}"
echo "BUILDENV_FIPS_ARTIFACT=buildenv-fips-image" >> "${GITHUB_ENV}"
echo "BUILDENV_FIPS_DOCKERFILE=${{ inputs.dockerfile-fips }}" >> "${GITHUB_ENV}"
echo "BUILDENV_TMPDIR=/tmp/buildenv-save" >> "${GITHUB_ENV}"
{
echo "image=mattermost/mattermost-build-server"
echo "artifact=buildenv-image"
echo "fips-image=mattermost/mattermost-build-server-fips"
echo "fips-artifact=buildenv-fips-image"
echo "tmpdir=/tmp/buildenv-save"
} >> "${GITHUB_OUTPUT}"
# ── Regular ────────────────────────────────────────────────────────
- name: Resolve regular image
id: resolve
shell: bash
env:
GO_VERSION: ${{ inputs.go-version }}
BUILDENV_IMAGE: ${{ steps.constants.outputs.image }}
BUILDENV_TAG: ${{ inputs.build-server-tag }}
run: |
docker manifest inspect "${BUILDENV_IMAGE}:${GO_VERSION}" > /dev/null 2>&1 || \
docker manifest inspect "${BUILDENV_IMAGE}:${BUILDENV_TAG}" > /dev/null 2>&1 || \
echo "needs-build=true" >> "${GITHUB_OUTPUT}"
- name: Build regular image
if: ${{ steps.resolve.outputs.needs-build == 'true' }}
shell: bash
env:
GO_VERSION: ${{ inputs.go-version }}
run: docker build -t "${BUILDENV_IMAGE}:${GO_VERSION}" - < "${BUILDENV_DOCKERFILE}"
BUILDENV_IMAGE: ${{ steps.constants.outputs.image }}
BUILDENV_TAG: ${{ inputs.build-server-tag }}
BUILDENV_DOCKERFILE: ${{ inputs.dockerfile }}
run: docker build -t "${BUILDENV_IMAGE}:${BUILDENV_TAG}" - < "${BUILDENV_DOCKERFILE}"
- name: Save and upload regular image
if: ${{ steps.resolve.outputs.needs-build == 'true' }}
shell: bash
env:
GO_VERSION: ${{ inputs.go-version }}
BUILDENV_IMAGE: ${{ steps.constants.outputs.image }}
BUILDENV_TAG: ${{ inputs.build-server-tag }}
BUILDENV_TMPDIR: ${{ steps.constants.outputs.tmpdir }}
run: |
mkdir -p "${BUILDENV_TMPDIR}"
docker save "${BUILDENV_IMAGE}:${GO_VERSION}" | gzip > "${BUILDENV_TMPDIR}/image.tar.gz"
docker save "${BUILDENV_IMAGE}:${BUILDENV_TAG}" | gzip > "${BUILDENV_TMPDIR}/image.tar.gz"
- name: Upload regular image artifact
if: ${{ steps.resolve.outputs.needs-build == 'true' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.BUILDENV_ARTIFACT }}
path: ${{ env.BUILDENV_TMPDIR }}/image.tar.gz
name: ${{ steps.constants.outputs.artifact }}
path: ${{ steps.constants.outputs.tmpdir }}/image.tar.gz
retention-days: 1
# ── FIPS (skipped on fork PRs) ─────────────────────────────────────
- name: Docker Hub login
@@ -77,28 +83,33 @@ runs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
shell: bash
env:
GO_VERSION: ${{ inputs.go-version }}
BUILDENV_FIPS_IMAGE: ${{ steps.constants.outputs.fips-image }}
BUILDENV_TAG: ${{ inputs.build-server-tag }}
run: |
docker manifest inspect "${BUILDENV_FIPS_IMAGE}:${GO_VERSION}" > /dev/null 2>&1 || \
docker manifest inspect "${BUILDENV_FIPS_IMAGE}:${BUILDENV_TAG}" > /dev/null 2>&1 || \
echo "needs-build=true" >> "${GITHUB_OUTPUT}"
- name: Build FIPS image
if: ${{ steps.resolve-fips.outputs.needs-build == 'true' }}
shell: bash
env:
GO_VERSION: ${{ inputs.go-version }}
run: docker build -t "${BUILDENV_FIPS_IMAGE}:${GO_VERSION}" - < "${BUILDENV_FIPS_DOCKERFILE}"
BUILDENV_FIPS_IMAGE: ${{ steps.constants.outputs.fips-image }}
BUILDENV_TAG: ${{ inputs.build-server-tag }}
BUILDENV_FIPS_DOCKERFILE: ${{ inputs.dockerfile-fips }}
run: docker build -t "${BUILDENV_FIPS_IMAGE}:${BUILDENV_TAG}" - < "${BUILDENV_FIPS_DOCKERFILE}"
- name: Save and upload FIPS image
if: ${{ steps.resolve-fips.outputs.needs-build == 'true' }}
shell: bash
env:
GO_VERSION: ${{ inputs.go-version }}
BUILDENV_FIPS_IMAGE: ${{ steps.constants.outputs.fips-image }}
BUILDENV_TAG: ${{ inputs.build-server-tag }}
BUILDENV_TMPDIR: ${{ steps.constants.outputs.tmpdir }}
run: |
mkdir -p "${BUILDENV_TMPDIR}"
docker save "${BUILDENV_FIPS_IMAGE}:${GO_VERSION}" | gzip > "${BUILDENV_TMPDIR}/image-fips.tar.gz"
docker save "${BUILDENV_FIPS_IMAGE}:${BUILDENV_TAG}" | gzip > "${BUILDENV_TMPDIR}/image-fips.tar.gz"
- name: Upload FIPS image artifact
if: ${{ steps.resolve-fips.outputs.needs-build == 'true' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.BUILDENV_FIPS_ARTIFACT }}
path: ${{ env.BUILDENV_TMPDIR }}/image-fips.tar.gz
name: ${{ steps.constants.outputs.fips-artifact }}
path: ${{ steps.constants.outputs.tmpdir }}/image-fips.tar.gz
retention-days: 1
+16 -10
View File
@@ -8,11 +8,17 @@ on:
paths:
- server/build/Dockerfile.buildenv
- server/build/Dockerfile.buildenv-fips
- server/build/buildenv-tag.sh
- server/.go-version
- .nvmrc
- .github/workflows/build-server-image.yml
pull_request:
paths:
- server/build/Dockerfile.buildenv
- server/build/Dockerfile.buildenv-fips
- server/build/buildenv-tag.sh
- server/.go-version
- .nvmrc
- .github/workflows/build-server-image.yml
workflow_dispatch:
@@ -52,11 +58,11 @@ jobs:
run: |
docker run --rm mattermost/mattermost-build-server:test /bin/sh -c "go version && node --version"
- name: buildenv/calculate-golang-version
id: go
- name: buildenv/calculate-tag
id: tag
run: |
GO_VERSION=$(docker run --rm mattermost/mattermost-build-server:test go version | awk '{print $3}' | sed 's/go//')
echo "GO_VERSION=${GO_VERSION}" >> "${GITHUB_OUTPUT}"
tag=$(./server/build/buildenv-tag.sh)
echo "TAG=${tag}" >> "${GITHUB_OUTPUT}"
- name: buildenv/push
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-')
@@ -67,7 +73,7 @@ jobs:
load: false
push: true
pull: true
tags: mattermost/mattermost-build-server:${{ steps.go.outputs.GO_VERSION }}
tags: mattermost/mattermost-build-server:${{ steps.tag.outputs.TAG }}
build-image-fips:
permissions:
@@ -103,11 +109,11 @@ jobs:
run: |
docker run --rm --entrypoint bash mattermost/mattermost-build-server-fips:test -c "go version && node --version"
- name: buildenv/calculate-golang-version
id: go
- name: buildenv/calculate-tag
id: tag
run: |
GO_VERSION=$(docker run --rm --entrypoint bash mattermost/mattermost-build-server-fips:test -c "go version" | awk '{print $3}' | sed 's/go//')
echo "GO_VERSION=${GO_VERSION}" >> "${GITHUB_OUTPUT}"
tag=$(./server/build/buildenv-tag.sh)
echo "TAG=${tag}" >> "${GITHUB_OUTPUT}"
- name: buildenv/push
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-')
@@ -118,4 +124,4 @@ jobs:
load: false
push: true
pull: true
tags: mattermost/mattermost-build-server-fips:${{ steps.go.outputs.GO_VERSION }}
tags: mattermost/mattermost-build-server-fips:${{ steps.tag.outputs.TAG }}
+3 -3
View File
@@ -14,7 +14,8 @@ on:
logsartifact:
required: true
type: string
go-version:
build-server-tag:
description: "Build image tag (e.g. go1.26.3-node24.11.1)"
required: true
type: string
fips-enabled:
@@ -43,7 +44,6 @@ jobs:
COMPOSE_PROJECT_NAME: ghactions
INPUT_DATASOURCE: ${{ inputs.datasource }}
INPUT_FIPS_ENABLED: ${{ inputs.fips-enabled }}
INPUT_GO_VERSION: ${{ inputs.go-version }}
INPUT_LOGSARTIFACT: ${{ inputs.logsartifact }}
INPUT_NAME: ${{ inputs.name }}
INPUT_PR_NUMBER: ${{ github.event.pull_request.number }}
@@ -64,7 +64,7 @@ jobs:
id: buildenv
uses: ./.github/actions/load-buildenv
with:
go-version: ${{ inputs.go-version }}
build-server-tag: ${{ inputs.build-server-tag }}
fips-enabled: ${{ inputs.fips-enabled }}
- name: Setup log artifact name
id: log-artifact
+6 -4
View File
@@ -25,16 +25,18 @@ jobs:
name: Build Environment
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.calculate.outputs.GO_VERSION }}
build-server-tag: ${{ steps.calculate.outputs.BUILD_SERVER_TAG }}
steps:
- name: Checkout mattermost project
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Calculate version
- name: Calculate build image tag
id: calculate
working-directory: server/
run: echo GO_VERSION=$(cat .go-version) >> "${GITHUB_OUTPUT}"
run: |
tag=$(./build/buildenv-tag.sh)
echo "BUILD_SERVER_TAG=${tag}" >> "${GITHUB_OUTPUT}"
test-race:
name: Race Detector
@@ -48,7 +50,7 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10
drivername: postgres
logsartifact: race-detector-server-test-logs
go-version: ${{ needs.buildenv.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: false
fullyparallel: false
race-enabled: true
+8 -6
View File
@@ -26,16 +26,18 @@ jobs:
name: Build Environment
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.calculate.outputs.GO_VERSION }}
build-server-tag: ${{ steps.calculate.outputs.BUILD_SERVER_TAG }}
steps:
- name: Checkout mattermost project
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Calculate version
- name: Calculate build image tag
id: calculate
working-directory: server/
run: echo GO_VERSION=$(cat .go-version) >> "${GITHUB_OUTPUT}"
run: |
tag=$(./build/buildenv-tag.sh)
echo "BUILD_SERVER_TAG=${tag}" >> "${GITHUB_OUTPUT}"
test-postgres-binary:
name: Postgres with binary parameters
@@ -49,7 +51,7 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10&binary_parameters=yes
drivername: postgres
logsartifact: postgres-binary-server-test-logs
go-version: ${{ needs.buildenv.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: false
# Unsharded run on a single 8-core runner: fullyparallel=true causes
# resource exhaustion (too many server instances, WebSocket hubs, and
@@ -71,7 +73,7 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10
drivername: postgres
logsartifact: postgres-server-fips-test-logs
go-version: ${{ needs.buildenv.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: true
# Unsharded run on a single 8-core runner: see note on test-postgres-binary.
fullyparallel: false
@@ -92,5 +94,5 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10
drivername: postgres
logsartifact: mmctl-fips-test-logs
go-version: ${{ needs.buildenv.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: true
+48 -43
View File
@@ -20,8 +20,8 @@ permissions:
contents: read
jobs:
go:
name: Compute Go Version
buildenv:
name: Build Environment
runs-on: ubuntu-22.04
permissions:
contents: read
@@ -29,7 +29,7 @@ jobs:
actions: write
id-token: write # for chainguard (FIPS base image pull)
outputs:
version: ${{ steps.calculate.outputs.GO_VERSION }}
build-server-tag: ${{ steps.calculate.outputs.BUILD_SERVER_TAG }}
gomod-changed: ${{ steps.changed-files.outputs.any_changed }}
# Individual jobs below use this to skip via `if:` rather
# than the workflow being skipped entirely,
@@ -40,10 +40,12 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Calculate version
- name: Calculate build image tag
id: calculate
working-directory: server/
run: echo GO_VERSION=$(cat .go-version) >> "${GITHUB_OUTPUT}"
run: |
tag=$(./build/buildenv-tag.sh)
echo "BUILD_SERVER_TAG=${tag}" >> "${GITHUB_OUTPUT}"
- name: Check for go.mod changes
id: changed-files
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
@@ -61,6 +63,9 @@ jobs:
.github/workflows/server-test-template.yml
.github/workflows/server-test-merge-template.yml
.github/workflows/mmctl-test-template.yml
.github/actions/setup-buildenv/**
.github/actions/load-buildenv/**
.github/actions/run-in-buildenv/**
tools/mattermost-govet/**
files_ignore: |
server/**/*.md
@@ -73,11 +78,11 @@ jobs:
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
uses: ./.github/actions/setup-buildenv
with:
go-version: ${{ steps.calculate.outputs.GO_VERSION }}
build-server-tag: ${{ steps.calculate.outputs.BUILD_SERVER_TAG }}
check-generated:
name: Check generated files
needs: go
if: needs.go.outputs.relevant-changed == 'true'
needs: buildenv
if: needs.buildenv.outputs.relevant-changed == 'true'
runs-on: ubuntu-22.04
services:
# make default-roles-permissions snapshots a live database.
@@ -115,9 +120,9 @@ jobs:
fi
check-backport-migrations:
name: Check backport migrations
needs: go
needs: buildenv
# Only backports (release-* base) must keep a migration's version+name; new master migrations are expected.
if: needs.go.outputs.relevant-changed == 'true' && startsWith(github.base_ref, 'release-')
if: needs.buildenv.outputs.relevant-changed == 'true' && startsWith(github.base_ref, 'release-')
runs-on: ubuntu-22.04
steps:
- name: Checkout mattermost project
@@ -134,8 +139,8 @@ jobs:
make check-backport-migrations
check-style:
name: check-style
needs: go
if: needs.go.outputs.relevant-changed == 'true'
needs: buildenv
if: needs.buildenv.outputs.relevant-changed == 'true'
runs-on: ubuntu-22.04
steps:
- name: Checkout mattermost project
@@ -147,8 +152,8 @@ jobs:
run: make check-style
check-mattermost-vet-api:
name: Vet API
needs: go
if: needs.go.outputs.relevant-changed == 'true'
needs: buildenv
if: needs.buildenv.outputs.relevant-changed == 'true'
runs-on: ubuntu-22.04
steps:
- name: Checkout mattermost project
@@ -165,8 +170,8 @@ jobs:
# -- Sharded into 4 parallel runners for ~88% wall-time improvement --
test-postgres-normal:
name: Postgres (shard ${{ matrix.shard }})
needs: go
if: needs.go.outputs.relevant-changed == 'true'
needs: buildenv
if: needs.buildenv.outputs.relevant-changed == 'true'
strategy:
fail-fast: false # Let all shards complete so we get full test results
matrix:
@@ -184,7 +189,7 @@ jobs:
# Each shard gets a unique artifact name so they don't collide
logsartifact: "postgres-server-test-logs-shard-${{ matrix.shard }}"
enablecoverage: ${{ github.event_name != 'pull_request' || !startsWith(github.event.pull_request.base.ref, 'release-') }}
go-version: ${{ needs.go.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: false
shard-index: ${{ matrix.shard }}
shard-total: 4
@@ -205,8 +210,8 @@ jobs:
test-elasticsearch-v8:
name: Elasticsearch v8 Compatibility
needs: go
if: needs.go.outputs.relevant-changed == 'true'
needs: buildenv
if: needs.buildenv.outputs.relevant-changed == 'true'
permissions:
contents: read
actions: write
@@ -216,15 +221,15 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10
drivername: postgres
logsartifact: elasticsearch-v8-server-test-logs
go-version: ${{ needs.go.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: false
elasticsearch-version: "8.9.0"
test-target: "test-server-elasticsearch"
test-opensearch-v2:
name: OpenSearch v2 Compatibility
needs: go
if: needs.go.outputs.relevant-changed == 'true'
needs: buildenv
if: needs.buildenv.outputs.relevant-changed == 'true'
permissions:
contents: read
actions: write
@@ -234,7 +239,7 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10
drivername: postgres
logsartifact: opensearch-v2-server-test-logs
go-version: ${{ needs.go.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: false
opensearch-version: "2.19.0"
test-target: "test-server-opensearch"
@@ -243,10 +248,10 @@ jobs:
# Sharded for fast iteration. Weekly workflow provides regular full coverage.
test-postgres-normal-fips:
if: >-
needs.go.outputs.relevant-changed == 'true' &&
(contains(github.head_ref, 'fips') || needs.go.outputs.gomod-changed == 'true')
needs.buildenv.outputs.relevant-changed == 'true' &&
(contains(github.head_ref, 'fips') || needs.buildenv.outputs.gomod-changed == 'true')
name: "Postgres FIPS (shard ${{ matrix.shard }})"
needs: go
needs: buildenv
strategy:
fail-fast: false
matrix:
@@ -263,7 +268,7 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10
drivername: postgres
logsartifact: "postgres-server-fips-test-logs-shard-${{ matrix.shard }}"
go-version: ${{ needs.go.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: true
shard-index: ${{ matrix.shard }}
shard-total: 4
@@ -281,8 +286,8 @@ jobs:
test-mmctl:
name: Run mmctl tests
needs: go
if: needs.go.outputs.relevant-changed == 'true'
needs: buildenv
if: needs.buildenv.outputs.relevant-changed == 'true'
permissions:
contents: read
actions: write
@@ -294,14 +299,14 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10
drivername: postgres
logsartifact: mmctl-test-logs
go-version: ${{ needs.go.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: false
test-mmctl-fips:
if: >-
needs.go.outputs.relevant-changed == 'true' &&
(contains(github.head_ref, 'fips') || needs.go.outputs.gomod-changed == 'true')
needs.buildenv.outputs.relevant-changed == 'true' &&
(contains(github.head_ref, 'fips') || needs.buildenv.outputs.gomod-changed == 'true')
name: Run mmctl tests (FIPS)
needs: go
needs: buildenv
permissions:
contents: read
actions: write
@@ -315,13 +320,13 @@ jobs:
datasource: postgres://mmuser:mostest_password@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10
drivername: postgres
logsartifact: mmctl-fips-test-logs
go-version: ${{ needs.go.outputs.version }}
build-server-tag: ${{ needs.buildenv.outputs.build-server-tag }}
fips-enabled: true
build-mattermost-server:
name: Build mattermost server app
needs: go
if: needs.go.outputs.relevant-changed == 'true'
needs: buildenv
if: needs.buildenv.outputs.relevant-changed == 'true'
permissions:
contents: read
actions: write
@@ -370,7 +375,7 @@ jobs:
permissions: {}
runs-on: ubuntu-22.04
needs:
- go
- buildenv
- merge-postgres-test-results
- test-elasticsearch-v8
- test-opensearch-v2
@@ -383,8 +388,8 @@ jobs:
run: |
# Explicitly fail here rather than relying on `if:`, since a job
# skipped via `if:` is reported as "Success" by GitHub.
if [ "${{ needs.go.result }}" != "success" ]; then
echo "::error::'go' job did not succeed (result: ${{ needs.go.result }}); relevant-changed could not be computed reliably. Failing Server CI Complete."
if [ "${{ needs.buildenv.result }}" != "success" ]; then
echo "::error::'buildenv' job did not succeed (result: ${{ needs.buildenv.result }}); relevant-changed could not be computed reliably. Failing Server CI Complete."
exit 1
fi
echo "Server CI Complete"
@@ -393,7 +398,7 @@ jobs:
name: Server CI Report
# Skip cleanly (rather than fail) when no relevant Server CI paths
# changed, since none of the test jobs it reports on will have run.
if: always() && needs.go.outputs.relevant-changed == 'true'
if: always() && needs.buildenv.outputs.relevant-changed == 'true'
permissions:
contents: read
actions: read
@@ -401,7 +406,7 @@ jobs:
pull-requests: write
issues: write
needs:
- go
- buildenv
- ci-complete
uses: ./.github/workflows/server-ci-report.yml
secrets:
@@ -421,7 +426,7 @@ jobs:
# (and thus server-dist-artifact) will not have run.
if: >-
success('ci-complete') &&
needs.go.outputs.relevant-changed == 'true' &&
needs.buildenv.outputs.relevant-changed == 'true' &&
github.repository_owner == 'mattermost' &&
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository
@@ -430,7 +435,7 @@ jobs:
actions: read
statuses: write
needs:
- go
- buildenv
- ci-complete
uses: ./.github/workflows/server-ci-artifacts.yml
secrets:
+4 -4
View File
@@ -26,7 +26,8 @@ on:
required: false
type: boolean
default: false
go-version:
build-server-tag:
description: "Build image tag (e.g. go1.26.3-node24.11.1)"
required: true
type: string
fips-enabled:
@@ -90,7 +91,6 @@ jobs:
INPUT_ENABLECOVERAGE: ${{ inputs.enablecoverage }}
INPUT_FIPS_ENABLED: ${{ inputs.fips-enabled }}
INPUT_FULLYPARALLEL: ${{ inputs.fullyparallel }}
INPUT_GO_VERSION: ${{ inputs.go-version }}
INPUT_LOGSARTIFACT: ${{ inputs.logsartifact }}
INPUT_NAME: ${{ inputs.name }}
INPUT_PR_NUMBER: ${{ github.event.pull_request.number }}
@@ -114,7 +114,7 @@ jobs:
id: buildenv
uses: ./.github/actions/load-buildenv
with:
go-version: ${{ inputs.go-version }}
build-server-tag: ${{ inputs.build-server-tag }}
fips-enabled: ${{ inputs.fips-enabled }}
- name: Restore test timing data
@@ -180,7 +180,7 @@ jobs:
if: inputs.shard-total > 1
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: ${{ inputs.go-version }}
go-version-file: server/.go-version
- name: Split tests across shards
if: inputs.shard-total > 1
+1 -1
View File
@@ -1 +1 @@
24.11
24.11.1
+1 -1
View File
@@ -1,5 +1,5 @@
FROM golang:1.26.7-bookworm@sha256:6ef6e30f0ea5c384f6d111cf856e024e3086bbdcb1779da3f3b3fbba0aea53d2
ARG NODE_VERSION=20.11.1
ARG NODE_VERSION=24.11.1
RUN apt-get update && apt-get install -y make git apt-transport-https ca-certificates curl software-properties-common build-essential zip xmlsec1 jq pgloader gnupg
+1 -1
View File
@@ -1,5 +1,5 @@
FROM cgr.dev/mattermost.com/go-msft-fips:1.26.7.1-dev@sha256:ae345a37a612265894a7e7f318be45680663346cb7992613916e196daf38e839
ARG NODE_VERSION=20.11.1
ARG NODE_VERSION=24.11.1
RUN apk add curl ca-certificates mailcap unrtf wv poppler-utils tzdata gpg xmlsec
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# Prints the tag identifying the mattermost-build-server image, e.g.
# "go1.26.7-node24.11.1". The image bakes in both toolchains, so the tag names
# both. CI resolves this once and propagates it as BUILD_SERVER_TAG.
#
# The tag comes from the Dockerfiles, since those are what actually determine
# the image, and they are checked against .go-version and .nvmrc so the pins
# cannot drift apart unnoticed.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/../.."
DOCKERFILES=(server/build/Dockerfile.buildenv server/build/Dockerfile.buildenv-fips)
fail() {
echo "$*" >&2
exit 1
}
# A pin may be more specific than its source of truth: the FIPS base image
# carries a fourth component, 1.26.7.1 for Go 1.26.7.
satisfies() {
[[ "$1" == "$2" || "$1" == "$2."* ]]
}
go_version=$(cat server/.go-version)
node_spec=$(cat .nvmrc)
node_version=""
for dockerfile in "${DOCKERFILES[@]}"; do
from=$(awk '$1 == "FROM" { print $2; exit }' "${dockerfile}")
[[ -n "${from}" ]] || fail "${dockerfile}: no FROM"
base_version="${from#*:}"
base_version="${base_version%%@*}"
base_version="${base_version%-*}"
satisfies "${base_version}" "${go_version}" ||
fail "${dockerfile}: base image is Go ${base_version}, but server/.go-version says ${go_version}"
version=$(awk -F= '$1 == "ARG NODE_VERSION" { print $2; exit }' "${dockerfile}")
[[ -n "${version}" ]] || fail "${dockerfile}: no ARG NODE_VERSION"
satisfies "${version}" "${node_spec}" ||
fail "${dockerfile}: ARG NODE_VERSION is ${version}, but .nvmrc says ${node_spec}"
# A single tag covers both images, so they have to agree.
if [[ -n "${node_version}" && "${version}" != "${node_version}" ]]; then
fail "${DOCKERFILES[0]} has Node ${node_version} but ${dockerfile} has ${version}"
fi
node_version="${version}"
done
tag="go${go_version}-node${node_version}"
# CI interpolates this tag into shell commands and workflow files, so refuse
# anything that isn't a well-formed Docker tag.
[[ "${tag}" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]] || fail "invalid tag: ${tag}"
echo "${tag}"