Skip to content

Repository files navigation

solen — Development Container Base

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/*.yamlgenerated/profiles/
Features features/
Version source of truth versions/versions.yaml
Flat build manifest versions.json (generated)
CLI solen-cli/ (pip install -e solen-cli)

Quick start (pull a prebuilt image)

docker pull ghcr.io/ebpro/solen:<profile>-<tag>
docker run --rm -it ghcr.io/ebpro/solen:<profile>-<tag> bash

Tags 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.

Concepts

  • Feature — a folder under features/<name> with feature.json (metadata, options, dependsOn) and an idempotent install.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/*.yaml and expanded by solen generate profiles ... --chain into generated/profiles/<name> (chained profiles inherit their parents).
  • Versionsversions/versions.yaml is the single source of truth (pinned versions
    • upstream sources). solen versions sync flattens it into versions.json, the manifest consumed at build time; solen versions sync --check (CI) fails on drift. Per-arch release checksums live in checksums.json (tools.<name>.checksums.<version>.<arch>).

Building locally

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-arch

build.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.

Artefacts offload pipeline

Large tool archives can be fetched once, verified, and reused across builds:

  • scripts/utils/fetch-artefacts.sh — download + sha256-verify an offloaded tarball into artefacts/ (env: ARTEFACTS_BASE_URL, ARTEFACT_SHA256, or --manifest).
  • scripts/utils/offload-artefacts.sh — tar up artefacts/ + write artefacts/offload/toolcache-manifest-<ts>.json.
  • scripts/validate-artefacts.sh — verify manifests, tarball checksums, and checksums.json validity (run by the Validate Artefacts workflow).

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).

CI

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

Contributor guide

  • Add a feature: create features/<name>/feature.json + idempotent install.sh, then validate: solen validate features.
  • Add a profile: edit a profiles/matrix/*.yaml (or add one) and regenerate with solen generate profiles --matrix profiles/matrix --out generated/profiles --chain.
  • Pin a version: edit versions/versions.yaml, run solen versions sync, and update checksums.json via scripts/utils/update-checksums.sh when 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.

Appendix: useful commands

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

About

A base image for the UTLN Jupyter

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages