Provisioning a TypeScript (bun) Project¶
This tutorial sets up .shed/ provisioning for a TypeScript project that uses
bun as its runtime and package manager, with Postgres and
Redis supplied by the project's compose.yaml.
It assumes the full image (the default), which ships
bun and the Docker daemon.
Layout¶
provision.yaml¶
hooks:
install: .shed/scripts/install.sh
startup: .shed/scripts/startup.sh
shutdown: .shed/scripts/shutdown.sh
# First bun install + image pulls can be slow under the vfs storage driver.
timeout: 30m
scripts/lib.sh¶
#!/bin/bash
log() { echo "[provision $(date +%H:%M:%S)] $*"; }
# bun ships in the `full` image; the guard installs it on leaner images.
ensure_bun() { command -v bun >/dev/null 2>&1 || curl -fsSL https://bun.com/install | bash; }
wait_for_docker() {
for i in $(seq 1 30); do docker info >/dev/null 2>&1 && break; sleep 1; done
docker info >/dev/null 2>&1 || log "WARN: docker not ready (needs the 'full' image)"
}
# Wait until every compose service with a healthcheck reports healthy.
wait_for_compose_healthy() {
for i in $(seq 1 30); do
pending="$(docker compose ps --format '{{.Name}} {{.Health}}' \
| awk '$2 != "" && $2 != "healthy" {print $1}')"
[ -z "$pending" ] && return 0
sleep 2
done
}
# Persist KEY=VALUE env to every exec/console session (read per-exec).
persist_session_env() {
local name="$1"; shift
sudo mkdir -p /etc/environment.d
printf '%s\n' "$@" | sudo tee "/etc/environment.d/90-${name}.conf" >/dev/null
}
scripts/install.sh¶
#!/bin/bash
set -euo pipefail
source "$(dirname "$0")/lib.sh"
cd "${SHED_WORKSPACE:-$(cd "$(dirname "$0")/../.." && pwd)}"
ensure_bun
wait_for_docker
# App/session env (used by the dev server, migrations, and `shed exec`). Tests
# usually load their own .env.test, so this just mirrors compose.yaml.
persist_session_env myapp \
DATABASE_URL=postgres://dev:dev@localhost:5432/myapp \
REDIS_URL=redis://localhost:6379
bun install # workspace deps
docker pull postgres:16-alpine redis:7-alpine || true # warm the compose images
scripts/startup.sh¶
#!/bin/bash
set -euo pipefail
source "$(dirname "$0")/lib.sh"
cd "${SHED_WORKSPACE:-$(cd "$(dirname "$0")/../.." && pwd)}"
wait_for_docker
docker compose up -d
wait_for_compose_healthy
# Apply schema/migrations the tests rely on (adjust to your tool).
bun run db:push || log "WARN: db:push failed"
scripts/shutdown.sh¶
#!/bin/bash
set -euo pipefail
source "$(dirname "$0")/lib.sh"
cd "${SHED_WORKSPACE:-$(cd "$(dirname "$0")/../.." && pwd)}"
docker compose down || true
Build and test¶
shed create myproj --local-dir .
shed exec myproj -- bash -lc 'cd ~/myproj && bun run test'
shed exec myproj -- bash -lc 'cd ~/myproj && bun run build'
shed exec myproj -- bash -lc 'cd ~/myproj && bun run lint'
Compose and Testcontainers networking
docker compose creates its own user-defined network, and the full image
enables Docker's default docker0 bridge — both work on the VZ and
Firecracker backends, so published ports and Testcontainers behave the same
on each.