product:build

Table of contents

  1. Introduction
  2. What it writes
  3. Writing the files without the image
  4. Fixing a failed check
  5. Keeping the files in step with the pins
  6. Advanced
  7. Reference
    1. Options
    2. Errors and warnings
    3. deploy/docker-compose.yml
    4. deploy/.env.example
    5. [tool.splent.runtime] in pyproject.toml
    6. docker/requirements.lock
    7. docker/frontend.lock.json
    8. splent.contracts.json
    9. The SPL model in the image
    10. Docker image
  8. Requirements
  9. See also

Introduction

product:build prepares your product for production. It checks the product, writes the deploy bundle deploy/ and the pins the image is built from, and builds the production image. Commit what it writes, and a clone of the repository deploys with docker compose alone.

splent product:build
🏗️  Building deployment artifacts for product: my_first_app

  validate UVL configuration is satisfiable
  contract no conflicts detected
  refiners no collisions detected (0 refiners)
  features  all 7 feature(s) versioned and on PyPI
📦 Generating deploy/.env.example...
✅ Created: /workspace/my_first_app/deploy/.env.example

🐳 Generating deploy/docker-compose.yml...
✅ Created: /workspace/my_first_app/deploy/docker-compose.yml
✅ Pinned splent_framework 1.26.0, splent_cli 1.44.0, gunicorn >=23,<24 in pyproject.toml [tool.splent.runtime]
✅ Created: /workspace/my_first_app/splent.contracts.json
✅ Created: /workspace/my_first_app/docker/frontend.lock.json
✅ Staged the UVL model: /workspace/my_first_app/docker/spl/sample_splent_spl/sample_splent_spl.uvl

🐳 Building Docker image: my_first_app:0.0.1...
...
✅ Image built: my_first_app:0.0.1
✅ Pinned 45 third-party package(s) in docker/requirements.lock

🎯 product:build completed successfully.

The image takes several minutes the first time. Build, deploy and release tells this command apart from product:deploy and product:release.


What it writes

What product:build reads and what it writes READS pyproject.toml feature and runtime pins Pinned releases in .splent_cache UVL model the pinned version product:build 1 Check model, contracts, releases 2 Write the bundle and the pins 3 Build the image skipped with --no-image WRITES deploy/, what a server runs docker-compose.yml .env.example features/<service>/ README.md What the image is built from [tool.splent.runtime] docker/requirements.lock splent.contracts.json docker/spl/<spl>/ <product>:<version> the image, on this machine
Everything it writes except the image is committed with the product. A clone deploys with it and builds the same image.

Everything comes from the releases your pyproject.toml pins, never from the editable copies in your workspace.

Path What it is
deploy/docker-compose.yml Every service of the product and its features, in one Compose project that runs with Docker alone
deploy/.env.example Every production setting. Secrets are empty and marked # required
deploy/features/<service>/ The Dockerfiles and files of the features’ own services
deploy/docker-compose.build.yml An override that builds the web image instead of pulling a published one
deploy/README.md How to run and upgrade the bundle, for whoever runs the server
[tool.splent.runtime] The framework, the CLI and gunicorn the image installs, in pyproject.toml
docker/requirements.lock Every third-party package the image installs, at an exact version
docker/frontend.lock.json The bundle of every feature with frontend/, which the image checks
splent.contracts.json The contracts and parameter schemas of the features, which the image reads
docker/spl/<spl>/ The UVL model the product pins

The settings of a real deployment go in deploy/.env, which product:deploy writes and git never sees.


Writing the files without the image

splent product:build --no-image

Every file is written and no image is built. Use it when a server or your CI builds the image.


Fixing a failed check

Before it writes anything, the build checks the prod selection (features plus features_prod) as product:validate would, in phases 1, 2, 4, 6 and 7, and adds two checks of its own. When one fails it says why and writes nothing.

Check Fails when Fix
Pinned releases A pinned prod feature is not in .splent_cache or not linked in <product>/features/ product:resolve
Feature readiness A prod feature has no version, or its version is neither on PyPI nor tagged on the forge feature:release it, and pin the release
Validation A feature listed twice, an spl with no model, or an error in the selection, the contracts, the refiners, a parameter or a bundle What the line says, or product:validate for details
  The build reads the releases pyproject.toml pins, and these are not resolved in this workspace.
    splent_feature_reports@v0.2.1  not in .splent_cache

  Run splent product:resolve and build again.

A feature only tagged on the forge passes with a warning, and the image installs it from its tag.


Keeping the files in step with the pins

The product’s CI fails when the committed files do not match the pinned releases. The commands that change the selection or the pins write them again for you, such as feature:attach, feature:upgrade, feature:pin, feature:add, feature:remove and product:resolve.

  Updated deploy, splent.contracts.json for the pins, as product:build --no-image writes them. Commit them with the pins.

They do it only in a product that already has deploy/docker-compose.yml, and leave [tool.splent.runtime] alone.


Advanced

A feature service that runs once, such as an init job, must say restart: "no", quoted, or it gets unless-stopped and runs again after every exit. See Features that run a server.

deploy/features/ is rebuilt on every build from each feature’s docker/ directory. A file you add there by hand disappears.


Reference

Options

Option Description
--no-image Write every file, but do not build the image

Errors and warnings

Message What to do
Product has no docker/ directory. Run product:sync-template
Dockerfile not found Run splent product:sync-template. The files are written, the image is not
docker/Dockerfile.<product>.prod comes from an older CLI Run splent product:sync-template <product> --docker and build again
PyPI does not serve splent_framework==<version> Release the framework or the CLI first, or the image build fails at that install
.gitignore leaves out what a clean clone needs Remove the rules that match the listed files
Docker image build failed. Read the Docker output above it

deploy/docker-compose.yml

The product’s docker-compose.prod.yml (or docker-compose.yml) merged with every feature’s. A feature service replaces a product service of the same name, with a warning.

Setting Value
Project <product>_deploy, on its own network
Env file deploy/.env, for every service
Ports ${SPLENT_WEB_PORT:-<port>} and ${SPLENT_DB_PORT:-<port>}, the database on loopback unless SPLENT_DB_BIND_HOST says otherwise
Web image ${SPLENT_IMAGE:-<registry>/<org>/<product>:stable} once a release publishes an image, otherwise a build from the repository. See Running a published image
Required settings Under x-required, so docker compose up names the missing one and refuses to start
Restart and health unless-stopped for every service without a policy. The web asks /readyz after a 180 second start period, a MariaDB runs SELECT 1

deploy/.env.example

Four layers merged, a later one winning. Each feature’s env file (.env.prod.example, otherwise .env.example), each feature’s [tool.splent.config], the product’s [tool.splent.config], and the product’s docker/.env.prod.example (otherwise docker/.env.example).

SPLENT_ENV is prod, __PRODUCT__ is resolved and feature ports get the product’s offset, as in product:env --merge. The database names are filled in. Every other secret is empty under # required, and the optional settings come last, commented out.

# required: docker compose up refuses to start until this is set. A long random string will do: openssl rand -hex 32
SECRET_KEY=

[tool.splent.runtime] in pyproject.toml

[tool.splent.runtime]
splent_framework = "1.26.0"
splent_cli = "1.44.0"
gunicorn = ">=23,<24"

A bare version means exactly that version, anything else is a pip specifier. product:create writes the table, and every build sets splent_framework and splent_cli to the workspace’s framework and the running CLI.

docker/requirements.lock

Read back from the built image with pip freeze, and used by the Dockerfile as a constraint, so a clone of a commit installs exactly what that commit’s image had. Every image build resolves afresh and rewrites it.

docker/frontend.lock.json

For each feature with frontend/, its release, its entries, the sha256 of its bundle’s manifest, what it bundles with the licences, and who provides each peer, as phase 7 of product:validate reads them. The image checks the installed bundles against it, and product:frontend-audit audits it.

splent.contracts.json

Features installed with pip carry no pyproject.toml, so their refinement contracts and their [tool.splent.config_schema] come here, with every default but a secret’s filled in. The image reads it to load the features and check the parameters.

The SPL model in the image

The framework orders features and their migrations by the UVL model, so the image ships the model the product pins. When your working copy differs from the published model, the image ships the published one and a warning asks you to publish and pin yours. When the published model cannot be fetched, it ships the working copy and asks you to run splent spl:fetch <spl>. With no model at all, features load in pyproject.toml order.

Docker image

docker build -t <product>:<version> -t <product>:latest -f <product>/docker/Dockerfile.<product>.prod <product>

The context is the product’s repository alone, so a clone builds the same image. The Dockerfile installs the runtime pins and every feature from PyPI, or from its git tag, under the lock. It builds the product’s own frontend with npm ci when package.json has a build script, and runs feature:compile --verify on the bundles the features bring.

The app runs as the user splent (uid 10001), with mariadb-client for db:dump, db:restore and db:console, and without Node.js or compilers. No .env file but the *.example templates reaches the image.


Requirements

  • A selected product (SPLENT_APP) with a docker/ directory.
  • Every prod feature pinned and resolved.
  • To build the image, Docker, and PyPI serving the versions [tool.splent.runtime] pins.

See also


Back to top

splent. Distributed by an LGPL license v3. Contact us: drorganvidez@us.es