Files
fush/build.sh

566 lines
22 KiB
Bash
Raw Permalink Normal View History

2026-05-22 00:26:27 +03:00
#!/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 "$@"