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:
		| @@ -16,6 +16,26 @@ RUN if getent passwd "${USER_ID}" >/dev/null; then \ | ||||
|     fi \ | ||||
|     && 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 | ||||
| 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 | ||||
|  | ||||
|   | ||||
| @@ -17,7 +17,7 @@ Current state: | ||||
| - See ../PROMPT for shared toolbox contribution expectations (documentation sync, build cadence, commit/push discipline, Conventional Commits, atomic history). | ||||
|  | ||||
| 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. | ||||
| 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. | ||||
|   | ||||
| @@ -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 | ||||
| - **docker-compose.yml**: Configure service with inherited + custom settings | ||||
| - **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 | ||||
| - **SEED**: Define toolbox-specific objectives (this file) | ||||
| - **PROMPT**: LLM onboarding prompt for future contributors | ||||
|   | ||||
| @@ -6,3 +6,17 @@ packages: | ||||
|   # Add additional packages specific to your toolbox here | ||||
|   # Example: | ||||
|   # - 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 | ||||
| @@ -29,7 +29,6 @@ fi | ||||
| TOOLBOX_NAME="${TOOLBOX_NAME_OVERRIDE:-$(basename "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")}" | ||||
| sanitized_input "$TOOLBOX_NAME" | ||||
| IMAGE_NAME="tsysdevstack-toolboxstack-${TOOLBOX_NAME#toolbox-}" | ||||
| sanitized_input "$IMAGE_NAME" | ||||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||||
|  | ||||
| # Sanitize user input | ||||
| @@ -44,21 +43,46 @@ sanitized_input "$TEA_VERSION" | ||||
| BUILDER_NAME="${BUILDER_NAME:-tsysdevstack-toolboxstack-builder}" | ||||
| sanitized_input "$BUILDER_NAME" | ||||
| 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 "Primary tag: ${TAG}" | ||||
|  | ||||
| # Ensure builder exists | ||||
| if ! docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then | ||||
|     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 | ||||
|     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 | ||||
|  | ||||
| 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..." | ||||
| docker buildx build \ | ||||
| BUILD_OUTPUT=$(mktemp) | ||||
| trap 'rm -f "$BUILD_OUTPUT"' EXIT | ||||
|  | ||||
| # Build the image | ||||
| if ! docker buildx build \ | ||||
|     --builder "${BUILDER_NAME}" \ | ||||
|     --load \ | ||||
|     --progress=plain \ | ||||
| @@ -68,15 +92,102 @@ docker buildx build \ | ||||
|     --build-arg TEA_VERSION="${TEA_VERSION}" \ | ||||
|     --cache-from "type=local,src=${CACHE_DIR}" \ | ||||
|     --cache-to "type=local,dest=${CACHE_DIR},mode=max" \ | ||||
|     --tag "${IMAGE_NAME}" \ | ||||
|     "${SCRIPT_DIR}" | ||||
|     --tag "${IMAGE_NAME}:${TAG}" \ | ||||
|     "${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." | ||||
|  | ||||
| # 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 | ||||
| if command -v trivy &> /dev/null; then | ||||
|     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 | ||||
|     echo "Trivy not found. Install Trivy to perform security scanning." | ||||
| fi | ||||
|  | ||||
| echo "Build process completed successfully with all verifications." | ||||
| @@ -33,14 +33,3 @@ services: | ||||
|       # Additional AI tool directories | ||||
|       - ${HOME}/.config/codex:/home/toolbox/.config/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 | ||||
|   | ||||
| @@ -10,9 +10,12 @@ Examples: | ||||
|   ./release.sh 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>:v<semver> | ||||
|  | ||||
| When run without --dry-run it pushes all three tags. | ||||
| EOU | ||||
| } | ||||
|  | ||||
| @@ -71,32 +74,38 @@ elif [[ -z "${REPO_ROOT}" ]]; then | ||||
|     echo "Warning: unable to resolve git repository root; skipping clean tree check." >&2 | ||||
| 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)")}" | ||||
| 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 "  dry-run: ${DRY_RUN}" | ||||
| echo "  allow-dirty: ${ALLOW_DIRTY}" | ||||
|  | ||||
| # First, ensure we have the dev tag built | ||||
| 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 | ||||
|     echo "Building dev tag..." | ||||
|     "${SCRIPT_DIR}/build.sh" | ||||
| fi | ||||
|  | ||||
| # 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 "Building ${IMAGE_NAME}:${TAG}" | ||||
|     TAG_OVERRIDE="${TAG}" PUSH_OVERRIDE=true RELEASE_TAG_OVERRIDE="${RELEASE_TAG}" VERSION_TAG_OVERRIDE="${SEMVER}" "${SCRIPT_DIR}/build.sh" | ||||
|     echo "Release ${SEMVER} pushed as:" | ||||
|     echo "  - ${IMAGE_NAME}:dev" | ||||
|     echo "  - ${IMAGE_NAME}:release-current" | ||||
|     echo "  - ${IMAGE_NAME}:${SEMVER}" | ||||
| fi | ||||
| @@ -28,13 +28,14 @@ fi | ||||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||||
| COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.yml" | ||||
|  | ||||
| # Sanitize user input | ||||
| export LOCAL_UID="${USER_ID_OVERRIDE:-$(id -u)}" | ||||
| sanitized_input "$LOCAL_UID" | ||||
| export LOCAL_GID="${GROUP_ID_OVERRIDE:-$(id -g)}" | ||||
| sanitized_input "$LOCAL_GID" | ||||
| export LOCAL_USERNAME="${USERNAME_OVERRIDE:-toolbox}" | ||||
| sanitized_input "$LOCAL_USERNAME" | ||||
| export TOOLBOX_IMAGE="${TOOLBOX_IMAGE_OVERRIDE:-tsysdevstack-toolboxstack-{{toolbox_name}}}" | ||||
| sanitized_input "$TOOLBOX_IMAGE" | ||||
|  | ||||
| if [[ ! -f "${COMPOSE_FILE}" ]]; then | ||||
|     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}/.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/codex" "${HOME}/.cache/codex" | ||||
|      | ||||
|     # Set proper permissions for created directories | ||||
|     chmod 700 "${HOME}/.config" "${HOME}/.local/share" "${HOME}/.cache" 2>/dev/null || true | ||||
| @@ -59,7 +61,7 @@ fi | ||||
| case "${ACTION}" in | ||||
|     up) | ||||
|         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) | ||||
|         docker compose -f "${COMPOSE_FILE}" down "$@" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user