#!/usr/bin/env bash # minimax-docx Environment Setup & Initialization Script # Supports: macOS (Homebrew), Linux (apt/dnf/pacman), WSL # License: MIT set -euo pipefail # Force English output for dotnet CLI export DOTNET_CLI_UI_LANGUAGE=en SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" DOTNET_DIR="$SCRIPT_DIR/dotnet" LOG_FILE="$PROJECT_DIR/.setup.log" # --- Colors --- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } fail() { echo -e "${RED}[FAIL]${NC} $*"; } info() { echo -e "${BLUE}[INFO]${NC} $*"; } step() { echo -e "\n${BLUE}=== $* ===${NC}"; } # --- Detect OS & Package Manager --- detect_platform() { OS="unknown" PKG_MGR="unknown" ARCH="$(uname -m)" case "$(uname -s)" in Darwin) OS="macos" if command -v brew &>/dev/null; then PKG_MGR="brew" else PKG_MGR="none" fi ;; Linux) OS="linux" if [ -f /etc/os-release ]; then . /etc/os-release case "$ID" in ubuntu|debian|linuxmint|pop) PKG_MGR="apt" ;; fedora|rhel|centos|rocky|alma) PKG_MGR="dnf" ;; arch|manjaro|endeavouros) PKG_MGR="pacman" ;; opensuse*|sles) PKG_MGR="zypper" ;; alpine) PKG_MGR="apk" ;; *) PKG_MGR="unknown" ;; esac fi # Detect WSL if grep -qi microsoft /proc/version 2>/dev/null; then OS="wsl" fi ;; MINGW*|MSYS*|CYGWIN*) OS="windows-git-bash" PKG_MGR="none" ;; esac echo "Platform: $OS ($ARCH), Package Manager: $PKG_MGR" } # --- .NET SDK Installation --- install_dotnet() { step "Checking .NET SDK" if command -v dotnet &>/dev/null; then local ver ver=$(dotnet --version 2>/dev/null || echo "0") local major="${ver%%.*}" if [ "$major" -ge 8 ] 2>/dev/null; then log "dotnet $ver already installed (>= 8.0 OK)" return 0 else warn "dotnet $ver found but < 8.0, upgrading..." fi fi info "Installing .NET SDK..." case "$PKG_MGR" in brew) brew install --cask dotnet-sdk ;; apt) # Microsoft package repo for Ubuntu/Debian if ! dpkg -l dotnet-sdk-8.0 &>/dev/null 2>&1; then info "Adding Microsoft package repository..." sudo apt-get update -qq sudo apt-get install -y -qq wget apt-transport-https wget -q "https://dot.net/v1/dotnet-install.sh" -O /tmp/dotnet-install.sh chmod +x /tmp/dotnet-install.sh /tmp/dotnet-install.sh --channel 8.0 --install-dir "$HOME/.dotnet" export PATH="$HOME/.dotnet:$PATH" echo 'export PATH="$HOME/.dotnet:$PATH"' >> "$HOME/.bashrc" fi ;; dnf) sudo dnf install -y dotnet-sdk-8.0 ;; pacman) sudo pacman -S --noconfirm dotnet-sdk ;; zypper) sudo zypper install -y dotnet-sdk-8.0 ;; apk) apk add --no-cache dotnet8-sdk ;; none) if [ "$OS" = "windows-git-bash" ]; then fail "On Windows, install .NET SDK from: https://dotnet.microsoft.com/download" fail "Then restart your terminal and re-run this script." return 1 fi # Fallback: use Microsoft install script info "Using Microsoft install script..." wget -q "https://dot.net/v1/dotnet-install.sh" -O /tmp/dotnet-install.sh || \ curl -sSL "https://dot.net/v1/dotnet-install.sh" -o /tmp/dotnet-install.sh chmod +x /tmp/dotnet-install.sh /tmp/dotnet-install.sh --channel 8.0 --install-dir "$HOME/.dotnet" export PATH="$HOME/.dotnet:$PATH" echo 'export PATH="$HOME/.dotnet:$PATH"' >> "$HOME/.bashrc" ;; *) warn "Unknown package manager. Install .NET SDK manually: https://dotnet.microsoft.com/download" return 1 ;; esac # Verify if command -v dotnet &>/dev/null; then log "dotnet $(dotnet --version) installed" else fail "dotnet installation failed. Install manually: https://dotnet.microsoft.com/download" return 1 fi } # --- Pandoc Installation (Optional) --- install_pandoc() { step "Checking pandoc (optional: content preview)" if command -v pandoc &>/dev/null; then log "pandoc $(pandoc --version | head -1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?') already installed" return 0 fi info "Installing pandoc..." case "$PKG_MGR" in brew) brew install pandoc ;; apt) sudo apt-get install -y -qq pandoc ;; dnf) sudo dnf install -y pandoc ;; pacman) sudo pacman -S --noconfirm pandoc ;; zypper) sudo zypper install -y pandoc ;; apk) apk add --no-cache pandoc ;; *) warn "Cannot auto-install pandoc. Install manually: https://pandoc.org/installing.html" return 0 ;; esac if command -v pandoc &>/dev/null; then log "pandoc installed" else warn "pandoc installation failed (optional, will degrade gracefully)" fi } # --- LibreOffice Installation (Optional) --- install_soffice() { step "Checking LibreOffice/soffice (optional: .doc conversion)" if command -v soffice &>/dev/null; then log "soffice already installed" return 0 fi # Also check common install paths local soffice_paths=( "/usr/bin/soffice" "/usr/local/bin/soffice" "/opt/libreoffice/program/soffice" "/snap/bin/libreoffice" "/Applications/LibreOffice.app/Contents/MacOS/soffice" ) for p in "${soffice_paths[@]}"; do if [ -x "$p" ]; then log "soffice found at $p" if [ "$OS" = "macos" ] && [ "$p" = "/Applications/LibreOffice.app/Contents/MacOS/soffice" ]; then info "Tip: Add to PATH: ln -s '$p' /usr/local/bin/soffice" fi return 0 fi done info "Installing LibreOffice (this may take a while)..." case "$PKG_MGR" in brew) brew install --cask libreoffice ;; apt) sudo apt-get install -y -qq libreoffice-core ;; dnf) sudo dnf install -y libreoffice-core ;; pacman) sudo pacman -S --noconfirm libreoffice-still ;; zypper) sudo zypper install -y libreoffice ;; apk) apk add --no-cache libreoffice ;; *) warn "Cannot auto-install LibreOffice. Install manually: https://www.libreoffice.org/download/" return 0 ;; esac if command -v soffice &>/dev/null; then log "soffice installed" else warn "soffice not found after install (optional, .doc conversion unavailable)" fi } # --- zip/unzip --- install_zip_tools() { step "Checking zip/unzip" local need_zip=false need_unzip=false command -v zip &>/dev/null && log "zip already installed" || need_zip=true command -v unzip &>/dev/null && log "unzip already installed" || need_unzip=true if ! $need_zip && ! $need_unzip; then return 0 fi info "Installing zip/unzip..." case "$PKG_MGR" in brew) brew install zip unzip 2>/dev/null || true ;; apt) sudo apt-get install -y -qq zip unzip ;; dnf) sudo dnf install -y zip unzip ;; pacman) sudo pacman -S --noconfirm zip unzip ;; zypper) sudo zypper install -y zip unzip ;; apk) apk add --no-cache zip unzip ;; *) warn "Install zip/unzip manually (optional, .NET handles DOCX natively)" ;; esac } # --- .NET Project Build --- build_project() { step "Building minimax-docx .NET project" if [ ! -d "$DOTNET_DIR" ]; then fail "Dotnet project directory not found: $DOTNET_DIR" return 1 fi cd "$DOTNET_DIR" info "Restoring NuGet packages..." if ! dotnet restore --verbosity quiet 2>>"$LOG_FILE"; then fail "NuGet restore failed. Check network and $LOG_FILE for details." fail "Common causes:" fail " - No internet access (NuGet needs to download packages)" fail " - Corporate proxy blocking nuget.org" fail " - Disk space insufficient" echo "" fail "Try manually: cd $DOTNET_DIR && dotnet restore --verbosity detailed" return 1 fi log "NuGet packages restored" info "Building project..." if ! dotnet build --verbosity quiet --no-restore 2>>"$LOG_FILE"; then fail "Build failed. Check $LOG_FILE for details." fail "Try manually: cd $DOTNET_DIR && dotnet build --verbosity normal" return 1 fi log "Project built successfully" cd "$PROJECT_DIR" } # --- Shell Script Permissions --- fix_permissions() { step "Setting script permissions" local scripts=( "$SCRIPT_DIR/env_check.sh" "$SCRIPT_DIR/docx_preview.sh" "$SCRIPT_DIR/doc_to_docx.sh" "$SCRIPT_DIR/setup.sh" ) for s in "${scripts[@]}"; do if [ -f "$s" ]; then chmod +x "$s" log "chmod +x $(basename "$s")" fi done } # --- NuGet Proxy / Certificate Issues (Corporate Environments) --- check_nuget_config() { step "Checking NuGet configuration" local nuget_config="$HOME/.nuget/NuGet/NuGet.Config" if [ -f "$nuget_config" ]; then log "NuGet config exists: $nuget_config" else info "No custom NuGet config found (using defaults)" fi # Test NuGet connectivity if dotnet nuget list source 2>/dev/null | grep -q "nuget.org"; then log "nuget.org source is configured" else warn "nuget.org not in sources. Adding..." dotnet nuget add source "https://api.nuget.org/v3/index.json" --name "nuget.org" 2>/dev/null || true fi } # --- Locale / Encoding Check --- check_locale() { step "Checking locale and encoding" local current_lang="${LANG:-not set}" local current_lc="${LC_ALL:-not set}" if echo "$current_lang" | grep -qi "utf-8\|utf8"; then log "Locale supports UTF-8: LANG=$current_lang" else warn "Locale may not support UTF-8: LANG=$current_lang" warn "CJK document processing requires UTF-8. Set: export LANG=en_US.UTF-8" if [ "$OS" = "linux" ] || [ "$OS" = "wsl" ]; then info "To fix permanently: sudo locale-gen en_US.UTF-8 && sudo update-locale LANG=en_US.UTF-8" fi fi } # --- Font Check (for CJK and professional documents) --- check_fonts() { step "Checking fonts for document rendering" if [ "$OS" = "macos" ]; then # macOS has good CJK support built-in log "macOS: built-in CJK font support (PingFang, Hiragino, Apple SD Gothic)" log "macOS: built-in Western fonts (Helvetica, Times, Calibri via Office)" if [ -d "/Applications/Microsoft Word.app" ] || [ -d "/Applications/Microsoft Office" ]; then log "Microsoft Office fonts available (Calibri, Cambria, etc.)" else warn "Microsoft Office not installed — Calibri/Cambria fonts may be missing" info "Documents will render with fallback fonts on this machine" info "Recipients with Office installed will see correct fonts" fi elif [ "$OS" = "linux" ] || [ "$OS" = "wsl" ]; then # Check for key font packages local missing_fonts=() if ! fc-list 2>/dev/null | grep -qi "liberation\|times new roman\|calibri"; then missing_fonts+=("Western: liberation-fonts or msttcorefonts") fi if ! fc-list 2>/dev/null | grep -qi "noto.*cjk\|wqy\|simsun\|pingfang"; then missing_fonts+=("CJK: noto-fonts-cjk or wqy-microhei") fi if [ ${#missing_fonts[@]} -eq 0 ]; then log "Font support looks good" else warn "Missing fonts may affect document rendering:" for f in "${missing_fonts[@]}"; do warn " - $f" done info "Install fonts:" case "$PKG_MGR" in apt) info " sudo apt-get install -y fonts-liberation fonts-noto-cjk" info " # For MS core fonts: sudo apt-get install -y ttf-mscorefonts-installer" ;; dnf) info " sudo dnf install -y liberation-fonts google-noto-sans-cjk-fonts" ;; pacman) info " sudo pacman -S ttf-liberation noto-fonts-cjk" ;; *) info " Install Liberation Fonts and Noto CJK fonts for your distribution" ;; esac fi fi } # --- Verification Run --- verify_installation() { step "Verification Test" local test_output="/tmp/minimax-docx-setup-test-$$.docx" info "Creating a test document..." if cd "$DOTNET_DIR" && dotnet run --project MiniMaxAIDocx.Cli -- create \ --type report --output "$test_output" --title "Setup Test" 2>>"$LOG_FILE"; then log "Test document created: $test_output" # Try preview if command -v pandoc &>/dev/null; then local preview preview=$(pandoc -f docx -t plain "$test_output" 2>/dev/null | head -5) if [ -n "$preview" ]; then log "Preview working: \"$preview\"" fi fi # Cleanup rm -f "$test_output" log "Test passed — minimax-docx is ready to use!" else fail "Test document creation failed. Check $LOG_FILE for details." return 1 fi cd "$PROJECT_DIR" } # --- Summary --- print_summary() { step "Setup Complete" echo "" echo " Environment: $OS ($ARCH)" echo " .NET SDK: $(dotnet --version 2>/dev/null || echo 'NOT FOUND')" echo " pandoc: $(pandoc --version 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || echo 'not installed (optional)')" echo " soffice: $(soffice --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || echo 'not installed (optional)')" echo " Project: $DOTNET_DIR" echo "" echo " Usage:" echo " dotnet run --project $DOTNET_DIR/MiniMaxAIDocx.Cli -- create --type report --output my_report.docx" echo " bash $SCRIPT_DIR/env_check.sh # Quick environment check" echo "" echo " Log file: $LOG_FILE" } # --- Main --- main() { echo "============================================" echo " minimax-docx Setup & Initialization" echo " $(date '+%Y-%m-%d %H:%M:%S')" echo "============================================" : > "$LOG_FILE" # Clear log detect_platform # Parse arguments local SKIP_OPTIONAL=false local SKIP_VERIFY=false for arg in "$@"; do case "$arg" in --minimal) SKIP_OPTIONAL=true ;; --skip-verify) SKIP_VERIFY=true ;; --help|-h) echo "Usage: setup.sh [options]" echo " --minimal Only install critical dependencies (skip pandoc, soffice, fonts)" echo " --skip-verify Skip the verification test at the end" echo " --help Show this help" exit 0 ;; esac done install_dotnet install_zip_tools if ! $SKIP_OPTIONAL; then install_pandoc install_soffice check_fonts fi check_locale check_nuget_config fix_permissions build_project if ! $SKIP_VERIFY; then verify_installation fi print_summary } main "$@"