Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 52 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Mendix Runtime Crates

> Model-agnostic, app-agnostic, version-pinned Docker recipes for running **any**
> Mendix application — Mendix 7 through 11 — without baking the model into the image.
> Bind-mount your unzipped MDA, set a few env vars, get a working Mendix runtime.
> Model-agnostic, app-agnostic, version-pinned Docker recipes for **running** and
> **building** any Mendix application — Mendix 7 through 11 — without baking the
> model into the image. Bind-mount your unzipped MDA, set a few env vars, get a
> working Mendix runtime; or bind-mount a project and compile it to an `.mda`,
> with no Studio Pro on the host.

[![no-mendix-binaries](https://github.com/ontologylabs/mendix-runtime-crates/actions/workflows/no-mendix-binaries.yml/badge.svg)](https://github.com/ontologylabs/mendix-runtime-crates/actions/workflows/no-mendix-binaries.yml)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
Expand All @@ -19,11 +21,14 @@ zero dangling layers. **[Jump to Quick start ↓](#quick-start)**

## What this is

A set of **recipes** — `Dockerfile`, `start.sh`, and supporting scaffolding — that
build a clean, reusable Docker image for each Mendix major version. They contain
**no Mendix binaries**: the Mendix runtime tarball is downloaded from the official
Mendix CDN (`cdn.mendix.com`) *at build time, on your machine*. You bring the
licensed runtime; we bring the build recipe.
A set of **recipes** — `Dockerfile`, `start.sh` / `build.sh`, and supporting
scaffolding — that build a clean, reusable Docker image for each Mendix major
version, in two flavours: a **runtime crate** (`crates/mendix-<N>/`) that runs an
app, and a **build crate** (`crates/mendix-<N>/build/`) that compiles a project to
an `.mda` and runs `mx check`. They contain **no Mendix binaries**: the Mendix
runtime/toolchain tarball is downloaded from the official Mendix CDN
(`cdn.mendix.com`) *at build time, on your machine*. You bring the licensed
runtime; we bring the build recipe.

## The problem it solves

Expand All @@ -50,17 +55,44 @@ multiple bind-mounts.

## Supported versions

### Runtime crates — *run* an app

| Crate | Verified Mendix version | JRE | Base image | Status |
|---|---|---|---|---|
| [`crates/mendix-11`](crates/mendix-11) | `11.6.4` | Java 21 | `eclipse-temurin:21-jre-jammy` | verified |
| [`crates/mendix-10`](crates/mendix-10) | `10.24.13.86719` (LTS) | Java 21 | `eclipse-temurin:21-jre-jammy` | verified |
| [`crates/mendix-9`](crates/mendix-9) | `9.24.20.33307` | Java 11 | `eclipse-temurin:11-jre-jammy` | verified |
| [`crates/mendix-8`](crates/mendix-8) | `8.18.35.97` (final LTS) | Java 11 | `eclipse-temurin:11-jre-jammy` | recipe (CDN 200) |
| [`crates/mendix-7`](crates/mendix-7) | `7.23.8.58888` | Java 8 | `eclipse-temurin:8-jre-jammy` | verified |

Each crate's `versions.yaml` lists the exact versions it has been built and
smoke-tested against, plus planned versions. The boot path
(`runtimelauncher.jar` + the m2ee admin protocol) is identical from Mendix 7
through 11.
### Build crates — *compile* an `.mpr` → `.mda`

Each version also ships a **build crate** under `crates/mendix-<N>/build/`: a
version-pinned `mxbuild` + `mx` toolchain image that compiles a project and runs
`mx check`, with nothing on the host but Docker (no Studio Pro). The toolchain is
pulled from the same CDN as `mxbuild-<version>.tar.gz`. See
**[Building MDAs in Docker](docs/building-mendix-apps-in-docker.md)**.

| Build crate | mxbuild version | JDK | Base image | Status |
|---|---|---|---|---|
| [`crates/mendix-11/build`](crates/mendix-11/build) | `11.6.4` | Java 21 | `eclipse-temurin:21-jdk-jammy` | image-verified¹ |
| [`crates/mendix-10/build`](crates/mendix-10/build) | `10.24.13.86719` | Java 21 | `eclipse-temurin:21-jdk-jammy` | recipe (CDN 200) |
| [`crates/mendix-9/build`](crates/mendix-9/build) | `9.24.20.33307` | Java 11 | `eclipse-temurin:11-jdk-jammy` | recipe (CDN 200) |
| [`crates/mendix-8/build`](crates/mendix-8/build) | `8.18.35.97` | Java 11 | `eclipse-temurin:11-jdk-jammy` | recipe (CDN 200) |
| [`crates/mendix-7/build`](crates/mendix-7/build) | `7.23.8.58888` | Java 8 | `eclipse-temurin:8-jdk-jammy` | recipe (CDN 200) |

¹ *image-verified* = the image builds, the CDN toolchain pull succeeds, and the
binaries + entrypoint resolve and run; a full `.mpr → .mda` compile against a
licensed project is the remaining smoke gate (per crate `provenance.yaml`).
*recipe (CDN 200)* = authored to the same proven pattern with the CDN source
URL verified reachable, image build pending.

Each crate's `versions.yaml` lists the exact versions it targets. The runtime
boot path (`runtimelauncher.jar` + the m2ee admin protocol) and the build
invocation are identical from Mendix 7 through 11. **Mendix 8** is out of
standard support but its final LTS patch (`8.18.35.97`) is still CDN-hosted;
older MX8 patches that aren't are covered by
[`crates/mendix-8/PORTAL-DOWNLOAD.md`](crates/mendix-8/PORTAL-DOWNLOAD.md).

## Quick start

Expand Down Expand Up @@ -118,11 +150,16 @@ mendix-runtime-crates/
├── LICENSE # Apache-2.0
├── guard.sh # the no-Mendix-binary CI check
├── .github/workflows/no-mendix-binaries.yml # runs guard.sh on push + PR
├── docs/
│ ├── running-mendix-<N>-in-docker.md # per-version run guides
│ └── building-mendix-apps-in-docker.md # compile an .mpr → .mda
└── crates/
├── mendix-7/ { Dockerfile, start.sh, versions.yaml, provenance.yaml, README.md, CHANGELOG.md, tests/ }
├── mendix-9/
├── mendix-10/
└── mendix-11/
│ └── build/ { Dockerfile, build.sh, versions.yaml, provenance.yaml, README.md, CHANGELOG.md, tests/ }
├── mendix-8/ { …runtime… , PORTAL-DOWNLOAD.md, build/ } # final 8.18 LTS; portal fallback for older patches
├── mendix-9/ { …runtime… , build/ }
├── mendix-10/ { …runtime… , build/ }
└── mendix-11/ { …runtime… , build/ }
```

## Contributing a new version
Expand Down
29 changes: 29 additions & 0 deletions crates/mendix-10/build/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Changelog — Mendix 10 Build Crate

## [0.1.0] — 2026-06-23

Initial release. The build (mxbuild + mx) companion to the mendix-10 runtime crate.

* `Dockerfile`: model-agnostic; JDK 21 base (`eclipse-temurin:21-jdk-jammy`);
downloads the Mendix 10 build toolchain from the official CDN at build
(`mxbuild-${MENDIX_VERSION}.tar.gz`); never commits a Mendix binary
(recipe-pull, D-DOCKER-LIB-001/002). apt deps for the .NET/Mono toolchain:
`libgdiplus libicu70 libssl3 sqlite3`.
* `build.sh`: `build` / `check` / `version` dispatch. Auto-injects
`--java-home` / `--java-exe-path` / `--gradle-home` / `--loose-version-check`
and defaults `--output=/workspace/<App>.mda`. `mx check` exit codes normalised
to the CI convention (0 clean / 1 errors / 2 warnings). Derived from the
production AIDE aide-mxtools entrypoint.
* Pre-creates `$HOME/.local/share/Mendix` for the MX 10 `.NET` mxbuild whitelist
FailFast (same fix as aide-mxtools).
* Default Mendix version: 10.24.13.86719 (override via `--build-arg MENDIX_VERSION`).

### Verification status
* CDN source `mxbuild-10.24.13.86719.tar.gz`: **HTTP 200 verified (2026-06-23)**.
* Image build + `mx version` / MDA smoke: **pending** — recorded honestly in
`provenance.yaml` (`smoke_verified: pending`). The mxbuild invocation itself is
the production-proven aide-mxtools path; the remaining gate is a release build.

### Why
Lets a Docker-only environment (CI, a contributor without Studio Pro) compile and
validate a Mendix 10 app, producing the `.mda` the runtime crate runs.
113 changes: 113 additions & 0 deletions crates/mendix-10/build/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Mendix 10 Build Crate — model-agnostic mxbuild + mx toolchain (recipe-pull).
#
# Image: ontologylabs/mendix-mxbuild:10
# ontologylabs/mendix-mxbuild:10.24.13.86719 (immutable)
#
# Contract:
# * Bake: JDK 21 + the Mendix 10.x build toolchain (mxbuild + mx), pulled from
# the official Mendix CDN at `docker build` time.
# * NEVER commit a Mendix binary to the repo. The toolchain tarball is curl'd
# from cdn.mendix.com on the licensed user's own machine — exactly like the
# runtime crate downloads the runtime tarball (D-DOCKER-LIB-001 recipe-pull;
# D-DOCKER-LIB-002 no-binaries guard). You bring the licensed toolchain; we
# bring the build recipe.
# * Bind-mount the project directory (containing the `.mpr`) at /workspace.
# * Compile : `docker run -v <proj>:/workspace ... build /workspace/App.mpr`
# → writes App.mda next to the .mpr in /workspace.
# * Validate: `docker run -v <proj>:/workspace ... check /workspace/App.mpr`
# → `mx check` with CI-normalised exit codes (0 clean / 1 error / 2 warn).
#
# Why a "build crate" (companion to the runtime crate):
# The AIDE pipeline drives mxbuild via a bind-mounted Studio Pro modeler
# (the aide-mxtools image). This crate makes the toolchain self-contained and
# version-pinned: one image per Mendix major, the matching mxbuild fetched
# from the CDN at build, so a contributor with only Docker can compile any app
# at that version without a local Studio Pro install. Runtime crate runs the
# app; build crate produces the .mda the runtime crate runs.
#
# Provenance:
# * mxbuild + mx from https://cdn.mendix.com/runtime/mxbuild-${MENDIX_VERSION}.tar.gz
# * Tarball top-level is `modeler/` → modeler/mxbuild, modeler/mx,
# modeler/tools/gradle (verified against the AIDE build-server cache layout).
# * Java: Eclipse Temurin 21 JDK — Mendix 10 compiles Java actions at release 21.
# A full JDK (not JRE) is required: mxbuild invokes javac to compile the
# app's javasource and Java actions.

FROM --platform=linux/amd64 eclipse-temurin:21-jdk-jammy

ARG MENDIX_VERSION=10.24.13.86719
ARG MXBUILD_CDN_BASE=https://cdn.mendix.com/runtime

LABEL ai.ayios.purpose="Mendix 10 build crate (mxbuild + mx, model-agnostic)"
LABEL ai.ayios.crate="mendix-10/build"
LABEL ai.ayios.mendix-version="${MENDIX_VERSION}"
LABEL maintainer="hardy@agileworks.co.za"

# Layer 1 — apt deps for the mxbuild toolchain. mxbuild is a .NET/Mono program:
# libgdiplus — System.Drawing (image/PDF generation during build)
# libicu70 — globalization/ICU (jammy ships ICU 70)
# libssl3 — TLS for any build-time fetch
# sqlite3 — read the .mpr (modern Mendix projects are SQLite databases)
# curl — pull the toolchain tarball
# Stable layer; survives churn in the heavier toolchain layer below.
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
libgdiplus \
libicu70 \
libssl3 \
sqlite3 \
&& rm -rf /var/lib/apt/lists/*

# Layer 2 — Mendix build toolchain tarball (mxbuild + mx). ~300-500 MB download.
# Recipe-pull: curl'd from the CDN at build, never committed. Cached unless
# MENDIX_VERSION or MXBUILD_CDN_BASE changes.
#
# Tarball top-level is `modeler/`, so extracting at /opt/mxtools produces the
# same layout the AIDE build-server caches and mounts at /opt/mxtools:
# /opt/mxtools/modeler/mxbuild
# /opt/mxtools/modeler/mx
# /opt/mxtools/modeler/tools/gradle
RUN set -eux; \
mkdir -p /opt/mxtools /workspace; \
echo "Downloading Mendix build toolchain ${MENDIX_VERSION} from ${MXBUILD_CDN_BASE}..."; \
curl -fsSL "${MXBUILD_CDN_BASE}/mxbuild-${MENDIX_VERSION}.tar.gz" -o /tmp/mxbuild.tar.gz; \
echo "Extracting..."; \
tar -xzf /tmp/mxbuild.tar.gz -C /opt/mxtools; \
rm /tmp/mxbuild.tar.gz; \
test -f /opt/mxtools/modeler/mxbuild || { \
echo "FATAL: modeler/mxbuild not found at expected path /opt/mxtools/modeler/mxbuild."; \
echo "Tarball top-level may have changed. Inspecting:"; \
find /opt/mxtools -maxdepth 3 -type f -name 'mx*'; \
exit 1; \
}; \
chmod +x /opt/mxtools/modeler/mxbuild /opt/mxtools/modeler/mx 2>/dev/null || true; \
echo "Mendix ${MENDIX_VERSION} build toolchain ready at /opt/mxtools/modeler/"

# Mendix mxbuild — MX 11's .NET toolchain in particular — creates settings under
# $HOME/.local/share/Mendix via GetFolderPath(LocalApplicationData). In a fresh
# container that directory does not exist, and mxbuild's WhitelistAware
# filesystem FailFast-rejects *creating* it ("UnauthorizedAccessException …
# EnsurePathSafety") before the build starts. Pre-create it for HOME=/root so
# mxbuild writes settings without tripping the create-time whitelist check (a
# harmless no-op on older Mono-based mxbuild). Proven in AIDE aide-mxtools.
RUN mkdir -p /root/.local/share/Mendix /root/.config/Mendix /root/.cache/Mendix

# Crate entrypoint.
COPY build.sh /opt/mxtools/build.sh
RUN chmod +x /opt/mxtools/build.sh

# Runs as root by design — an ephemeral, single-shot build container (not a
# long-running service). Root avoids the bind-mount uid-mismatch that would
# otherwise block writing the produced .mda back into the operator's mounted
# /workspace. On a Linux host where root-owned output is undesirable, pass
# `--user $(id -u)`; build.sh defensively re-creates the Mendix settings dir
# under whatever $HOME resolves to.
ENV JAVA_HOME=/opt/java/openjdk \
MENDIX_VERSION=${MENDIX_VERSION}

VOLUME ["/workspace"]
WORKDIR /workspace

ENTRYPOINT ["/opt/mxtools/build.sh"]
CMD ["--help"]
102 changes: 102 additions & 0 deletions crates/mendix-10/build/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Mendix 10 Build Crate

> A model-agnostic, version-pinned Docker image that compiles **any** Mendix 10.x
> project (`.mpr`) into a deployable `.mda` — and runs `mx check` — with nothing
> on the host but Docker. The Mendix build toolchain (`mxbuild` + `mx`) is pulled
> from the official Mendix CDN at build time; no Studio Pro install, no committed
> binaries.

This is the **build** companion to the [`mendix-10` runtime crate](../). The build
crate *produces* the `.mda`; the runtime crate *runs* it.

## Why this exists

The AIDE pipeline compiles models with `mxbuild` driven from a bind-mounted Studio
Pro install. That requires a licensed modeler on the host. This crate makes the
toolchain self-contained and version-pinned: one image per Mendix major, the
matching `mxbuild` fetched from `cdn.mendix.com` at `docker build` — so a CI runner
or a contributor with only Docker can compile a Mendix 10 app without Studio Pro.

| | Studio Pro on host | aide-mxtools (bind-mount) | This build crate |
|---|---|---|---|
| Needs Studio Pro installed | yes | yes (modeler bind-mounted) | **no** |
| Toolchain source | local install | local install | **CDN at build** |
| Version pinning | manual | manual | **one image per version** |
| Runs in plain CI | no | partial | **yes** |

## Image tags

| Tag | Pins | Use |
|---|---|---|
| `ontologylabs/mendix-mxbuild:10.24.13.86719` | Exact version (immutable) | Reproducible CI |
| `ontologylabs/mendix-mxbuild:10` | Latest 10.x in this crate | Dev, demo |

## Build the image

```bash
cd crates/mendix-10/build
docker build \
--platform linux/amd64 \
--build-arg MENDIX_VERSION=10.24.13.86719 \
-t ontologylabs/mendix-mxbuild:10.24.13.86719 .
# mxbuild + mx are pulled from cdn.mendix.com/runtime/mxbuild-10.24.13.86719.tar.gz at build.
```

## Compile an app

```bash
# /path/to/project contains App.mpr
docker run --rm \
--platform linux/amd64 \
-v /path/to/project:/workspace \
ontologylabs/mendix-mxbuild:10.24.13.86719 \
build /workspace/App.mpr
# → writes /path/to/project/App.mda
```

Then run it with the runtime crate:

```bash
unzip /path/to/project/App.mda -d /path/to/project/app
cd ../ # the mendix-10 runtime crate
docker compose -f tests/docker-compose.smoke.yml up # or your own compose
```

## Validate a model (`mx check`)

```bash
docker run --rm -v /path/to/project:/workspace \
ontologylabs/mendix-mxbuild:10.24.13.86719 \
check /workspace/App.mpr
# exit 0 = clean · 1 = errors · 2 = warnings only
```

## What the entrypoint auto-injects

`build.sh` supplies the toolchain paths `mxbuild` needs unless you pass them:

* `--java-home` / `--java-exe-path` → the baked JDK (`$JAVA_HOME`)
* `--gradle-home` → the toolchain's bundled Gradle
* `--loose-version-check` → tolerate patch drift between toolchain and model
* `--output=/workspace/<App>.mda` → default output beside the `.mpr`

Pass any of these explicitly to override.

## Notes & gotchas

* **Runs as root by design.** A single-shot build container, not a service. Root
avoids the bind-mount uid mismatch that would block writing the `.mda` back into
your mounted `/workspace`. On Linux, pass `--user $(id -u)` if you want
host-uid-owned output; the entrypoint re-creates the Mendix settings dir under
the resulting `$HOME`.
* **`.NET` whitelist (MX 11 origin).** mxbuild creates settings under
`$HOME/.local/share/Mendix` and FailFast-rejects *creating* that path itself; the
image pre-creates it. (Same fix as the AIDE aide-mxtools image.)
* **amd64 emulation.** On Apple Silicon, `mxbuild` runs x86_64 under emulation.
Enable Colima Rosetta (`colima start --vm-type vz --vz-rosetta`) for speed; a
hung emulated build is the classic symptom of Rosetta being off.
* **No Mendix binary is committed.** The toolchain is fetched from the CDN at build,
on your licensed machine (D-DOCKER-LIB-002). See repo `guard.sh`.

See [`provenance.yaml`](provenance.yaml) for the exact CDN source and
[`versions.yaml`](versions.yaml) for verified versions.
Loading
Loading