#!/bin/bash # build.sh - универсальный скрипт сборки для fush shell # Поддерживает операционные системы Linux и OpenIndiana/Hipster # Обеспечивает компиляцию, тестирование, установку и запуск shell # Использует цветной вывод и автоматическое определение окружения set -e # Определение цветов для вывода if [ -t 1 ]; then RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' MAGENTA='\033[0;35m' NC='\033[0m' # No Color else RED='' GREEN='' YELLOW='' BLUE='' CYAN='' MAGENTA='' NC='' fi # Определение корневой директории проекта fush SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$SCRIPT_DIR" FUSH_DIR="$PROJECT_ROOT" # Проверяем, что мы находимся в директории проекта fush if [ ! -d "$FUSH_DIR" ] || [ ! -f "$FUSH_DIR/go.mod" ]; then # Пробуем подняться на уровень выше (на случай если скрипт в scripts/) if [ -f "$SCRIPT_DIR/../go.mod" ]; then FUSH_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" PROJECT_ROOT="$FUSH_DIR" cd "$FUSH_DIR" echo -e "${YELLOW}Скрипт запущен из поддиректории. Переход в корень проекта: ${FUSH_DIR}${NC}" else echo -e "${RED}error: Не найдена директория проекта fush с файлом go.mod${NC}" echo -e "${RED}error: Проверены директории:${NC}" echo -e "${RED} - ${FUSH_DIR}${NC}" echo -e "${RED} - ${SCRIPT_DIR}/..${NC}" echo -e "${RED}Убедитесь, что файл go.mod существует в корне проекта${NC}" exit 1 fi else cd "$FUSH_DIR" fi # Определение переменных PROJECT_NAME="fush" BINARY_NAME="bin/${PROJECT_NAME}" ROOT_BINARY_NAME="${PROJECT_NAME}" BUILD_DIR="bin" GO="go" # Получение информации о версии BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S') GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") GO_VERSION=$(go version | awk '{print $3}' | sed 's/go//') REQUIRED_VERSION="1.26.0" # Функция для сравнения версий (работает без sort -V) version_compare() { # Удаляем префикс 'v' если есть local v1="${1#v}" local v2="${2#v}" # Разбиваем на компоненты IFS='.' read -ra v1_arr <<< "$v1" IFS='.' read -ra v2_arr <<< "$v2" # Сравниваем компоненты for i in 0 1 2; do # Получаем числовые значения, удаляя ведущие нули local num1="${v1_arr[$i]}" local num2="${v2_arr[$i]}" # Удаляем ведущие нули num1="${num1#0}" num2="${num2#0}" # Если строка пустая после удаления нулей, значит было "0" if [ -z "$num1" ]; then num1=0 fi if [ -z "$num2" ]; then num2=0 fi # Сравниваем как числа if [ $num1 -lt $num2 ]; then echo "-1" return elif [ $num1 -gt $num2 ]; then echo "1" return fi done echo "0" } # Функция для отображения прав и информации о файле show_file_info() { local file_path="$1" local file_name="$2" if [ -f "$file_path" ]; then echo -e "${CYAN}================================${NC}" echo -e "${GREEN}Информация о собранном файле:${NC}" echo -e "${CYAN}Имя файла:${NC} ${YELLOW}${file_name}${NC}" echo -e "${CYAN}Полный путь:${NC} ${YELLOW}${file_path}${NC}" echo -e "${CYAN}Размер:${NC} ${YELLOW}$(ls -lh "$file_path" | awk '{print $5}')${NC}" echo -e "${CYAN}Права доступа:${NC} ${YELLOW}$(ls -l "$file_path" | awk '{print $1}')${NC}" echo -e "${CYAN}Владелец:${NC} ${YELLOW}$(ls -l "$file_path" | awk '{print $3":"$4}')${NC}" echo -e "${CYAN}Тип файла:${NC} ${YELLOW}$(file "$file_path" | cut -d: -f2)${NC}" echo -e "${CYAN}================================${NC}" else echo -e "${RED}Файл не найден: ${file_path}${NC}" fi } # Функция для вывода справки show_help() { echo -e "${CYAN}================================${NC}" echo -e "${CYAN}fush Shell - Скрипт сборки${NC}" echo -e "${CYAN}================================${NC}" echo -e "${GREEN}Использование:${NC}" echo -e " $0 [команда]" echo echo -e "${GREEN}Доступные команды:${NC}" echo -e " ${YELLOW}all${NC} - Очистка и сборка (по умолчанию)" echo -e " ${YELLOW}build${NC} - Сборка проекта" echo -e " ${YELLOW}clean${NC} - Очистка временных файлов" echo -e " ${YELLOW}install${NC} - Установка в систему" echo -e " ${YELLOW}test${NC} - Запуск тестов" echo -e " ${YELLOW}run${NC} - Сборка и запуск" echo -e " ${YELLOW}help${NC} - Показать эту справку" echo echo -e "${GREEN}Примеры:${NC}" echo -e " $0 # Выполнить очистку и сборку" echo -e " $0 build # Только сборка" echo -e " $0 install # Собрать и установить" echo -e " $0 run # Собрать и запустить" echo } # Функция проверки версии Go check_go_version() { echo -e "${YELLOW}Проверка версии Go...${NC}" if [ -z "$GO_VERSION" ]; then echo -e "${RED}error: Go не установлен${NC}" exit 1 fi echo -e "${GREEN}✓ Go версия ${GO_VERSION}${NC}" # Сравнение версий с помощью собственной функции local cmp=$(version_compare "$GO_VERSION" "$REQUIRED_VERSION") if [ "$cmp" = "-1" ]; then echo -e "${RED}error: Требуется Go версии ${REQUIRED_VERSION} или выше. Текущая версия: ${GO_VERSION}${NC}" exit 1 fi echo -e "${GREEN}✓ Версия Go соответствует требованиям${NC}" } # Функция проверки операционной системы check_os() { OS=$(uname -s) case "$OS" in Linux|SunOS) echo -e "${GREEN}✓ Поддерживаемая ОС: ${OS}${NC}" ;; *) echo -e "${RED}error: Неподдерживаемая ОС: ${OS}${NC}" echo -e "${RED}error: fush поддерживает только Linux и OpenIndiana${NC}" exit 1 ;; esac } # Функция проверки наличия файлов проекта check_project_files() { echo -e "${YELLOW}Проверка файлов проекта в ${FUSH_DIR}...${NC}" # Проверяем наличие go.mod if [ ! -f "$FUSH_DIR/go.mod" ]; then echo -e "${RED}error: Файл go.mod не найден в ${FUSH_DIR}${NC}" exit 1 fi echo -e "${GREEN}✓ Найден go.mod${NC}" # Проверяем наличие go.sum if [ ! -f "$FUSH_DIR/go.sum" ]; then echo -e "${YELLOW}Предупреждение: Файл go.sum не найден в ${FUSH_DIR}${NC}" echo -e "${YELLOW}Он будет создан при установке зависимостей${NC}" else echo -e "${GREEN}✓ Найден go.sum${NC}" fi # Проверяем наличие директории cmd/fush if [ ! -d "$FUSH_DIR/cmd/fush" ]; then echo -e "${RED}error: Директория cmd/fush не найдена в ${FUSH_DIR}${NC}" exit 1 fi echo -e "${GREEN}✓ Найдена директория cmd/fush${NC}" # Проверяем наличие main.go if [ ! -f "$FUSH_DIR/cmd/fush/main.go" ]; then echo -e "${RED}error: Файл cmd/fush/main.go не найден${NC}" exit 1 fi echo -e "${GREEN}✓ Найден main.go${NC}" } # Функция установки зависимостей install_deps() { echo -e "${YELLOW}Установка зависимостей в ${FUSH_DIR}...${NC}" # Проверяем наличие go.mod if [ ! -f "go.mod" ]; then echo -e "${RED}error: Файл go.mod не найден в текущей директории${NC}" echo -e "${RED}error: Текущая директория: $(pwd)${NC}" exit 1 fi # Обновляем go.mod и создаем go.sum echo -e "${YELLOW}Выполнение go mod download...${NC}" $GO mod download 2>&1 | while IFS= read -r line; do if [[ "$line" == *"error"* ]] || [[ "$line" == *"Error"* ]]; then echo -e "${RED}error: $line${NC}" else echo "$line" fi done echo -e "${YELLOW}Выполнение go mod tidy...${NC}" $GO mod tidy 2>&1 | while IFS= read -r line; do if [[ "$line" == *"error"* ]] || [[ "$line" == *"Error"* ]]; then echo -e "${RED}error: $line${NC}" else echo "$line" fi done # Проверяем наличие go.sum после установки if [ -f "go.sum" ]; then echo -e "${GREEN}✓ Файл go.sum успешно создан/обновлен${NC}" GO_SUM_SIZE=$(ls -lh go.sum | awk '{print $5}') echo -e "${GREEN}✓ Размер go.sum: ${GO_SUM_SIZE}${NC}" else echo -e "${RED}error: Файл go.sum не был создан после go mod tidy${NC}" exit 1 fi # Проверка наличия Lua echo -e "${YELLOW}Проверка наличия Lua...${NC}" if command -v lua >/dev/null 2>&1; then echo -e "${GREEN}✓ Lua найден${NC}" else echo -e "${YELLOW}Предупреждение: Lua не найден. Для работы скриптов потребуется установить Lua${NC}" echo -e "${YELLOW}Установите Lua с помощью вашего пакетного менеджера:${NC}" echo -e " - Ubuntu/Debian: sudo apt-get install lua5.3" echo -e " - OpenIndiana: sudo pkg install lua" fi } # Функция сборки проекта build_project() { echo -e "${YELLOW}Компиляция ${PROJECT_NAME}...${NC}" echo -e "${YELLOW}Директория проекта: ${FUSH_DIR}${NC}" echo -e "${YELLOW}Текущая директория: $(pwd)${NC}" # Проверяем наличие go.mod if [ ! -f "go.mod" ]; then echo -e "${RED}error: Файл go.mod не найден. Убедитесь, что вы находитесь в директории проекта fush${NC}" return 1 fi # Проверяем наличие go.sum if [ ! -f "go.sum" ]; then echo -e "${RED}error: Файл go.sum не найден. Выполните сначала установку зависимостей${NC}" return 1 fi # Проверяем существование директории cmd/fush if [ ! -d "cmd/fush" ]; then echo -e "${RED}error: Директория cmd/fush не найдена${NC}" return 1 fi # Проверяем существование main.go if [ ! -f "cmd/fush/main.go" ]; then echo -e "${RED}error: Файл cmd/fush/main.go не найден${NC}" return 1 fi # Создаем директорию для бинарных файлов mkdir -p "$BUILD_DIR" echo -e "${GREEN}✓ Директория ${BUILD_DIR} создана${NC}" # Формируем флаги сборки LDFLAGS="-X main.BuildTime=${BUILD_TIME} -X main.GitCommit=${GIT_COMMIT}" # Выполняем сборку с явным указанием выходного файла echo -e "${YELLOW}Выполнение: go build -v -ldflags=\"${LDFLAGS}\" -o ${BINARY_NAME} ./cmd/fush${NC}" # Захватываем вывод команды и выводим в реальном времени go build -v -ldflags="${LDFLAGS}" -o "${BINARY_NAME}" ./cmd/fush 2>&1 | while IFS= read -r line; do if [[ "$line" == *"error"* ]] || [[ "$line" == *"Error"* ]] || [[ "$line" == *"undefined"* ]] || [[ "$line" == *"cannot"* ]]; then echo -e "${RED}error: $line${NC}" else echo "$line" fi done BUILD_EXIT_CODE=${PIPESTATUS[0]} # Проверяем результат сборки if [ $BUILD_EXIT_CODE -ne 0 ]; then echo -e "${RED}error: Сборка завершилась с ошибкой (код: $BUILD_EXIT_CODE)${NC}" return 1 fi # Проверяем, создался ли бинарный файл if [ -f "${BINARY_NAME}" ]; then echo -e "${GREEN}✓ Сборка успешно завершена${NC}" echo -e "${GREEN}✓ Исполняемый файл: ${BINARY_NAME}${NC}" echo -e "${GREEN}✓ Полный путь: ${FUSH_DIR}/${BINARY_NAME}${NC}" # Показываем размер бинарного файла SIZE=$(ls -lh "${BINARY_NAME}" | awk '{print $5}') echo -e "${GREEN}✓ Размер бинарного файла: ${SIZE}${NC}" # Проверка прав доступа if [ -x "${BINARY_NAME}" ]; then echo -e "${GREEN}✓ Бинарный файл имеет права на выполнение${NC}" else echo -e "${YELLOW}Добавление прав на выполнение...${NC}" chmod +x "${BINARY_NAME}" echo -e "${GREEN}✓ Права на выполнение добавлены${NC}" fi # Копируем бинарный файл в корень проекта echo -e "${YELLOW}Копирование бинарного файла в корень проекта...${NC}" cp "${BINARY_NAME}" "${ROOT_BINARY_NAME}" if [ -f "${ROOT_BINARY_NAME}" ]; then chmod +x "${ROOT_BINARY_NAME}" echo -e "${GREEN}✓ Бинарный файл скопирован в: ${ROOT_BINARY_NAME}${NC}" else echo -e "${RED}error: Не удалось скопировать бинарный файл в корень проекта${NC}" return 1 fi # Отображаем информацию о собранных файлах echo "" show_file_info "${BINARY_NAME}" "fush (в папке bin)" echo "" show_file_info "${ROOT_BINARY_NAME}" "fush (в корне проекта)" return 0 else echo -e "${RED}error: Бинарный файл не создан по пути: ${BINARY_NAME}${NC}" echo -e "${RED}error: Содержимое директории ${BUILD_DIR}:${NC}" ls -la "$BUILD_DIR" 2>/dev/null || echo "Директория пуста или не существует" return 1 fi } # Функция очистки clean_project() { echo -e "${YELLOW}Очистка в ${FUSH_DIR}...${NC}" if [ -d "$BUILD_DIR" ]; then rm -rf "$BUILD_DIR" echo -e "${GREEN}✓ Директория ${BUILD_DIR} удалена${NC}" fi if [ -f "$PROJECT_NAME" ]; then rm -f "$PROJECT_NAME" echo -e "${GREEN}✓ Файл ${PROJECT_NAME} удален${NC}" fi if [ -f "${ROOT_BINARY_NAME}" ]; then rm -f "${ROOT_BINARY_NAME}" echo -e "${GREEN}✓ Файл ${ROOT_BINARY_NAME} удален${NC}" fi echo -e "${GREEN}✓ Очистка завершена${NC}" } # Функция установки install_project() { echo -e "${YELLOW}Установка ${PROJECT_NAME}...${NC}" # Проверяем, существует ли бинарный файл в корне if [ ! -f "$ROOT_BINARY_NAME" ]; then echo -e "${YELLOW}Бинарный файл не найден в корне. Выполняется сборка...${NC}" build_project if [ $? -ne 0 ]; then echo -e "${RED}error: Ошибка сборки. Установка прервана.${NC}" return 1 fi fi # Копируем в системную директорию if [ -w "/usr/local/bin" ]; then cp "$ROOT_BINARY_NAME" /usr/local/bin/ echo -e "${GREEN}✓ Установлен в /usr/local/bin/${PROJECT_NAME}${NC}" else echo -e "${YELLOW}Нет прав на запись в /usr/local/bin. Используем sudo...${NC}" sudo cp "$ROOT_BINARY_NAME" /usr/local/bin/ if [ $? -eq 0 ]; then echo -e "${GREEN}✓ Установлен в /usr/local/bin/${PROJECT_NAME}${NC}" else echo -e "${RED}error: Не удалось установить в /usr/local/bin/${PROJECT_NAME}${NC}" return 1 fi fi # Создание директорий конфигурации if [ ! -d ~/.config/fush ]; then mkdir -p ~/.config/fush if [ -f "config/fush.toml" ]; then cp config/fush.toml ~/.config/fush/ echo -e "${GREEN}✓ Конфигурация скопирована в ~/.config/fush/${NC}" fi fi # Создание директории для Lua скриптов if [ ! -d ~/.local/share/fush/lua ]; then mkdir -p ~/.local/share/fush/lua if [ -f "lua/example.lua" ]; then cp lua/example.lua ~/.local/share/fush/lua/ echo -e "${GREEN}✓ Пример Lua скрипта скопирован в ~/.local/share/fush/lua/${NC}" fi fi echo -e "${GREEN}✓ Установка завершена${NC}" } # Функция запуска тестов test_project() { echo -e "${YELLOW}Запуск тестов в ${FUSH_DIR}...${NC}" # Проверяем наличие go.mod if [ ! -f "go.mod" ]; then echo -e "${RED}error: Файл go.mod не найден${NC}" return 1 fi TEST_OUTPUT=$($GO test -v ./... 2>&1) TEST_EXIT_CODE=$? echo "$TEST_OUTPUT" | while IFS= read -r line; do if [[ "$line" == *"FAIL"* ]] || [[ "$line" == *"error"* ]] || [[ "$line" == *"Error"* ]]; then echo -e "${RED}$line${NC}" elif [[ "$line" == *"PASS"* ]] || [[ "$line" == *"ok"* ]]; then echo -e "${GREEN}$line${NC}" else echo "$line" fi done if [ $TEST_EXIT_CODE -eq 0 ]; then echo -e "${GREEN}✓ Все тесты пройдены${NC}" return 0 else echo -e "${RED}error: Некоторые тесты не пройдены${NC}" return 1 fi } # Функция запуска run_project() { echo -e "${YELLOW}Запуск ${PROJECT_NAME}...${NC}" # Проверяем, существует ли бинарный файл в корне if [ ! -f "$ROOT_BINARY_NAME" ]; then echo -e "${YELLOW}Бинарный файл не найден. Выполняется сборка...${NC}" build_project if [ $? -ne 0 ]; then echo -e "${RED}error: Ошибка сборки. Запуск невозможен.${NC}" return 1 fi fi # Запускаем shell "./$ROOT_BINARY_NAME" } # Функция полной сборки (очистка + сборка) all_build() { clean_project install_deps build_project } # Основная логика main() { echo -e "${CYAN}================================${NC}" echo -e "${CYAN}fush Shell - Скрипт сборки${NC}" echo -e "${CYAN}================================${NC}" echo -e "${YELLOW}Директория проекта fush: ${FUSH_DIR}${NC}" # Проверяем наличие файлов проекта check_project_files # Проверяем окружение check_os check_go_version # Определяем команду COMMAND="${1:-all}" case "$COMMAND" in all) all_build ;; build) install_deps build_project ;; clean) clean_project ;; install) install_deps build_project install_project ;; test) install_deps test_project ;; run) install_deps build_project run_project ;; help|--help|-h) show_help ;; *) echo -e "${RED}error: Неизвестная команда: $COMMAND${NC}" show_help exit 1 ;; esac if [ $? -eq 0 ] && [ "$COMMAND" != "help" ]; then echo -e "${BLUE}================================${NC}" echo -e "${GREEN}Операция '${COMMAND}' успешно завершена!${NC}" echo -e "${BLUE}================================${NC}" fi } # Запуск основной функции main "$@"