#!/usr/bin/env bash # ============================================================================== # SMARTGIFT AI - MULTI-FRAMEWORK UNIT TESTING TOOLKIT INSTALLER # Hỗ trợ: Laravel & CakePHP # Cài đặt bộ công cụ Unit Test vào bất kỳ source code nào mà KHÔNG CẦN clone repo về máy. # # Cách dùng 1 (Khuyên dùng - Đứng trong source code và chạy): # curl -fsSL https://raw.githubusercontent.com//agent-unit-testing/main/install.sh | bash # # Cách dùng 2 (Cài Toàn Cục vào máy): # curl -fsSL https://raw.githubusercontent.com//agent-unit-testing/main/install.sh | bash -s -- --global # ============================================================================== set -e GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' RED='\033[0;31m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # Remote Repository URL công khai qua HTTPS DEFAULT_REPO="https://git.smartgift.io.vn/Smartgift-AI/agent-unit-testing.git" REPO_URL="${SG_TEST_REPO:-${LARAVEL_TEST_REPO:-$DEFAULT_REPO}}" TARGET_DIR="" GLOBAL_MODE=false FORCE_MODE=false for arg in "$@"; do case "$arg" in --global|-g) GLOBAL_MODE=true ;; --force|-f) FORCE_MODE=true ;; --help|-h) echo -e "${BLUE}===================================================================${NC}" echo -e "${CYAN}${BOLD} SMARTGIFT AI: PHP UNIT TESTING TOOLKIT (LARAVEL & CAKEPHP)${NC}" echo -e "${BLUE}===================================================================${NC}" echo -e "Cách dùng:" echo -e " 1. Cài đặt trực tiếp vào source code hiện tại:" echo -e " ${YELLOW}curl -fsSL /install.sh | bash${NC}" echo -e " 2. Cài đặt vào một source code ở đường dẫn cụ thể:" echo -e " ${YELLOW}curl -fsSL /install.sh | bash -s -- /duong-dan/source-code${NC}" echo -e " 3. Cài đặt Toàn Cục vào máy tính (dùng cho mọi folder):" echo -e " ${YELLOW}curl -fsSL /install.sh | bash -s -- --global${NC}" echo -e " 4. Cài đặt vào thư mục trống:" echo -e " ${YELLOW}curl -fsSL /install.sh | bash -s -- . --force${NC}" exit 0 ;; -*) # Bỏ qua các flag khác ;; *) if [ -z "$TARGET_DIR" ]; then TARGET_DIR="$arg" fi ;; esac done TARGET_DIR="${TARGET_DIR:-.}" echo -e "${BLUE}===================================================================${NC}" echo -e "${CYAN}${BOLD} 🚀 SMARTGIFT AI - UNIT TESTING TOOLKIT (LARAVEL & CAKEPHP)${NC}" echo -e "${BLUE}===================================================================${NC}" # 1. Tìm hoặc Tải nguồn tài nguyên (Local nếu có, hoặc tải tạm qua Git) CLEANUP_TEMP=false PACKAGE_ROOT="" SOURCE="${BASH_SOURCE[0]:-$0}" SCRIPT_DIR="" if [ -f "$SOURCE" ]; then SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" 2>/dev/null && pwd || echo "")" fi if [ -n "$SCRIPT_DIR" ] && [ -d "$SCRIPT_DIR/.claude/skills" ]; then PACKAGE_ROOT="$SCRIPT_DIR" echo -e "${GREEN}✓ Sử dụng tài nguyên có sẵn tại local.${NC}" else echo -e "${YELLOW}📥 Đang tải bộ công cụ từ remote repository...${NC}" TMP_DIR="$(mktemp -d 2>/dev/null || mktemp -d -t 'sg_unit_testing')" if ! git clone --depth 1 "$REPO_URL" "$TMP_DIR" --quiet 2>/dev/null; then echo -e "${RED}❌ Không thể clone từ repository: $REPO_URL${NC}" rm -rf "$TMP_DIR" exit 1 fi PACKAGE_ROOT="$TMP_DIR" CLEANUP_TEMP=true echo -e "${GREEN}✓ Tải thành công!${NC}" fi cleanup() { if [ "$CLEANUP_TEMP" = true ] && [ -d "$PACKAGE_ROOT" ]; then rm -rf "$PACKAGE_ROOT" fi } trap cleanup EXIT # ============================================================================== # 2. XỬ LÝ CÀI ĐẶT TOÀN CỤC (GLOBAL MODE: CHO TẤT CẢ FOLDER TRÊN MÁY) # ============================================================================== if [ "$GLOBAL_MODE" = true ]; then echo -e "${YELLOW}⚙️ Đang cài đặt chế độ Toàn Cục vào $HOME/.claude/skills/...${NC}" mkdir -p "$HOME/.claude/skills" cp -R "$PACKAGE_ROOT/.claude/skills/"* "$HOME/.claude/skills/" mkdir -p "$HOME/.claude/testing-docs/templates" cp -R "$PACKAGE_ROOT/docs/"* "$HOME/.claude/testing-docs/" cp -R "$PACKAGE_ROOT/rules/"* "$HOME/.claude/testing-docs/" cp -R "$PACKAGE_ROOT/templates/"* "$HOME/.claude/testing-docs/templates/" mkdir -p "$HOME/.local/bin" # Lệnh khởi tạo test runner cat << 'EOF' > "$HOME/.local/bin/sg-test-init" #!/usr/bin/env bash TARGET_DIR="${1:-.}" SOURCE_DIR="$HOME/.claude/testing-docs" if [ ! -d "$TARGET_DIR" ]; then echo "Lỗi: Thư mục '$TARGET_DIR' không tồn tại!" exit 1 fi echo "🚀 Đang khởi tạo test runner cho dự án tại: $TARGET_DIR" if [ -f "$SOURCE_DIR/templates/docker/test-runner.sh" ]; then cp "$SOURCE_DIR/templates/docker/test-runner.sh" "$TARGET_DIR/test-runner.sh" chmod +x "$TARGET_DIR/test-runner.sh" echo " ✓ Đã tạo $TARGET_DIR/test-runner.sh" fi if [ -f "$SOURCE_DIR/templates/setup-dependencies.sh" ]; then cp "$SOURCE_DIR/templates/setup-dependencies.sh" "$TARGET_DIR/setup-test-deps.sh" chmod +x "$TARGET_DIR/setup-test-deps.sh" echo " ✓ Đã tạo $TARGET_DIR/setup-test-deps.sh" fi echo "🎉 Hoàn tất! Mở Claude Code và gõ lệnh test tương ứng:" echo " - CakePHP: /cakephp-unit-test --all --run" echo " - Laravel: /laravel-unit-test --all --run" EOF chmod +x "$HOME/.local/bin/sg-test-init" cp "$HOME/.local/bin/sg-test-init" "$HOME/.local/bin/laravel-test-init" chmod +x "$HOME/.local/bin/laravel-test-init" cp "$HOME/.local/bin/sg-test-init" "$HOME/.local/bin/cakephp-test-init" chmod +x "$HOME/.local/bin/cakephp-test-init" # Lệnh cập nhật bộ công cụ (Update) cat << 'EOF' > "$HOME/.local/bin/sg-test-update" #!/usr/bin/env bash REPO_URL="${SG_TEST_REPO:-https://git.smartgift.io.vn/Smartgift-AI/agent-unit-testing.git}" echo "🔄 Đang cập nhật bộ công cụ Unit Test từ Git repository..." TMP_DIR="$(mktemp -d 2>/dev/null || mktemp -d -t 'sg_update')" if git clone --depth 1 "$REPO_URL" "$TMP_DIR" --quiet 2>/dev/null; then bash "$TMP_DIR/install.sh" --global rm -rf "$TMP_DIR" echo "✅ Cập nhật toàn bộ công cụ thành công!" else echo "❌ Lỗi: Không thể kết nối hoặc clone repository $REPO_URL" rm -rf "$TMP_DIR" exit 1 fi EOF chmod +x "$HOME/.local/bin/sg-test-update" cp "$HOME/.local/bin/sg-test-update" "$HOME/.local/bin/laravel-test-update" chmod +x "$HOME/.local/bin/laravel-test-update" cp "$HOME/.local/bin/sg-test-update" "$HOME/.local/bin/cakephp-test-update" chmod +x "$HOME/.local/bin/cakephp-test-update" echo -e "${GREEN}${BOLD}✓ CÀI ĐẶT TOÀN CỤC HOÀN TẤT THÀNH CÔNG!${NC}" echo -e "Kể từ bây giờ, ở bất kỳ source code nào, bạn mở Claude Code là có thể gõ ngay:" echo -e " • CakePHP: ${CYAN}/cakephp-test-setup --install${NC} | ${CYAN}/cakephp-unit-test --all --run${NC}" echo -e " • Laravel: ${CYAN}/laravel-test-setup --install${NC} | ${CYAN}/laravel-unit-test --all --run${NC}" echo -e "Và các lệnh terminal: ${CYAN}cakephp-test-init${NC}, ${CYAN}laravel-test-init${NC}, ${CYAN}cakephp-test-update${NC}, ${CYAN}laravel-test-update${NC}" exit 0 fi # ============================================================================== # 3. XỬ LÝ CÀI ĐẶT TRỰC TIẾP VÀO SOURCE CODE HIỆN TẠI (PROJECT MODE - KHUYÊN DÙNG) # ============================================================================== TARGET_DIR="$(cd "$TARGET_DIR" 2>/dev/null && pwd || echo "$TARGET_DIR")" if [ ! -d "$TARGET_DIR" ]; then echo -e "${RED}Lỗi: Thư mục '$TARGET_DIR' không tồn tại!${NC}" exit 1 fi # Phát hiện framework FRAMEWORK="generic" if [ -f "$TARGET_DIR/bin/cake" ] || ( [ -f "$TARGET_DIR/composer.json" ] && grep -q '"cakephp/cakephp"' "$TARGET_DIR/composer.json" ); then FRAMEWORK="cakephp" echo -e "${GREEN}✓ Phát hiện dự án: CakePHP${NC}" elif [ -f "$TARGET_DIR/artisan" ] || ( [ -f "$TARGET_DIR/composer.json" ] && grep -q '"laravel/framework"' "$TARGET_DIR/composer.json" ); then FRAMEWORK="laravel" echo -e "${GREEN}✓ Phát hiện dự án: Laravel${NC}" elif [ ! -f "$TARGET_DIR/composer.json" ]; then FILE_COUNT=$(ls -A "$TARGET_DIR" 2>/dev/null | wc -l | tr -d ' ') if [ "$FILE_COUNT" -le 2 ]; then echo -e "${YELLOW}ℹ Thư mục mới/trống: Tự động khởi tạo bộ công cụ Unit Test...${NC}" elif [ "$FORCE_MODE" = false ]; then echo -e "${YELLOW}⚠️ Cảnh báo: Thư mục '$TARGET_DIR' đang chứa dữ liệu khác và chưa có file composer.json.${NC}" echo -e "Nếu bạn vẫn muốn cài đặt vào thư mục này, hãy chạy:" echo -e " ${CYAN}bash <(curl -fsSL /install.sh) --force${NC}" exit 1 fi fi echo -e "📦 Đang cài đặt công cụ vào source code: ${YELLOW}$TARGET_DIR${NC}" # A. Copy Claude Skills vào .claude/skills/ của source code mkdir -p "$TARGET_DIR/.claude/skills" cp -R "$PACKAGE_ROOT/.claude/skills/"* "$TARGET_DIR/.claude/skills/" echo -e " ${GREEN}✓${NC} Đã cài bộ Claude Code Skills (Laravel & CakePHP)" # B. Copy cẩm nang tài liệu vào .claude/testing-docs/ mkdir -p "$TARGET_DIR/.claude/testing-docs" cp -R "$PACKAGE_ROOT/docs/"* "$TARGET_DIR/.claude/testing-docs/" cp -R "$PACKAGE_ROOT/rules/"* "$TARGET_DIR/.claude/testing-docs/" echo -e " ${GREEN}✓${NC} Đã đồng bộ cẩm nang & rules kiểm thử (.claude/testing-docs/)" # C. Copy Test Runners vào root của source code cp "$PACKAGE_ROOT/templates/docker/test-runner.sh" "$TARGET_DIR/test-runner.sh" chmod +x "$TARGET_DIR/test-runner.sh" echo -e " ${GREEN}✓${NC} Đã tạo smart runner: ./test-runner.sh" cp "$PACKAGE_ROOT/templates/setup-dependencies.sh" "$TARGET_DIR/setup-test-deps.sh" chmod +x "$TARGET_DIR/setup-test-deps.sh" echo -e " ${GREEN}✓${NC} Đã tạo dependency installer: ./setup-test-deps.sh" # D. Cấu hình môi trường an toàn SQLite In-Memory if [ "$FRAMEWORK" = "laravel" ]; then PHPUNIT_XML="$TARGET_DIR/phpunit.xml" PHPUNIT_DIST="$TARGET_DIR/phpunit.xml.dist" TARGET_XML="" if [ -f "$PHPUNIT_XML" ]; then TARGET_XML="$PHPUNIT_XML" elif [ -f "$PHPUNIT_DIST" ]; then TARGET_XML="$PHPUNIT_DIST" fi if [ -n "$TARGET_XML" ]; then if ! grep -q 'name="DB_CONNECTION"' "$TARGET_XML" || ! grep -q 'value="sqlite"' "$TARGET_XML"; then if grep -q '' "$TARGET_XML"; then sed -i.bak '//a\ \ \ \ \ \ ' "$TARGET_XML" 2>/dev/null || true rm -f "$TARGET_XML.bak" 2>/dev/null || true echo -e " ${GREEN}✓${NC} Đã cấu hình SQLite in-memory (:memory:) an toàn vào phpunit.xml" fi else echo -e " ${GREEN}✓${NC} Cấu hình SQLite in-memory trong phpunit.xml đã chuẩn xác" fi fi elif [ "$FRAMEWORK" = "cakephp" ]; then # Nếu chưa có phpunit.xml, copy file mẫu if [ ! -f "$TARGET_DIR/phpunit.xml" ] && [ ! -f "$TARGET_DIR/phpunit.xml.dist" ]; then cp "$PACKAGE_ROOT/templates/cakephp/configs/phpunit.xml.dist.template" "$TARGET_DIR/phpunit.xml.dist" echo -e " ${GREEN}✓${NC} Đã tạo phpunit.xml.dist chuẩn cho CakePHP" else echo -e " ${GREEN}✓${NC} Đã phát hiện cấu hình PHPUnit CakePHP" fi if [ -f "$TARGET_DIR/config/app_local.php" ]; then if grep -q "'test'" "$TARGET_DIR/config/app_local.php"; then echo -e " ${GREEN}✓${NC} Cấu hình Datasources.test trong config/app_local.php đã sẵn sàng" else echo -e " ${YELLOW}ℹ${NC} Lưu ý: Hãy đảm bảo Datasources.test trong config/app_local.php sử dụng SQLite :memory:" fi fi fi # E. Tạo GitHub Actions CI workflow nếu có .git if [ -d "$TARGET_DIR/.git" ]; then mkdir -p "$TARGET_DIR/.github/workflows" if [ ! -f "$TARGET_DIR/.github/workflows/unit-tests.yml" ]; then if [ "$FRAMEWORK" = "cakephp" ]; then cp "$PACKAGE_ROOT/templates/ci/github-actions-cakephp.yml.template" "$TARGET_DIR/.github/workflows/unit-tests.yml" else cp "$PACKAGE_ROOT/templates/ci/github-actions.yml.template" "$TARGET_DIR/.github/workflows/unit-tests.yml" fi echo -e " ${GREEN}✓${NC} Đã tạo CI/CD Quality Gate (.github/workflows/unit-tests.yml)" fi fi # F. Tạo file cấu hình SonarQube chuẩn (sonar-project.properties) nếu chưa có if [ ! -f "$TARGET_DIR/sonar-project.properties" ]; then if [ "$FRAMEWORK" = "cakephp" ]; then cp "$PACKAGE_ROOT/templates/cakephp/configs/sonar-project.properties.template" "$TARGET_DIR/sonar-project.properties" else cp "$PACKAGE_ROOT/templates/configs/sonar-project.properties.template" "$TARGET_DIR/sonar-project.properties" fi echo -e " ${GREEN}✓${NC} Đã tạo cấu hình SonarQube (sonar-project.properties) nhắm mục tiêu Coverage > 90%" fi echo -e "\n${GREEN}${BOLD}🎉 CÀI ĐẶT HOÀN TẤT THÀNH CÔNG VÀO SOURCE CODE!${NC}" echo -e "\n${YELLOW}${BOLD}BÂY GIỜ BẠN CÓ THỂ VIẾT UNIT TEST NGAY:${NC}" echo -e "1. Mở Claude Code tại source code này:" echo -e " ${CYAN}claude${NC}" if [ "$FRAMEWORK" = "cakephp" ]; then echo -e "2. Gõ lệnh dành riêng cho CakePHP:" echo -e " - Setup môi trường: ${CYAN}/cakephp-test-setup --install${NC}" echo -e " - Viết test & chạy test: ${CYAN}/cakephp-unit-test --all --run${NC}" echo -e " - Hoặc theo tầng: ${CYAN}/cakephp-unit-test src/Model/Table/${NC}" elif [ "$FRAMEWORK" = "laravel" ]; then echo -e "2. Gõ lệnh dành riêng cho Laravel:" echo -e " - Setup môi trường: ${CYAN}/laravel-test-setup --install${NC}" echo -e " - Viết test & chạy test: ${CYAN}/laravel-unit-test --all --run${NC}" echo -e " - Hoặc theo tầng: ${CYAN}/laravel-unit-test app/Services/${NC}" else echo -e "2. Gõ lệnh tương ứng với framework của bạn:" echo -e " - CakePHP: ${CYAN}/cakephp-unit-test --all --run${NC}" echo -e " - Laravel: ${CYAN}/laravel-unit-test --all --run${NC}" fi echo -e "3. Chạy test trực tiếp từ terminal bất kỳ lúc nào:" echo -e " ${CYAN}./test-runner.sh${NC}"