From 1ed45ab1ddf1b981ae7972715c982cd59c307467 Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:21:18 +0200 Subject: [PATCH 01/12] Restructure to OS/version layout (Option B); implement Ubuntu Key images by OS/OS-version built from a CI matrix (.ci/matrix.yml). - ubuntu/Dockerfile: one multi-stage Dockerfile for all Ubuntu versions (22.04/24.04/26.04); base = `full` stage, add-ons (cmake-4) = extra stages - single .github/workflows/build.yml pipeline: discover -> build -> release -> publish GHCR (signed) + Nexus, so a token-created release still publishes - debian/almalinux/arch: empty multi-stage placeholders, absent from the matrix - README + RELEASING document the layout and release flow Co-Authored-By: Claude Opus 4.8 --- .ci/matrix.py | 127 ++++++++++++++++ .ci/matrix.yml | 87 +++++++++++ .ci/publish.sh | 72 +++++++++ .github/workflows/build.yml | 227 +++++++++++++++++++++++++--- .github/workflows/publish-nexus.yml | 48 ------ .github/workflows/publish.yml | 87 ----------- .github/workflows/release.yml | 37 ----- .gitignore | 2 + Dockerfile | 117 -------------- README.md | 175 ++++++++++++++++----- RELEASING.md | 132 ++++++++++++++++ almalinux/Dockerfile | 26 ++++ arch/Dockerfile | 25 +++ debian/Dockerfile | 25 +++ ubuntu/Dockerfile | 179 ++++++++++++++++++++++ 15 files changed, 1023 insertions(+), 343 deletions(-) create mode 100755 .ci/matrix.py create mode 100644 .ci/matrix.yml create mode 100755 .ci/publish.sh delete mode 100644 .github/workflows/publish-nexus.yml delete mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/release.yml create mode 100644 .gitignore delete mode 100644 Dockerfile create mode 100644 RELEASING.md create mode 100644 almalinux/Dockerfile create mode 100644 arch/Dockerfile create mode 100644 debian/Dockerfile create mode 100644 ubuntu/Dockerfile diff --git a/.ci/matrix.py b/.ci/matrix.py new file mode 100755 index 0000000..fa774a4 --- /dev/null +++ b/.ci/matrix.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +"""Helpers for the CI. + +Reads ``.ci/matrix.yml`` (the single source of truth for which images exist) +and answers two questions for the GitHub Actions workflows: + + matrix.py all + Print, on one line, a JSON array of every image. Used as the + ``strategy.matrix`` for the build workflow. Each element looks like:: + + {"os": "ubuntu", "version": "24.04", "dir": "ubuntu/24.04", + "base_tag": "ubuntu-24.04", "context": "ubuntu", + "dockerfile": "ubuntu/Dockerfile", "target": "full", + "build_args": "UBUNTU_VERSION=24.04", "addons": "cmake-4"} + + ``target`` is the build stage for the base image ("" = final stage). + Each add-on in ``addons`` is itself a build stage (--target). Both + ``build_args`` and ``addons`` are space-separated strings so they can be + looped over in shell directly. + + matrix.py image + Resolve a release tag such as ``ubuntu-24.04-2.1.0`` to the image it + refers to and print shell ``key='value'`` assignments to stdout:: + + dir='ubuntu/24.04' + base_tag='ubuntu-24.04' + semver='2.1.0' + context='ubuntu' + dockerfile='ubuntu/Dockerfile' + target='full' + build_args='UBUNTU_VERSION=24.04' + addons='cmake-4' + + Intended to be consumed with ``eval "$(python .ci/matrix.py image …)"``. +""" + +from __future__ import annotations + +import json +import os +import re +import sys + +import yaml + +HERE = os.path.dirname(os.path.abspath(__file__)) +MATRIX_FILE = os.path.join(HERE, "matrix.yml") + +SEMVER_RE = re.compile(r"^(?P.+)-(?P\d+\.\d+\.\d+)$") + + +def load_images(): + with open(MATRIX_FILE, encoding="utf-8") as handle: + data = yaml.safe_load(handle) + images = data.get("images") or [] + result = [] + for img in images: + os_name = str(img["os"]) + version = str(img["version"]) + directory = f"{os_name}/{version}" + context = str(img.get("context") or f"{directory}/base") + dockerfile = str(img.get("dockerfile") or f"{context}/Dockerfile") + target = str(img.get("target") or "") + build_args = " ".join( + f"{k}={v}" for k, v in (img.get("build_args") or {}).items() + ) + addons = [str(a) for a in (img.get("addons") or [])] + result.append( + { + "os": os_name, + "version": version, + "dir": directory, + "base_tag": f"{os_name}-{version}", + "context": context, + "dockerfile": dockerfile, + "target": target, + "build_args": build_args, + "addons": " ".join(addons), + } + ) + return result + + +def cmd_all(): + print(json.dumps(load_images(), separators=(",", ":"))) + + +def cmd_image(tag: str): + match = SEMVER_RE.match(tag) + if not match: + sys.exit( + f"error: tag '{tag}' is not of the form -- " + f"(e.g. ubuntu-24.04-2.1.0)" + ) + prefix = match.group("prefix") + semver = match.group("semver") + + for img in load_images(): + if img["base_tag"] == prefix: + print(f"dir='{img['dir']}'") + print(f"base_tag='{img['base_tag']}'") + print(f"semver='{semver}'") + print(f"context='{img['context']}'") + print(f"dockerfile='{img['dockerfile']}'") + print(f"target='{img['target']}'") + print(f"build_args='{img['build_args']}'") + print(f"addons='{img['addons']}'") + return + + valid = ", ".join(img["base_tag"] for img in load_images()) + sys.exit( + f"error: no image matches tag prefix '{prefix}'. " + f"Known images: {valid}" + ) + + +def main(argv): + if len(argv) >= 2 and argv[1] == "all": + cmd_all() + elif len(argv) >= 3 and argv[1] == "image": + cmd_image(argv[2]) + else: + sys.exit(f"usage: {argv[0]} all | image ") + + +if __name__ == "__main__": + main(sys.argv) diff --git a/.ci/matrix.yml b/.ci/matrix.yml new file mode 100644 index 0000000..83e6d9c --- /dev/null +++ b/.ci/matrix.yml @@ -0,0 +1,87 @@ +# Declares every image this repository builds. +# +# Each entry is one OS / OS-version. Every OS uses a single multi-stage +# Dockerfile at /Dockerfile (e.g. ubuntu/Dockerfile) covering all of its +# versions: the base image is the `full` stage and add-ons are extra stages. +# +# Per-image fields: +# context build context directory (default: //base) +# dockerfile path to the Dockerfile (default: /Dockerfile) +# build_args map of --build-arg values, e.g. the version (default: none) +# target build stage for the BASE image (default: final stage) +# addons list of add-ons. Each add-on is a build STAGE (--target) in the +# image's own Dockerfile (default: none) +# The shared-Dockerfile images set context/dockerfile/target explicitly (e.g. +# all Ubuntu versions use ubuntu/Dockerfile and differ only by UBUNTU_VERSION; +# the base is the `full` stage and add-ons such as `cmake-4` are extra stages). +# +# Tag grammar (see README.md): +# base moving : - e.g. ubuntu-24.04 +# base immutable : -- e.g. ubuntu-24.04-2.1.0 +# addon moving : -- e.g. ubuntu-24.04-cmake-4 +# addon immutable : --- e.g. ubuntu-24.04-cmake-4-2.1.0 +# +# is THIS repository's version (the recipe), independent of the +# OpenModelica version. Pushing the git tag `--` releases +# the base image and all of its add-ons (see RELEASING.md). + +images: + # All Ubuntu versions share the multi-stage ubuntu/Dockerfile; only + # UBUNTU_VERSION differs. The base image is the `full` stage; add-ons are + # further stages (e.g. `cmake-4`). + - os: ubuntu + version: "26.04" + context: ubuntu + dockerfile: ubuntu/Dockerfile + target: full + build_args: + UBUNTU_VERSION: "26.04" + addons: [] + + - os: ubuntu + version: "24.04" + context: ubuntu + dockerfile: ubuntu/Dockerfile + target: full + build_args: + UBUNTU_VERSION: "24.04" + addons: + - cmake-4 + + - os: ubuntu + version: "22.04" + context: ubuntu + dockerfile: ubuntu/Dockerfile + target: full + build_args: + UBUNTU_VERSION: "22.04" + addons: [] + + # Placeholders — not implemented yet (see /Dockerfile). Each OS uses the + # same single multi-stage Dockerfile layout as Ubuntu. Uncomment and flesh out + # once the corresponding Dockerfile is real, so CI starts building them. + # + # - os: debian + # version: "13" + # context: debian + # dockerfile: debian/Dockerfile + # target: full + # build_args: + # DEBIAN_VERSION: "13" + # addons: [] + # + # - os: almalinux + # version: "9" + # context: almalinux + # dockerfile: almalinux/Dockerfile + # target: full + # build_args: + # ALMALINUX_VERSION: "9" + # addons: [] + # + # - os: arch + # version: "rolling" + # context: arch + # dockerfile: arch/Dockerfile + # target: full + # addons: [] diff --git a/.ci/publish.sh b/.ci/publish.sh new file mode 100755 index 0000000..485cfa5 --- /dev/null +++ b/.ci/publish.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# +# Build and push one image (base + all its add-ons) to a registry. +# +# Used by both publish.yml (GHCR) and publish-nexus.yml (Nexus). The base image +# is pushed under a moving tag (-) and an immutable tag +# (--). Each add-on is a build STAGE (--target) in the same +# Dockerfile and is pushed under its own moving + immutable tags; shared layers +# come from the build cache, so the base is effectively built only once. +# +# Required environment variables: +# REGISTRY Image repository, e.g. ghcr.io/openmodelica/build-deps +# TAG Release tag, e.g. ubuntu-24.04-2.1.0 +# SIGN "true" to cosign-sign every pushed tag (keyless OIDC), else "false" +set -euo pipefail + +: "${REGISTRY:?REGISTRY is required}" +: "${TAG:?TAG is required}" +SIGN="${SIGN:-false}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Resolve the tag to base_tag / semver / context / dockerfile / target / +# build_args / addons. +eval "$(python3 "${SCRIPT_DIR}/matrix.py" image "${TAG}")" + +PUSHED_TAGS=() + +build_and_push() { + # $1 = moving tag, $2 = immutable tag, remaining args go to `docker buildx`. + local moving="$1" immutable="$2" + shift 2 + echo "::group::Building ${moving}" + docker buildx build \ + --pull \ + --file "${dockerfile}" \ + --tag "${REGISTRY}:${moving}" \ + --tag "${REGISTRY}:${immutable}" \ + --push \ + "$@" \ + "${context}" + echo "::endgroup::" + PUSHED_TAGS+=("${REGISTRY}:${moving}" "${REGISTRY}:${immutable}") +} + +# Common build-args (e.g. UBUNTU_VERSION) apply to every stage. +common_args=() +for kv in ${build_args}; do + common_args+=(--build-arg "${kv}") +done + +# 1. Base image: the image's `target` stage (or the final stage). +base_target_arg=() +[ -n "${target}" ] && base_target_arg=(--target "${target}") +build_and_push "${base_tag}" "${base_tag}-${semver}" \ + "${common_args[@]}" "${base_target_arg[@]}" + +# 2. Add-ons: each is a --target stage in the same Dockerfile. +for addon in ${addons}; do + build_and_push "${base_tag}-${addon}" "${base_tag}-${addon}-${semver}" \ + "${common_args[@]}" --target "${addon}" +done + +# 3. Optionally sign every pushed tag (GHCR / cosign keyless). +if [ "${SIGN}" = "true" ]; then + for image in "${PUSHED_TAGS[@]}"; do + echo "Signing ${image}" + cosign sign --yes "${image}" + done +fi + +printf '%s\n' "${PUSHED_TAGS[@]}" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 802707e..c67034b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,37 +1,230 @@ -name: Build Docker Image +name: Build, Release & Publish + +# Single pipeline so a release created here can trigger publishing in the SAME +# run. (A GitHub Release created with GITHUB_TOKEN does NOT trigger other +# workflows, which is why release + publish must live together.) +# +# discover ─▶ build (all images, no push) +# └─▶ release (tag only) ─▶ publish-ghcr + publish-nexus +# +# Release/publish are keyed on image tags: --, +# e.g. ubuntu-24.04-2.1.0. on: push: branches: - main - - 'releases/**' tags: - - v* + - '*-*.*.*' pull_request: branches: - main - - 'releases/**' + workflow_dispatch: + inputs: + tag: + description: 'Image tag to (re)publish (e.g. ubuntu-24.04-2.1.0)' + required: true + +permissions: + contents: read jobs: + discover: + name: Discover images + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set.outputs.matrix }} + steps: + - name: Check out the repo + uses: actions/checkout@v7.0.0 + + - name: Install dependencies + run: pip install pyyaml + + - name: Compute build matrix + id: set + run: echo "matrix=$(python .ci/matrix.py all)" >> "$GITHUB_OUTPUT" + build: - name: Build Dockerfile + name: Build ${{ matrix.image.os }}-${{ matrix.image.version }} + needs: discover + runs-on: ubuntu-latest + timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + image: ${{ fromJson(needs.discover.outputs.matrix) }} + steps: + - name: Check out the repo + uses: actions/checkout@v7.0.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build base image and add-ons + env: + BASE_TAG: ${{ matrix.image.base_tag }} + CONTEXT: ${{ matrix.image.context }} + DOCKERFILE: ${{ matrix.image.dockerfile }} + TARGET: ${{ matrix.image.target }} + BUILD_ARGS: ${{ matrix.image.build_args }} + ADDONS: ${{ matrix.image.addons }} + run: | + set -euo pipefail + + # Common build-args (e.g. UBUNTU_VERSION) apply to every stage. + common_args=() + for kv in ${BUILD_ARGS}; do common_args+=(--build-arg "${kv}"); done + + # Base image: the image's `target` stage (or the final stage). + base_target_arg=() + [ -n "${TARGET}" ] && base_target_arg=(--target "${TARGET}") + echo "::group::Building base ${BASE_TAG}" + docker buildx build \ + --file "${DOCKERFILE}" \ + "${common_args[@]}" "${base_target_arg[@]}" \ + --tag "local/build-deps:${BASE_TAG}" \ + --load \ + "${CONTEXT}" + echo "::endgroup::" + + # Add-ons: each is a --target stage in the same Dockerfile. + for addon in ${ADDONS}; do + echo "::group::Building add-on ${addon}" + docker buildx build \ + --file "${DOCKERFILE}" \ + "${common_args[@]}" --target "${addon}" \ + --tag "local/build-deps:${BASE_TAG}-${addon}" \ + --load \ + "${CONTEXT}" + echo "::endgroup::" + done + + release: + name: Create GitHub Release + needs: build + if: ${{ startsWith(github.ref, 'refs/tags/') }} runs-on: ubuntu-latest - timeout-minutes: 60 + permissions: + contents: write steps: - name: Check out the repo uses: actions/checkout@v7.0.0 - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v6 + - name: Create or update release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Per-image releases are never marked as the repository "latest": + # there is no single newest image in the monorepo. + if gh release view "${{ github.ref_name }}" &>/dev/null; then + gh release edit "${{ github.ref_name }}" \ + --title "${{ github.ref_name }}" \ + --generate-notes \ + --latest=false + else + gh release create "${{ github.ref_name }}" \ + --title "${{ github.ref_name }}" \ + --generate-notes \ + --verify-tag \ + --latest=false + fi + + publish-ghcr: + name: Publish to GHCR + needs: [build, release] + # Runs on a release tag (after release succeeds) or on a manual re-publish + # dispatch (where the release job is skipped). + if: >- + ${{ always() + && needs.build.result == 'success' + && (needs.release.result == 'success' || needs.release.result == 'skipped') + && (startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch') }} + runs-on: ubuntu-latest + timeout-minutes: 90 + permissions: + contents: read + packages: write + id-token: write # needed for signing the images with GitHub OIDC Token + env: + REGISTRY: ghcr.io/openmodelica/build-deps + TAG: ${{ github.event.inputs.tag || github.ref_name }} + steps: + - name: Check out the repo + uses: actions/checkout@v7.0.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install dependencies + run: pip install pyyaml + + - name: Install cosign + uses: sigstore/cosign-installer@v4.1.2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4.2.0 with: - images: ghcr.io/openmodelica/build-deps + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build, push and sign image (base + add-ons) + env: + SIGN: "true" + run: ./.ci/publish.sh - - name: Build Docker image - uses: docker/build-push-action@v7 + - name: Verify signatures + run: | + set -euo pipefail + eval "$(python .ci/matrix.py image "${TAG}")" + CERT_ID="https://github.com/${{ github.repository }}/.github/workflows/build.yml@${{ github.ref }}" + CERT_ISSUER="https://token.actions.githubusercontent.com" + tags=("${base_tag}" "${base_tag}-${semver}") + for addon in ${addons}; do + tags+=("${base_tag}-${addon}" "${base_tag}-${addon}-${semver}") + done + for t in "${tags[@]}"; do + echo "Verifying ${REGISTRY}:${t}" + cosign verify \ + --certificate-identity="${CERT_ID}" \ + --certificate-oidc-issuer="${CERT_ISSUER}" \ + "${REGISTRY}:${t}" + done + + publish-nexus: + name: Publish to Nexus + needs: [build, release] + if: >- + ${{ always() + && needs.build.result == 'success' + && (needs.release.result == 'success' || needs.release.result == 'skipped') + && (startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch') }} + runs-on: ubuntu-latest + timeout-minutes: 90 + permissions: + contents: read + env: + REGISTRY: docker.openmodelica.org/build-deps + TAG: ${{ github.event.inputs.tag || github.ref_name }} + steps: + - name: Check out the repo + uses: actions/checkout@v7.0.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install dependencies + run: pip install pyyaml + + - name: Login to Nexus Docker registry + uses: docker/login-action@v4.2.0 with: - context: . - file: ./Dockerfile - tags: ${{ steps.meta.outputs.tags }} - annotations: ${{ steps.meta.outputs.annotations }} - push: false + registry: docker.openmodelica.org + username: ${{ secrets.NEXUS_OPENMODELICABOT_USER }} + password: ${{ secrets.NEXUS_OPENMODELICABOT_PASSWORD }} + + - name: Build and push image (base + add-ons) + env: + SIGN: "false" + run: ./.ci/publish.sh diff --git a/.github/workflows/publish-nexus.yml b/.github/workflows/publish-nexus.yml deleted file mode 100644 index 3f39a03..0000000 --- a/.github/workflows/publish-nexus.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Publish Docker Image to Nexus - -on: - release: - types: [published, edited] - workflow_dispatch: - inputs: - tag: - description: 'Image tag to publish (e.g. v1.22.3)' - required: true - -permissions: - contents: read - -jobs: - push_to_nexus: - name: Push Docker image to Nexus (docker.openmodelica.org) - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - name: Check out the repo - uses: actions/checkout@v7.0.0 - - - name: Login to Nexus Docker registry - uses: docker/login-action@v4.2.0 - with: - registry: docker.openmodelica.org - username: ${{ secrets.NEXUS_OPENMODELICABOT_USER }} - password: ${{ secrets.NEXUS_OPENMODELICABOT_PASSWORD }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v6 - with: - images: docker.openmodelica.org/build-deps - tags: | - type=ref,event=tag - type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }} - - - name: Build and push Docker image - uses: docker/build-push-action@v7 - with: - context: . - file: ./Dockerfile - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ steps.meta.outputs.annotations }} - push: true diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 248f19f..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Publish Docker Image - -on: - release: - types: [published, edited] - workflow_dispatch: - inputs: - tag: - description: 'Image tag to publish (e.g. v1.22.3)' - required: true - -# Required for cosign keyless (OIDC) to mint tokens -permissions: - contents: read - packages: write - id-token: write # needed for signing the images with GitHub OIDC Token - -jobs: - push_to_registry: - name: Push Docker image to GitHub Container registry - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - name: Check out the repo - uses: actions/checkout@v7.0.0 - - - name: Install cosign - uses: sigstore/cosign-installer@v4.1.2 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v4.2.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v6 - with: - images: ghcr.io/openmodelica/build-deps - tags: | - type=ref,event=tag - type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }} - - - name: Build and push Docker image - uses: docker/build-push-action@v7 - with: - context: . - file: ./Dockerfile - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ steps.meta.outputs.annotations }} - push: true - - - name: Sign the images with GitHub OIDC Token (tag-based) - env: - TAGS: ${{ steps.meta.outputs.tags }} - run: | - set -euo pipefail - [ -n "${TAGS:-}" ] || { echo "No tags found"; exit 1; } - images="" - while IFS= read -r tag; do - tag="$(echo "$tag" | xargs)" # trim whitespace - [ -z "$tag" ] && continue - images+="${tag} " - echo "Signing tag: ${tag}" - cosign sign --yes "${tag}" - done < <(echo "${TAGS:-}" | tr ',' '\n') - - - name: Verify signatures (tag-based) - env: - TAGS: ${{ steps.meta.outputs.tags }} - run: | - set -euo pipefail - [ -n "${TAGS:-}" ] || { echo "No tags found"; exit 1; } - CERT_ID="https://github.com/${{ github.repository }}/.github/workflows/publish.yml@${{ github.ref }}" - CERT_ISSUER="https://token.actions.githubusercontent.com" - while IFS= read -r tag; do - tag="$(echo "$tag" | xargs)" - [ -z "$tag" ] && continue - echo "Verifying tag: ${tag}" - cosign verify \ - --certificate-identity="${CERT_ID}" \ - --certificate-oidc-issuer="${CERT_ISSUER}" \ - "${tag}" - done < <(echo "${TAGS:-}" | tr ',' '\n') diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index b30e8a4..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Create Release - -on: - push: - tags: - - 'v*' - -permissions: - contents: write - -jobs: - release: - name: Create GitHub Release - runs-on: ubuntu-latest - steps: - - name: Check out the repo - uses: actions/checkout@v7.0.0 - - - name: Create or update release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Only mark as latest when the tag is on the default branch - BASE_BRANCH=$(gh api repos/${{ github.repository }} --jq '.default_branch') - ON_DEFAULT=$(git branch -r --contains "${{ github.ref_name }}" | grep -qE "origin/${BASE_BRANCH}$" && echo true || echo false) - if gh release view "${{ github.ref_name }}" &>/dev/null; then - gh release edit "${{ github.ref_name }}" \ - --title "${{ github.ref_name }}" \ - --generate-notes \ - --latest="${ON_DEFAULT}" - else - gh release create "${{ github.ref_name }}" \ - --title "${{ github.ref_name }}" \ - --generate-notes \ - --verify-tag \ - --latest="${ON_DEFAULT}" - fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ace5d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +CLAUDE.md diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index b8d4338..0000000 --- a/Dockerfile +++ /dev/null @@ -1,117 +0,0 @@ -FROM ubuntu:noble - -# Image / OCI metadata -LABEL maintainer="AnHeuermann" -LABEL description="OpenModelica build-deps Docker Image" -LABEL organization="OpenModelica" - -LABEL org.opencontainers.image.vendor="OpenModelica" -LABEL org.opencontainers.image.authors="AnHeuermann" -LABEL org.opencontainers.image.version="v1.26.1" -LABEL org.opencontainers.image.description="OpenModelica build-deps Docker Image " -LABEL org.opencontainers.image.source="https://github.com/OpenModelica/build-deps" -LABEL org.opencontainers.image.license="MIT" - -ENV SHELL=/bin/bash - -# Ensure DEBIAN_FRONTEND is only set during build -ARG DEBIAN_FRONTEND=noninteractive - -# Install OpenModelica build-deps -RUN apt-get update \ - && apt-get upgrade -qy \ - && apt-get dist-upgrade -qy \ - && apt-get install -qy \ - ca-certificates \ - curl \ - gnupg \ - lsb-release \ - && curl -fsSL https://build.openmodelica.org/apt/openmodelica.asc | gpg --dearmor -o /usr/share/keyrings/openmodelica-keyring.gpg \ - && echo \ - "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openmodelica-keyring.gpg] https://build.openmodelica.org/apt \ - $(cat /etc/os-release | grep "\(UBUNTU\\|DEBIAN\\|VERSION\)_CODENAME" | sort | cut -d= -f 2 | head -1) \ - nightly" | tee /etc/apt/sources.list.d/openmodelica.list > /dev/null \ - && echo \ - "deb-src [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openmodelica-keyring.gpg] https://build.openmodelica.org/apt \ - $(cat /etc/os-release | grep "\(UBUNTU\\|DEBIAN\\|VERSION\)_CODENAME" | sort | cut -d= -f 2 | head -1) \ - nightly" | tee -a /etc/apt/sources.list.d/openmodelica.list > /dev/null \ - && apt-get update \ - && apt-get build-dep -qy openmodelica \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install additional dependencies -# - tools to build the User's Guide -# - Qt5, Qt6 packages -RUN apt-get update \ - && apt-get install -qy \ - aspell \ - bibtex2html \ - bison \ - ccache \ - clang-tools \ - devscripts \ - docker.io \ - doxygen \ - equivs \ - flex \ - git \ - gnuplot-nox \ - inkscape \ - intel-opencl-icd \ - latexmk \ - libcurl4-gnutls-dev \ - libmldbm-perl \ - libqt6core5compat6-dev \ - libqt6opengl6-dev \ - libqt6openglwidgets6 \ - libqt6svg6-dev \ - locales \ - ocl-icd-opencl-dev \ - opencl-headers \ - pandoc \ - pocl-opencl-icd \ - poppler-utils \ - python3-pip \ - python3-venv \ - qt6-base-dev \ - qt6-httpserver-dev \ - qt6-scxml-dev \ - qt6-tools-dev \ - qt6-tools-dev-tools \ - qt6-webengine-dev \ - qt6-httpserver-dev \ - qt6-websockets-dev \ - qtwebengine5-dev \ - subversion \ - texlive-base \ - texlive-bibtex-extra \ - texlive-lang-greek \ - texlive-latex-extra \ - unzip \ - wget \ - xsltproc \ - xvfb \ - zip \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install Python packages in a default virtual environment -# Use permalink for doc/UsersGuide/source/requirements.txt to keep builds -# deterministic. -RUN python3 -m venv /opt/venv -ENV PATH="/opt/venv/bin:$PATH" -RUN pip install --no-cache-dir \ - junit_xml \ - lxml \ - ompython==3.6.0 \ - PyGithub \ - simplejson \ - svgwrite \ - && pip install --no-cache-dir -r \ - https://raw.githubusercontent.com/OpenModelica/OpenModelica/9c0dc9a8ab50ba652109584cb3fecaef86640b66/doc/UsersGuide/source/requirements.txt - -# Set locale -ENV LANGUAGE=en_US:en -ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 diff --git a/README.md b/README.md index 1e525a2..5ca4fb1 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,161 @@ -# OpenModelica build-deps Docker Image +# OpenModelica build-deps Docker Images -[![Build Docker Image](https://github.com/OpenModelica/build-deps/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/OpenModelica/build-deps/actions/workflows/build.yml) -[![Publish Docker Image](https://github.com/OpenModelica/build-deps/actions/workflows/publish.yml/badge.svg)](https://github.com/OpenModelica/build-deps/actions/workflows/publish.yml) +[![Build, Release & Publish][badge-build-img]][workflow-build] The Docker images used to build and deploy -[OpenModelica](https://github.com/OpenModelica/OpenModelica) with -[Jenkins](https://test.openmodelica.org/jenkins/). +[OpenModelica][openmodelica] with +[Jenkins][jenkins]. + +Images are published to: + +- `ghcr.io/openmodelica/build-deps` (GitHub Container Registry) +- `docker.openmodelica.org/build-deps` (Nexus) ## Structure of the Repository -Each minor version of the Dockerfile corresponds to a OpenModelica minor version -and has its own branch. Each branch has tags for each patch version. +Every image lives on `main`, keyed by **operating system and OS version** rather +than by OpenModelica version. Each image is a **base** plus optional, layered +**add-ons**, so the heavy common tooling is built once and reused. + +```text +main +├── ubuntu/ +│ └── Dockerfile # multi-stage: ALL Ubuntu versions + add-ons +├── debian/Dockerfile # placeholder (not implemented yet) +├── almalinux/Dockerfile # placeholder (not implemented yet) +├── arch/Dockerfile # placeholder (not implemented yet) +└── .ci/ + ├── matrix.yml # source of truth: which images exist + ├── matrix.py # matrix.yml -> CI matrix / tag lookup + └── publish.sh # build + push one image (base + add-ons) +``` + +> **Status:** only the **Ubuntu** images are implemented. Debian, AlmaLinux and +> Arch are empty placeholders (an `/Dockerfile` with a TODO header) and are +> intentionally left out of [.ci/matrix.yml][matrix-yml] until implemented, +> so CI does not try to build them. + +- **Base image** — one per OS/OS-version. Contains everything needed to build + OpenModelica (distro packages + common tooling: TeX, Qt, Python venv, + ccache, …). This is what most CI jobs use. +- **Add-on image** — the base plus *one* thing the distro package manager can't + provide or that needs a pinned version (e.g. CMake 4). Realised as an extra + build **stage** (`FROM` the base stage) in the same Dockerfile, so shared + layers are reused from cache. -When creating a release form a tag the -[workflow](./.github/workflows/publish.yml) will publish the Docker image to -[GitHub Container registry](https://github.com/OpenModelica/openmodelica-build-deps/pkgs/container/build-deps). +Every OS uses a single **multi-stage** Dockerfile at `/Dockerfile` covering +all of its versions (the Debian/AlmaLinux/Arch placeholders follow this too). +All Ubuntu versions build from [ubuntu/Dockerfile][ubuntu-dockerfile]: -### Ubuntu based Images +- the FROM tag is set by the `UBUNTU_VERSION` build-arg, and the Qt package set + is picked from the image's `VERSION_ID` at build time; +- the base image is the `full` stage (`--target full`); +- each add-on is a further stage (e.g. `--target cmake-4`). -- 24.04 Noble: - - [releases/v1.26](https://github.com/OpenModelica/build-deps/tree/releases/v1.26) - - [releases/v1.26-cmake4](https://github.com/OpenModelica/build-deps/tree/releases/v1.26-cmake4) -- 22.04 Jammy: - - [releases/v1.22](https://github.com/OpenModelica/build-deps/tree/releases/v1.22) - - [releases/v1.22-qtwebengine](https://github.com/OpenModelica/build-deps/tree/releases/v1.22-qtwebengine), replaced by [releases/v1.22](https://github.com/OpenModelica/build-deps/tree/releases/v1.22) v1.22.3 or higher - - [releases/v1.24-qt5qt6](https://github.com/OpenModelica/build-deps/tree/releases/v1.22-qtwebengine), replaced by [releases/v1.22](https://github.com/OpenModelica/build-deps/tree/releases/v1.22) v1.22.3 or higher -- 20.04 Focal: [releases/v1.21](https://github.com/OpenModelica/build-deps/tree/releases/v1.21) -- 18.04 Bionic + cmake: [releases/v1.16-cmake](https://github.com/OpenModelica/build-deps/tree/releases/v1.16-cmake) -- 18.04 Bionic: [releases/v1.16](https://github.com/OpenModelica/build-deps/tree/releases/v1.16) +Each image's `context`, `dockerfile`, `target`, `build_args` and `addons` +(add-on stage names) are declared in [.ci/matrix.yml][matrix-yml]. -### Debian based Images +To add a new image, create or extend the OS's `/Dockerfile` and list it in +[.ci/matrix.yml][matrix-yml] (a new version of an existing OS needs only a +matrix entry). -- 12 Bookworm -- 11 Bullseye +### Image naming & tags -### CentOS based Images +One image repository per registry; OS, version and variant are encoded in the +**tag**: -- CentOS7 +| Tag | Mutable? | Meaning | +| --- | --- | --- | +| `ubuntu-24.04` | moving | Latest base image for Ubuntu 24.04 | +| `ubuntu-24.04-2.1.0` | immutable | Pinned base, `2.1.0` = this repo's semver | +| `ubuntu-24.04-cmake-4` | moving | Latest CMake 4 add-on on the 24.04 base | +| `ubuntu-24.04-cmake-4-2.1.0` | immutable | Pinned add-on | +| `arch-rolling-2026.06.01` | immutable | Date-stamped snapshot for the rolling distro | -## Build +The repo's own semver (`MAJOR.MINOR.PATCH`) versions the **recipe**, not +OpenModelica. Day-to-day CI uses the **moving** tag; when an OpenModelica +release needs a frozen environment it pins the **immutable** tag. + +### Currently provided images + +| OS / version | Base tag | Add-ons | Status | Source | +| --- | --- | --- | --- | --- | +| Ubuntu 26.04 | `ubuntu-26.04` | – | implemented | [ubuntu/Dockerfile][ubuntu-dockerfile] | +| Ubuntu 24.04 (Noble) | `ubuntu-24.04` | `cmake-4` | implemented | [ubuntu/Dockerfile][ubuntu-dockerfile] | +| Ubuntu 22.04 (Jammy) | `ubuntu-22.04` | – | implemented | [ubuntu/Dockerfile][ubuntu-dockerfile] | +| Debian 13 (Trixie) | `debian-13` | – | placeholder | [debian/Dockerfile][debian-dockerfile] | +| AlmaLinux 9 | `almalinux-9` | – | placeholder | [almalinux/Dockerfile][almalinux-dockerfile] | +| Arch Linux (rolling) | `arch-rolling` | – | placeholder | [arch/Dockerfile][arch-dockerfile] | + +## Build locally + +**Base image** — for Ubuntu, pick the version with `UBUNTU_VERSION` and build +the `full` stage: ```bash -export TAG=v1.26.1 -docker build --pull --no-cache --tag build-deps:$TAG . +docker build --pull --no-cache \ + --target full \ + --build-arg UBUNTU_VERSION=24.04 \ + --tag build-deps:ubuntu-24.04 \ + ubuntu + +# Debian/AlmaLinux/Arch follow the same pattern once implemented, e.g.: +# docker build --pull --target full --build-arg DEBIAN_VERSION=13 \ +# --tag build-deps:debian-13 debian ``` -## Upload +**Add-on image** — build the add-on's stage with `--target`. It reuses the +base's cached layers, so it only adds the extra step: + +```bash +docker build --pull \ + --target cmake-4 \ + --build-arg UBUNTU_VERSION=24.04 \ + --tag build-deps:ubuntu-24.04-cmake-4 \ + ubuntu +``` + +> The values to pass (`context`, `--file`, `--target`, `--build-arg`) for any +> image are exactly its fields in [.ci/matrix.yml][matrix-yml]. + +## CI workflow + +A single workflow, [build.yml][workflow-build-file], runs the whole pipeline so +that a release it creates can publish in the **same** run (a release created with +`GITHUB_TOKEN` cannot trigger a separate workflow): + +```text +discover ─▶ build (all images, no push) + └─▶ release (tag only) ─▶ publish-ghcr + publish-nexus +``` + +- **build** — on every push/PR to `main` (and as the gate before release), + builds every base + add-on declared in `.ci/matrix.yml` (no push). +- **release** — on an image release tag, creates/updates the GitHub Release. +- **publish-ghcr / publish-nexus** — build, push (and on GHCR **sign**) the + tagged image (base + add-ons) to GHCR and Nexus. Also runnable via + `workflow_dispatch` with a `tag` input to re-publish without a new tag. + +## Releasing a new image version -The [publish.yml](./.github/workflows/publish.yml) workflow will build, sign and -upload the Docker image to -[GitHub Container registry](https://github.com/OpenModelica/openmodelica-build-deps/pkgs/container/build-deps) -for each release. -The [publish-nexus](./.github/workflows/publish-nexus.yml) workflow will build and upload the Docker image to [docker.openmodelica.org](https://nexus.openmodelica.org/#browse/browse:openmodelica:v2%2Fbuild-deps) +See **[RELEASING.md][releasing-md]** for the step-by-step process. ## License The original Dockerfile was taken from -[OpenModelica/OpenModelicaBuildScripts](https://github.com/OpenModelica/OpenModelicaBuildScripts). -See [LICENSE.md](./LICENSE.md). +[OpenModelica/OpenModelicaBuildScripts][build-scripts]. +See [LICENSE.md][license-md]. + +[badge-build-img]: https://github.com/OpenModelica/build-deps/actions/workflows/build.yml/badge.svg?branch=main +[workflow-build]: https://github.com/OpenModelica/build-deps/actions/workflows/build.yml +[openmodelica]: https://github.com/OpenModelica/OpenModelica +[jenkins]: https://test.openmodelica.org/jenkins/ +[matrix-yml]: ./.ci/matrix.yml +[ubuntu-dockerfile]: ./ubuntu/Dockerfile +[debian-dockerfile]: ./debian/Dockerfile +[almalinux-dockerfile]: ./almalinux/Dockerfile +[arch-dockerfile]: ./arch/Dockerfile +[workflow-build-file]: ./.github/workflows/build.yml +[releasing-md]: ./RELEASING.md +[build-scripts]: https://github.com/OpenModelica/OpenModelicaBuildScripts +[license-md]: ./LICENSE.md diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..fff1c9e --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,132 @@ +# Releasing a new image version + +A "release" publishes **one image** — a base image and *all* of its add-ons — +under both a moving and an immutable tag, to GHCR and Nexus. Releases are +driven entirely by **git tags**; you never push images by hand. + +> TL;DR — merge your change to `main`, then push a tag +> `--` (e.g. `ubuntu-24.04-2.1.0`). CI does the rest. + +## The tag grammar + +``` +-- +``` + +| Part | Example | Notes | +| --- | --- | --- | +| `` | `ubuntu` | A directory at the repo root holding `/Dockerfile`. | +| `` | `24.04`, `13`, `rolling` | Selects the version (a build-arg) within that OS. | +| `` | `2.1.0` | **This repository's** version (the recipe), independent of OpenModelica. `MAJOR.MINOR.PATCH`. | + +The pair `-` must match an entry in +[.ci/matrix.yml](./.ci/matrix.yml) (you can list valid prefixes with +`python .ci/matrix.py all`). + +### What gets published + +For tag `ubuntu-24.04-2.1.0` the publish workflows build and push: + +| Image | Moving tag | Immutable tag | +| --- | --- | --- | +| base | `ubuntu-24.04` | `ubuntu-24.04-2.1.0` | +| add-on `cmake-4` | `ubuntu-24.04-cmake-4` | `ubuntu-24.04-cmake-4-2.1.0` | + +to both `ghcr.io/openmodelica/build-deps` (signed with cosign) and +`docker.openmodelica.org/build-deps`. Add-ons are extra stages that build +`FROM` the base stage in the same Dockerfile, so a release is internally +consistent and shared layers come from the build cache. + +## Choosing the next semver + +Bump relative to the last tag **for that image** (`git tag --list '--*'`): + +- **PATCH** (`2.1.0 → 2.1.1`) — rebuild for upstream package updates / security + fixes, no intended behavior change. +- **MINOR** (`2.1.0 → 2.2.0`) — added a tool or an add-on, backward compatible. +- **MAJOR** (`2.1.0 → 3.0.0`) — removed/renamed something consumers rely on, or + a base OS bump that changes the toolchain. + +Each image has its **own** semver line; bumping `ubuntu-24.04` does not affect +`debian-13`. + +## Step by step + +1. **Edit the Dockerfile** for the image: each OS has a single multi-stage + `/Dockerfile` (e.g. all Ubuntu versions share `ubuntu/Dockerfile`) where + the base is the `full` stage and add-ons are extra stages such as `cmake-4`. + Its `context`/`dockerfile`/`target`/`build_args` are in + [.ci/matrix.yml](./.ci/matrix.yml). + +2. **Open a PR to `main`.** [build.yml](./.github/workflows/build.yml) builds + every image (no push). Confirm your image builds. Build it locally too — + reuse the `context` / `dockerfile` / `target` / `build_args` from + `.ci/matrix.yml`: + + ```bash + # Ubuntu base (the `full` stage): + docker build --pull --target full --build-arg UBUNTU_VERSION=24.04 \ + --tag build-deps:ubuntu-24.04 ubuntu + # each add-on (its own --target stage, same Dockerfile): + docker build --pull --target cmake-4 --build-arg UBUNTU_VERSION=24.04 \ + --tag build-deps:ubuntu-24.04-cmake-4 ubuntu + ``` + +3. **Merge to `main`.** + +4. **Tag and push** the release from the merge commit on `main`: + + ```bash + git checkout main && git pull + git tag ubuntu-24.04-2.1.0 + git push origin ubuntu-24.04-2.1.0 + ``` + +5. **CI publishes automatically.** Pushing the tag runs the single + [build.yml](./.github/workflows/build.yml) pipeline, which in one run: + builds the images, creates/updates the GitHub Release, then builds + pushes + the tagged image (base + add-ons) to GHCR (signed) and Nexus. + + Watch the Actions tab; when green the new tags are live. + +## Re-running a publish without a new tag + +Use the **workflow_dispatch** trigger on +[build.yml](./.github/workflows/build.yml) and pass the existing tag (e.g. +`ubuntu-24.04-2.1.0`). The `publish-ghcr` / `publish-nexus` jobs rebuild and +re-push the same tags (the release step is skipped) — handy after a transient +failure. + +## Adding a brand-new image + +1. For a **new OS**, create the single multi-stage `/Dockerfile` (see + `ubuntu/Dockerfile`). For a **new version of an existing OS**, no new + Dockerfile is needed — just add a matrix entry: e.g. a new Ubuntu version + adds `dockerfile: ubuntu/Dockerfile`, `target: full` and + `build_args: { UBUNTU_VERSION: "" }`. +2. Add the image to [.ci/matrix.yml](./.ci/matrix.yml). +3. PR → merge → release as above with `--1.0.0`. + +## Adding an add-on + +Add-ons are extra build **stages** in the image's Dockerfile that build `FROM` +the base stage and add exactly one thing: + +```dockerfile +FROM full AS my-addon # `full` is the base stage +# ... install the one extra thing (a pinned toolchain, a source-built lib) ... +``` + +Then list the stage name under the image's `addons:` in +[.ci/matrix.yml](./.ci/matrix.yml). CI builds it with `--target my-addon` and +publishes `…-my-addon` (moving) and `…-my-addon-` (immutable). For +single-stage bases (Debian/Arch) add the `full`/base stage name and a `target:` +to the entry first, or split that Dockerfile into stages the same way. + +## Arch / rolling snapshots + +Arch has no version number. The moving tag `arch-rolling` always tracks the +latest build. For a reproducible pin, release with a date-stamped semver-like +tag, e.g. `arch-rolling-2026.06.01` — but note the publish workflows expect a +`MAJOR.MINOR.PATCH` semver suffix, so use `YYYY.MM.DD` (which is valid semver) +as the `` component. diff --git a/almalinux/Dockerfile b/almalinux/Dockerfile new file mode 100644 index 0000000..fd8ca1e --- /dev/null +++ b/almalinux/Dockerfile @@ -0,0 +1,26 @@ +# PLACEHOLDER — OpenModelica build-deps image for AlmaLinux. +# +# Not implemented yet. Use the same layout as ../ubuntu/Dockerfile: a single +# multi-stage Dockerfile covering ALL AlmaLinux versions, with these stages: +# base OpenModelica build dependencies only +# venv Python virtualenv at /opt/venv, built in isolation and copied later +# full the published base image (build-deps:almalinux-) +# optional add-on stages (FROM full) +# +# The AlmaLinux version is selected with a build-arg, e.g.: +# ARG ALMALINUX_VERSION=9 +# FROM almalinux:${ALMALINUX_VERSION} AS base +# +# Note: AlmaLinux is RPM-based (dnf) and there is no OpenModelica apt repo — +# install the build dependencies directly with dnf. +# +# To implement: +# 1. Replace this file with a real multi-stage Dockerfile (see +# ../ubuntu/Dockerfile and RELEASING.md). +# 2. Add the almalinux entries to .ci/matrix.yml +# (context: almalinux, dockerfile: almalinux/Dockerfile, target: full, +# build_args: { ALMALINUX_VERSION: "" }). +# 3. Open a PR; build.yml will build it. +# +# Until then AlmaLinux is intentionally absent from .ci/matrix.yml, so CI does +# not try to build it. diff --git a/arch/Dockerfile b/arch/Dockerfile new file mode 100644 index 0000000..7f51d9e --- /dev/null +++ b/arch/Dockerfile @@ -0,0 +1,25 @@ +# PLACEHOLDER — OpenModelica build-deps image for Arch Linux. +# +# Not implemented yet. Use the same layout as ../ubuntu/Dockerfile: a single +# multi-stage Dockerfile, with these stages: +# base OpenModelica build dependencies only +# venv Python virtualenv at /opt/venv, built in isolation and copied later +# full the published base image (build-deps:arch-rolling) +# optional add-on stages (FROM full) +# +# Arch is rolling-release, so there is no version build-arg: +# FROM archlinux:latest AS base +# +# Note: pacman-based, with no OpenModelica apt repo — install the build +# dependencies directly with pacman, and pin reproducible snapshots via a +# date-stamped immutable tag (arch-rolling-YYYY.MM.DD). +# +# To implement: +# 1. Replace this file with a real multi-stage Dockerfile (see +# ../ubuntu/Dockerfile and RELEASING.md). +# 2. Add the arch entry to .ci/matrix.yml +# (context: arch, dockerfile: arch/Dockerfile, target: full). +# 3. Open a PR; build.yml will build it. +# +# Until then Arch is intentionally absent from .ci/matrix.yml, so CI does not try +# to build it. diff --git a/debian/Dockerfile b/debian/Dockerfile new file mode 100644 index 0000000..ad9ee42 --- /dev/null +++ b/debian/Dockerfile @@ -0,0 +1,25 @@ +# PLACEHOLDER — OpenModelica build-deps image for Debian. +# +# Not implemented yet. Use the same layout as ../ubuntu/Dockerfile: a single +# multi-stage Dockerfile covering ALL Debian versions, with these stages: +# base OpenModelica build dependencies only +# venv Python virtualenv at /opt/venv, built in isolation and copied later +# full the published base image (build-deps:debian-) +# optional add-on stages (FROM full) +# +# The Debian version is selected with a build-arg, e.g.: +# ARG DEBIAN_VERSION=13 +# FROM debian:${DEBIAN_VERSION} AS base +# +# To implement: +# 1. Replace this file with a real multi-stage Dockerfile (see +# ../ubuntu/Dockerfile and RELEASING.md). A starting point exists on the +# old Debian branches / PR #36 +# (https://github.com/OpenModelica/build-deps/pull/36). +# 2. Add the debian entries to .ci/matrix.yml +# (context: debian, dockerfile: debian/Dockerfile, target: full, +# build_args: { DEBIAN_VERSION: "" }). +# 3. Open a PR; build.yml will build it. +# +# Until then Debian is intentionally absent from .ci/matrix.yml, so CI does not +# try to build it. diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile new file mode 100644 index 0000000..f6e0bd1 --- /dev/null +++ b/ubuntu/Dockerfile @@ -0,0 +1,179 @@ +# Multi-stage, shared image for all supported Ubuntu versions. +# +# The FROM tag is the only per-version difference besides a handful of Qt +# packages, so every Ubuntu image (and its add-ons) builds from this one file. +# +# Stages / build targets: +# base OpenModelica build dependencies only (apt build-dep openmodelica) +# venv Python virtualenv at /opt/venv, built in isolation and copied later +# full the published BASE image: base + User's Guide toolchain + Qt + venv +# cmake-4 ADD-ON: full + CMake 4 from the official Kitware release +# +# Build the base image (the `full` target): +# docker build --target full --build-arg UBUNTU_VERSION=24.04 \ +# --tag build-deps:ubuntu-24.04 ubuntu +# +# Build the CMake 4 add-on image (the `cmake-4` target): +# docker build --target cmake-4 --build-arg UBUNTU_VERSION=24.04 \ +# --tag build-deps:ubuntu-24.04-cmake-4 ubuntu +# +# CI passes UBUNTU_VERSION and the --target per image from .ci/matrix.yml. + +ARG UBUNTU_VERSION=24.04 + +# ── base: OpenModelica build dependencies only ──────────────────────── +FROM ubuntu:${UBUNTU_VERSION} AS base + +# Image / OCI metadata (inherited by the stages built FROM this one) +LABEL maintainer="AnHeuermann" +LABEL description="OpenModelica build-deps Docker Image" +LABEL organization="OpenModelica" + +LABEL org.opencontainers.image.vendor="OpenModelica" +LABEL org.opencontainers.image.authors="AnHeuermann" +LABEL org.opencontainers.image.description="OpenModelica build-deps base image (Ubuntu)" +LABEL org.opencontainers.image.source="https://github.com/OpenModelica/build-deps" +LABEL org.opencontainers.image.license="MIT" + +ENV SHELL=/bin/bash +# Set locale (inherited by later stages) +ENV LANGUAGE=en_US:en +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 + +# Ensure DEBIAN_FRONTEND is only set during build +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get upgrade -qy \ + && apt-get dist-upgrade -qy \ + && apt-get install -qy \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + && curl -fsSL https://build.openmodelica.org/apt/openmodelica.asc | gpg --dearmor -o /usr/share/keyrings/openmodelica-keyring.gpg \ + && echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openmodelica-keyring.gpg] https://build.openmodelica.org/apt \ + $(cat /etc/os-release | grep "\(UBUNTU\\|DEBIAN\\|VERSION\)_CODENAME" | sort | cut -d= -f 2 | head -1) \ + nightly" | tee /etc/apt/sources.list.d/openmodelica.list > /dev/null \ + && echo \ + "deb-src [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openmodelica-keyring.gpg] https://build.openmodelica.org/apt \ + $(cat /etc/os-release | grep "\(UBUNTU\\|DEBIAN\\|VERSION\)_CODENAME" | sort | cut -d= -f 2 | head -1) \ + nightly" | tee -a /etc/apt/sources.list.d/openmodelica.list > /dev/null \ + && apt-get update \ + && apt-get build-dep -qy openmodelica \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# ── venv: Python tooling, isolated so it caches independently ───────── +# Built once and copied into `full`. Uses the same python3 as `full` (same +# Ubuntu base), so the relocated /opt/venv works there unchanged. +FROM base AS venv +RUN apt-get update \ + && apt-get install -qy python3-pip python3-venv \ + && rm -rf /var/lib/apt/lists/* +RUN python3 -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" +# Use permalink for doc/UsersGuide/source/requirements.txt to keep builds +# deterministic. +RUN pip install --no-cache-dir \ + junit_xml \ + lxml \ + ompython==3.6.0 \ + PyGithub \ + simplejson \ + svgwrite \ + && pip install --no-cache-dir -r \ + https://raw.githubusercontent.com/OpenModelica/OpenModelica/9c0dc9a8ab50ba652109584cb3fecaef86640b66/doc/UsersGuide/source/requirements.txt + +# ── full: the published base image ─────────────────────────────────── +# - tools to build the User's Guide (common to all Ubuntu versions) +# - Qt5/Qt6 packages (version-specific: Jammy ships an older Qt6 that lacks +# some packages available on 24.04+, so the set is chosen from VERSION_ID) +FROM base AS full +ARG DEBIAN_FRONTEND=noninteractive + +# Package lists as build-args, so they are easy to read and to override: +# - COMMON_PKGS : tools common to all Ubuntu versions (User's Guide, …) +# - QT_PKGS_22_04 : Qt set for Jammy (older Qt6, lacks some 24.04+ packages) +# - QT_PKGS : Qt set for 24.04+ (the default) +ARG COMMON_PKGS="\ + aspell \ + bibtex2html \ + bison \ + ccache \ + clang-tools \ + devscripts \ + docker.io \ + doxygen \ + equivs \ + flex \ + git \ + gnuplot-nox \ + inkscape \ + intel-opencl-icd \ + latexmk \ + libcurl4-gnutls-dev \ + libmldbm-perl \ + locales \ + ocl-icd-opencl-dev \ + opencl-headers \ + pandoc \ + pocl-opencl-icd \ + poppler-utils \ + python3-pip \ + python3-venv \ + subversion \ + texlive-base \ + texlive-bibtex-extra \ + texlive-lang-greek \ + texlive-latex-extra \ + unzip \ + wget \ + xsltproc \ + xvfb \ + zip" +ARG QT_PKGS_22_04="\ + qt6-base-dev \ + qt6-tools-dev \ + qt6-tools-dev-tools \ + qttools5-dev \ + qtwebengine5-dev" +ARG QT_PKGS="\ + libqt6core5compat6-dev \ + libqt6opengl6-dev \ + libqt6openglwidgets6 \ + libqt6svg6-dev \ + qt6-base-dev \ + qt6-httpserver-dev \ + qt6-scxml-dev \ + qt6-tools-dev \ + qt6-tools-dev-tools \ + qt6-webengine-dev \ + qt6-websockets-dev \ + qtwebengine5-dev" +RUN apt-get update \ + && . /etc/os-release \ + && case "${VERSION_ID}" in \ + 22.04) qt="${QT_PKGS_22_04}" ;; \ + *) qt="${QT_PKGS}" ;; \ + esac \ + && apt-get install -qy ${COMMON_PKGS} ${qt} \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Pull in the venv built in its own stage instead of rebuilding it here. +COPY --from=venv /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# ── cmake-4 add-on: full + CMake 4 from the official Kitware release ── +# Overrides any apt-provided cmake. Built with `--target cmake-4`. +FROM full AS cmake-4 +LABEL org.opencontainers.image.description="OpenModelica build-deps (Ubuntu) with CMake 4" +ARG CMAKE_VERSION=4.2.3 +RUN curl -fsSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-$(uname -m).sh" \ + -o /tmp/cmake-install.sh \ + && chmod +x /tmp/cmake-install.sh \ + && /tmp/cmake-install.sh --skip-license --prefix=/usr/local \ + && rm /tmp/cmake-install.sh From 476537ddf106d93ecdfccb2cdb2b86da42c1dd32 Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:35:41 +0200 Subject: [PATCH 02/12] Fixup --- .github/workflows/build.yml | 13 ++++++------ .github/workflows/release.yml | 37 +++++++++++++++++++++++++++++++++++ RELEASING.md | 11 +++++++---- ubuntu/Dockerfile | 6 +++--- 4 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c67034b..d534831 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,7 @@ name: Build, Release & Publish # Single pipeline so a release created here can trigger publishing in the SAME -# run. (A GitHub Release created with GITHUB_TOKEN does NOT trigger other -# workflows, which is why release + publish must live together.) +# run. # # discover ─▶ build (all images, no push) # └─▶ release (tag only) ─▶ publish-ghcr + publish-nexus @@ -36,7 +35,7 @@ jobs: matrix: ${{ steps.set.outputs.matrix }} steps: - name: Check out the repo - uses: actions/checkout@v7.0.0 + uses: actions/checkout@v7 - name: Install dependencies run: pip install pyyaml @@ -56,7 +55,7 @@ jobs: image: ${{ fromJson(needs.discover.outputs.matrix) }} steps: - name: Check out the repo - uses: actions/checkout@v7.0.0 + uses: actions/checkout@v7 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -109,7 +108,7 @@ jobs: contents: write steps: - name: Check out the repo - uses: actions/checkout@v7.0.0 + uses: actions/checkout@v7 - name: Create or update release env: @@ -151,7 +150,7 @@ jobs: TAG: ${{ github.event.inputs.tag || github.ref_name }} steps: - name: Check out the repo - uses: actions/checkout@v7.0.0 + uses: actions/checkout@v7 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -209,7 +208,7 @@ jobs: TAG: ${{ github.event.inputs.tag || github.ref_name }} steps: - name: Check out the repo - uses: actions/checkout@v7.0.0 + uses: actions/checkout@v7 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0bb6a9b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,37 @@ +name: Create Release + +on: + push: + tags: + # Image release tags: --, e.g. ubuntu-24.04-2.1.0 + - '*-*.*.*' + +permissions: + contents: write + +jobs: + release: + name: Create GitHub Release + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v7 + + - name: Create or update release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Per-image releases are never marked as the repository "latest": + # there is no single newest image in the monorepo. + if gh release view "${{ github.ref_name }}" &>/dev/null; then + gh release edit "${{ github.ref_name }}" \ + --title "${{ github.ref_name }}" \ + --generate-notes \ + --latest=false + else + gh release create "${{ github.ref_name }}" \ + --title "${{ github.ref_name }}" \ + --generate-notes \ + --verify-tag \ + --latest=false + fi diff --git a/RELEASING.md b/RELEASING.md index fff1c9e..c0c0778 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -9,7 +9,7 @@ driven entirely by **git tags**; you never push images by hand. ## The tag grammar -``` +```text -- ``` @@ -20,7 +20,7 @@ driven entirely by **git tags**; you never push images by hand. | `` | `2.1.0` | **This repository's** version (the recipe), independent of OpenModelica. `MAJOR.MINOR.PATCH`. | The pair `-` must match an entry in -[.ci/matrix.yml](./.ci/matrix.yml) (you can list valid prefixes with +[.ci/matrix.yml][ci-matrix] (you can list valid prefixes with `python .ci/matrix.py all`). ### What gets published @@ -56,9 +56,9 @@ Each image has its **own** semver line; bumping `ubuntu-24.04` does not affect `/Dockerfile` (e.g. all Ubuntu versions share `ubuntu/Dockerfile`) where the base is the `full` stage and add-ons are extra stages such as `cmake-4`. Its `context`/`dockerfile`/`target`/`build_args` are in - [.ci/matrix.yml](./.ci/matrix.yml). + [.ci/matrix.yml][ci-matrix]. -2. **Open a PR to `main`.** [build.yml](./.github/workflows/build.yml) builds +2. **Open a PR to `main`.** [build.yml][build-yml] builds every image (no push). Confirm your image builds. Build it locally too — reuse the `context` / `dockerfile` / `target` / `build_args` from `.ci/matrix.yml`: @@ -130,3 +130,6 @@ latest build. For a reproducible pin, release with a date-stamped semver-like tag, e.g. `arch-rolling-2026.06.01` — but note the publish workflows expect a `MAJOR.MINOR.PATCH` semver suffix, so use `YYYY.MM.DD` (which is valid semver) as the `` component. + +[ci-matrix]: ./.ci/matrix.yml +[build-yml]: ./.github/workflows/build.yml diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index f6e0bd1..046fd4f 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -21,7 +21,7 @@ ARG UBUNTU_VERSION=24.04 -# ── base: OpenModelica build dependencies only ──────────────────────── +# ── base: OpenModelica build dependencies only ──────────────────────────────── FROM ubuntu:${UBUNTU_VERSION} AS base # Image / OCI metadata (inherited by the stages built FROM this one) @@ -87,7 +87,7 @@ RUN pip install --no-cache-dir \ && pip install --no-cache-dir -r \ https://raw.githubusercontent.com/OpenModelica/OpenModelica/9c0dc9a8ab50ba652109584cb3fecaef86640b66/doc/UsersGuide/source/requirements.txt -# ── full: the published base image ─────────────────────────────────── +# ── full: the published base image ──────────────────────────────────────────── # - tools to build the User's Guide (common to all Ubuntu versions) # - Qt5/Qt6 packages (version-specific: Jammy ships an older Qt6 that lacks # some packages available on 24.04+, so the set is chosen from VERSION_ID) @@ -167,7 +167,7 @@ RUN apt-get update \ COPY --from=venv /opt/venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" -# ── cmake-4 add-on: full + CMake 4 from the official Kitware release ── +# ── cmake-4 add-on: full + CMake 4 from the official Kitware release ────────── # Overrides any apt-provided cmake. Built with `--target cmake-4`. FROM full AS cmake-4 LABEL org.opencontainers.image.description="OpenModelica build-deps (Ubuntu) with CMake 4" From b02a14a5bd796b7dff8e0b1963cfd3094cc2bc80 Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:46:34 +0200 Subject: [PATCH 03/12] Fix review comments --- .ci/matrix.py | 17 ++++++++-------- .github/workflows/build.yml | 20 ++++++++++--------- .github/workflows/release.yml | 37 ----------------------------------- .gitignore | 1 + RELEASING.md | 8 ++++---- 5 files changed, 25 insertions(+), 58 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.ci/matrix.py b/.ci/matrix.py index fa774a4..f6048a2 100755 --- a/.ci/matrix.py +++ b/.ci/matrix.py @@ -39,6 +39,7 @@ import json import os import re +import shlex import sys import yaml @@ -97,14 +98,14 @@ def cmd_image(tag: str): for img in load_images(): if img["base_tag"] == prefix: - print(f"dir='{img['dir']}'") - print(f"base_tag='{img['base_tag']}'") - print(f"semver='{semver}'") - print(f"context='{img['context']}'") - print(f"dockerfile='{img['dockerfile']}'") - print(f"target='{img['target']}'") - print(f"build_args='{img['build_args']}'") - print(f"addons='{img['addons']}'") + print(f"dir={shlex.quote(img['dir'])}") + print(f"base_tag={shlex.quote(img['base_tag'])}") + print(f"semver={shlex.quote(semver)}") + print(f"context={shlex.quote(img['context'])}") + print(f"dockerfile={shlex.quote(img['dockerfile'])}") + print(f"target={shlex.quote(img['target'])}") + print(f"build_args={shlex.quote(img['build_args'])}") + print(f"addons={shlex.quote(img['addons'])}") return valid = ", ".join(img["base_tag"] for img in load_images()) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d534831..b0f0a4f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@v7 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Build base image and add-ons env: @@ -114,16 +114,18 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + # Validate tag + python .ci/matrix.py image "${GITHUB_REF_NAME}" >/dev/null # Per-image releases are never marked as the repository "latest": # there is no single newest image in the monorepo. - if gh release view "${{ github.ref_name }}" &>/dev/null; then - gh release edit "${{ github.ref_name }}" \ - --title "${{ github.ref_name }}" \ + if gh release view "${GITHUB_REF_NAME}" &>/dev/null; then + gh release edit "${GITHUB_REF_NAME}" \ + --title "${GITHUB_REF_NAME}" \ --generate-notes \ --latest=false else - gh release create "${{ github.ref_name }}" \ - --title "${{ github.ref_name }}" \ + gh release create "${GITHUB_REF_NAME}" \ + --title "${GITHUB_REF_NAME}" \ --generate-notes \ --verify-tag \ --latest=false @@ -153,7 +155,7 @@ jobs: uses: actions/checkout@v7 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Install dependencies run: pip install pyyaml @@ -177,7 +179,7 @@ jobs: run: | set -euo pipefail eval "$(python .ci/matrix.py image "${TAG}")" - CERT_ID="https://github.com/${{ github.repository }}/.github/workflows/build.yml@${{ github.ref }}" + CERT_ID="https://github.com/${GITHUB_REPOSITORY}/.github/workflows/build.yml@${GITHUB_REF}" CERT_ISSUER="https://token.actions.githubusercontent.com" tags=("${base_tag}" "${base_tag}-${semver}") for addon in ${addons}; do @@ -211,7 +213,7 @@ jobs: uses: actions/checkout@v7 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Install dependencies run: pip install pyyaml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 0bb6a9b..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Create Release - -on: - push: - tags: - # Image release tags: --, e.g. ubuntu-24.04-2.1.0 - - '*-*.*.*' - -permissions: - contents: write - -jobs: - release: - name: Create GitHub Release - runs-on: ubuntu-latest - steps: - - name: Check out the repo - uses: actions/checkout@v7 - - - name: Create or update release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Per-image releases are never marked as the repository "latest": - # there is no single newest image in the monorepo. - if gh release view "${{ github.ref_name }}" &>/dev/null; then - gh release edit "${{ github.ref_name }}" \ - --title "${{ github.ref_name }}" \ - --generate-notes \ - --latest=false - else - gh release create "${{ github.ref_name }}" \ - --title "${{ github.ref_name }}" \ - --generate-notes \ - --verify-tag \ - --latest=false - fi diff --git a/.gitignore b/.gitignore index 1ace5d1..76088b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +.venv/ .vscode/ CLAUDE.md diff --git a/RELEASING.md b/RELEASING.md index c0c0778..7fdf7f2 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -126,10 +126,10 @@ to the entry first, or split that Dockerfile into stages the same way. ## Arch / rolling snapshots Arch has no version number. The moving tag `arch-rolling` always tracks the -latest build. For a reproducible pin, release with a date-stamped semver-like -tag, e.g. `arch-rolling-2026.06.01` — but note the publish workflows expect a -`MAJOR.MINOR.PATCH` semver suffix, so use `YYYY.MM.DD` (which is valid semver) -as the `` component. +latest build. For a reproducible pin, release with a date-stamped tag, +e.g. `arch-rolling-2026.6.1` — the publish workflows match a `\d+\.\d+\.\d+` +suffix, so use `YYYY.M.D` (without padding, e.g. `2026.6.1` not `2026.06.01`): +SemVer 2.0.0 forbids leading zeros in numeric identifiers. [ci-matrix]: ./.ci/matrix.yml [build-yml]: ./.github/workflows/build.yml From ea496b1d4a193c6bfcf4a167efd92320df98343c Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Tue, 23 Jun 2026 16:19:00 +0200 Subject: [PATCH 04/12] Adding GBD add-on --- .ci/requirements.txt | 1 + .github/workflows/build.yml | 10 +++++----- README.md | 18 +++++++++--------- ubuntu/Dockerfile | 25 ++++++++++++++++++++++--- 4 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 .ci/requirements.txt diff --git a/.ci/requirements.txt b/.ci/requirements.txt new file mode 100644 index 0000000..86d967e --- /dev/null +++ b/.ci/requirements.txt @@ -0,0 +1 @@ +pyyaml==6.0.3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b0f0a4f..4ce311e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v7 - name: Install dependencies - run: pip install pyyaml + run: pip install -r .ci/requirements.txt - name: Compute build matrix id: set @@ -110,6 +110,9 @@ jobs: - name: Check out the repo uses: actions/checkout@v7 + - name: Install dependencies + run: pip install -r .ci/requirements.txt + - name: Create or update release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -158,7 +161,7 @@ jobs: uses: docker/setup-buildx-action@v4 - name: Install dependencies - run: pip install pyyaml + run: pip install -r .ci/requirements.txt - name: Install cosign uses: sigstore/cosign-installer@v4.1.2 @@ -215,9 +218,6 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - - name: Install dependencies - run: pip install pyyaml - - name: Login to Nexus Docker registry uses: docker/login-action@v4.2.0 with: diff --git a/README.md b/README.md index 5ca4fb1..9ca8e87 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,14 @@ than by OpenModelica version. Each image is a **base** plus optional, layered ```text main ├── ubuntu/ -│ └── Dockerfile # multi-stage: ALL Ubuntu versions + add-ons -├── debian/Dockerfile # placeholder (not implemented yet) -├── almalinux/Dockerfile # placeholder (not implemented yet) -├── arch/Dockerfile # placeholder (not implemented yet) +│ └── Dockerfile # multi-stage: ALL Ubuntu versions + add-ons +├── debian/Dockerfile # placeholder (not implemented yet) +├── almalinux/Dockerfile # placeholder (not implemented yet) +├── arch/Dockerfile # placeholder (not implemented yet) └── .ci/ - ├── matrix.yml # source of truth: which images exist - ├── matrix.py # matrix.yml -> CI matrix / tag lookup - └── publish.sh # build + push one image (base + add-ons) + ├── matrix.yml # source of truth: which images exist + ├── matrix.py # matrix.yml -> CI matrix / tag lookup + └── publish.sh # build + push one image (base + add-ons) ``` > **Status:** only the **Ubuntu** images are implemented. Debian, AlmaLinux and @@ -121,8 +121,8 @@ docker build --pull \ ## CI workflow A single workflow, [build.yml][workflow-build-file], runs the whole pipeline so -that a release it creates can publish in the **same** run (a release created with -`GITHUB_TOKEN` cannot trigger a separate workflow): +that a release it creates can publish in the **same** run (a release created +with `GITHUB_TOKEN` cannot trigger a separate workflow): ```text discover ─▶ build (all images, no push) diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index 046fd4f..0518e6d 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -5,9 +5,10 @@ # # Stages / build targets: # base OpenModelica build dependencies only (apt build-dep openmodelica) -# venv Python virtualenv at /opt/venv, built in isolation and copied later -# full the published BASE image: base + User's Guide toolchain + Qt + venv +# python Python virtualenv at /opt/venv, built in isolation and copied later +# full the published BASE image: base + User's Guide toolchain + Qt + python # cmake-4 ADD-ON: full + CMake 4 from the official Kitware release +# debug ADD-ON: full + GDB, Valgrind, and other debugging tools # # Build the base image (the `full` target): # docker build --target full --build-arg UBUNTU_VERSION=24.04 \ @@ -17,6 +18,10 @@ # docker build --target cmake-4 --build-arg UBUNTU_VERSION=24.04 \ # --tag build-deps:ubuntu-24.04-cmake-4 ubuntu # +# Build the debug add-on image (the `debug` target): +# docker build --target debug --build-arg UBUNTU_VERSION=24.04 \ +# --tag build-deps:ubuntu-24.04-debug ubuntu +# # CI passes UBUNTU_VERSION and the --target per image from .ci/matrix.yml. ARG UBUNTU_VERSION=24.04 @@ -66,7 +71,7 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -# ── venv: Python tooling, isolated so it caches independently ───────── +# ── venv: Python tooling, isolated so it caches independently ───────────────── # Built once and copied into `full`. Uses the same python3 as `full` (same # Ubuntu base), so the relocated /opt/venv works there unchanged. FROM base AS venv @@ -177,3 +182,17 @@ RUN curl -fsSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERS && chmod +x /tmp/cmake-install.sh \ && /tmp/cmake-install.sh --skip-license --prefix=/usr/local \ && rm /tmp/cmake-install.sh + +# ── debug add-on: full + GDB, Valgrind, and other debugging tools ───────────── +# Overrides nothing in full; adds debuggers and profilers on top. +# Built with `--target debug`. +FROM full AS debug +LABEL org.opencontainers.image.description="OpenModelica build-deps (Ubuntu) with debug tools" +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update \ + && apt-get install -qy \ + gdb \ + linux-tools-generic \ + valgrind \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* From 7d91b4900f121804903173f37282af145c92ba64 Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:04:05 +0200 Subject: [PATCH 05/12] Automate rolling release --- .ci/matrix.py | 19 ++++++++++-- .ci/matrix.yml | 9 ++++++ .github/workflows/build.yml | 62 +++++++++++++++++++++++++++++++------ README.md | 2 ++ opensuse-leap/Dockerfile | 23 ++++++++++++++ ubuntu/Dockerfile | 31 ++++++++++++------- 6 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 opensuse-leap/Dockerfile diff --git a/.ci/matrix.py b/.ci/matrix.py index f6048a2..c527014 100755 --- a/.ci/matrix.py +++ b/.ci/matrix.py @@ -2,7 +2,7 @@ """Helpers for the CI. Reads ``.ci/matrix.yml`` (the single source of truth for which images exist) -and answers two questions for the GitHub Actions workflows: +and answers questions for the GitHub Actions workflows: matrix.py all Print, on one line, a JSON array of every image. Used as the @@ -32,6 +32,13 @@ addons='cmake-4' Intended to be consumed with ``eval "$(python .ci/matrix.py image …)"``. + + matrix.py publish-matrix + Print, on one line, a JSON array of ``{"tag": "-"}`` + objects for every image in the matrix. Used by the scheduled workflow + to build the publish job matrix with date-stamped immutable tags:: + + [{"tag":"ubuntu-24.04-2026.06.23"},{"tag":"ubuntu-22.04-2026.06.23"}] """ from __future__ import annotations @@ -86,6 +93,12 @@ def cmd_all(): print(json.dumps(load_images(), separators=(",", ":"))) +def cmd_publish_matrix(date: str): + images = load_images() + result = [{"tag": f"{img['base_tag']}-{date}"} for img in images] + print(json.dumps(result, separators=(",", ":"))) + + def cmd_image(tag: str): match = SEMVER_RE.match(tag) if not match: @@ -120,8 +133,10 @@ def main(argv): cmd_all() elif len(argv) >= 3 and argv[1] == "image": cmd_image(argv[2]) + elif len(argv) >= 3 and argv[1] == "publish-matrix": + cmd_publish_matrix(argv[2]) else: - sys.exit(f"usage: {argv[0]} all | image ") + sys.exit(f"usage: {argv[0]} all | image | publish-matrix ") if __name__ == "__main__": diff --git a/.ci/matrix.yml b/.ci/matrix.yml index 83e6d9c..6b30023 100644 --- a/.ci/matrix.yml +++ b/.ci/matrix.yml @@ -85,3 +85,12 @@ images: # dockerfile: arch/Dockerfile # target: full # addons: [] + # + # - os: opensuse-leap + # version: "16.0" + # context: opensuse-leap + # dockerfile: opensuse-leap/Dockerfile + # target: full + # build_args: + # LEAP_VERSION: "16.0" + # addons: [] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4ce311e..107e224 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,8 +8,18 @@ name: Build, Release & Publish # # Release/publish are keyed on image tags: --, # e.g. ubuntu-24.04-2.1.0. +# +# Triggers: +# schedule Weekly rolling rebuild (Sunday night → Monday 00:00 UTC). +# Publishes every image with a date-based immutable tag +# (YYYY.MM.DD) in addition to the moving tag, without creating +# a GitHub Release. +# push tag Full release: GitHub Release + immutable semver tag. +# workflow_dispatch Re-publish a single image tag on demand. on: + schedule: + - cron: '0 0 * * 1' # Monday 00:00 UTC — Sunday-night rolling rebuild push: branches: - main @@ -33,6 +43,7 @@ jobs: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set.outputs.matrix }} + publish_matrix: ${{ steps.publish_tags.outputs.matrix }} steps: - name: Check out the repo uses: actions/checkout@v7 @@ -44,6 +55,29 @@ jobs: id: set run: echo "matrix=$(python .ci/matrix.py all)" >> "$GITHUB_OUTPUT" + - name: Compute publish matrix + id: publish_tags + run: | + case "${{ github.event_name }}" in + schedule) + DATE=$(date +%Y.%m.%d) + echo "matrix=$(python3 .ci/matrix.py publish-matrix "${DATE}")" >> "$GITHUB_OUTPUT" + ;; + workflow_dispatch) + echo 'matrix=[{"tag":"${{ github.event.inputs.tag }}"}]' >> "$GITHUB_OUTPUT" + ;; + push) + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + echo 'matrix=[{"tag":"${{ github.ref_name }}"}]' >> "$GITHUB_OUTPUT" + else + echo 'matrix=[]' >> "$GITHUB_OUTPUT" + fi + ;; + *) + echo 'matrix=[]' >> "$GITHUB_OUTPUT" + ;; + esac + build: name: Build ${{ matrix.image.os }}-${{ matrix.image.version }} needs: discover @@ -135,24 +169,28 @@ jobs: fi publish-ghcr: - name: Publish to GHCR - needs: [build, release] - # Runs on a release tag (after release succeeds) or on a manual re-publish - # dispatch (where the release job is skipped). + name: Publish ${{ matrix.image.tag }} to GHCR + needs: [discover, build, release] + # Runs on: a release tag, a manual re-publish dispatch, or the weekly schedule. + # publish_matrix is [] for PR and branch-push runs, so those are excluded. if: >- ${{ always() && needs.build.result == 'success' && (needs.release.result == 'success' || needs.release.result == 'skipped') - && (startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch') }} + && needs.discover.outputs.publish_matrix != '[]' }} runs-on: ubuntu-latest timeout-minutes: 90 permissions: contents: read packages: write id-token: write # needed for signing the images with GitHub OIDC Token + strategy: + fail-fast: false + matrix: + image: ${{ fromJson(needs.discover.outputs.publish_matrix) }} env: REGISTRY: ghcr.io/openmodelica/build-deps - TAG: ${{ github.event.inputs.tag || github.ref_name }} + TAG: ${{ matrix.image.tag }} steps: - name: Check out the repo uses: actions/checkout@v7 @@ -197,20 +235,24 @@ jobs: done publish-nexus: - name: Publish to Nexus - needs: [build, release] + name: Publish ${{ matrix.image.tag }} to Nexus + needs: [discover, build, release] if: >- ${{ always() && needs.build.result == 'success' && (needs.release.result == 'success' || needs.release.result == 'skipped') - && (startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch') }} + && needs.discover.outputs.publish_matrix != '[]' }} runs-on: ubuntu-latest timeout-minutes: 90 permissions: contents: read + strategy: + fail-fast: false + matrix: + image: ${{ fromJson(needs.discover.outputs.publish_matrix) }} env: REGISTRY: docker.openmodelica.org/build-deps - TAG: ${{ github.event.inputs.tag || github.ref_name }} + TAG: ${{ matrix.image.tag }} steps: - name: Check out the repo uses: actions/checkout@v7 diff --git a/README.md b/README.md index 9ca8e87..eb31590 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ release needs a frozen environment it pins the **immutable** tag. | Debian 13 (Trixie) | `debian-13` | – | placeholder | [debian/Dockerfile][debian-dockerfile] | | AlmaLinux 9 | `almalinux-9` | – | placeholder | [almalinux/Dockerfile][almalinux-dockerfile] | | Arch Linux (rolling) | `arch-rolling` | – | placeholder | [arch/Dockerfile][arch-dockerfile] | +| openSUSE Leap 16.0 | `opensuse-leap-16.0` | – | placeholder | [opensuse-leap/Dockerfile][opensuse-leap-dockerfile] | ## Build locally @@ -155,6 +156,7 @@ See [LICENSE.md][license-md]. [debian-dockerfile]: ./debian/Dockerfile [almalinux-dockerfile]: ./almalinux/Dockerfile [arch-dockerfile]: ./arch/Dockerfile +[opensuse-leap-dockerfile]: ./opensuse-leap/Dockerfile [workflow-build-file]: ./.github/workflows/build.yml [releasing-md]: ./RELEASING.md [build-scripts]: https://github.com/OpenModelica/OpenModelicaBuildScripts diff --git a/opensuse-leap/Dockerfile b/opensuse-leap/Dockerfile new file mode 100644 index 0000000..ac943fc --- /dev/null +++ b/opensuse-leap/Dockerfile @@ -0,0 +1,23 @@ +# PLACEHOLDER — OpenModelica build-deps image for openSUSE Leap. +# +# Not implemented yet. Use the same layout as ../ubuntu/Dockerfile: a single +# multi-stage Dockerfile covering ALL openSUSE Leap versions, with these stages: +# base OpenModelica build dependencies only +# python Python virtualenv at /opt/venv, built in isolation and copied later +# full the published base image (build-deps:opensuse-leap-) +# optional add-on stages (FROM full) +# +# The Leap version is selected with a build-arg, e.g.: +# ARG LEAP_VERSION=15.6 +# FROM opensuse/leap:${LEAP_VERSION} AS base +# +# To implement: +# 1. Replace this file with a real multi-stage Dockerfile (see +# ../ubuntu/Dockerfile and RELEASING.md). +# 2. Uncomment the opensuse-leap entries in .ci/matrix.yml +# (context: opensuse-leap, dockerfile: opensuse-leap/Dockerfile, +# target: full, build_args: { LEAP_VERSION: "" }). +# 3. Open a PR; build.yml will build it. +# +# Until then openSUSE Leap is intentionally absent from .ci/matrix.yml, so CI +# does not try to build it. diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index 0518e6d..4192677 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -11,20 +11,20 @@ # debug ADD-ON: full + GDB, Valgrind, and other debugging tools # # Build the base image (the `full` target): -# docker build --target full --build-arg UBUNTU_VERSION=24.04 \ -# --tag build-deps:ubuntu-24.04 ubuntu +# docker build --target full --build-arg UBUNTU_VERSION=26.04 \ +# --tag build-deps:ubuntu-26.04 ubuntu # # Build the CMake 4 add-on image (the `cmake-4` target): -# docker build --target cmake-4 --build-arg UBUNTU_VERSION=24.04 \ -# --tag build-deps:ubuntu-24.04-cmake-4 ubuntu +# docker build --target cmake-4 --build-arg UBUNTU_VERSION=26.04 \ +# --tag build-deps:ubuntu-26.04-cmake-4 ubuntu # # Build the debug add-on image (the `debug` target): -# docker build --target debug --build-arg UBUNTU_VERSION=24.04 \ -# --tag build-deps:ubuntu-24.04-debug ubuntu +# docker build --target debug --build-arg UBUNTU_VERSION=26.04 \ +# --tag build-deps:ubuntu-26.04-debug ubuntu # # CI passes UBUNTU_VERSION and the --target per image from .ci/matrix.yml. -ARG UBUNTU_VERSION=24.04 +ARG UBUNTU_VERSION=26.04 # ── base: OpenModelica build dependencies only ──────────────────────────────── FROM ubuntu:${UBUNTU_VERSION} AS base @@ -100,9 +100,9 @@ FROM base AS full ARG DEBIAN_FRONTEND=noninteractive # Package lists as build-args, so they are easy to read and to override: -# - COMMON_PKGS : tools common to all Ubuntu versions (User's Guide, …) -# - QT_PKGS_22_04 : Qt set for Jammy (older Qt6, lacks some 24.04+ packages) -# - QT_PKGS : Qt set for 24.04+ (the default) +# - COMMON_PKGS : tools common to all Ubuntu versions (User's Guide, …) +# - QT_PKGS_22_04 : Qt set for Jammy (older Qt6, lacks some 24.04+ packages) +# - QT_PKGS : Qt set for 24.04+ (the default) ARG COMMON_PKGS="\ aspell \ bibtex2html \ @@ -140,9 +140,16 @@ ARG COMMON_PKGS="\ xvfb \ zip" ARG QT_PKGS_22_04="\ + libqt6core5compat6-dev \ + libqt6opengl6-dev \ + libqt6openglwidgets6 \ + libqt6svg6-dev \ qt6-base-dev \ + qt6-l10n-tools \ qt6-tools-dev \ qt6-tools-dev-tools \ + qt6-webengine-dev \ + qt6-webengine-dev-tools \ qttools5-dev \ qtwebengine5-dev" ARG QT_PKGS="\ @@ -175,8 +182,8 @@ ENV PATH="/opt/venv/bin:$PATH" # ── cmake-4 add-on: full + CMake 4 from the official Kitware release ────────── # Overrides any apt-provided cmake. Built with `--target cmake-4`. FROM full AS cmake-4 -LABEL org.opencontainers.image.description="OpenModelica build-deps (Ubuntu) with CMake 4" -ARG CMAKE_VERSION=4.2.3 +LABEL org.opencontainers.image.description="OpenModelica build-deps (Ubuntu) with latest CMake 4" +ARG CMAKE_VERSION=4.3.4 RUN curl -fsSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-$(uname -m).sh" \ -o /tmp/cmake-install.sh \ && chmod +x /tmp/cmake-install.sh \ From fbcb3971e839973d1664d625f32d8aa247c0f20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sj=C3=B6lund?= Date: Wed, 24 Jun 2026 09:27:36 +0200 Subject: [PATCH 06/12] Add first draft of Rust addon --- .ci/matrix.yml | 3 +- ubuntu/Dockerfile | 105 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 92 insertions(+), 16 deletions(-) diff --git a/.ci/matrix.yml b/.ci/matrix.yml index 6b30023..c0a3e87 100644 --- a/.ci/matrix.yml +++ b/.ci/matrix.yml @@ -36,7 +36,8 @@ images: target: full build_args: UBUNTU_VERSION: "26.04" - addons: [] + addons: + - rust - os: ubuntu version: "24.04" diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index 4192677..7f98e45 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -30,21 +30,20 @@ ARG UBUNTU_VERSION=26.04 FROM ubuntu:${UBUNTU_VERSION} AS base # Image / OCI metadata (inherited by the stages built FROM this one) -LABEL maintainer="AnHeuermann" -LABEL description="OpenModelica build-deps Docker Image" -LABEL organization="OpenModelica" - -LABEL org.opencontainers.image.vendor="OpenModelica" -LABEL org.opencontainers.image.authors="AnHeuermann" -LABEL org.opencontainers.image.description="OpenModelica build-deps base image (Ubuntu)" -LABEL org.opencontainers.image.source="https://github.com/OpenModelica/build-deps" -LABEL org.opencontainers.image.license="MIT" - -ENV SHELL=/bin/bash -# Set locale (inherited by later stages) -ENV LANGUAGE=en_US:en -ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 +LABEL maintainer="AnHeuermann" \ + description="OpenModelica build-deps Docker Image" \ + organization="OpenModelica" + +LABEL org.opencontainers.image.vendor="OpenModelica" \ + org.opencontainers.image.authors="AnHeuermann" \ + org.opencontainers.image.description="OpenModelica build-deps base image (Ubuntu)" \ + org.opencontainers.image.source="https://github.com/OpenModelica/build-deps" \ + org.opencontainers.image.license="MIT" + +ENV SHELL=/bin/bash \ + LANGUAGE=en_US:en \ + LANG=C.UTF-8 \ + LC_ALL=C.UTF-8 # Ensure DEBIAN_FRONTEND is only set during build ARG DEBIAN_FRONTEND=noninteractive @@ -92,6 +91,62 @@ RUN pip install --no-cache-dir \ && pip install --no-cache-dir -r \ https://raw.githubusercontent.com/OpenModelica/OpenModelica/9c0dc9a8ab50ba652109584cb3fecaef86640b66/doc/UsersGuide/source/requirements.txt +# ── rustdeps: Dependencies for the Rust build ───────────────── +# Built once and copied into `rust`. +FROM base AS rustdeps +RUN apt-get update \ + && apt-get install --no-install-recommends -qy python3-pip python3-venv curl \ + && rm -rf /var/lib/apt/lists/* + +# Specific versions needed for caching Rust crates +ARG WASM_BINDGEN_VERSION="0.2.125" +ARG RUST_NIGHTLY="nightly-2026-05-31" + +ENV RUSTUP_HOME=/opt/rust/rustup \ + CARGO_HOME=/opt/rust/cargo \ + XWIN_CACHE_DIR=/opt/cargo-xwin + +# Install Rust +RUN curl https://sh.rustup.rs | bash -s -- -y \ + --profile minimal \ + --default-toolchain "${RUST_NIGHTLY}" \ + --target x86_64-pc-windows-msvc \ + --target aarch64-pc-windows-msvc \ + --target wasm32-unknown-unknown && \ + . "$CARGO_HOME/env" && \ + which cargo && cargo --version && \ + rustup component add rustc-codegen-cranelift-preview clippy rustfmt && \ + cargo install wasm-bindgen-cli --version "${WASM_BINDGEN_VERSION}" && \ + cargo install sccache --locked && \ + cargo install cargo-nextest --locked && \ + cargo install wasm-opt && \ + cargo install --locked cargo-xwin && \ + cargo xwin cache xwin \ + --xwin-arch x86_64,aarch64 && \ + rm -rf "$CARGO_HOME/registry" "$CARGO_HOME/git" \ + "$CARGO_HOME/.package-cache" \ + "$HOME/.rustup/downloads" "$HOME/.rustup/tmp" \ + "${CARGO_TARGET_DIR:-/nonexistent}" && \ + mkdir -p "$CARGO_HOME/registry" && chmod ugo+rwx -R /opt/rust + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + git python3 python3-pip python3-venv ca-certificates xz-utils && \ + # --- Emscripten 4.0.7 (required by Qt 6.10.x; Ubuntu has 3.1.69) --- + git clone --depth 1 https://github.com/emscripten-core/emsdk.git /opt/emsdk; \ + cd /opt/emsdk && \ + ./emsdk install 4.0.7 && \ + ./emsdk activate 4.0.7 && \ + # --- Qt 6.10.2: WebAssembly (single-threaded) + matching 26.04 host --- + python3 -m pip install --no-cache-dir --break-system-packages aqtinstall && \ + aqt install-qt linux desktop 6.10.2 linux_gcc_64 -O /opt/Qt && \ + aqt install-qt all_os wasm 6.10.2 wasm_singlethread -m qt5compat -O /opt/Qt && \ + rm -rf /var/lib/apt/lists/* && \ + chmod +x /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake \ + /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake-create \ + /opt/Qt/6.10.2/wasm_singlethread/bin/qt-configure-module + # ── full: the published base image ──────────────────────────────────────────── # - tools to build the User's Guide (common to all Ubuntu versions) # - Qt5/Qt6 packages (version-specific: Jammy ships an older Qt6 that lacks @@ -203,3 +258,23 @@ RUN apt-get update \ valgrind \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* + +FROM full AS rust +LABEL org.opencontainers.image.description="OpenModelica build-deps (Ubuntu) for the Rust targets" + +ENV EMSDK=/opt/emsdk \ + QT_HOST_PATH=/opt/Qt/6.10.2/gcc_64 \ + RUSTUP_HOME=/opt/rust/rustup \ + CARGO_HOME=/opt/rust/cargo \ + XWIN_CACHE_DIR=/opt/cargo-xwin \ + PATH=/opt/emsdk:/opt/emsdk/upstream/emscripten:/opt/Qt/6.10.2/wasm_singlethread/bin:/opt/rust/cargo/bin:$PATH + +COPY --from=rustdeps /opt/emsdk /opt/emsdk +COPY --from=rustdeps /opt/Qt /opt/Qt +COPY --from=rustdeps /opt/rust /opt/rust + +RUN export DEBIAN_FRONTEND=noninteractive && apt-get update \ + && apt-get install -qy \ + mold \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* From d021c00c9c07ac7e2a9ffc7a53ffd5fbd6070e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sj=C3=B6lund?= Date: Thu, 25 Jun 2026 10:23:00 +0200 Subject: [PATCH 07/12] Also install Qt Quick3D and the Qt env for MSVC --- ubuntu/Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index 7f98e45..308c4ed 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -140,8 +140,9 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ./emsdk activate 4.0.7 && \ # --- Qt 6.10.2: WebAssembly (single-threaded) + matching 26.04 host --- python3 -m pip install --no-cache-dir --break-system-packages aqtinstall && \ - aqt install-qt linux desktop 6.10.2 linux_gcc_64 -O /opt/Qt && \ - aqt install-qt all_os wasm 6.10.2 wasm_singlethread -m qt5compat -O /opt/Qt && \ + aqt install-qt linux desktop 6.10.2 linux_gcc_64 -m qtquick3d qtshadertools -O /opt/Qt && \ + aqt install-qt all_os wasm 6.10.2 wasm_singlethread -m qt5compat qtquick3d qtshadertools -O /opt/Qt && \ + aqt install-qt windows desktop 6.10.2 win64_msvc2022_64 -m qtquick3d qtshadertools -O /opt/Qt && \ rm -rf /var/lib/apt/lists/* && \ chmod +x /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake \ /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake-create \ From b74445d32c0fe23ea11e097d4c0f5a358227e55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sj=C3=B6lund?= Date: Thu, 25 Jun 2026 16:12:11 +0200 Subject: [PATCH 08/12] Will also need LLVM for llvm-lib, etc --- ubuntu/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index 308c4ed..db7fd28 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -132,7 +132,7 @@ RUN curl https://sh.rustup.rs | bash -s -- -y \ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y --no-install-recommends \ - git python3 python3-pip python3-venv ca-certificates xz-utils && \ + git python3 python3-pip python3-venv ca-certificates xz-utils flang llvm && \ # --- Emscripten 4.0.7 (required by Qt 6.10.x; Ubuntu has 3.1.69) --- git clone --depth 1 https://github.com/emscripten-core/emsdk.git /opt/emsdk; \ cd /opt/emsdk && \ From df672fbf355ce1509ae449556cf743508134abfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sj=C3=B6lund?= Date: Fri, 26 Jun 2026 09:08:27 +0200 Subject: [PATCH 09/12] Install Quick3D also for the base image, and qt5compat for aqt --- ubuntu/Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index db7fd28..aef28ad 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -140,9 +140,9 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ./emsdk activate 4.0.7 && \ # --- Qt 6.10.2: WebAssembly (single-threaded) + matching 26.04 host --- python3 -m pip install --no-cache-dir --break-system-packages aqtinstall && \ - aqt install-qt linux desktop 6.10.2 linux_gcc_64 -m qtquick3d qtshadertools -O /opt/Qt && \ + aqt install-qt linux desktop 6.10.2 linux_gcc_64 -m qt5compat qtquick3d qtshadertools -O /opt/Qt && \ aqt install-qt all_os wasm 6.10.2 wasm_singlethread -m qt5compat qtquick3d qtshadertools -O /opt/Qt && \ - aqt install-qt windows desktop 6.10.2 win64_msvc2022_64 -m qtquick3d qtshadertools -O /opt/Qt && \ + aqt install-qt windows desktop 6.10.2 win64_msvc2022_64 -m qt5compat qtquick3d qtshadertools -O /opt/Qt && \ rm -rf /var/lib/apt/lists/* && \ chmod +x /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake \ /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake-create \ @@ -206,6 +206,7 @@ ARG QT_PKGS_22_04="\ qt6-tools-dev-tools \ qt6-webengine-dev \ qt6-webengine-dev-tools \ + qt6-quick3d-dev \ qttools5-dev \ qtwebengine5-dev" ARG QT_PKGS="\ @@ -220,6 +221,7 @@ ARG QT_PKGS="\ qt6-tools-dev-tools \ qt6-webengine-dev \ qt6-websockets-dev \ + qt6-quick3d-dev \ qtwebengine5-dev" RUN apt-get update \ && . /etc/os-release \ From ef7ad70e1d14e172e9722661fb20aa110fb629f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sj=C3=B6lund?= Date: Fri, 26 Jun 2026 15:54:16 +0200 Subject: [PATCH 10/12] We will need wine to run qtdeploy --- ubuntu/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index aef28ad..5fd3533 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -278,6 +278,7 @@ COPY --from=rustdeps /opt/rust /opt/rust RUN export DEBIAN_FRONTEND=noninteractive && apt-get update \ && apt-get install -qy \ + wine \ mold \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* From 37080e32cad1759502d2a5c04450ee32d8149e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sj=C3=B6lund?= Date: Sat, 27 Jun 2026 10:03:49 +0200 Subject: [PATCH 11/12] Add qtwebengine and qhttpserver --- ubuntu/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index 5fd3533..cd9e3fa 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -140,9 +140,9 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ./emsdk activate 4.0.7 && \ # --- Qt 6.10.2: WebAssembly (single-threaded) + matching 26.04 host --- python3 -m pip install --no-cache-dir --break-system-packages aqtinstall && \ - aqt install-qt linux desktop 6.10.2 linux_gcc_64 -m qt5compat qtquick3d qtshadertools -O /opt/Qt && \ + aqt install-qt linux desktop 6.10.2 linux_gcc_64 -m qtwebengine qthttpserver qtquick3d qtshadertools -O /opt/Qt && \ aqt install-qt all_os wasm 6.10.2 wasm_singlethread -m qt5compat qtquick3d qtshadertools -O /opt/Qt && \ - aqt install-qt windows desktop 6.10.2 win64_msvc2022_64 -m qt5compat qtquick3d qtshadertools -O /opt/Qt && \ + aqt install-qt windows desktop 6.10.2 win64_msvc2022_64 -m qtwebengine qthttpserver qtquick3d qtshadertools -O /opt/Qt && \ rm -rf /var/lib/apt/lists/* && \ chmod +x /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake \ /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake-create \ From f15099d81487b0e03bb53444d92de66b5d67f545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sj=C3=B6lund?= Date: Sat, 27 Jun 2026 10:09:37 +0200 Subject: [PATCH 12/12] Add some more WebEngine deps --- ubuntu/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntu/Dockerfile b/ubuntu/Dockerfile index cd9e3fa..f7c1ba4 100644 --- a/ubuntu/Dockerfile +++ b/ubuntu/Dockerfile @@ -140,9 +140,9 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ./emsdk activate 4.0.7 && \ # --- Qt 6.10.2: WebAssembly (single-threaded) + matching 26.04 host --- python3 -m pip install --no-cache-dir --break-system-packages aqtinstall && \ - aqt install-qt linux desktop 6.10.2 linux_gcc_64 -m qtwebengine qthttpserver qtquick3d qtshadertools -O /opt/Qt && \ + aqt install-qt linux desktop 6.10.2 linux_gcc_64 -m qtwebchannel qtpositioning qtpdf qthttpserver qtquick3d qtshadertools -O /opt/Qt && \ aqt install-qt all_os wasm 6.10.2 wasm_singlethread -m qt5compat qtquick3d qtshadertools -O /opt/Qt && \ - aqt install-qt windows desktop 6.10.2 win64_msvc2022_64 -m qtwebengine qthttpserver qtquick3d qtshadertools -O /opt/Qt && \ + aqt install-qt windows desktop 6.10.2 win64_msvc2022_64 -m qtwebchannel qtpositioning qtpdf qthttpserver qtquick3d qtshadertools -O /opt/Qt && \ rm -rf /var/lib/apt/lists/* && \ chmod +x /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake \ /opt/Qt/6.10.2/wasm_singlethread/bin/qt-cmake-create \