Skip to content

Scenarios using published assets #239

Scenarios using published assets

Scenarios using published assets #239

Workflow file for this run

---
name: docker-linux
"on":
pull_request:
paths:
- .github/workflows/docker-linux.yaml
- Dockerfile
workflow_dispatch:
permissions:
contents: read
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
defaults:
run:
shell: bash --noprofile --norc -euo pipefail {0}
env:
MISE_ENV: dart,dotnet,java,js,kotlin,python,r,rust,zig
jobs:
build:
runs-on: ubuntu-latest
# Raised from 120 for the opensuse lane's CARGO_BUILD_JOBS=2 cap (see the cargo-test step).
# Capping concurrent jobs trades wall-clock for peak memory, and opensuse was already the slowest lane at
# 47m51s when green, so the old ceiling left too little margin. The other lanes finish in 39-44m and are
# unaffected; this only stops a slower-but-succeeding build from being cut off.
timeout-minutes: 150
strategy:
fail-fast: false
matrix:
base:
- amazonlinux:2023
- azurelinux v3.0
- debian:bookworm
- debian:trixie
- fedora:42
- opensuse/leap:15.6
- ubuntu:22.04
- ubuntu:24.04
- ubuntu:26.04
name: build (${{ matrix.base }})
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
persist-credentials: false
- name: Show MISE_ENV
run: echo "MISE_ENV=$MISE_ENV"
# Relocate Docker's data dir onto an LVM volume to maximize build space.
# jlumbroso/free-disk-space (which test.yaml uses) only frees space on the existing root partition. This
# workflow needs more: the image build writes to /var/lib/docker, so we have to relocate Docker's data dir
# onto an LVM volume that concatenates the freed root space with /mnt. easimon is the only maintained action
# that does that LVM remount, so we stay on it here despite its rougher edges (hard-fails on runners without
# /mnt or without a Docker daemon -- not a concern for this workflow's ubuntu-latest matrix, which has both).
- name: Maximize build space
uses: easimon/maximize-build-space@v10
with:
root-reserve-mb: 4096
swap-size-mb: 1024
remove-dotnet: "true"
remove-android: "true"
remove-haskell: "true"
remove-codeql: "true"
remove-docker-images: "true"
build-mount-path: /var/lib/docker
build-mount-path-ownership: "root:root"
- name: Restart Docker on the maximized volume
run: sudo systemctl restart docker
- name: Resolve BASE_IMAGE
env:
MATRIX_BASE: ${{ matrix.base }}
run: |
if [ "$MATRIX_BASE" = "azurelinux v3.0" ]; then
echo "BASE_IMAGE=mcr.microsoft.com/azurelinux/base/core:3.0" >> "$GITHUB_ENV"
else
echo "BASE_IMAGE=$MATRIX_BASE" >> "$GITHUB_ENV"
fi
- name: Build stage test
env:
DOCKER_BUILDKIT: "1"
GITHUB_TOKEN: ${{ github.token }}
run: |
args="--target test --build-arg BASE_IMAGE --build-arg MISE_ENV"
args="$args --secret id=gh_token,env=GITHUB_TOKEN -t et-test"
# $args is a word-split flag list by design; do not quote it.
# shellcheck disable=SC2086
docker build $args .
# The check stage needs `.git/` + every tracked Dockerfile.
# The main `.dockerignore` excludes both (`**/Dockerfile*` so Dockerfiles in subdirs don't bloat the build
# context), so we pre-stage them into a directory and hand it to docker as a named build context (`extras`).
# The check stage does `COPY --from=extras . ./` to land them in /workspace. `cp --parents` preserves the
# subdir path of each Dockerfile (e.g. services/ws-server/Dockerfile), so hadolint's
# `git ls-files '*Dockerfile' '*Dockerfile.*' | xargs hadolint` finds every entry on disk -- a top-level cp
# would have left subdir Dockerfiles unfindable.
- name: Build stage check
env:
CTX: target/check-ctx
DOCKER_BUILDKIT: "1"
TAG: et-check
gh_token: ${{ github.token }}
run: |
mkdir -p "$CTX"
cp -r .git "$CTX/"
git ls-files '*Dockerfile' '*Dockerfile.*' | xargs -I{} cp --parents {} "$CTX/"
args="--target check --build-arg BASE_IMAGE --build-arg MISE_ENV"
args="$args --build-context extras=$CTX --secret id=gh_token -t $TAG"
# $args is a word-split flag list by design; do not quote it.
# shellcheck disable=SC2086
docker build $args .
# Capture image + intermediate stage sizes after the builds.
# This also runs after the test/check runs leave any -rm'd container layers behind. `always()` so we still
# get the breakdown when a downstream step ran the disk dry; the typical failure mode (test-ws-web-runner
# compile inside `docker run`) leaves the host's image catalogue intact and this step is what surfaces
# which layer ballooned. -a includes intermediate/dangling layers each stage produces; `system df -v`
# totals per cache type (Images, Build Cache, Containers, Volumes).
- name: Docker disk usage (debug)
if: always()
run: |
echo "::group::docker images -a"
docker images -a
echo "::endgroup::"
echo "::group::docker system df -v"
docker system df -v
echo "::endgroup::"
echo "::group::df -h /var/lib/docker"
df -h /var/lib/docker || true
echo "::endgroup::"
# Report which paths inside the et-test image are eating space.
# `du --threshold=10M --max-depth=2` for a top-level subtree map (catches install dirs, tool caches,
# /var stragglers); `find ... -size +50M` for the individual heavyweights. Run in a throwaway container
# so the host's writable layer doesn't tilt the numbers; find/du stderr noise (unreadable /proc entries
# etc.) prints to the log but doesn't affect the sort pipeline.
#
# Both runs are gated on the tag existing locally: this step is `always()`, and when the build died
# before tagging, a bare `docker run et-test` falls through to pulling `et-test:latest` from Docker
# Hub -- failing this debug step with the misleading
#
# docker: Error response from daemon: pull access denied for et-test, repository does not exist
# or may require 'docker login': denied: requested access to the resource is denied
#
# on top of the real build error (observed on commit
# https://github.com/edge-toolkit/core/commit/5c08a759202d166ac9fac48f5e54ec998d5fcb6e at
# https://github.com/edge-toolkit/core/actions/runs/31659062139/job/94319942583), and handing
# execution to whoever squats that name on Docker Hub if the pull ever resolved. `--pull=never`
# keeps the registry unreachable even if the gate is edited away.
if docker image inspect et-test >/dev/null 2>&1; then
echo "::group::et-test: largest dirs (depth 2, >=10M)"
du_cmd="du -h --threshold=10M --max-depth=2 / | sort -h | tail -50"
docker run --rm --pull=never et-test sh -c "$du_cmd"
echo "::endgroup::"
echo "::group::et-test: individual files >50M"
find_cmd="find / -xdev -type f -size +50M -exec du -h {} + | sort -h | tail -50"
docker run --rm --pull=never et-test sh -c "$find_cmd"
echo "::endgroup::"
else
echo "et-test image not present (build failed before tagging); skipping in-image size report"
fi
# Every cargo knob here is passed INTO the container, not set on the job.
# The compile happens inside `docker run`, so a job-level env would never reach cargo.
#
# CARGO_INCREMENTAL=0: incremental caches buy nothing on a single-shot CI compile and cost disk.
#
# CARGO_PROFILE_DEV_DEBUG=0: debug info is the bulk of what the linker reads and writes, so dropping it
# cuts peak linker memory and disk while making the build faster -- unlike a parallelism cap, it costs no
# wall-clock. The trade is shallower backtraces if a test panics on these lanes. Applied to every lane
# because two of them have already died mid-`cargo-test`: amazonlinux once, and opensuse on all three
# attempts of run 30872860977, each time with the log never published and the step frozen `in_progress`
# at ~1h13m against a 120m limit -- the shape of the runner being killed rather than a build error.
#
# CARGO_BUILD_JOBS=2 on opensuse only: it is the slowest lane even when green (47m51s vs 39-44m) and the
# only one still failing, so it links under the most memory pressure. Capping concurrent codegen/link jobs
# is the direct lever, kept off the other lanes because it does cost wall-clock -- which is why
# timeout-minutes went to 150 alongside it.
- name: Run cargo-test
env:
LANE_ARGS: ${{ matrix.base == 'opensuse/leap:15.6' && '-e CARGO_BUILD_JOBS=2' || '' }}
run: |
args="--rm --pull=never -e CARGO_INCREMENTAL=0 -e CARGO_PROFILE_DEV_DEBUG=0 $LANE_ARGS"
# $args is a word-split flag list by design; do not quote it.
# shellcheck disable=SC2086
docker run $args et-test mise run cargo-test
- name: Run mise check
run: docker run --rm --pull=never et-check