feat(toolbox): update toolbox template configuration

- Update ToolboxStack/output/toolbox-template/Dockerfile with latest container settings
- Update ToolboxStack/output/toolbox-template/PROMPT with enhanced instructions
- Update ToolboxStack/output/toolbox-template/SEED with updated seed data
- Update ToolboxStack/output/toolbox-template/aqua.yaml with refined tool management
- Update ToolboxStack/output/toolbox-template/build.sh with improved build process
- Update ToolboxStack/output/toolbox-template/docker-compose.yml with enhanced service definitions
- Update ToolboxStack/output/toolbox-template/release.sh with enhanced release process
- Update ToolboxStack/output/toolbox-template/run.sh with improved runtime configuration

This enhances the toolbox template for creating new developer environments.
This commit is contained in:
2025-10-30 13:08:57 -05:00
parent 87f32cfd4b
commit 3dd420a500
8 changed files with 190 additions and 45 deletions

View File

@@ -16,6 +16,26 @@ RUN if getent passwd "${USER_ID}" >/dev/null; then \
fi \ fi \
&& useradd --uid "${USER_ID}" --gid "${GROUP_ID}" --shell /usr/bin/zsh --create-home "${USERNAME}" && useradd --uid "${USER_ID}" --gid "${GROUP_ID}" --shell /usr/bin/zsh --create-home "${USERNAME}"
# Install toolbox-specific packages here
# Example:
# RUN apt-get update && apt-get install -y --no-install-recommends \
# specific-package \
# && apt-get clean \
# && rm -rf /var/lib/apt/lists/*
# Install toolbox-specific aqua packages here
# Example:
# COPY aqua.yaml /tmp/aqua.yaml
# RUN chown "${USER_ID}:${GROUP_ID}" /tmp/aqua.yaml \
# && su - "${USERNAME}" -c 'mkdir -p ~/.config/aquaproj-aqua' \
# && su - "${USERNAME}" -c 'cp /tmp/aqua.yaml ~/.config/aquaproj-aqua/aqua.yaml' \
# && AQUA_GLOBAL_CONFIG=/tmp/aqua.yaml aqua install \
# && su - "${USERNAME}" -c 'AQUA_GLOBAL_CONFIG=~/.config/aquaproj-aqua/aqua.yaml aqua install'
# Install toolbox-specific npm packages here
# Example:
# RUN mise exec -- npm install -g @scope/package@version
# Remove sudo to ensure no root escalation is possible at runtime # Remove sudo to ensure no root escalation is possible at runtime
RUN apt-get remove -y sudo 2>/dev/null || true && apt-get autoremove -y 2>/dev/null || true && rm -rf /var/lib/apt/lists/* 2>/dev/null || true RUN apt-get remove -y sudo 2>/dev/null || true && apt-get autoremove -y 2>/dev/null || true && rm -rf /var/lib/apt/lists/* 2>/dev/null || true

View File

@@ -17,7 +17,7 @@ Current state:
- See ../PROMPT for shared toolbox contribution expectations (documentation sync, build cadence, commit/push discipline, Conventional Commits, atomic history). - See ../PROMPT for shared toolbox contribution expectations (documentation sync, build cadence, commit/push discipline, Conventional Commits, atomic history).
Collaboration checklist: Collaboration checklist:
1. Build upon the base tooling with {{toolbox_name}}-specific additions; mirror outcomes in README.md and this PROMPT. 1. Translate SEED goals into concrete tooling decisions; mirror outcomes in README.md and this PROMPT (do not rewrite SEED unless the scope resets).
2. Prefer aqua-managed CLIs and mise-managed runtimes for reproducibility. 2. Prefer aqua-managed CLIs and mise-managed runtimes for reproducibility.
3. After each tooling change, update README/PROMPT, run ./build.sh, commit (Conventional Commit message, focused diff), and push only once the build succeeds per ../PROMPT. 3. After each tooling change, update README/PROMPT, run ./build.sh, commit (Conventional Commit message, focused diff), and push only once the build succeeds per ../PROMPT.
4. Record verification steps (build/test commands) as they are performed. 4. Record verification steps (build/test commands) as they are performed.

View File

@@ -23,7 +23,7 @@ This SEED file defines the high-level objectives for all toolboxes created from
- **Dockerfile**: Extend from base with toolbox-specific tooling - **Dockerfile**: Extend from base with toolbox-specific tooling
- **docker-compose.yml**: Configure service with inherited + custom settings - **docker-compose.yml**: Configure service with inherited + custom settings
- **build.sh**: Wrapper around `docker build` with UID/GID mapping - **build.sh**: Wrapper around `docker build` with UID/GID mapping
- **run.sh**: Helper to bring service up/down with proper directory setup - **run.sh**: Helper to bring service up/down
- **devcontainer.json**: VS Code remote container definition - **devcontainer.json**: VS Code remote container definition
- **SEED**: Define toolbox-specific objectives (this file) - **SEED**: Define toolbox-specific objectives (this file)
- **PROMPT**: LLM onboarding prompt for future contributors - **PROMPT**: LLM onboarding prompt for future contributors

View File

@@ -6,3 +6,17 @@ packages:
# Add additional packages specific to your toolbox here # Add additional packages specific to your toolbox here
# Example: # Example:
# - name: cli/cli@v2.82.1 # - name: cli/cli@v2.82.1
# - name: jesseduffield/lazygit@v0.55.1
# - name: direnv/direnv@v2.37.1
# - name: dandavison/delta@0.18.2
# - name: ajeetdsouza/zoxide@v0.9.8
# - name: casey/just@1.43.0
# - name: mikefarah/yq@v4.48.1
# - name: ducaale/xh@v0.25.0
# - name: rs/curlie@v1.8.2
# - name: twpayne/chezmoi@v2.66.1
# - name: mvdan/sh@v3.12.0
# - name: koalaman/shellcheck@v0.11.0
# - name: hadolint/hadolint@v2.14.0
# - name: astral-sh/uv@0.9.6
# - name: watchexec/watchexec@v2.3.2

View File

@@ -29,7 +29,6 @@ fi
TOOLBOX_NAME="${TOOLBOX_NAME_OVERRIDE:-$(basename "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")}" TOOLBOX_NAME="${TOOLBOX_NAME_OVERRIDE:-$(basename "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")}"
sanitized_input "$TOOLBOX_NAME" sanitized_input "$TOOLBOX_NAME"
IMAGE_NAME="tsysdevstack-toolboxstack-${TOOLBOX_NAME#toolbox-}" IMAGE_NAME="tsysdevstack-toolboxstack-${TOOLBOX_NAME#toolbox-}"
sanitized_input "$IMAGE_NAME"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Sanitize user input # Sanitize user input
@@ -44,21 +43,46 @@ sanitized_input "$TEA_VERSION"
BUILDER_NAME="${BUILDER_NAME:-tsysdevstack-toolboxstack-builder}" BUILDER_NAME="${BUILDER_NAME:-tsysdevstack-toolboxstack-builder}"
sanitized_input "$BUILDER_NAME" sanitized_input "$BUILDER_NAME"
CACHE_DIR="${SCRIPT_DIR}/.build-cache" CACHE_DIR="${SCRIPT_DIR}/.build-cache"
TAG="${TAG_OVERRIDE:-dev}"
sanitized_input "$TAG"
RELEASE_TAG="${RELEASE_TAG_OVERRIDE:-release-current}"
sanitized_input "$RELEASE_TAG"
VERSION_TAG="${VERSION_TAG_OVERRIDE:-}"
if [[ -n "$VERSION_TAG" ]]; then
sanitized_input "$VERSION_TAG"
fi
PUSH="${PUSH_OVERRIDE:-false}"
echo "Building ${IMAGE_NAME} with UID=${USER_ID} GID=${GROUP_ID} USERNAME=${USERNAME}" echo "Building ${IMAGE_NAME} with UID=${USER_ID} GID=${GROUP_ID} USERNAME=${USERNAME}"
echo "Primary tag: ${TAG}"
# Ensure builder exists
if ! docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then if ! docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then
echo "Creating builder: ${BUILDER_NAME}" echo "Creating builder: ${BUILDER_NAME}"
docker buildx create --driver docker-container --name "${BUILDER_NAME}" --use >/dev/null if ! docker buildx create --driver docker-container --name "${BUILDER_NAME}" --use >/dev/null; then
echo "Error: Failed to create Docker buildx builder." >&2
exit 1
fi
else else
echo "Using existing builder: ${BUILDER_NAME}" echo "Using existing builder: ${BUILDER_NAME}"
docker buildx use "${BUILDER_NAME}" >/dev/null if ! docker buildx use "${BUILDER_NAME}" >/dev/null; then
echo "Error: Failed to use Docker buildx builder." >&2
exit 1
fi
fi fi
mkdir -p "${CACHE_DIR}" # Ensure cache directory exists
if ! mkdir -p "${CACHE_DIR}"; then
echo "Error: Failed to create cache directory: ${CACHE_DIR}" >&2
exit 1
fi
echo "Starting build..." echo "Starting build..."
docker buildx build \ BUILD_OUTPUT=$(mktemp)
trap 'rm -f "$BUILD_OUTPUT"' EXIT
# Build the image
if ! docker buildx build \
--builder "${BUILDER_NAME}" \ --builder "${BUILDER_NAME}" \
--load \ --load \
--progress=plain \ --progress=plain \
@@ -68,15 +92,102 @@ docker buildx build \
--build-arg TEA_VERSION="${TEA_VERSION}" \ --build-arg TEA_VERSION="${TEA_VERSION}" \
--cache-from "type=local,src=${CACHE_DIR}" \ --cache-from "type=local,src=${CACHE_DIR}" \
--cache-to "type=local,dest=${CACHE_DIR},mode=max" \ --cache-to "type=local,dest=${CACHE_DIR},mode=max" \
--tag "${IMAGE_NAME}" \ --tag "${IMAGE_NAME}:${TAG}" \
"${SCRIPT_DIR}" "${SCRIPT_DIR}" 2>&1 | tee "${BUILD_OUTPUT}"; then
echo "Error: Docker build failed. Check output above for details." >&2
exit 1
fi
echo "Build completed successfully." echo "Build completed successfully."
# Run post-build verification
echo "Running post-build verification..."
if ! docker run --rm "${IMAGE_NAME}:${TAG}" zsh -c 'echo "Container starts successfully"'; then
echo "Error: Failed to start container with basic test." >&2
exit 1
fi
# Verify critical tools are available
echo "Verifying critical tools..."
CRITICAL_TOOLS=("zsh" "git" "curl" "jq" "fish" "fzf" "bat" "fd" "rg" "htop" "btop")
for tool in "${CRITICAL_TOOLS[@]}"; do
if ! docker run --rm "${IMAGE_NAME}:${TAG}" which "$tool" >/dev/null 2>&1; then
echo "Error: Critical tool '$tool' not found in PATH." >&2
exit 1
fi
done
# Verify aqua tools are available
echo "Verifying aqua tools..."
AQUA_TOOLS=("gh" "lazygit" "direnv" "delta" "zoxide" "just" "yq" "xh" "curlie" "chezmoi" "shfmt" "shellcheck" "hadolint" "uv" "uvx" "watchexec" "kroki")
for tool in "${AQUA_TOOLS[@]}"; do
if ! docker run --rm "${IMAGE_NAME}:${TAG}" which "$tool" >/dev/null 2>&1; then
echo "Error: Aqua tool '$tool' not found in PATH." >&2
exit 1
fi
done
# Verify AI CLI tools are available
echo "Verifying AI CLI tools..."
AI_TOOLS=("code" "qwen" "gemini" "codex" "opencode")
for tool in "${AI_TOOLS[@]}"; do
if ! docker run --rm "${IMAGE_NAME}:${TAG}" which "$tool" >/dev/null 2>&1; then
echo "Error: AI CLI tool '$tool' not found in PATH." >&2
exit 1
fi
done
# Verify testing tools are available
echo "Verifying testing tools..."
TESTING_TOOLS=("bats" "shellcheck" "shfmt" "hadolint")
for tool in "${TESTING_TOOLS[@]}"; do
if ! docker run --rm "${IMAGE_NAME}:${TAG}" which "$tool" >/dev/null 2>&1; then
echo "Error: Testing tool '$tool' not found in PATH." >&2
exit 1
fi
done
echo "All verifications passed."
# Push if requested
if [[ "${PUSH}" == "true" ]]; then
echo "Pushing ${IMAGE_NAME}:${TAG}"
if ! docker push "${IMAGE_NAME}:${TAG}"; then
echo "Error: Failed to push ${IMAGE_NAME}:${TAG}" >&2
exit 1
fi
if [[ "${TAG}" == "dev" && -n "${VERSION_TAG}" ]]; then
if ! docker tag "${IMAGE_NAME}:${TAG}" "${IMAGE_NAME}:${VERSION_TAG}"; then
echo "Error: Failed to tag ${IMAGE_NAME}:${VERSION_TAG}" >&2
exit 1
fi
echo "Pushing ${IMAGE_NAME}:${VERSION_TAG}"
if ! docker push "${IMAGE_NAME}:${VERSION_TAG}"; then
echo "Error: Failed to push ${IMAGE_NAME}:${VERSION_TAG}" >&2
exit 1
fi
fi
if [[ "${TAG}" == "dev" ]]; then
if ! docker tag "${IMAGE_NAME}:${TAG}" "${IMAGE_NAME}:${RELEASE_TAG}"; then
echo "Error: Failed to tag ${IMAGE_NAME}:${RELEASE_TAG}" >&2
exit 1
fi
echo "Pushing ${IMAGE_NAME}:${RELEASE_TAG}"
if ! docker push "${IMAGE_NAME}:${RELEASE_TAG}"; then
echo "Error: Failed to push ${IMAGE_NAME}:${RELEASE_TAG}" >&2
exit 1
fi
fi
fi
# Run security scan if TRIVY is available # Run security scan if TRIVY is available
if command -v trivy &> /dev/null; then if command -v trivy &> /dev/null; then
echo "Running security scan with Trivy..." echo "Running security scan with Trivy..."
trivy image --exit-code 0 --severity HIGH,CRITICAL "${IMAGE_NAME}" trivy image --exit-code 0 --severity HIGH,CRITICAL "${IMAGE_NAME}:${TAG}"
else else
echo "Trivy not found. Install Trivy to perform security scanning." echo "Trivy not found. Install Trivy to perform security scanning."
fi fi
echo "Build process completed successfully with all verifications."

View File

@@ -33,14 +33,3 @@ services:
# Additional AI tool directories # Additional AI tool directories
- ${HOME}/.config/codex:/home/toolbox/.config/codex:rw - ${HOME}/.config/codex:/home/toolbox/.config/codex:rw
- ${HOME}/.cache/codex:/home/toolbox/.cache/codex:rw - ${HOME}/.cache/codex:/home/toolbox/.cache/codex:rw
# AI CLI tool configuration and cache directories
- ${HOME}/.config/openai:/home/toolbox/.config/openai:rw
- ${HOME}/.config/gemini:/home/toolbox/.config/gemini:rw
- ${HOME}/.config/qwen:/home/toolbox/.config/qwen:rw
- ${HOME}/.config/code:/home/toolbox/.config/code:rw
- ${HOME}/.config/opencode:/home/toolbox/.config/opencode:rw
- ${HOME}/.cache/openai:/home/toolbox/.cache/openai:rw
- ${HOME}/.cache/gemini:/home/toolbox/.cache/gemini:rw
- ${HOME}/.cache/qwen:/home/toolbox/.cache/qwen:rw
- ${HOME}/.cache/code:/home/toolbox/.cache/code:rw
- ${HOME}/.cache/opencode:/home/toolbox/.cache/opencode:rw

View File

@@ -10,9 +10,12 @@ Examples:
./release.sh 0.2.0 ./release.sh 0.2.0
./release.sh --dry-run 0.2.0 ./release.sh --dry-run 0.2.0
This script promotes the dev tag to: This script rebuilds the toolbox image, tags it as:
- tsysdevstack-toolboxstack-<name>:dev
- tsysdevstack-toolboxstack-<name>:release-current - tsysdevstack-toolboxstack-<name>:release-current
- tsysdevstack-toolboxstack-<name>:v<semver> - tsysdevstack-toolboxstack-<name>:v<semver>
When run without --dry-run it pushes all three tags.
EOU EOU
} }
@@ -71,32 +74,38 @@ elif [[ -z "${REPO_ROOT}" ]]; then
echo "Warning: unable to resolve git repository root; skipping clean tree check." >&2 echo "Warning: unable to resolve git repository root; skipping clean tree check." >&2
fi fi
# Get the toolbox name from the directory name # Get the toolbox name from the directory name (or you can pass it as an argument)
TOOLBOX_NAME="${TOOLBOX_NAME_OVERRIDE:-$(basename "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")}" TOOLBOX_NAME="${TOOLBOX_NAME_OVERRIDE:-$(basename "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")}"
IMAGE_NAME="tsysdevstack-toolboxstack-${TOOLBOX_NAME#toolbox-}" IMAGE_NAME="tsysdevstack-toolboxstack-${TOOLBOX_NAME#toolbox-}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
USER_ID="${USER_ID_OVERRIDE:-$(id -u)}"
GROUP_ID="${GROUP_ID_OVERRIDE:-$(id -g)}"
USERNAME="${USERNAME_OVERRIDE:-toolbox}"
TEA_VERSION="${TEA_VERSION_OVERRIDE:-0.11.1}"
BUILDER_NAME="${BUILDER_NAME:-tsysdevstack-toolboxstack-builder}"
CACHE_DIR="${SCRIPT_DIR}/.build-cache"
TAG="${TAG_OVERRIDE:-dev}"
RELEASE_TAG="${RELEASE_TAG_OVERRIDE:-release-current}"
VERSION_TAG="${VERSION_TAG_OVERRIDE:-}"
if [[ -n "$VERSION_TAG" ]]; then
VERSION_TAG="$SEMVER"
fi
PUSH="${PUSH_OVERRIDE:-false}"
echo "Preparing release for ${SEMVER}" echo "Preparing release for ${SEMVER}"
echo " dry-run: ${DRY_RUN}" echo " dry-run: ${DRY_RUN}"
echo " allow-dirty: ${ALLOW_DIRTY}" echo " allow-dirty: ${ALLOW_DIRTY}"
# First, ensure we have the dev tag built
if [[ "${DRY_RUN}" == "true" ]]; then if [[ "${DRY_RUN}" == "true" ]]; then
echo "[dry-run] Would build dev tag" echo "[dry-run] Would build ${IMAGE_NAME}:${TAG}"
TAG_OVERRIDE="${TAG}" PUSH_OVERRIDE=false "${SCRIPT_DIR}/build.sh"
echo "[dry-run] Skipped pushing tags."
else else
echo "Building dev tag..." echo "Building ${IMAGE_NAME}:${TAG}"
"${SCRIPT_DIR}/build.sh" TAG_OVERRIDE="${TAG}" PUSH_OVERRIDE=true RELEASE_TAG_OVERRIDE="${RELEASE_TAG}" VERSION_TAG_OVERRIDE="${SEMVER}" "${SCRIPT_DIR}/build.sh"
fi echo "Release ${SEMVER} pushed as:"
echo " - ${IMAGE_NAME}:dev"
# Tag the dev image as release-current and with the version
if [[ "${DRY_RUN}" == "true" ]]; then
echo "[dry-run] Would tag ${IMAGE_NAME}:dev as:"
echo " - ${IMAGE_NAME}:release-current"
echo " - ${IMAGE_NAME}:${SEMVER}"
else
echo "Tagging ${IMAGE_NAME}:dev as release-current and ${SEMVER}..."
docker tag "${IMAGE_NAME}:dev" "${IMAGE_NAME}:release-current"
docker tag "${IMAGE_NAME}:dev" "${IMAGE_NAME}:${SEMVER}"
echo "Release ${SEMVER} tagged as:"
echo " - ${IMAGE_NAME}:release-current" echo " - ${IMAGE_NAME}:release-current"
echo " - ${IMAGE_NAME}:${SEMVER}" echo " - ${IMAGE_NAME}:${SEMVER}"
fi fi

View File

@@ -28,13 +28,14 @@ fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.yml" COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.yml"
# Sanitize user input
export LOCAL_UID="${USER_ID_OVERRIDE:-$(id -u)}" export LOCAL_UID="${USER_ID_OVERRIDE:-$(id -u)}"
sanitized_input "$LOCAL_UID" sanitized_input "$LOCAL_UID"
export LOCAL_GID="${GROUP_ID_OVERRIDE:-$(id -g)}" export LOCAL_GID="${GROUP_ID_OVERRIDE:-$(id -g)}"
sanitized_input "$LOCAL_GID" sanitized_input "$LOCAL_GID"
export LOCAL_USERNAME="${USERNAME_OVERRIDE:-toolbox}" export LOCAL_USERNAME="${USERNAME_OVERRIDE:-toolbox}"
sanitized_input "$LOCAL_USERNAME" sanitized_input "$LOCAL_USERNAME"
export TOOLBOX_IMAGE="${TOOLBOX_IMAGE_OVERRIDE:-tsysdevstack-toolboxstack-{{toolbox_name}}}"
sanitized_input "$TOOLBOX_IMAGE"
if [[ ! -f "${COMPOSE_FILE}" ]]; then if [[ ! -f "${COMPOSE_FILE}" ]]; then
echo "Error: docker-compose.yml not found at ${COMPOSE_FILE}" >&2 echo "Error: docker-compose.yml not found at ${COMPOSE_FILE}" >&2
@@ -51,6 +52,7 @@ if [[ "${ACTION}" == "up" ]]; then
mkdir -p "${HOME}/.config" "${HOME}/.local/share" mkdir -p "${HOME}/.config" "${HOME}/.local/share"
mkdir -p "${HOME}/.cache/openai" "${HOME}/.cache/gemini" "${HOME}/.cache/qwen" "${HOME}/.cache/code" "${HOME}/.cache/opencode" mkdir -p "${HOME}/.cache/openai" "${HOME}/.cache/gemini" "${HOME}/.cache/qwen" "${HOME}/.cache/code" "${HOME}/.cache/opencode"
mkdir -p "${HOME}/.config/openai" "${HOME}/.config/gemini" "${HOME}/.config/qwen" "${HOME}/.config/code" "${HOME}/.config/opencode" mkdir -p "${HOME}/.config/openai" "${HOME}/.config/gemini" "${HOME}/.config/qwen" "${HOME}/.config/code" "${HOME}/.config/opencode"
mkdir -p "${HOME}/.config/codex" "${HOME}/.cache/codex"
# Set proper permissions for created directories # Set proper permissions for created directories
chmod 700 "${HOME}/.config" "${HOME}/.local/share" "${HOME}/.cache" 2>/dev/null || true chmod 700 "${HOME}/.config" "${HOME}/.local/share" "${HOME}/.cache" 2>/dev/null || true
@@ -59,7 +61,7 @@ fi
case "${ACTION}" in case "${ACTION}" in
up) up)
docker compose -f "${COMPOSE_FILE}" up --build --detach "$@" docker compose -f "${COMPOSE_FILE}" up --build --detach "$@"
echo "Container started. Use 'docker exec -it $(basename "$SCRIPT_DIR" | sed 's/toolbox-//') zsh' to access the shell." echo "Container started. Use 'docker exec -it tsysdevstack-toolboxstack-{{toolbox_name}} zsh' to access the shell."
;; ;;
down) down)
docker compose -f "${COMPOSE_FILE}" down "$@" docker compose -f "${COMPOSE_FILE}" down "$@"