allinssl/frontend/scripts/temp/file-operations.sh

709 lines
22 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.

#!/bin/bash
# ===================================================
# 文件操作脚本 - file-operations.sh
# 用于处理文件同步、路径处理和目录结构管理
# ===================================================
# 颜色定义
if [[ -t 1 ]]; then # 检查是否在终端中运行
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
BLUE=$(tput setaf 4)
MAGENTA=$(tput setaf 5)
CYAN=$(tput setaf 6)
BOLD=$(tput bold)
DIM=$(tput dim)
NC=$(tput sgr0) # No Color
else
RED=""
GREEN=""
YELLOW=""
BLUE=""
MAGENTA=""
CYAN=""
BOLD=""
DIM=""
NC=""
fi
# 工具函数
log_info() {
printf "%s%s[INFO]%s %s\n" "${BOLD}" "${GREEN}" "${NC}" "$1"
}
log_debug() {
if [[ "$DEBUG_MODE" == "true" ]]; then
printf "%s%s[DEBUG]%s %s\n" "${BOLD}" "${MAGENTA}" "${NC}" "$1"
fi
}
log_warn() {
printf "%s%s[WARN]%s %s\n" "${BOLD}" "${YELLOW}" "${NC}" "$1"
}
log_error() {
printf "%s%s[ERROR]%s %s\n" "${BOLD}" "${RED}" "${NC}" "$1"
}
# 显示分隔线
show_separator() {
printf "%s%s%s\n" "${DIM}" "──────────────────────────────────────────" "${NC}"
}
# 显示标题
show_title() {
printf "\n%s%s%s%s\n\n" "${BOLD}" "${CYAN}" "$1" "${NC}"
}
# 显示进度条
show_progress() {
local current=$1
local total=$2
local width=30
local percentage=$((current * 100 / total))
local completed=$((width * current / total))
local remaining=$((width - completed))
printf "\r%s%s[%s%s%s] %d%%" \
"${BOLD}" "${CYAN}" \
"$(printf '%*s' "$completed" | tr ' ' '●')" \
"$(printf '%*s' "$remaining" | tr ' ' '○')" \
"${NC}" \
"$percentage"
}
# 初始化同步目录结构
init_sync_dirs() {
if [[ -z "$PROJECT_ROOT" ]]; then
log_error "项目根目录未初始化"
return 1
fi
# 创建同步配置目录
SYNC_DIR="$PROJECT_ROOT/.sync"
if [[ ! -d "$SYNC_DIR" ]]; then
mkdir -p "$SYNC_DIR" || {
log_error "创建同步配置目录失败: $SYNC_DIR"
return 1
}
log_info "已创建同步配置目录: $SYNC_DIR"
fi
# 创建 Git 仓库目录
GIT_DIR="$PROJECT_ROOT/.git-sync"
if [[ ! -d "$GIT_DIR" ]]; then
mkdir -p "$GIT_DIR" || {
log_error "创建 Git 仓库目录失败: $GIT_DIR"
return 1
}
log_info "已创建 Git 仓库目录: $GIT_DIR"
fi
# 初始化历史记录文件
HISTORY_FILE="$SYNC_DIR/history"
if [[ ! -f "$HISTORY_FILE" ]]; then
touch "$HISTORY_FILE" || {
log_error "创建历史记录文件失败: $HISTORY_FILE"
return 1
}
log_info "已创建历史记录文件: $HISTORY_FILE"
fi
# 初始化插件目录
PLUGINS_DIR="$SYNC_DIR/plugins"
if [[ ! -d "$PLUGINS_DIR" ]]; then
mkdir -p "$PLUGINS_DIR" || {
log_error "创建插件目录失败: $PLUGINS_DIR"
return 1
}
log_info "已创建插件目录: $PLUGINS_DIR"
fi
# 初始化同步配置文件
SYNC_CONFIG_FILE="$SYNC_DIR/sync-config.yaml"
if [[ ! -f "$SYNC_CONFIG_FILE" ]]; then
cat > "$SYNC_CONFIG_FILE" << EOF
# 工具配置
config:
parallel_build: false # 是否并行编译
dry_run: false # 是否干运行
# 工作区配置
workspaces:
# 示例配置
# app-name:
# sync_mappings:
# - source:
# git_url: "https://github.com/user/repo.git"
# branch: "main"
# target:
# sync_dir: "dist" # 要同步的目录
# git_dir: "dist" # Git 仓库中的目标目录
EOF
log_info "已创建同步配置文件: $SYNC_CONFIG_FILE"
fi
return 0
}
# 查找项目根目录
find_project_root() {
local current_dir="$PWD"
while [[ "$current_dir" != "/" ]]; do
if [[ -f "$current_dir/pnpm-workspace.yaml" ]]; then
PROJECT_ROOT="$current_dir"
log_info "找到项目根目录: $PROJECT_ROOT"
# 在找到根目录后,初始化相关路径
SYNC_DIR="$PROJECT_ROOT/.sync"
GIT_DIR="$PROJECT_ROOT/.git-sync"
SYNC_CONFIG_FILE="$SYNC_DIR/sync-config.yaml"
HISTORY_FILE="$SYNC_DIR/history"
PLUGINS_DIR="$SYNC_DIR/plugins"
return 0
fi
current_dir="$(dirname "$current_dir")"
done
log_error "未找到项目根目录"
return 1
}
# 检查并创建工作区配置目录
check_workspace_config_dir() {
# 检查工作区配置目录是否存在
if [[ ! -d "$WORKSPACE_CONFIG_DIR" ]]; then
log_info "工作区配置目录不存在,正在创建..."
mkdir -p "$WORKSPACE_CONFIG_DIR" || {
log_error "创建工作区配置目录失败: $WORKSPACE_CONFIG_DIR"
return 1
}
log_info "已创建工作区配置目录: $WORKSPACE_CONFIG_DIR"
# 创建 .gitignore 文件
cat > "$WORKSPACE_CONFIG_DIR/.gitignore" << EOF
# 忽略所有文件
*
# 不忽略 .gitignore
!.gitignore
EOF
log_info "已创建 .gitignore 文件"
# 创建 README.md 文件
cat > "$WORKSPACE_CONFIG_DIR/README.md" << EOF
# 工作区配置目录
此目录用于存储各个工作区的同步配置信息。
## 配置文件格式
每个工作区对应一个 YAML 配置文件,命名格式为 \`{workspace}.yaml\`。
### 配置示例
\`\`\`yaml
# 工作区同步配置
workspace: "app-name"
sync_mappings:
- source:
git_url: "https://github.com/user/repo.git"
branch: "main"
target:
sync_dir: "dist" # 要同步的目录
git_dir: "dist" # Git 仓库中的目标目录
\`\`\`
## 注意事项
1. 此目录不应被 Git 追踪
2. 配置文件包含敏感信息,请妥善保管
3. 建议定期备份配置文件
EOF
log_info "已创建 README.md 文件"
fi
# 检查目录权限
if [[ ! -w "$WORKSPACE_CONFIG_DIR" ]]; then
log_error "工作区配置目录没有写入权限: $WORKSPACE_CONFIG_DIR"
return 1
fi
return 0
}
# 同步文件操作函数
sync_files() {
local source_path="$PROJECT_ROOT/$SELECTED_WORKSPACE"
local dist_path="$source_path/dist"
# 检查源目录
if [[ ! -d "$dist_path" ]]; then
log_error "源目录不存在: $dist_path"
return 1
fi
# 询问是否同步项目结构
read -p "是否同步项目结构?(y/n): " sync_structure
if [[ "$sync_structure" == "y" ]]; then
# 同步项目结构
log_info "开始同步项目结构..."
# 创建临时目录
local temp_dir=$(mktemp -d)
# 复制项目结构
find "$source_path" -type f -not -path "*/node_modules/*" -not -path "*/dist/*" | while IFS= read -r file; do
local rel_path="${file#$source_path/}"
local target_path="$temp_dir/$rel_path"
mkdir -p "$(dirname "$target_path")"
cp "$file" "$target_path"
done
# 移动临时目录内容到目标目录
cp -r "$temp_dir"/* "$TARGET_GIT_DIR/"
rm -rf "$temp_dir"
fi
# 同步编译结果
log_info "开始同步编译结果..."
if [[ "$OS" == "windows" ]]; then
# Windows 路径处理
local target_dist="${TARGET_GIT_DIR}\\dist"
if [[ -d "$target_dist" ]]; then
rm -rf "$target_dist"
fi
cp -r "$dist_path" "$target_dist"
else
# Unix 路径处理
local target_dist="$TARGET_GIT_DIR/dist"
if [[ -d "$target_dist" ]]; then
rm -rf "$target_dist"
fi
cp -r "$dist_path" "$target_dist"
fi
log_info "文件同步完成"
return 0
}
# 同步所有源码文件
sync_all_source_files() {
local files=("$@")
local total=${#files[@]}
local current=0
for target_dir in "${TARGET_GIT_DIRS[@]}"; do
log_info "开始同步到: $target_dir"
for file in "${files[@]}"; do
local rel_path="${file#$PROJECT_ROOT/$SELECTED_WORKSPACE/}"
local target_path="$target_dir/$rel_path"
mkdir -p "$(dirname "$target_path")"
cp "$file" "$target_path"
((current++))
show_progress "$current" "$total"
done
printf "\n"
done
log_info "源码同步完成"
return 0
}
# 仅同步配置文件
sync_config_files() {
local files=("$@")
local config_files=()
for file in "${files[@]}"; do
if [[ "$file" =~ \.(json|yaml|yml|config\.js|config\.ts)$ ]]; then
config_files+=("$file")
fi
done
if [[ ${#config_files[@]} -eq 0 ]]; then
log_error "未找到配置文件"
return 1
fi
sync_all_source_files "${config_files[@]}"
}
# 仅同步源代码
sync_source_files() {
local files=("$@")
local source_files=()
for file in "${files[@]}"; do
if [[ "$file" =~ \.(js|ts|jsx|tsx|vue|css|scss|less)$ ]]; then
source_files+=("$file")
fi
done
if [[ ${#source_files[@]} -eq 0 ]]; then
log_error "未找到源代码文件"
return 1
fi
sync_all_source_files "${source_files[@]}"
}
# 记录操作历史
record_history() {
if [[ -z "$HISTORY_FILE" ]]; then
log_error "历史记录文件未初始化,请确保已找到项目根目录"
return 1
fi
local operation="$1" # 操作
local timestamp=$(date '+%Y-%m-%d %H:%M:%S') # 时间戳
echo "$timestamp|$operation" >> "$HISTORY_FILE" # 写入历史记录文件
# 保持历史记录在最大限制内
if [[ -f "$HISTORY_FILE" ]]; then
tail -n "$MAX_HISTORY" "$HISTORY_FILE" > "${HISTORY_FILE}.tmp"
mv "${HISTORY_FILE}.tmp" "$HISTORY_FILE"
fi
}
# 显示操作历史
show_history() {
if [[ -z "$HISTORY_FILE" ]]; then
log_error "历史记录文件未初始化,请确保已找到项目根目录"
return 1
fi
if [[ -f "$HISTORY_FILE" ]]; then
log_info "最近的操作历史:"
while IFS='|' read -r timestamp operation; do
echo "$timestamp - $operation"
done < "$HISTORY_FILE"
else
log_info "暂无操作历史"
fi
}
# 同步源码
sync_source_code() {
local workspace_path="$PROJECT_ROOT/$SELECTED_WORKSPACE"
local source_files=()
# 收集需要同步的源码文件
for file in $(find "$workspace_path" -type f); do
if [[ ! "$file" =~ /(node_modules|dist|\.git)/ ]]; then
source_files+=("$file")
fi
done
if [[ ${#source_files[@]} -eq 0 ]]; then
log_error "未找到需要同步的源码文件"
return 1
fi
# 显示同步选项
show_title "同步源码选项"
printf "%s%s%s\n" "${DIM}" "选择要同步的内容" "${NC}"
show_separator
local sync_options=(
"同步所有源码文件"
"仅同步配置文件"
"仅同步源代码"
"自定义同步"
)
local selected_index=0
local max_index=$((${#sync_options[@]}-1))
local padding=2
while true; do
clear
# 显示选项列表
show_title "同步源码选项"
printf "%s%s%s\n" "${DIM}" "选择要同步的内容" "${NC}"
show_separator
for i in "${!sync_options[@]}"; do
if [[ $i -eq $selected_index ]]; then
printf "%s%s%s%*s%s%s%s\n" \
"${BOLD}" "${CYAN}" "" \
"$padding" "" \
"${GREEN}" "${sync_options[$i]}" "${NC}"
else
printf "%*s%s%s%s\n" \
"$((padding + 1))" "" \
"${DIM}" "${sync_options[$i]}" "${NC}"
fi
done
show_separator
# 读取用户输入
local key_pressed=""
local key
read -r -n 1 key
# 获取ASCII码用于调试针对回车键
if [[ -z "$key" ]]; then
[[ "$DEBUG_MODE" == "true" ]] && printf "空字符,可能是回车键\n"
key_pressed="ENTER"
else
[[ "$DEBUG_MODE" == "true" ]] && printf "按键ASCII码: %d\n" "'$key"
fi
# 处理按键
case "$key" in
$'\x1b') # ESC 序列
read -r -n 2 key
case "$key" in
"[A") # 上箭头
if [[ $selected_index -gt 0 ]]; then
selected_index=$((selected_index-1))
else
# 循环到最后一项
selected_index=$max_index
fi
;;
"[B") # 下箭头
if [[ $selected_index -lt $max_index ]]; then
selected_index=$((selected_index+1))
else
# 循环到第一项
selected_index=0
fi
;;
esac
;;
"") # 回车
case $selected_index in
0) # 同步所有源码文件
sync_all_source_files "${source_files[@]}"
;;
1) # 仅同步配置文件
sync_config_files "${source_files[@]}"
;;
2) # 仅同步源代码
sync_source_files "${source_files[@]}"
;;
3) # 自定义同步
sync_custom_files "${source_files[@]}"
;;
esac
return $?
;;
"q") # 退出
log_error "操作已取消"
return 1
;;
esac
done
}
# 自定义同步文件选择
sync_custom_files() {
local files=("$@")
local selected_files=()
local selected_indices=()
local cursor_index=0
local max_index=$((${#files[@]}-1))
local padding=2
while true; do
clear
show_title "选择要同步的文件"
printf "%s%s%s\n" "${DIM}" "使用数字键1选中/0取消回车确认q 退出" "${NC}"
show_separator
# 显示文件列表
for i in "${!files[@]}"; do
local is_selected=false
# 检查当前文件是否已被选中
for idx in "${selected_indices[@]}"; do
if [[ $i -eq $idx ]]; then
is_selected=true
break
fi
done
if [[ $i -eq $cursor_index ]]; then
# 当前光标位置
if [[ "$is_selected" == "true" ]]; then
printf "%s%s%s%*s%s%s%s\n" \
"${BOLD}" "${CYAN}" "" \
"$padding" "" \
"${GREEN}" "[✓] ${files[$i]#$PROJECT_ROOT/}" "${NC}"
else
printf "%s%s%s%*s%s%s%s\n" \
"${BOLD}" "${CYAN}" "" \
"$padding" "" \
"${DIM}" "[ ] ${files[$i]#$PROJECT_ROOT/}" "${NC}"
fi
else
# 非光标位置
if [[ "$is_selected" == "true" ]]; then
printf "%*s%s%s%s\n" \
"$((padding + 1))" "" \
"${GREEN}" "[✓] ${files[$i]#$PROJECT_ROOT/}" "${NC}"
else
printf "%*s%s%s%s\n" \
"$((padding + 1))" "" \
"${DIM}" "[ ] ${files[$i]#$PROJECT_ROOT/}" "${NC}"
fi
fi
done
show_separator
# 显示操作提示
printf "\n%s%s使用上下箭头选择数字键1选中/0取消回车确认q键退出%s\n" \
"${BOLD}" "${CYAN}" "${NC}"
# 读取用户输入 - 统一处理方式
local key_pressed=""
local key
read -r -n 1 key
# 获取ASCII码用于调试
if [[ -z "$key" ]]; then
[[ "$DEBUG_MODE" == "true" ]] && printf "空字符,可能是回车键\n"
key_pressed="ENTER" # 空字符通常是回车键
elif [[ "$key" == "1" ]]; then
printf "检测到数字键1执行选中操作\n" # 始终输出不依赖DEBUG_MODE
key_pressed="SELECT" # 选中
elif [[ "$key" == "0" ]]; then
printf "检测到数字键0执行取消选中操作\n" # 始终输出不依赖DEBUG_MODE
key_pressed="DESELECT" # 取消选中
else
[[ "$DEBUG_MODE" == "true" ]] && printf "按键ASCII码: %d\n" "'$key"
# 处理其他按键
case "$key" in
$'\x1b') # ESC 序列,包括方向键
read -r -n 2 seq
[[ "$DEBUG_MODE" == "true" ]] && printf "ESC序列: %s\n" "$seq"
case "$seq" in
"[A") key_pressed="UP" ;; # 上箭头
"[B") key_pressed="DOWN" ;; # 下箭头
*) key_pressed="ESC" ;; # 其他ESC序列
esac
;;
"q"|"Q") # q键退出
key_pressed="QUIT"
;;
*) # 其他按键忽略
key_pressed="OTHER"
;;
esac
fi
# 调试信息
[[ "$DEBUG_MODE" == "true" ]] && log_debug "按键被解析为: $key_pressed"
# 处理操作
case "$key_pressed" in
"UP") # 上箭头
if [[ $cursor_index -gt 0 ]]; then
cursor_index=$((cursor_index-1))
else
# 如果已经是第一项,跳到最后一项
cursor_index=$max_index
fi
;;
"DOWN") # 下箭头
if [[ $cursor_index -lt $max_index ]]; then
cursor_index=$((cursor_index+1))
else
# 如果已经是最后一项,回到第一项
cursor_index=0
fi
;;
"SELECT") # 数字键1 - 选中当前项
printf "处理SELECT操作 - 选中当前项: ${cursor_index}\n"
# 检查当前索引是否已在选中列表中
local found=false
for idx in "${selected_indices[@]}"; do
if [[ $idx -eq $cursor_index ]]; then
found=true
break
fi
done
# 如果未选中,则添加到选中列表
if [[ "$found" == "false" ]]; then
selected_indices+=($cursor_index)
printf "已添加索引 ${cursor_index} 到选中列表\n"
# 对选中项排序
if [[ ${#selected_indices[@]} -gt 0 ]]; then
IFS=$'\n'
selected_indices=($(sort -n <<<"${selected_indices[*]}"))
unset IFS
fi
else
printf "索引 ${cursor_index} 已在选中列表中\n"
fi
;;
"DESELECT") # 数字键0 - 取消选中当前项
printf "处理DESELECT操作 - 取消选中当前项: ${cursor_index}\n"
# 检查当前索引是否已在选中列表中
local found=false
local new_indices=()
# 创建新数组,排除当前索引
for idx in "${selected_indices[@]}"; do
if [[ $idx -ne $cursor_index ]]; then
new_indices+=($idx)
else
found=true # 标记找到了要删除的索引
fi
done
# 只有在找到并删除了索引的情况下才更新选中列表
if [[ "$found" == "true" ]]; then
selected_indices=("${new_indices[@]}")
printf "已从选中列表中移除索引 ${cursor_index}\n"
else
printf "索引 ${cursor_index} 不在选中列表中\n"
fi
;;
"ENTER") # 回车 - 确认选择
if [[ ${#selected_indices[@]} -eq 0 ]]; then
log_error "请至少选择一个文件"
sleep 2 # 暂停显示错误信息
continue
fi
# 重建选中文件列表
selected_files=()
for idx in "${selected_indices[@]}"; do
selected_files+=("${files[$idx]}")
done
sync_all_source_files "${selected_files[@]}"
return $?
;;
"QUIT") # 退出
log_error "操作已取消"
return 1
;;
esac
done
}
# 导出函数
export -f log_info
export -f log_debug
export -f log_warn
export -f log_error
export -f show_separator
export -f show_title
export -f show_progress
export -f init_sync_dirs
export -f find_project_root
export -f check_workspace_config_dir
export -f sync_files
export -f sync_all_source_files
export -f sync_config_files
export -f sync_source_files
export -f record_history
export -f show_history
export -f sync_source_code
export -f sync_custom_files