From 3dd420a500ce7613b331e6e0f8da38167a819458 Mon Sep 17 00:00:00 2001 From: ReachableCEO Date: Thu, 30 Oct 2025 13:08:57 -0500 Subject: [PATCH] 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. --- .../output/toolbox-template/Dockerfile | 20 +++ ToolboxStack/output/toolbox-template/PROMPT | 2 +- ToolboxStack/output/toolbox-template/SEED | 2 +- .../output/toolbox-template/aqua.yaml | 16 ++- ToolboxStack/output/toolbox-template/build.sh | 129 ++++++++++++++++-- .../toolbox-template/docker-compose.yml | 13 +- .../output/toolbox-template/release.sh | 45 +++--- ToolboxStack/output/toolbox-template/run.sh | 8 +- 8 files changed, 190 insertions(+), 45 deletions(-) diff --git a/ToolboxStack/output/toolbox-template/Dockerfile b/ToolboxStack/output/toolbox-template/Dockerfile index ee5a0e7..90f9a2f 100644 --- a/ToolboxStack/output/toolbox-template/Dockerfile +++ b/ToolboxStack/output/toolbox-template/Dockerfile @@ -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 diff --git a/ToolboxStack/output/toolbox-template/PROMPT b/ToolboxStack/output/toolbox-template/PROMPT index 9cce101..c919e69 100644 --- a/ToolboxStack/output/toolbox-template/PROMPT +++ b/ToolboxStack/output/toolbox-template/PROMPT @@ -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. diff --git a/ToolboxStack/output/toolbox-template/SEED b/ToolboxStack/output/toolbox-template/SEED index d661c3c..009667c 100644 --- a/ToolboxStack/output/toolbox-template/SEED +++ b/ToolboxStack/output/toolbox-template/SEED @@ -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 diff --git a/ToolboxStack/output/toolbox-template/aqua.yaml b/ToolboxStack/output/toolbox-template/aqua.yaml index f464bc3..c9ef85e 100644 --- a/ToolboxStack/output/toolbox-template/aqua.yaml +++ b/ToolboxStack/output/toolbox-template/aqua.yaml @@ -5,4 +5,18 @@ registries: packages: # Add additional packages specific to your toolbox here # Example: - # - name: cli/cli@v2.82.1 \ No newline at end of file + # - 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 \ No newline at end of file diff --git a/ToolboxStack/output/toolbox-template/build.sh b/ToolboxStack/output/toolbox-template/build.sh index a735e7c..f430aae 100755 --- a/ToolboxStack/output/toolbox-template/build.sh +++ b/ToolboxStack/output/toolbox-template/build.sh @@ -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 \ No newline at end of file +fi + +echo "Build process completed successfully with all verifications." \ No newline at end of file diff --git a/ToolboxStack/output/toolbox-template/docker-compose.yml b/ToolboxStack/output/toolbox-template/docker-compose.yml index c963e90..bfb5501 100644 --- a/ToolboxStack/output/toolbox-template/docker-compose.yml +++ b/ToolboxStack/output/toolbox-template/docker-compose.yml @@ -32,15 +32,4 @@ services: - ${HOME}/.cache/opencode:/home/toolbox/.cache/opencode:rw # 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 + - ${HOME}/.cache/codex:/home/toolbox/.cache/codex:rw \ No newline at end of file diff --git a/ToolboxStack/output/toolbox-template/release.sh b/ToolboxStack/output/toolbox-template/release.sh index 1aa8801..ce9f4ea 100755 --- a/ToolboxStack/output/toolbox-template/release.sh +++ b/ToolboxStack/output/toolbox-template/release.sh @@ -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-:dev - tsysdevstack-toolboxstack-:release-current - tsysdevstack-toolboxstack-:v + +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 \ No newline at end of file diff --git a/ToolboxStack/output/toolbox-template/run.sh b/ToolboxStack/output/toolbox-template/run.sh index c7d5a54..8433cdd 100755 --- a/ToolboxStack/output/toolbox-template/run.sh +++ b/ToolboxStack/output/toolbox-template/run.sh @@ -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 "$@" @@ -69,4 +71,4 @@ case "${ACTION}" in echo "Usage: $0 [up|down] [additional docker compose args]" >&2 exit 1 ;; -esac +esac \ No newline at end of file