Netlify
A Mateu UI exported as a static bundle is a folder of plain files, so any static host serves it. This guide uses Netlify. It covers a one-off deploy from your machine and a CI deploy from GitHub Actions — the same setup that publishes this documentation site.
A UI that needs a server (view models with logic, proxied sources with secrets) does not fit here;
see Kubernetes or your platform of choice. A hybrid
bundle — static screens, the rest falling through to a backend at baseUrl — works on Netlify too,
as long as that backend allows the site’s origin (CORS).
1. Build the bundle
Section titled “1. Build the bundle”A project generated as YAML only, static (-Dauthoring=static) has the bundle goal bound
already:
mvn package# → target/mateu-bundle/# index.html boots the renderer# manifest.json the screens, routes and REST source catalogue# assets/ the renderer# _redirects /* → /index.html (SPA fallback)For a code project, add the mateu-bundle-maven-plugin as shown in
Static bundle › Exporting. Set -Dmateu.bundle.static=true to
fail the build if any screen still needs a server.
Preview it locally before deploying:
npx serve -s target/mateu-bundleWhat Netlify needs from the folder
Section titled “What Netlify needs from the folder”- SPA fallback. The bundle ships a
_redirectsfile with/* /index.html 200, which Netlify reads as is, so deep links like/orders/42boot the app. Nothing to configure. - Caching. Netlify’s default (
max-age=0, must-revalidatewith an ETag) is what the renderer’s assets want: their names are stable across releases, so the browser keeps them and revalidates with a cheap 304. Don’t mark/assets/*asimmutable— a new release would not reach users with a cached copy. - CORS on your APIs. The browser calls the APIs named in
sources.yamldirectly, so each must allow the site’s origin (https://<site>.netlify.appand your custom domain). Alternatively proxy them through Netlify with a rewrite (/api/* https://api.example.com/:splat 200) and use relative URLs in the sources: same origin, no CORS. The rule must come before the bundle’s/*fallback, and the bundle rewrites_redirectson every build, so prepend it in a step aftermvn package(or put it in anetlify.toml[[redirects]]block). - No secrets. Anything in the bundle is readable by every visitor. An API that needs a key must
be called through a server (a source in
proxymode on a Mateu backend), never with a key insources.yaml.
2. Deploy by hand
Section titled “2. Deploy by hand”npx netlify-cli login # opens the browser oncenpx netlify-cli deploy --dir=target/mateu-bundle # a draft URL to checknpx netlify-cli deploy --dir=target/mateu-bundle --prod # publishThe first deploy asks whether to link the folder to an existing site or create one; it writes
the choice to .netlify/state.json (keep that out of version control).
3. Deploy from CI
Section titled “3. Deploy from CI”Let CI build and publish on every push to your main branch, so the published site is always a function of the repository and never of someone’s laptop. Netlify’s own Git integration works too, but its build image is not set up for a Maven build; building in your CI and uploading the folder keeps the toolchain where it already is.
Secrets
Section titled “Secrets”Create two repository secrets (GitHub › Settings › Secrets and variables › Actions):
| Secret | Where to find it |
|---|---|
NETLIFY_AUTH_TOKEN | Netlify › User settings › Applications › Personal access tokens |
NETLIFY_SITE_ID | Netlify › your site › Site configuration › General › Site details › Site ID |
The token can deploy every site of that Netlify account; treat it like a password and scope the secret to the repository that needs it.
Workflow
Section titled “Workflow”name: Deploy UIon: push: branches: [main] workflow_dispatch:
permissions: contents: read
concurrency: group: deploy-ui cancel-in-progress: true
jobs: deploy: runs-on: ubuntu-latest timeout-minutes: 20 steps: - uses: actions/checkout@v4 with: persist-credentials: false - uses: actions/setup-java@v4 with: distribution: temurin java-version: 21 cache: maven - name: Build the bundle run: mvn -B -ntp package -Dmateu.bundle.static=true - uses: actions/setup-node@v4 with: node-version: 22 - name: Deploy to Netlify env: NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} run: | if [ -z "$NETLIFY_AUTH_TOKEN" ] || [ -z "$NETLIFY_SITE_ID" ]; then echo "::notice::Netlify secrets not configured; skipping the deploy." exit 0 fi # A pinned version, installed outside the project, without install scripts. npm install --prefix "$RUNNER_TEMP/netlify" --no-save --ignore-scripts netlify-cli@<version> "$RUNNER_TEMP/netlify/node_modules/.bin/netlify" deploy \ --dir=target/mateu-bundle --prod --no-build \ --site "$NETLIFY_SITE_ID" --auth "$NETLIFY_AUTH_TOKEN" \ --message "${GITHUB_SHA::9}"A few choices in it are deliberate:
- Pin netlify-cli to an exact version instead of
npx netlify-cli@latest, and install it with--ignore-scriptsoutside the project: a deploy step holds a token that can publish your site. --no-build: the bundle is already built; don’t let the CLI try to run a build of its own.- Skip, don’t fail, without secrets: forks and pull requests from forks have no access to secrets, and a red build for that reason helps nobody. Pull requests can still run the build step to prove the bundle builds.
- Re-pointing without rebuilding: to deploy the same screens against another environment, build
with
-Dmateu.bundle.environment=pro(see Environments); the screens’structureHashstays the same.
The documentation you are reading is deployed exactly this way: the repository’s
.github/workflows/docs.yml builds the site, checks every internal link and, on master and on each
release, runs netlify deploy --prod --no-build with the same two secrets.
Custom domain and HTTPS
Section titled “Custom domain and HTTPS”Add the domain in Netlify › Domain management and point DNS at it (a CNAME to
<site>.netlify.app, or Netlify DNS). Netlify provisions the certificate. Remember to add the new
origin to the CORS allow-list of every API the bundle calls.
Related
Section titled “Related”- Static bundle and A 100 % static UI
- Production checklist — CSP and CORS apply to static sites too