- Renamed DocStack to dockstack - Transformed toolbox-template into toolbox-qadocker with new functionality - Removed NewToolbox.sh script - Updated PROMPT and configuration files across all toolboxes - Consolidated audit and testing scripts - Updated QWEN.md to reflect new filesystem structure as authoritative source - Merged PROMPT content into QWEN.md as requested Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> The filesystem structure has been intentionally restructured and is now the authoritative source of truth for the project organization.
195 lines
6.4 KiB
Bash
Executable File
195 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
# Security: Validate input parameters to prevent command injection
|
|
sanitized_input() {
|
|
local input="$1"
|
|
# Check for potentially dangerous characters/commands
|
|
case "$input" in
|
|
*[\;\|\&\`\$]*)
|
|
echo "Error: Invalid input detected: $input" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Validate dependencies
|
|
if ! command -v docker &> /dev/null; then
|
|
echo "Error: docker is required but not installed." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker buildx version &> /dev/null; then
|
|
echo "Error: docker buildx is required but not available." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# 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)")}"
|
|
sanitized_input "$TOOLBOX_NAME"
|
|
IMAGE_NAME="tsysdevstack-toolboxstack-$(echo "${TOOLBOX_NAME#toolbox-}" | tr '[:upper:]' '[:lower:]')"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Sanitize user input
|
|
USER_ID="${USER_ID_OVERRIDE:-$(id -u)}"
|
|
sanitized_input "$USER_ID"
|
|
GROUP_ID="${GROUP_ID_OVERRIDE:-$(id -g)}"
|
|
sanitized_input "$GROUP_ID"
|
|
USERNAME="${USERNAME_OVERRIDE:-toolbox}"
|
|
sanitized_input "$USERNAME"
|
|
TEA_VERSION="${TEA_VERSION_OVERRIDE:-0.11.1}"
|
|
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}"
|
|
# Use the default docker driver instead of docker-container to access local images
|
|
if ! docker buildx create --driver docker --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}"
|
|
if ! docker buildx use "${BUILDER_NAME}" >/dev/null; then
|
|
echo "Error: Failed to use Docker buildx builder." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# 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..."
|
|
BUILD_OUTPUT=$(mktemp)
|
|
trap 'rm -f "$BUILD_OUTPUT"' EXIT
|
|
|
|
# Build the image
|
|
if ! docker buildx build \
|
|
--builder "${BUILDER_NAME}" \
|
|
--load \
|
|
--progress=plain \
|
|
--build-arg USER_ID="${USER_ID}" \
|
|
--build-arg GROUP_ID="${GROUP_ID}" \
|
|
--build-arg USERNAME="${USERNAME}" \
|
|
--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}:${TAG}" \
|
|
--allow network.host \
|
|
"${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}:${TAG}"
|
|
else
|
|
echo "Trivy not found. Install Trivy to perform security scanning."
|
|
fi
|
|
|
|
echo "Build process completed successfully with all verifications." |