allinssl/frontend/scripts/sync-project.sh

661 lines
17 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
#######################################
# Turborepo工作区编译部署自动化工具
#
# 这是一款基于纯Shell脚本开发的自动化工具用于Turborepo项目的
# 应用工作区扫描、编译及Git同步。支持跨平台操作可在Windows、
# macOS和Linux系统上稳定运行。
#
# 作者: chudong
# 版本: 1.1.0
#######################################
# 设置严格模式
set -e
# 避免变量名冲突,重命名为唯一的名称
SYNC_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 导入工具脚本
source "$SYNC_SCRIPT_DIR/tools/git-handle.sh"
source "$SYNC_SCRIPT_DIR/tools/file-handle.sh"
source "$SYNC_SCRIPT_DIR/tools/notice-handle.sh"
source "$SYNC_SCRIPT_DIR/tools/other-handle.sh"
# 全局变量
SYNC_DIR=".sync"
SYNC_CONFIG="$SYNC_DIR/sync-config.yaml"
HISTORY_FILE="$SYNC_DIR/history"
GIT_SYNC_DIR="$SYNC_DIR/git-repos"
# 禁用插件扩展
# PLUGINS_DIR="$SYNC_DIR/plugins"
CONFIG_LOADED="false"
ENVIRONMENT_CHECKED="false"
# 配置变量
# 禁用并行编译模式
# CONFIG_PARALLEL_BUILD="false"
# 禁用干运行模式
# CONFIG_DRY_RUN="false"
#######################################
# 初始化工具环境
# 返回值:
# 0 - 成功
# 1 - 失败
#######################################
init_environment() {
# 避免重复初始化环境
if [ "$ENVIRONMENT_CHECKED" = "true" ]; then
return 0
fi
notice_info "正在初始化环境..."
# 检查系统环境
check_environment
# 检查工作区
check_workspace "package.json" "pnpm-workspace.yaml" "turbo.json"
if [ $? -ne 0 ]; then
notice_error "必须在Turborepo项目根目录下运行此脚本"
return 1
fi
# 检查依赖工具
check_command_dependency "pnpm" || return 1
check_command_dependency "git" || return 1
# 确保YQ_PATH变量已设置
if [ -z "$YQ_PATH" ]; then
check_yq_dependency || notice_warning "未安装yq工具部分功能可能受限"
fi
# 创建必要的目录结构 - 全部移至.sync目录
if [ ! -d "$WORKSPACE_ROOT/$SYNC_DIR" ]; then
notice_info "创建同步配置目录..."
create_directory "$WORKSPACE_ROOT/$SYNC_DIR"
fi
if [ ! -d "$WORKSPACE_ROOT/$GIT_SYNC_DIR" ]; then
notice_info "创建Git同步目录..."
create_directory "$WORKSPACE_ROOT/$GIT_SYNC_DIR"
fi
# 禁用插件目录创建
# if [ ! -d "$WORKSPACE_ROOT/$PLUGINS_DIR" ]; then
# notice_info "创建插件目录..."
# create_directory "$WORKSPACE_ROOT/$PLUGINS_DIR"
# fi
# 初始化配置文件
if [ ! -f "$WORKSPACE_ROOT/$SYNC_CONFIG" ]; then
init_config_file
fi
# 初始化历史记录文件
if [ ! -f "$WORKSPACE_ROOT/$HISTORY_FILE" ]; then
create_file "$WORKSPACE_ROOT/$HISTORY_FILE"
fi
ENVIRONMENT_CHECKED="true"
notice_success "环境初始化完成"
return 0
}
#######################################
# 检查命令是否可用
# 参数:
# $1: 命令名称
# 返回值:
# 0 - 命令可用
# 1 - 命令不可用
#######################################
check_command_dependency() {
local cmd="$1"
if ! command -v "$cmd" &> /dev/null; then
notice_error "未找到命令: $cmd"
return 1
fi
notice_success "命令检查通过: $cmd"
return 0
}
#######################################
# 初始化配置文件
# 返回值:
# 0 - 成功
# 1 - 失败
#######################################
init_config_file() {
notice_info "初始化配置文件..."
local config_content="# 工具配置
config:
# 禁用并行编译
# parallel_build: false # 是否并行编译
# 禁用干运行
# dry_run: false # 是否干运行
# 工作区配置
workspaces:
# 示例工作区配置
# app-name:
# sync_mappings:
# - git:
# url: \"https://github.com/user/repo.git\" # Git 仓库地址
# branch: \"main\" # 分支名称
# alias: \"repo-name\" # 仓库别名(可选)
# sync: [\"/dist\", \"/build\"] # 要同步的目录列表
# source: false # 是否同步源码
"
write_file_content "$WORKSPACE_ROOT/$SYNC_CONFIG" "$config_content" "force"
if [ $? -eq 0 ]; then
notice_success "配置文件已初始化"
return 0
else
notice_error "配置文件初始化失败"
return 1
fi
}
#######################################
# 扫描所有工作区
# 返回值:
# 0 - 成功
# 1 - 失败
# 全局变量:
# WORKSPACES - 包含所有工作区路径的数组
# DISPLAY_NAMES - 用于显示的工作区名称移除apps/前缀)
#######################################
scan_workspaces() {
notice_info "扫描/apps目录下的应用工作区..."
local apps_dir="$WORKSPACE_ROOT/apps"
if [ ! -d "$apps_dir" ]; then
notice_error "未找到apps目录: $apps_dir"
return 1
fi
# 清空工作区数组
WORKSPACES=()
DISPLAY_NAMES=()
# 直接扫描apps目录下的所有子目录
for dir in $(find "$apps_dir" -maxdepth 1 -mindepth 1 -type d -not -path "*/node_modules/*" -not -path "*/.*/*"); do
if [ -f "$dir/package.json" ]; then
# 添加完整路径(相对于工作区根目录)
local full_path="${dir#$WORKSPACE_ROOT/}"
WORKSPACES+=("$full_path")
# 添加没有apps/前缀的显示名称
local display_name="${full_path#apps/}"
DISPLAY_NAMES+=("$display_name")
fi
done
if [ ${#WORKSPACES[@]} -eq 0 ]; then
notice_warning "未在apps目录下找到应用工作区"
return 1
fi
notice_success "在apps目录下找到 ${#WORKSPACES[@]} 个应用工作区"
for i in "${!WORKSPACES[@]}"; do
notice_info " - ${DISPLAY_NAMES[$i]}"
done
return 0
}
#######################################
# 加载配置文件
# 返回值:
# 0 - 成功
# 1 - 失败
# 全局变量:
# CONFIG_DRY_RUN - 是否干运行模式
#######################################
load_config() {
# 避免重复加载配置
if [ "$CONFIG_LOADED" = "true" ]; then
return 0
fi
notice_info "加载配置文件..."
local config_file="$WORKSPACE_ROOT/$SYNC_CONFIG"
if [ ! -f "$config_file" ]; then
notice_error "配置文件不存在: $config_file"
return 1
fi
# 确保yq可用
if [ -z "$YQ_PATH" ]; then
check_yq_dependency || return 1
fi
# 加载基本配置
# 禁用并行编译设置加载
# CONFIG_PARALLEL_BUILD=$("$YQ_PATH" e '.config.parallel_build // false' "$config_file")
# 禁用干运行设置加载
# CONFIG_DRY_RUN=$("$YQ_PATH" e '.config.dry_run // false' "$config_file")
# 标记配置已加载
CONFIG_LOADED="true"
notice_success "配置文件加载完成"
# 显示当前配置
notice_info "当前配置:"
# notice_info " - 并行编译: $CONFIG_PARALLEL_BUILD"
# notice_info " - 干运行模式: $CONFIG_DRY_RUN"
return 0
}
#######################################
# 选择工作区
# 返回值:
# 0 - 成功
# 1 - 失败
# 全局变量:
# SELECTED_WORKSPACES - 存储用户选择的工作区
#######################################
select_workspaces() {
notice_info "请选择要操作的工作区:"
SELECTED_WORKSPACES=()
local selected_index
# 使用没有apps/前缀的显示名称展示选项
notice_select_menu "选择要编译和同步的工作区" "selected_index" "${DISPLAY_NAMES[@]}"
if [ $? -ne 0 ]; then
notice_error "工作区选择被取消"
return 1
fi
# 添加选择的工作区(使用完整路径)
SELECTED_WORKSPACES+=("${WORKSPACES[$selected_index]}")
# 使用没有apps/前缀的显示名称展示选项
notice_success "已选择工作区: ${DISPLAY_NAMES[$selected_index]}"
return 0
}
#######################################
# 编译工作区
# 参数:
# $1: 工作区名称
# 返回值:
# 0 - 成功
# 1 - 失败
#######################################
build_workspace() {
local workspace="$1"
notice_info "正在编译工作区: $workspace..."
# 检查是否为干运行模式
# if [ "$CONFIG_DRY_RUN" = "true" ]; then
# notice_info "干运行模式:跳过实际编译"
# return 0
# fi
# 执行编译命令
(cd "$WORKSPACE_ROOT" && pnpm --filter "$workspace" build)
if [ $? -eq 0 ]; then
notice_success "工作区 $workspace 编译成功"
# 记录到历史文件
echo "$(date '+%Y-%m-%d %H:%M:%S') - 编译工作区 $workspace 成功" >> "$WORKSPACE_ROOT/$HISTORY_FILE"
return 0
else
notice_error "工作区 $workspace 编译失败"
# 记录到历史文件
echo "$(date '+%Y-%m-%d %H:%M:%S') - 编译工作区 $workspace 失败" >> "$WORKSPACE_ROOT/$HISTORY_FILE"
return 1
fi
}
#######################################
# 同步工作区到Git仓库
# 参数:
# $1: 工作区名称
# 返回值:
# 0 - 成功
# 1 - 失败
#######################################
sync_workspace() {
local workspace="$1"
notice_info "正在同步工作区: $workspace..."
# 确保配置已加载
if [ "$CONFIG_LOADED" != "true" ]; then
load_config || return 1
fi
# 确保yq可用
if [ -z "$YQ_PATH" ]; then
check_yq_dependency || return 1
fi
# 获取工作区配置
local sync_mappings_count
sync_mappings_count=$("$YQ_PATH" e ".workspaces.$workspace.sync_mappings | length // 0" "$WORKSPACE_ROOT/$SYNC_CONFIG")
if [ "$sync_mappings_count" -eq 0 ]; then
notice_warning "工作区 $workspace 没有同步映射配置"
return 1
fi
# 遍历所有同步映射
for i in $(seq 0 $((sync_mappings_count - 1))); do
local git_url
local branch
local alias
local sync_dirs
local sync_source
git_url=$("$YQ_PATH" e ".workspaces.$workspace.sync_mappings[$i].git.url" "$WORKSPACE_ROOT/$SYNC_CONFIG")
branch=$("$YQ_PATH" e ".workspaces.$workspace.sync_mappings[$i].git.branch // \"main\"" "$WORKSPACE_ROOT/$SYNC_CONFIG")
alias=$("$YQ_PATH" e ".workspaces.$workspace.sync_mappings[$i].git.alias // \"\"" "$WORKSPACE_ROOT/$SYNC_CONFIG")
sync_dirs=$("$YQ_PATH" e ".workspaces.$workspace.sync_mappings[$i].git.sync | join(\",\")" "$WORKSPACE_ROOT/$SYNC_CONFIG")
sync_source=$("$YQ_PATH" e ".workspaces.$workspace.sync_mappings[$i].git.source // false" "$WORKSPACE_ROOT/$SYNC_CONFIG")
# 如果alias为空从git_url生成
if [ -z "$alias" ]; then
alias=$(basename "$git_url" .git)
fi
notice_info "正在处理同步映射: $git_url -> $branch (别名: $alias)"
# 准备Git仓库
local git_repo_path="$WORKSPACE_ROOT/$GIT_SYNC_DIR/$alias"
if [ ! -d "$git_repo_path" ]; then
notice_info "克隆Git仓库: $git_url"
# 禁用干运行判断
# if [ "$CONFIG_DRY_RUN" = "true" ]; then
# notice_info "干运行模式:跳过实际克隆"
# else
git clone "$git_url" -b "$branch" "$git_repo_path"
if [ $? -ne 0 ]; then
notice_error "克隆仓库失败: $git_url"
continue
fi
# fi
else
# 确认是正确的仓库
local remote_url
remote_url=$(cd "$git_repo_path" && git config --get remote.origin.url)
if [ "$remote_url" != "$git_url" ]; then
notice_warning "仓库URL不匹配预期: $git_url, 实际: $remote_url"
notice_confirm "是否要重置仓库URL?" "n"
if [ $? -eq 0 ]; then
# 禁用干运行判断
# if [ "$CONFIG_DRY_RUN" = "true" ]; then
# notice_info "干运行模式跳过URL重置"
# else
(cd "$git_repo_path" && git remote set-url origin "$git_url")
# fi
else
notice_error "由于URL不匹配跳过同步"
continue
fi
fi
# 拉取最新代码
notice_info "拉取最新代码: $branch"
# 禁用干运行判断
# if [ "$CONFIG_DRY_RUN" = "true" ]; then
# notice_info "干运行模式:跳过实际拉取"
# else
git_pull "$git_repo_path" "$branch"
if [ $? -ne 0 ]; then
notice_warning "拉取代码失败,尝试继续同步"
fi
# fi
fi
# 同步目录列表
IFS=',' read -ra dirs <<< "$sync_dirs"
# 同步每个指定的目录
for dir in "${dirs[@]}"; do
# 去除前导斜杠
dir="${dir#/}"
# 复制编译结果到Git仓库
local source_dir="$WORKSPACE_ROOT/$workspace/$dir"
local target_dir="$git_repo_path/$dir"
if [ ! -d "$source_dir" ]; then
notice_warning "源目录不存在: $source_dir"
continue
fi
notice_info "同步目录: $source_dir -> $target_dir"
# 禁用干运行判断
# if [ "$CONFIG_DRY_RUN" = "true" ]; then
# notice_info "干运行模式:跳过实际复制"
# else
# 确保目标目录存在
if [ ! -d "$target_dir" ]; then
create_directory "$target_dir"
else
# 清空目标目录
notice_info "清空目标目录: $target_dir"
rm -rf "$target_dir"/*
fi
# 复制文件
cp -r "$source_dir"/* "$target_dir"/
if [ $? -ne 0 ]; then
notice_error "复制文件失败"
continue
fi
# fi
done
# 如果需要同步源代码
if [ "$sync_source" = "true" ]; then
notice_info "同步源代码..."
# 禁用干运行判断
# if [ "$CONFIG_DRY_RUN" = "true" ]; then
# notice_info "干运行模式:跳过源代码同步"
# else
# 复制源代码除了node_modules和构建目录
rsync -av --exclude node_modules --exclude .git --exclude dist --exclude build \
"$WORKSPACE_ROOT/$workspace/" "$git_repo_path/"
if [ $? -ne 0 ]; then
notice_error "源代码同步失败"
else
notice_success "源代码同步成功"
fi
# fi
fi
# 提交更改
notice_info "提交更改到Git仓库"
# 禁用干运行判断
# if [ "$CONFIG_DRY_RUN" = "true" ]; then
# notice_info "干运行模式:跳过提交和推送"
# else
local commit_message="自动同步: $workspace 工作区 - $(date '+%Y-%m-%d %H:%M:%S')"
(cd "$git_repo_path" && git add .)
git_commit "$git_repo_path" "$commit_message"
# 即使没有变更需要提交,也尝试推送
git_push "$git_repo_path" "$branch"
if [ $? -eq 0 ]; then
notice_success "同步工作区 $workspace$git_url 成功"
# 记录到历史文件
echo "$(date '+%Y-%m-%d %H:%M:%S') - 同步工作区 $workspace$git_url 成功" >> "$WORKSPACE_ROOT/$HISTORY_FILE"
else
notice_error "推送更改失败"
# 记录到历史文件
echo "$(date '+%Y-%m-%d %H:%M:%S') - 同步工作区 $workspace$git_url 失败" >> "$WORKSPACE_ROOT/$HISTORY_FILE"
continue
fi
# fi
done
return 0
}
#######################################
# 处理命令行参数
# 参数:
# $@: 命令行参数
# 返回值:
# 0 - 成功
# 1 - 失败
#######################################
parse_args() {
# 解析命令行参数
while [[ "$#" -gt 0 ]]; do
case $1 in
# 禁用并行编译选项
# --parallel|-p)
# CONFIG_PARALLEL_BUILD="true"
# ;;
# 禁用干运行选项
# --dry-run|-d)
# CONFIG_DRY_RUN="true"
# ;;
--help|-h)
show_help
exit 0
;;
*)
notice_error "未知参数: $1"
show_help
return 1
;;
esac
shift
done
return 0
}
#######################################
# 显示帮助信息
#######################################
show_help() {
echo "用法: sync-project.sh [选项]"
echo ""
echo "选项:"
# echo " --parallel, -p 启用并行编译"
# echo " --dry-run, -d 干运行模式,不执行实际操作"
echo " --help, -h 显示此帮助信息"
echo ""
echo "示例:"
# echo " ./sync-project.sh -p # 启用并行编译"
# echo " ./sync-project.sh --dry-run # 干运行模式"
}
#######################################
# 显示历史记录
# 返回值:
# 0 - 成功
# 1 - 失败
#######################################
show_history() {
local history_file="$WORKSPACE_ROOT/$HISTORY_FILE"
if [ ! -f "$history_file" ]; then
notice_warning "历史记录文件不存在"
return 1
fi
notice_info "最近的操作历史:"
# 显示最后10条记录
tail -n 10 "$history_file"
return 0
}
#######################################
# 主函数
# 参数:
# $@: 命令行参数
# 返回值:
# 0 - 成功
# 1 - 失败
#######################################
main() {
notice_info "欢迎使用 Turborepo 工作区编译部署自动化工具"
# 解析命令行参数
parse_args "$@"
# 初始化环境(只运行一次)
init_environment || exit 1
# 加载配置(只运行一次)
load_config || exit 1
# 扫描工作区
scan_workspaces || exit 1
# 选择工作区
select_workspaces || exit 1
# 编译和同步选中的工作区
for workspace in "${SELECTED_WORKSPACES[@]}"; do
# 禁用并行模式
# if [ "$CONFIG_PARALLEL_BUILD" = "true" ]; then
# # 并行模式
# build_workspace "$workspace" && sync_workspace "$workspace" &
# else
# 顺序模式
build_workspace "$workspace" && sync_workspace "$workspace"
# fi
done
# 禁用并行模式等待
# if [ "$CONFIG_PARALLEL_BUILD" = "true" ]; then
# wait
# fi
# 显示操作历史
show_history
notice_success "所有操作完成"
return 0
}
# 执行主函数
main "$@"