A configurable, profile-driven development container image and tooling built around a
modular feature system. solen composes reusable features (small install scripts +
metadata) into hierarchical profiles and generates a multi-stage Dockerfile plus a
docker-bake.hcl for multi-architecture builds.
Quick links
| What | Where |
|---|---|
| Image registry | ghcr.io/ebpro/solen |
| Generated Dockerfile | generated/Dockerfile |
| Bake definition | generated/docker-bake.hcl |
| Profile matrix | profiles/matrix/*.yaml → generated/profiles/ |
| Features | features/ |
| Version source of truth | versions/versions.yaml |
| Flat build manifest | versions.json (generated) |
| CLI | solen-cli/ (pip install -e solen-cli) |
docker pull ghcr.io/ebpro/solen:<profile>-<tag>
docker run --rm -it ghcr.io/ebpro/solen:<profile>-<tag> bashTags follow <profile-slug>-<tag> (e.g. data-science-<sha>, minimal-latest,
quarto-full-v<version>). List profiles with solen list profiles.
As a devcontainer:
{
"name": "solen",
"image": "ghcr.io/ebpro/solen:data-science-latest",
"workspaceFolder": "/home/jovyan/workspace"
}generated/devcontainer/ holds per-profile devcontainer.json (and docker-compose.yml
where a profile defines services), generated by solen generate devcontainer --all.
- Feature — a folder under
features/<name>withfeature.json(metadata, options,dependsOn) and an idempotentinstall.sh. Helpers are prebaked into the image at/opt/solen/_lib(FEATURE_HELPERS_DIR) and resolve versions/checksums centrally. - Profile — a named feature set defined in
profiles/matrix/*.yamland expanded bysolen generate profiles ... --chainintogenerated/profiles/<name>(chained profiles inherit their parents). - Versions —
versions/versions.yamlis the single source of truth (pinned versions- upstream sources).
solen versions syncflattens it intoversions.json, the manifest consumed at build time;solen versions sync --check(CI) fails on drift. Per-arch release checksums live inchecksums.json(tools.<name>.checksums.<version>.<arch>).
- upstream sources).
python -m venv .venv && . .venv/bin/activate
pip install -e solen-cli
solen generate profiles --matrix profiles/matrix --out generated/profiles --chain
solen generate dockerfile --all --output generated/Dockerfile
solen generate bake --output generated/docker-bake.hcl
# or simply:
./build.sh data-science # positional profile; flags: --push --load --no-cache --multi-archbuild.sh runs the generation steps and then docker buildx bake final-<profile>
(solen build for a single profile). On machines without buildx (e.g. podman-only),
fall back to podman build -f generated/Dockerfile --target final-<profile> ..
Optional: pre-download tool binaries (kubectl, helm, gh, quarto, …) into artefacts/
with scripts/prebake-toolcache.sh for faster offline-ish builds.
Large tool archives can be fetched once, verified, and reused across builds:
scripts/utils/fetch-artefacts.sh— download + sha256-verify an offloaded tarball intoartefacts/(env:ARTEFACTS_BASE_URL,ARTEFACT_SHA256, or--manifest).scripts/utils/offload-artefacts.sh— tar upartefacts/+ writeartefacts/offload/toolcache-manifest-<ts>.json.scripts/validate-artefacts.sh— verify manifests, tarball checksums, andchecksums.jsonvalidity (run by theValidate Artefactsworkflow).
At build time the Dockerfile copies artefacts/ to /opt/solen/artefacts and the feature
helpers look for versions.json/checksums.json in the standard search order
(/tmp/… during the build stage, /opt/solen/artefacts/… in the final image).
Workflows live in .github/workflows/ and run on the org's in-cluster GitHub Actions
runner (actions-runner-controller runner set ebpro-org, with a docker:dind sidecar so
docker buildx works as usual):
| Workflow | Trigger | What it does |
|---|---|---|
ci-validate.yml |
PR / push | solen validate features, profile + devcontainer generation, solen versions sync --check, pytest, ruff |
ci-build.yml |
PR / push to develop |
Build selected profiles (default data-science), Syft SBOM + Trivy scan |
ci-publish.yml |
push to main / tag / dispatch |
Multi-arch (amd64 + arm64 via QEMU) publish to GHCR |
release.yml |
tag v* |
Build + push all profiles multi-arch, promote by digest, cosign sign |
validate-artefacts.yml |
artefacts/** changes |
Run scripts/validate-artefacts.sh |
cleanup-ghcr.yml |
schedule | GC old GHCR tags |
- Add a feature: create
features/<name>/feature.json+ idempotentinstall.sh, then validate:solen validate features. - Add a profile: edit a
profiles/matrix/*.yaml(or add one) and regenerate withsolen generate profiles --matrix profiles/matrix --out generated/profiles --chain. - Pin a version: edit
versions/versions.yaml, runsolen versions sync, and updatechecksums.jsonviascripts/utils/update-checksums.shwhen adding releases. - Check for drift:
solen versions sync --check,solen validate features,python -m pytest tests/,ruff check solen-cli.
Best practices: keep install.sh small and idempotent; prefer system package managers
where sensible; route version lookups through the shared helpers (fh_resolve_version,
fh_resolve_checksum) instead of hardcoding.
solen list profiles # available generated profiles
solen inspect-profile data-science # resolved features/options/services
solen generate devcontainer --all # per-profile devcontainer.json
solen analyze features --output generated/feature-matrix.md
solen validate propagate-versions # feature.json options vs versions.yaml
docker buildx bake --file generated/docker-bake.hcl --print