Your first workspace
A directory, a manifest, and one member that does something.
A workspace is a directory with a bldr-workspace.yml in it. Everything
bldr builds for you lives in one.
Create it
mkdir hello-bldr && cd hello-bldr
bldr ws initThat writes a bldr-workspace.yml. Open it and you will find a members: map,
which is the only part that matters yet.
Add a member
A member is one buildable thing: a directory with its own .bldr.ts that
says what it produces. Make one:
mkdir -p members/sitemembers:
site: members/site
# A base image, pinned by digest. bldr fetches it once and stores it by content.
node:
content:
kind: container-registry
reference: docker.io/library/node@sha256:...
tracking:
kind: container-registry
reference: docker.io/library/node:22-slim
options:
build_controller: defaultTwo members, two kinds. site is a local directory, so its content is whatever
is on disk when you build. node is pinned to an exact image digest, with a
separate note saying where a newer one would come from. Asking what upstream
offers never changes what builds; only bldr ws pull node does.
Say what the member produces
import { ContainerImage } from "bldr";
import nodeImage from "node";
// One pod: run a command in the node image, keep what it wrote to /out.
const site = ContainerImage.fromMember(nodeImage, "amd64")
.pod()
.run("mkdir -p /out && echo '<h1>hello from bldr</h1>' > /out/index.html")
.capture("/out");
scope.addOutputDirectory("site", site);Three things are worth noticing. import nodeImage from "node" refers to the
member you declared, and that import is the dependency edge. scope and
member are already in scope; a .bldr.ts does not import them. And
addOutputDirectory names a target, which is what you will ask bldr to
build.
Register the workspace
The node builds workspaces it knows about.
bldr ws register
bldr ws statusNext: build it.