initial commit

This commit is contained in:
khannurien
2026-09-12 09:38:07 +02:00
commit 9a39e16c1f
7 changed files with 285 additions and 0 deletions

41
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,41 @@
# One image for the whole stack, so every repo's toolchain is installed here.
#
# Start from the image of whichever toolchain is the most awkward to add by
# hand, and add the others on top. The devcontainers images are at
# mcr.microsoft.com/devcontainers/: base, go, typescript-node, python, rust,
# java. The remote user is `vscode` on most of them and `node` on
# typescript-node; keep USERNAME below in sync.
FROM mcr.microsoft.com/devcontainers/base:bookworm
ARG USERNAME=vscode
# A second toolchain, copied from its official image. Pin the same version the
# repo's CI uses.
#
# COPY --from=golang:1.26-bookworm /usr/local/go /usr/local/go
# ENV GOPATH=/home/${USERNAME}/go
# ENV PATH=/usr/local/go/bin:/home/${USERNAME}/go/bin:$PATH
#
# Watch out. `ENV PATH` alone is not enough: /etc/profile reassigns PATH
# outright, so any login shell (VS Code terminals, and anything Claude Code
# spawns) loses the toolchain. Other variables survive, only PATH needs
# replaying:
#
# RUN printf 'export PATH=/usr/local/go/bin:${GOPATH:-$HOME/go}/bin:$PATH\n' \
# > /etc/profile.d/go.sh
# Whatever the servers and their tests shell out to at runtime, plus the
# packages the repos' own release images install.
#
# RUN apt-get update \
# && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
# build-essential \
# && rm -rf /var/lib/apt/lists/*
# Linters and package managers, at the versions CI pins.
#
# RUN su ${USERNAME} -c "npm install -g pnpm@10"
# The repos are bind mounted as siblings under /workspaces, which is also the
# workspace root. Create it here so it belongs to the remote user rather than
# being left root-owned by the daemon creating the mount points.
RUN mkdir -p /workspaces && chown ${USERNAME}:${USERNAME} /workspaces

View File

@@ -0,0 +1,50 @@
{
"name": "dev stack",
"build": {
"dockerfile": "Dockerfile"
},
// Every repo lands as a sibling inside /workspaces, which is ordinary
// container filesystem, and /workspaces itself is the workspace root.
// Nesting the other repos *inside* this one's bind mount instead would
// make the Docker daemon create those mount points as root through the
// bind, leaving root-owned empty directories behind on the host.
//
// With /workspaces as the workspace root, one Claude Code session started
// there reaches every repo as an ordinary subdirectory, with no --add-dir
// and no additionalDirectories permission entry.
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/${localWorkspaceFolderBasename},type=bind",
"workspaceFolder": "/workspaces",
// One entry per repo, matching the directories listed in stack.conf.
"mounts": [
"source=${localWorkspaceFolder}/../my-backend,target=/workspaces/my-backend,type=bind",
"source=${localWorkspaceFolder}/../my-frontend,target=/workspaces/my-frontend,type=bind"
],
// Host networking, so the servers see each other and the host browser as
// plain localhost, with no forwardPorts list to keep in sync.
"runArgs": ["--network=host"],
// Environment for the servers: where each one keeps its dev state, which
// address it listens on, where it finds the other half of the stack.
// ${containerWorkspaceFolder} is /workspaces.
"containerEnv": {
// "MYAPP_DB_PATH": "${containerWorkspaceFolder}/my-backend/.dev/app.db",
// "MYAPP_LISTEN_ADDR": "0.0.0.0:4747"
},
"postCreateCommand": "${localWorkspaceFolderBasename}/.devcontainer/post-create.sh",
"customizations": {
"vscode": {
"extensions": [
// One list for the whole stack, so both toolchains are here.
]
}
},
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:4": {}
}
}

28
.devcontainer/post-create.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Runs once, when the container is created. Executes each repo's setup command
# from stack.conf.
set -euo pipefail
here="$(cd "$(dirname "$0")/.." && pwd)"
source "$here/stack.conf"
# The workspace root, which holds every repo of the stack as a sibling.
cd "$here/.."
for service in "${SERVICES[@]}"; do
IFS='|' read -r dir setup run <<<"$service"
[ -n "$setup" ] || continue
echo "==> $dir: $setup"
(cd "$dir" && exec bash -c "$setup")
done
# Anything else the stack needs once: writing a dev config file one of the
# repos expects, creating state directories, seeding a database. There is no
# TTY here, so nothing may prompt.
#
# echo "==> my-frontend/public/env.js"
# cat > my-frontend/public/env.js <<'JS'
# window.env = {
# SERVER_URL: 'http://localhost:4747'
# }
# JS