SpuntoSpunto/@spunto/build

Catalogs

The curated base images, devcontainer features and suggested extensions a project picker offers before anyone types anything.

Why it exists

Data, not logic — but shared data, because the alternative is what already happened: two hand-maintained copies that drifted. Spunto Lite's copy was missing java, kubectl-helm-minikube and google-cloud-cli, and described common-utils as installing Oh My Zsh at a time when the image recipe explicitly passes installOhMyZsh: false. Nobody decided either of those; they are the shape of a catalog nobody owns.

The catalogs are also load-bearing for the generators: buildImageScript in ./script resolves a selected feature id through AVAILABLE_FEATURES to find its OCI ref, its default options or its local script.

import { AVAILABLE_IMAGES, AVAILABLE_FEATURES, SUGGESTED_EXTENSIONS } from "@spunto/build/catalogs"

Project templates are deliberately absent

Spunto Cloud's templates cloned a starter repository and carried onboarding-gallery presentation (logo, accent, category); Lite's only configure an environment. They answer different questions and diverge on purpose, so they fail the test for being in this package.

API

DevImage and AVAILABLE_IMAGES

type DevImage = { id: string; label: string; image: string; description: string }
const AVAILABLE_IMAGES: DevImage[]

The base images a project can start from:

idlabelimage
typescript-node-20Node.js 20 (TypeScript)mcr.microsoft.com/devcontainers/typescript-node:20
javascript-node-20Node.js 20 (JavaScript)mcr.microsoft.com/devcontainers/javascript-node:20
python-312Python 3.12mcr.microsoft.com/devcontainers/python:3.12
go-121Go 1.21mcr.microsoft.com/devcontainers/go:1.21
rustRustmcr.microsoft.com/devcontainers/rust:latest
java-21Java 21mcr.microsoft.com/devcontainers/java:21

DevFeature, DevFeatureOption and AVAILABLE_FEATURES

type DevFeatureOption = { name: string; default: string; description: string }
const AVAILABLE_FEATURES: DevFeature[]

Prop

Type

idSourceUser optionsNotes
docker-in-dockerghcr.io/coderhammer/features/docker-in-docker:3version (latest)Fork of the upstream feature, which breaks when a buildx/compose tag is pushed before its release asset. defaultOptions: { moby: "false" } (moby-cli is unavailable on Debian trixie and newer).
claude-codeghcr.io/stu-bell/devcontainer-features/claude-code:0—
nodeghcr.io/devcontainers/features/node:1version (lts)
pythonghcr.io/devcontainers/features/python:1version (latest)
goghcr.io/devcontainers/features/go:1version (latest)
rustghcr.io/devcontainers/features/rust:1version (latest)
javaghcr.io/devcontainers/features/java:1version (latest)
aws-clighcr.io/devcontainers/features/aws-cli:1—
github-clighcr.io/devcontainers/features/github-cli:1—
kubectl-helm-minikubeghcr.io/devcontainers/features/kubectl-helm-minikube:1—
terraformghcr.io/devcontainers/features/terraform:1—
google-cloud-clilocal script google-cloud-cli.sh—Installed from Google's own apt repository.
common-utilsghcr.io/devcontainers/features/common-utils:2—Already in every image (it creates the vscode user). Listed so a project can override its options; they are merged over the platform's rather than installed twice.
// A feature picker with option inputs
for (const f of AVAILABLE_FEATURES) {
  renderFeature(f.label, f.description, f.options ?? [])
}

// Look up what a stored selection means
const node = AVAILABLE_FEATURES.find((f) => f.id === "node")
node?.options?.[0] // → { name: "version", default: "lts", description: "Node.js version (lts, 20, 22, etc.)" }

A project may also store a feature that is not in this catalog, by giving it an explicit ociRef (a hand-typed OCI reference). The generators install it from that ref.

SUGGESTED_EXTENSIONS

const SUGGESTED_EXTENSIONS: ExtensionSuggestion[]

The default state of an extension picker, shown before anyone types a query: esbenp.prettier-vscode, dbaeumer.vscode-eslint, ms-python.python, golang.go, rust-lang.rust-analyzer, bradlc.vscode-tailwindcss, eamodio.gitlens, ms-azuretools.vscode-docker.

Entries carry id, label and publisher only — no registry URLs. The catalog is registry-agnostic: attach links for whichever registry is active with withRegistryUrls from ./extensions.

import { parseGallery, withRegistryUrls } from "@spunto/build/extensions"

const defaults = withRegistryUrls(parseGallery(org.extensionsGallery), SUGGESTED_EXTENSIONS)

The bar for being listed here is being published on Open VSX — the default registry, the one every organization gets with no configuration — so the defaults stay installable without extra setup. Check before adding one: curl -s https://open-vsx.org/api/<publisher>/<name>; a 404 means it does not belong here.

Edit on GitHub

On this page