product:build
Build deployment artifacts (.env.deploy.example and docker-compose.deploy.yml) by merging the product and its features.
Usage
splent product:build [--no-image]
Options
| Option | Description |
|---|---|
--no-image |
Skip building the Docker image. Only generate the compose and env files. |
Pre-flight checks
Before generating any artifacts, product:build runs a 4-phase pre-flight sequence:
| Phase | Name | What it validates | Runs when |
|---|---|---|---|
| 0 | pyproject sanity | Duplicate features across features/features_dev/features_prod, inconsistent namespaces (splent_io vs splent-io), and SPL existence ([tool.splent].spl or legacy [tool.splent.uvl]). |
Always |
| 1 | SAT | The feature selection is satisfiable under the UVL variability model. | Always |
| 2 | Contracts | No ERROR-level conflicts between the contracts of the declared features (route or blueprint name collisions). | Always |
| 3 | PyPI | All prod features are versioned and published on PyPI. Editable (non-versioned) features trigger a warning since they cannot be installed from PyPI during the Docker build. | Build mode only |
Phase 0 aborts early if it fails – later phases are skipped. If any other phase fails, the build is also aborted. Use --skip-preflight (internal flag, used by product:derive to avoid double-checking) to bypass.
Description
This command generates two deployment-oriented files inside the product docker/ directory:
.env.deploy.example
Merges environment variables from:- the product (
.env.prod.examplepreferred, otherwise.env.example) - all feature docker directories (
.env.prod.examplepreferred, otherwise.env.example)
When merging, later values override earlier ones (features can override product keys).
Additional processing:
- Removes
FEATURE_HOST_DIRvariables — these reference host paths that do not exist in production (templates are baked into images or copied intodocker/features/). - Forces
SPLENT_ENV=prodin the output. - Resolves
__PRODUCT__placeholders to the actual product name. - Applies port offset to feature port variables (same logic as
product:env --merge).
- the product (
docker-compose.deploy.ymlMerges Compose definitions from:- the product (
docker-compose.prod.ymlpreferred, otherwisedocker-compose.yml) - all feature docker directories (
docker-compose.prod.ymlpreferred, otherwisedocker-compose.yml)
When merging:
services: product overrides same-named feature services. A warning is shown when a feature service name collides with an existing one.networksandvolumes: dictionaries are merged- Port conflicts: the command warns if two services expose the same host port in the merged compose file.
- the product (
Feature Dockerfiles and supporting assets
Before merging compose files, product:build processes each feature’s docker/ directory:
- Copies Dockerfiles — for features with
build:directives, the Dockerfile (and any supporting files in the feature’sdocker/directory, excluding compose and.envfiles) is copied to<product>/docker/features/<service>/. - Copies supporting assets — for all features with a
docker/directory (even those without a custom Dockerfile), supporting files such as config templates and scripts are copied todocker/features/<service>/. This ensures files needed by bind mounts (e.g., nginx templates) are available in the deploy artifact. - Rewrites bind mounts — volume mounts that reference
${*FEATURE_HOST_DIR}are rewritten to use host-side absolute paths pointing to the product’sdocker/features/directory. - Rewrites
build:directives — the build context is updated to point to the copied location indocker/features/. - Injects
.env.deployvolume mount — the web service ({product}_web) receives a read-only bind mount for.env.deployso the entrypoint can sync deploy variables.
The result is a single deploy-ready compose file and a single deploy-ready env template.
Output files
Created under:
<product>/docker/
Files:
.env.deploy.exampledocker-compose.deploy.yml
Requirements
- A product must be selected (
SPLENT_APPset). - The product must include a
docker/directory. - Features are read from
pyproject.toml(not from filesystem glob). Only features declared in[tool.splent].featuresare included in the build.