mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-08 15:51:11 +08:00
【新增】插件git同步模块,用于同步项目内容,加速项目开发
【调整】前端暗色问题
This commit is contained in:
150
frontend/scripts/clear-temp.sh
Normal file
150
frontend/scripts/clear-temp.sh
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
|
||||
#######################################
|
||||
# 临时文件清理脚本
|
||||
#
|
||||
# 此脚本用于清理项目中的临时文件和构建产物,包括
|
||||
# node_modules、pnpm-lock.yaml、dist目录和.turbo目录等,
|
||||
# 便于重新安装依赖或重新构建项目。
|
||||
#
|
||||
# 作者: chudong
|
||||
# 版本: 1.0.0
|
||||
#######################################
|
||||
|
||||
# 导入通知处理脚本
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [ -f "$SCRIPT_DIR/tools/notice-handle.sh" ]; then
|
||||
source "$SCRIPT_DIR/tools/notice-handle.sh"
|
||||
else
|
||||
# 如果没有找到通知处理脚本,定义简单的替代函数
|
||||
function notice_info() { echo "[信息] $1"; }
|
||||
function notice_success() { echo "[成功] $1"; }
|
||||
function notice_warning() { echo "[警告] $1"; }
|
||||
function notice_error() { echo "[错误] $1"; }
|
||||
fi
|
||||
|
||||
#######################################
|
||||
# 清理特定类型的文件或目录
|
||||
# 参数:
|
||||
# $1: 查找的文件/目录名
|
||||
# $2: 类型 (f: 文件, d: 目录)
|
||||
# $3: 描述信息
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
clean_item() {
|
||||
local item_name="$1"
|
||||
local item_type="$2"
|
||||
local description="$3"
|
||||
local count=0
|
||||
|
||||
if [ -z "$item_name" ] || [ -z "$item_type" ]; then
|
||||
notice_error "清理项目需要名称和类型"
|
||||
return 1
|
||||
fi
|
||||
|
||||
notice_info "开始清理$description..."
|
||||
|
||||
if [ "$item_type" = "d" ]; then
|
||||
# 查找并删除目录
|
||||
count=$(find . -name "$item_name" -type d | wc -l)
|
||||
if [ $count -gt 0 ]; then
|
||||
find . -name "$item_name" -type d -prune -exec rm -rf {} \; 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "已清理$count个$description"
|
||||
else
|
||||
notice_error "清理$description时出错"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
notice_info "未找到任何$description"
|
||||
fi
|
||||
elif [ "$item_type" = "f" ]; then
|
||||
# 查找并删除文件
|
||||
count=$(find . -name "$item_name" -type f | wc -l)
|
||||
if [ $count -gt 0 ]; then
|
||||
find . -name "$item_name" -type f -delete 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "已清理${count}个${description}"
|
||||
else
|
||||
notice_error "清理${description}时出错"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
notice_info "未找到任何${description}"
|
||||
fi
|
||||
else
|
||||
notice_error "不支持的项目类型: $item_type"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 清理项目中的所有临时文件和构建产物
|
||||
# 参数:
|
||||
# $1: 起始目录,默认为当前目录
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
clean_all() {
|
||||
local start_dir="${1:-.}"
|
||||
local total_success=0
|
||||
local total_failed=0
|
||||
|
||||
notice_info "开始清理临时文件和构建产物..."
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到指定目录
|
||||
cd "$start_dir" || {
|
||||
notice_error "无法切换到目录: $start_dir"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 清理 node_modules 目录
|
||||
clean_item "node_modules" "d" "依赖目录"
|
||||
[ $? -eq 0 ] && ((total_success++)) || ((total_failed++))
|
||||
|
||||
# 清理 pnpm-lock.yaml 文件
|
||||
clean_item "pnpm-lock.yaml" "f" "包管理器锁定文件"
|
||||
[ $? -eq 0 ] && ((total_success++)) || ((total_failed++))
|
||||
|
||||
# 清理 dist 目录
|
||||
clean_item "dist" "d" "构建产物目录"
|
||||
[ $? -eq 0 ] && ((total_success++)) || ((total_failed++))
|
||||
|
||||
# 清理 .turbo 目录
|
||||
clean_item ".turbo" "d" "Turbo缓存目录"
|
||||
[ $? -eq 0 ] && ((total_success++)) || ((total_failed++))
|
||||
|
||||
# 返回原始目录
|
||||
cd "$current_dir"
|
||||
|
||||
# 显示清理结果
|
||||
if [ $total_failed -eq 0 ]; then
|
||||
notice_success "所有项目清理成功"
|
||||
return 0
|
||||
else
|
||||
notice_warning "清理结果: $total_success 成功, $total_failed 失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 主函数
|
||||
#######################################
|
||||
main() {
|
||||
# 清理所有临时文件和构建产物
|
||||
clean_all
|
||||
# 显示完成信息
|
||||
notice_info "清理操作已完成"
|
||||
notice_info "您现在可以重新安装依赖并构建项目"
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main
|
||||
@@ -1,194 +0,0 @@
|
||||
|
||||
# 多功能任务同步工作流管理平台项目文档
|
||||
|
||||
|
||||
## 一、项目概述
|
||||
|
||||
### 1. 项目目标
|
||||
打造一个可视化、可扩展的任务同步工作流管理平台,支持多种主流任务执行类型的配置、管理与自动化执行,帮助用户高效管理跨平台、跨工具的任务流程,提升开发、运维及文件处理效率。
|
||||
|
||||
|
||||
### 2. 核心价值
|
||||
- **统一管理**:通过标准化界面管理文件同步、项目编译、代码同步等多类型任务,避免工具碎片化。
|
||||
- **灵活配置**:支持动态添加任务类型,每种任务类型提供专属配置表单,适配不同场景需求。
|
||||
- **高效执行**:集成主流协议(SFTP/FTP)、开发工具(TurboRepo、Git)及本地操作,一键触发任务执行并实时反馈状态。
|
||||
|
||||
|
||||
### 3. 技术架构
|
||||
#### 后端
|
||||
- **框架**:Egg.js(Node.js 企业级框架,支持高性能 API 开发与插件扩展)
|
||||
- **数据库**:SQLite(轻量级文件型数据库,适合快速原型与小型部署)
|
||||
- **任务执行**:通过 Node.js 原生模块(如 `child_process`)调用系统命令,集成 SFTP/FTP 客户端库(如 `ssh2-sftp-client`)、Git 工具库(如 `simple-git`)实现任务逻辑。
|
||||
|
||||
#### 前端
|
||||
- **构建工具**:Vite 6(极速开发服务器与优化构建)
|
||||
- **框架**:Svelte 5(轻量响应式 UI 框架,组件化开发)
|
||||
- **UI 组件库**:Shadcn-Svelte(现代风格、可自定义的组件集合,含表单、表格、模态框等)
|
||||
- **状态管理**:Svelte 原生响应式系统 + 轻量表单库(如 `svelte-forms-library`)
|
||||
|
||||
|
||||
### 4. 适用场景
|
||||
- **开发团队**:管理项目编译、代码同步(Git)、微服务架构下的项目分离(TurboRepo)。
|
||||
- **运维/文件处理**:批量配置文件本地同步、跨服务器 SFTP/FTP 上传任务。
|
||||
- **自动化场景**:通过定时触发或手动执行,减少重复性操作。
|
||||
|
||||
|
||||
## 二、核心功能模块
|
||||
|
||||
### 1. 工作流基础管理
|
||||
#### (1)工作流列表
|
||||
- **展示字段**:任务名称、类型(文件同步/SFTP上传等)、状态(待执行/进行中/成功/失败)、创建时间、操作(编辑/删除/立即执行)。
|
||||
- **交互功能**:
|
||||
- 搜索过滤:支持按任务名称、类型关键词搜索。
|
||||
- 排序功能:按创建时间、执行状态升/降序排列。
|
||||
- 批量操作:可选删除多个任务(需二次确认)。
|
||||
|
||||
#### (2)增删改查操作
|
||||
- **添加任务**:
|
||||
1. 选择任务类型(下拉菜单,含 5 种预设类型,预留扩展接口)。
|
||||
2. 动态加载该类型专属配置表单(见下文各类型详情)。
|
||||
3. 提交后生成任务记录,状态默认「待执行」。
|
||||
- **编辑任务**:支持修改配置参数,保存后不影响历史执行记录。
|
||||
- **删除任务**:物理删除数据库记录,关联执行日志可选择保留或清除。
|
||||
|
||||
|
||||
### 2. 任务类型与配置详情
|
||||
|
||||
#### (1)文件本地同步
|
||||
- **核心功能**:将本地文件/目录从源路径复制到目标路径,支持增量同步(可选)。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- 入口文件地址:文件/目录选择组件(支持系统文件弹窗选择,显示绝对路径)
|
||||
- 出口文件地址:同上
|
||||
- 同步模式:立即同步/定时同步(可选,需后续扩展定时模块)
|
||||
|
||||
#### (2)文件 SFTP/FTP 上传
|
||||
- **核心功能**:通过 SFTP(安全传输)或 FTP 协议将本地文件上传至远程服务器。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- 入口文件地址:本地文件/目录选择
|
||||
- 协议类型:单选按钮(SFTP/FTP,默认 SFTP)
|
||||
- 远程服务器信息:
|
||||
- IP 地址(必填)
|
||||
- 端口(选填,SFTP 默认为 22,FTP 默认为 21)
|
||||
- 用户名(必填)
|
||||
- 密码(必填,输入框掩码处理)
|
||||
|
||||
#### (3)项目编译
|
||||
- **核心功能**:在指定项目路径执行自定义编译命令(如 `npm run build`、`make` 等)。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- 项目路径:选择项目根目录(文件选择组件,校验是否存在 `package.json` 等标识文件)
|
||||
- 编译命令:文本输入框(支持多行,如分步骤执行命令,用 `&&` 分隔)
|
||||
- 环境变量:选填(可配置编译所需的环境参数,如 `NODE_ENV=production`)
|
||||
|
||||
#### (4)Git 项目同步
|
||||
- **核心功能**:克隆、拉取或推送 Git 仓库,支持代码同步与版本控制。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- Git 项目地址:输入框(支持 `http`/`https`/`ssh` 协议,如 `git@github.com:user/repo.git`)
|
||||
- 本地存储路径:选填(默认克隆至系统临时目录,可自定义本地存放路径)
|
||||
- 操作类型:单选(克隆/拉取/推送,默认拉取)
|
||||
|
||||
#### (5)项目分离(TurboRepo)
|
||||
- **核心功能**:基于 TurboRepo 架构分离 monorepo 中的子项目,支持独立构建或发布。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- 分离的项目名称:输入框(需与 TurboRepo 配置中的 `name` 字段匹配)
|
||||
- 根项目路径:选择 TurboRepo 根目录(校验是否存在 `turbo.json` 配置文件)
|
||||
- 操作类型:单选(分离并构建/仅分离,默认分离并构建)
|
||||
|
||||
|
||||
### 3. 任务执行与反馈
|
||||
- **触发方式**:
|
||||
- 手动触发:列表页「立即执行」按钮。
|
||||
- 定时触发:后续扩展(需添加 cron 表达式配置字段)。
|
||||
- **执行状态**:
|
||||
- 进行中:显示加载动画,禁用编辑/删除操作。
|
||||
- 成功:记录执行时间,可查看日志详情(如同步文件数量、编译输出)。
|
||||
- 失败:显示错误原因(如网络异常、权限不足),支持重试。
|
||||
- **日志系统**:
|
||||
- 记录每次执行的开始/结束时间、状态、关键参数及错误信息。
|
||||
- 日志详情页支持关键词搜索、导出为文本文件。
|
||||
|
||||
|
||||
## 三、技术实现细节
|
||||
|
||||
### 1. 后端(Egg.js + SQLite)
|
||||
#### (1)数据模型设计
|
||||
`Workflow` 表字段:
|
||||
| 字段名 | 类型 | 说明 |
|
||||
|----------------|--------------|----------------------------------------------------------------------|
|
||||
| id | INTEGER | 自增主键 |
|
||||
| name | VARCHAR(50) | 任务名称(唯一) |
|
||||
| type | VARCHAR(20) | 任务类型(file_local, sftp_ftp, compile, git, turborepo) |
|
||||
| config | TEXT | 配置参数(JSON 字符串,存储各类型专属字段,如 SFTP 的 IP、端口等) |
|
||||
| status | VARCHAR(20) | 状态(pending/running/success/failed,默认 pending) |
|
||||
| create_time | DATETIME | 创建时间(默认当前时间) |
|
||||
|
||||
#### (2)API 接口设计
|
||||
- **列表接口**:`GET /workflows`,支持分页、搜索、排序(返回包含状态、类型、操作按钮的列表数据)。
|
||||
- **创建/编辑接口**:`POST /workflows`、`PUT /workflows/:id`,接收包含 `type` 和 `config` 的 JSON 数据。
|
||||
- **执行接口**:`POST /workflows/:id/execute`,触发任务执行逻辑,返回执行任务 ID 用于状态轮询。
|
||||
- **日志接口**:`GET /workflows/:id/logs`,获取该任务历史执行日志。
|
||||
|
||||
#### (3)任务执行逻辑
|
||||
- 基于 `child_process.spawn` 执行本地命令(如编译、Git 操作),通过流处理实时捕获输出日志。
|
||||
- SFTP/FTP 上传使用 `ssh2-sftp-client` 库,支持连接池管理与错误重试(3 次失败后终止)。
|
||||
- 任务执行时通过 Egg.js 插件(如 `egg-redis`)实现分布式锁(后续扩展多实例部署)。
|
||||
|
||||
|
||||
### 2. 前端(Svelte 5 + Shadcn-Svelte)
|
||||
#### (1)组件结构
|
||||
- **核心组件**:
|
||||
- `WorkflowList.svelte`:任务列表,包含搜索栏、操作按钮,集成 `shadcn-svelte` 的 `Table`、`Button` 组件。
|
||||
- `WorkflowForm.svelte`:动态表单,根据 `type` 渲染不同字段(如 SFTP 表单包含 IP/端口输入,Git 表单包含仓库地址输入)。
|
||||
- `FileSelect.svelte`:封装文件/目录选择组件,基于原生 `<input type="file">` 扩展目录选择(需浏览器支持 `webkitdirectory` 属性)。
|
||||
- **状态管理**:
|
||||
- 使用 Svelte 响应式变量(`let config = {}`)存储表单数据,通过 `bind:value` 绑定输入框。
|
||||
- 表单提交前校验必填字段(如 SFTP 的 IP、用户名),错误信息通过 `shadcn-svelte` 的 `Alert` 组件提示。
|
||||
|
||||
#### (2)动态表单实现
|
||||
```svelte
|
||||
{#if type === 'sftp_ftp'}
|
||||
<SFTPConfigForm bind:config />
|
||||
{:else if type === 'compile'}
|
||||
<CompileConfigForm bind:config />
|
||||
{:else if type === 'git'}
|
||||
<GitConfigForm bind:config />
|
||||
{/if}
|
||||
```
|
||||
每个类型对应独立子组件,封装专属字段逻辑,提升可维护性。
|
||||
|
||||
#### (3)执行状态交互
|
||||
- 点击「立即执行」按钮后,禁用按钮并显示加载状态,通过 WebSocket(后续扩展)或轮询接口(`GET /workflows/:id/status`)实时更新任务状态。
|
||||
- 失败状态显示红色警告,附带「重试」按钮,一键重新提交执行请求。
|
||||
|
||||
|
||||
## 四、技术栈文档索引
|
||||
1. **Egg.js 框架**
|
||||
- 官网:[https://www.eggjs.org/zh-CN](https://www.eggjs.org/zh-CN)
|
||||
- 核心功能:插件机制、中间件、ORM 集成(本项目使用 SQLite 原生驱动)。
|
||||
|
||||
2. **Shadcn-Svelte**
|
||||
- 官网:[https://next.shadcn-svelte.com/](https://next.shadcn-svelte.com/)
|
||||
- 组件列表:按钮、表单、模态框、表格等,支持完全自定义样式(基于 Tailwind CSS)。
|
||||
|
||||
3. **Svelte 5**
|
||||
- 官网:[https://svelte.dev/](https://svelte.dev/)
|
||||
- 特性:组件化开发、响应式系统、编译时优化,适合构建高性能前端界面。
|
||||
|
||||
4. **Vite 6**
|
||||
- 官网:[https://cn.vitejs.dev/](https://cn.vitejs.dev/)
|
||||
- 优势:极速冷启动、按需编译、支持 Svelte 单文件组件(.svelte)热更新。
|
||||
|
||||
|
||||
## 五、后续扩展方向
|
||||
1. **定时任务**:添加 cron 表达式配置,支持按计划执行任务(如每天凌晨同步文件)。
|
||||
2. **可视化工作流编辑器**:允许用户通过拖拽节点配置多步骤任务流程(如先编译项目,再同步至服务器)。
|
||||
3. **权限管理**:区分管理员与普通用户,控制任务创建、编辑权限。
|
||||
4. **多语言支持**:集成 i18n 插件,适配中英文界面。
|
||||
|
||||
|
||||
|
||||
通过以上设计,项目实现了多类型任务的统一管理与高效执行,结合现代技术栈确保了开发效率与用户体验,适用于中小型团队及个人用户的自动化任务场景。
|
||||
96
frontend/scripts/sync-project.md
Normal file
96
frontend/scripts/sync-project.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# 项目同步工具文档
|
||||
|
||||
## 功能名称
|
||||
**跨平台 Turborepo 工作区编译部署自动化工具**
|
||||
|
||||
## 功能简述
|
||||
这是一款基于纯 Shell 脚本开发的自动化工具,借助交互式命令行界面,能够对 Turborepo 项目的应用工作区进行扫描。它支持选定工作区的编译操作,还能把编译结果同步到指定的 Git 仓库,并且可以选择性地同步过滤后的项目结构。该工具最大的特点是实现了跨平台兼容,可在 Windows、macOS 和 Linux 系统上稳定运行。
|
||||
|
||||
## 技术规格
|
||||
- **实现语言**:严格遵循 POSIX 标准的 Shell 脚本
|
||||
- **跨平台方案**:
|
||||
- 在 Windows 系统中,依赖 Git Bash/MSYS2 环境运行
|
||||
- 在 macOS/Linux 系统上,直接使用原生 bash/zsh 环境
|
||||
- **核心依赖工具**:
|
||||
- 需要预先安装 pnpm 包管理工具
|
||||
- 必须安装 git 版本控制工具
|
||||
- 要具备标准 Unix 工具集,像 find、grep、sed 等
|
||||
- 可选安装 yq 工具用于 YAML 配置管理
|
||||
|
||||
## 目录结构
|
||||
所有配置和生成的文件统一放在项目根目录的 `.sync` 目录中:
|
||||
|
||||
- **.sync/**:工具根目录
|
||||
- `sync-config.yaml`:工作区同步配置文件
|
||||
- `history`:操作历史记录
|
||||
- `git-repos/`:Git 仓库同步目录,用于存储同步的 Git 仓库内容
|
||||
- `plugins/`:插件目录
|
||||
|
||||
## 配置文件格式
|
||||
```yaml
|
||||
# 工具配置
|
||||
config:
|
||||
parallel_build: false # 是否并行编译
|
||||
dry_run: false # 是否干运行
|
||||
|
||||
# 工作区配置
|
||||
workspaces:
|
||||
# 证书管理项目同步配置
|
||||
allin-ssl:
|
||||
sync_mappings:
|
||||
- git:
|
||||
url: "http://git.bt.cn/wzz/allinssl.git"
|
||||
branch: "1.0.1"
|
||||
alias: "allinssl-gitlab"
|
||||
sync: ["/dist", "/build"] # 要同步的目录,只支持两个字段
|
||||
source: true # 是否同步源码
|
||||
- git:
|
||||
url: "https://github.com/allinssl/allinssl.git"
|
||||
branch: "1.0.1"
|
||||
alias: "allinssl-github"
|
||||
sync: ["/dist", "/build"] # 要同步的目录,只支持两个字段
|
||||
source: true # 是否同步源码
|
||||
```
|
||||
|
||||
## 交互方式
|
||||
- **菜单驱动界面**:
|
||||
- 运用上下方向键来进行选项导航
|
||||
- 通过回车键完成确认操作
|
||||
- 使用数字键1/0进行选择/取消选择
|
||||
- **多步骤流程引导**:每个操作步骤都配有明确的提示信息和状态反馈
|
||||
- **彩色日志输出**:对不同类型的信息,如错误、警告、成功等,使用不同颜色进行区分显示
|
||||
- **步骤可视化**:直观显示当前操作进度和已完成步骤
|
||||
|
||||
## 优化流程
|
||||
1. **初始化**:检查环境和项目结构(只执行一次)
|
||||
2. **编译阶段**:先编译当前工作区的代码
|
||||
3. **Git准备阶段**:检查Git目录,必要时拉取最新代码
|
||||
4. **同步阶段**:将编译后的代码复制到Git目录(支持多目录同步)
|
||||
5. **提交阶段**:提交并推送更改到远程仓库
|
||||
|
||||
## 使用方法
|
||||
```bash
|
||||
./sync-project.sh [选项]
|
||||
```
|
||||
|
||||
选项:
|
||||
- `--parallel, -p` - 启用并行编译
|
||||
- `--dry-run, -d` - 干运行模式,不执行实际操作
|
||||
- `--help, -h` - 显示帮助信息
|
||||
|
||||
## 兼容保证
|
||||
- **路径处理**:自动处理不同操作系统的路径分隔符差异
|
||||
- **命令适配**:针对不同系统提供兼容的命令实现
|
||||
- **错误处理**:提供统一的错误处理和日志记录机制
|
||||
|
||||
## 性能优化
|
||||
- 避免重复运行相同的检测逻辑
|
||||
- 缓存配置文件加载结果
|
||||
- 仅在必要时检查依赖工具
|
||||
- 并行执行支持用于多工作区同步
|
||||
|
||||
## 维护与更新
|
||||
- 定期检查和更新依赖工具
|
||||
- 添加新功能时遵循模块化设计
|
||||
- 保持脚本的跨平台兼容性
|
||||
- 更新文档以反映新增功能和变更
|
||||
660
frontend/scripts/sync-project.sh
Normal file
660
frontend/scripts/sync-project.sh
Normal file
@@ -0,0 +1,660 @@
|
||||
#!/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 "$@"
|
||||
589
frontend/scripts/temp/build-operations.sh
Normal file
589
frontend/scripts/temp/build-operations.sh
Normal file
@@ -0,0 +1,589 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ===================================================
|
||||
# 项目编译处理脚本 - build-operations.sh
|
||||
# 用于处理项目编译、工作区选择和编译结果检查
|
||||
# ===================================================
|
||||
|
||||
# 依赖文件操作脚本的函数
|
||||
source "$(dirname "$0")/file-operations.sh"
|
||||
|
||||
# 检查依赖
|
||||
check_dependencies() {
|
||||
local deps=("pnpm" "git")
|
||||
local missing_deps=()
|
||||
|
||||
# 检查基本依赖
|
||||
for dep in "${deps[@]}"; do
|
||||
if ! command -v "$dep" &> /dev/null; then
|
||||
missing_deps+=("$dep")
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查 yq
|
||||
if ! command -v yq &> /dev/null; then
|
||||
log_error "未安装 yq,这是必需的依赖"
|
||||
log_info "请按照以下步骤安装 yq:"
|
||||
case "$(uname -s)" in
|
||||
"Darwin")
|
||||
log_info "1. 使用 Homebrew 安装:"
|
||||
log_info " brew install yq"
|
||||
;;
|
||||
"Linux")
|
||||
log_info "1. 使用包管理器安装:"
|
||||
log_info " # Ubuntu/Debian"
|
||||
log_info " sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64"
|
||||
log_info " sudo chmod a+x /usr/local/bin/yq"
|
||||
log_info " # CentOS/RHEL"
|
||||
log_info " sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64"
|
||||
log_info " sudo chmod a+x /usr/local/bin/yq"
|
||||
;;
|
||||
"MINGW"*|"MSYS"*)
|
||||
log_info "1. 使用 Chocolatey 安装:"
|
||||
log_info " choco install yq"
|
||||
;;
|
||||
esac
|
||||
log_info "2. 安装完成后重新运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查其他缺失的依赖
|
||||
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
||||
log_error "未找到必要的依赖: ${missing_deps[*]}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检测操作系统
|
||||
detect_os() {
|
||||
case "$(uname -s)" in
|
||||
"Darwin")
|
||||
OS="macos"
|
||||
;;
|
||||
"Linux")
|
||||
OS="linux"
|
||||
;;
|
||||
"MINGW"*|"MSYS"*)
|
||||
OS="windows"
|
||||
;;
|
||||
*)
|
||||
log_error "不支持的操作系统"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
log_info "检测到操作系统: $OS"
|
||||
}
|
||||
|
||||
# 解析工作区
|
||||
parse_workspaces() {
|
||||
if [[ ! -f "$PROJECT_ROOT/pnpm-workspace.yaml" ]]; then
|
||||
log_error "未找到工作区配置文件"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查 apps 目录
|
||||
if [[ ! -d "$PROJECT_ROOT/apps" ]]; then
|
||||
log_error "未找到 apps 目录"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 扫描 apps 目录下的子目录作为工作区
|
||||
local workspaces=()
|
||||
for dir in "$PROJECT_ROOT/apps"/*/; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
local rel_path="${dir#$PROJECT_ROOT/apps/}"
|
||||
rel_path="${rel_path%/}"
|
||||
workspaces+=("$rel_path")
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查是否找到工作区
|
||||
if [[ ${#workspaces[@]} -eq 0 ]]; then
|
||||
log_error "未在 apps 目录下找到任何工作区"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 初始化选择
|
||||
local selected_index=0
|
||||
local max_index=$((${#workspaces[@]}-1))
|
||||
|
||||
# 设置当前步骤
|
||||
CURRENT_STEP=1
|
||||
|
||||
# 显示工作区列表
|
||||
while true; do
|
||||
# 显示步骤状态
|
||||
show_steps
|
||||
|
||||
# 打印工作区列表
|
||||
printf "%s%s选择当前项目工作区%s\n\n" "${BOLD}" "${CYAN}" "${NC}"
|
||||
|
||||
for i in "${!workspaces[@]}"; do
|
||||
if [[ $i -eq $selected_index ]]; then
|
||||
printf "%s%s❯ %s%s\n" \
|
||||
"${BOLD}" "${GREEN}" "${workspaces[$i]}" "${NC}"
|
||||
else
|
||||
printf " %s%s%s\n" \
|
||||
"${DIM}" "${workspaces[$i]}" "${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# 显示操作提示
|
||||
printf "\n%s%s使用上下箭头选择,回车确认,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" # 空字符通常是回车键
|
||||
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 [[ $selected_index -gt 0 ]]; then
|
||||
selected_index=$((selected_index-1))
|
||||
else
|
||||
# 如果已经是第一项,跳到最后一项
|
||||
selected_index=$max_index
|
||||
fi
|
||||
;;
|
||||
"DOWN") # 下箭头
|
||||
if [[ $selected_index -lt $max_index ]]; then
|
||||
selected_index=$((selected_index+1))
|
||||
else
|
||||
# 如果已经是最后一项,回到第一项
|
||||
selected_index=0
|
||||
fi
|
||||
;;
|
||||
"ENTER") # 回车
|
||||
SELECTED_WORKSPACE="${workspaces[$selected_index]}"
|
||||
STEP_WORKSPACE="$SELECTED_WORKSPACE"
|
||||
CURRENT_STEP=2
|
||||
return 0
|
||||
;;
|
||||
"QUIT") # 退出
|
||||
log_error "操作已取消"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# 显示步骤状态
|
||||
show_steps() {
|
||||
clear
|
||||
printf "\n%s%s项目同步向导%s\n\n" "${BOLD}" "${MAGENTA}" "${NC}"
|
||||
|
||||
# 定义灰色文本样式,用于未到达的步骤
|
||||
local GRAY="${DIM}"
|
||||
|
||||
# 步骤一:选择工作区
|
||||
if [[ $CURRENT_STEP -eq 1 ]]; then
|
||||
printf "%s%s▶ 第一步:选择工作区%s\n" "${BOLD}" "${GREEN}" "${NC}"
|
||||
elif [[ $CURRENT_STEP -gt 1 ]]; then
|
||||
printf "%s%s✓ 第一步:选择工作区%s %s- %s%s%s\n" \
|
||||
"${DIM}" "${GREEN}" "${NC}" \
|
||||
"${DIM}" "${CYAN}" "$STEP_WORKSPACE" "${NC}"
|
||||
else
|
||||
printf "%s%s○ 第一步:选择工作区%s\n" "${GRAY}" "${GRAY}" "${NC}"
|
||||
fi
|
||||
|
||||
# 步骤二:选择Git仓库
|
||||
if [[ $CURRENT_STEP -eq 2 ]]; then
|
||||
printf "%s%s▶ 第二步:选择Git仓库%s\n" "${BOLD}" "${GREEN}" "${NC}"
|
||||
elif [[ $CURRENT_STEP -gt 2 ]]; then
|
||||
printf "%s%s✓ 第二步:选择Git仓库%s %s- %s%s%s\n" \
|
||||
"${DIM}" "${GREEN}" "${NC}" \
|
||||
"${DIM}" "${CYAN}" "$STEP_GIT_REPOS" "${NC}"
|
||||
else
|
||||
printf "%s%s○ 第二步:选择Git仓库%s\n" "${GRAY}" "${GRAY}" "${NC}"
|
||||
fi
|
||||
|
||||
# 步骤三:选择同步方式
|
||||
if [[ $CURRENT_STEP -eq 3 ]]; then
|
||||
printf "%s%s▶ 第三步:选择同步方式%s\n" "${BOLD}" "${GREEN}" "${NC}"
|
||||
elif [[ $CURRENT_STEP -gt 3 ]]; then
|
||||
printf "%s%s✓ 第三步:选择同步方式%s %s- %s%s%s\n" \
|
||||
"${DIM}" "${GREEN}" "${NC}" \
|
||||
"${DIM}" "${CYAN}" "$STEP_SYNC_MODE" "${NC}"
|
||||
else
|
||||
printf "%s%s○ 第三步:选择同步方式%s\n" "${GRAY}" "${GRAY}" "${NC}"
|
||||
fi
|
||||
|
||||
# 步骤四:执行同步
|
||||
if [[ $CURRENT_STEP -eq 4 ]]; then
|
||||
printf "%s%s▶ 第四步:执行同步%s\n" "${BOLD}" "${GREEN}" "${NC}"
|
||||
elif [[ $CURRENT_STEP -gt 4 ]]; then
|
||||
printf "%s%s✓ 第四步:执行同步%s\n" "${DIM}" "${GREEN}" "${NC}"
|
||||
else
|
||||
printf "%s%s○ 第四步:执行同步%s\n" "${GRAY}" "${GRAY}" "${NC}"
|
||||
fi
|
||||
|
||||
# 分隔线
|
||||
printf "\n"
|
||||
show_separator
|
||||
printf "\n"
|
||||
}
|
||||
|
||||
# 编译执行模块
|
||||
build_workspace() {
|
||||
log_info "开始编译工作区: $SELECTED_WORKSPACE"
|
||||
|
||||
# 切换到项目根目录
|
||||
cd "$PROJECT_ROOT" || {
|
||||
log_error "无法切换到项目根目录"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 执行编译命令
|
||||
log_info "执行编译命令: pnpm build --filter $SELECTED_WORKSPACE"
|
||||
if pnpm build --filter "$SELECTED_WORKSPACE"; then
|
||||
log_info "编译成功"
|
||||
return 0
|
||||
else
|
||||
log_error "编译失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查编译结果
|
||||
check_build_result() {
|
||||
local workspace_path="$PROJECT_ROOT/apps/$SELECTED_WORKSPACE"
|
||||
local dist_path="$workspace_path/dist"
|
||||
|
||||
if [[ ! -d "$dist_path" ]]; then
|
||||
log_error "未找到编译输出目录: $dist_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -z "$(ls -A "$dist_path")" ]]; then
|
||||
log_error "编译输出目录为空"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "编译结果检查通过"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 并行编译功能
|
||||
parallel_build_workspaces() {
|
||||
local workspaces=("$@")
|
||||
local pids=()
|
||||
local results=()
|
||||
|
||||
# 检查参数
|
||||
if [[ ${#workspaces[@]} -eq 0 ]]; then
|
||||
log_error "未指定工作区"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "开始并行编译 ${#workspaces[@]} 个工作区..."
|
||||
|
||||
# 为每个工作区启动编译进程
|
||||
for workspace in "${workspaces[@]}"; do
|
||||
(
|
||||
log_info "开始编译工作区: $workspace"
|
||||
if pnpm build --filter "$workspace"; then
|
||||
echo "$workspace|success" > "/tmp/build_${workspace}.result"
|
||||
else
|
||||
echo "$workspace|failed" > "/tmp/build_${workspace}.result"
|
||||
fi
|
||||
) &
|
||||
pids+=($!)
|
||||
done
|
||||
|
||||
# 等待所有编译进程完成
|
||||
for pid in "${pids[@]}"; do
|
||||
wait "$pid" || {
|
||||
log_error "编译进程异常退出"
|
||||
return 1
|
||||
}
|
||||
done
|
||||
|
||||
# 收集编译结果
|
||||
local success=true
|
||||
for workspace in "${workspaces[@]}"; do
|
||||
if [[ -f "/tmp/build_${workspace}.result" ]]; then
|
||||
local result=$(cat "/tmp/build_${workspace}.result")
|
||||
local status=$(echo "$result" | cut -d'|' -f2)
|
||||
if [[ "$status" == "failed" ]]; then
|
||||
log_error "工作区 $workspace 编译失败"
|
||||
success=false
|
||||
else
|
||||
log_info "工作区 $workspace 编译成功"
|
||||
fi
|
||||
rm -f "/tmp/build_${workspace}.result"
|
||||
else
|
||||
log_error "工作区 $workspace 编译结果文件丢失"
|
||||
success=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$success" == "true" ]]; then
|
||||
log_info "所有工作区编译完成"
|
||||
return 0
|
||||
else
|
||||
log_error "部分工作区编译失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示选项列表
|
||||
show_option_list() {
|
||||
local title="$1"
|
||||
shift
|
||||
local selected_index="${!#}" # 取最后一个参数
|
||||
local items=("${@:1:$(($#-1))}") # 除最后一个参数外的所有参数
|
||||
local padding=2 # 选中标识的宽度
|
||||
|
||||
# 显示标题
|
||||
show_title "$title"
|
||||
printf "%s%s%s\n" "${DIM}" "使用方向键选择,回车确认,q 退出" "${NC}"
|
||||
show_separator
|
||||
|
||||
# 显示列表
|
||||
for i in "${!items[@]}"; do
|
||||
if [[ $i -eq $selected_index ]]; then
|
||||
# 选中项:使用固定宽度的选中标识
|
||||
printf "%s%s%s%*s%s%s%s\n" \
|
||||
"${BOLD}" "${CYAN}" "❯" \
|
||||
"$padding" "" \
|
||||
"${GREEN}" "${items[$i]}" "${NC}"
|
||||
else
|
||||
# 未选中项:使用相同的缩进保持对齐
|
||||
printf "%*s%s%s%s\n" \
|
||||
"$((padding + 1))" "" \
|
||||
"${DIM}" "${items[$i]}" "${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
show_separator
|
||||
}
|
||||
|
||||
# 显示多选列表
|
||||
show_multi_select_list() {
|
||||
local title="$1"
|
||||
shift
|
||||
|
||||
local items=()
|
||||
local i=0
|
||||
# 收集所有项目,直到遇到特殊标记 "--INDICES--"
|
||||
while [[ $i -lt $# && "$1" != "--INDICES--" ]]; do
|
||||
items+=("$1")
|
||||
shift
|
||||
((i++))
|
||||
done
|
||||
|
||||
shift # 跳过 "--INDICES--" 标记
|
||||
local selected_indices=($@) # 剩余的参数都是选中的索引
|
||||
local selected_index="${selected_indices[0]}" # 第一个是当前光标位置
|
||||
|
||||
# 移除当前索引,只保留选中项索引
|
||||
selected_indices=("${selected_indices[@]:1}")
|
||||
|
||||
local padding=2 # 选中标识的宽度
|
||||
|
||||
# 显示标题
|
||||
show_title "$title"
|
||||
printf "%s%s%s\n" "${DIM}" "使用数字键1选中/0取消,回车确认,q 退出" "${NC}"
|
||||
show_separator
|
||||
|
||||
# 显示列表
|
||||
for i in "${!items[@]}"; do
|
||||
# 检查当前索引是否在选中列表中
|
||||
local is_selected=false
|
||||
for sel_idx in "${selected_indices[@]}"; do
|
||||
if [[ $i -eq $sel_idx ]]; then
|
||||
is_selected=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $i -eq $selected_index ]]; then
|
||||
# 当前光标位置项 - 使用青色箭头标识
|
||||
if [[ "$is_selected" == "true" ]]; then
|
||||
# 选中项 - 绿色文本,带复选框
|
||||
printf "%s%s%s%*s%s%s%s\n" \
|
||||
"${BOLD}" "${CYAN}" "❯" \
|
||||
"$padding" "" \
|
||||
"${GREEN}" "[✓] ${items[$i]}" "${NC}"
|
||||
else
|
||||
# 未选中项 - 灰色文本,不带复选框
|
||||
printf "%s%s%s%*s%s%s%s\n" \
|
||||
"${BOLD}" "${CYAN}" "❯" \
|
||||
"$padding" "" \
|
||||
"${DIM}" "[ ] ${items[$i]}" "${NC}"
|
||||
fi
|
||||
else
|
||||
# 非当前光标位置项
|
||||
if [[ "$is_selected" == "true" ]]; then
|
||||
# 选中项 - 绿色文本,带复选框
|
||||
printf "%*s%s%s%s\n" \
|
||||
"$((padding + 1))" "" \
|
||||
"${GREEN}" "[✓] ${items[$i]}" "${NC}"
|
||||
else
|
||||
# 未选中项 - 灰色文本,不带复选框
|
||||
printf "%*s%s%s%s\n" \
|
||||
"$((padding + 1))" "" \
|
||||
"${DIM}" "[ ] ${items[$i]}" "${NC}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
show_separator
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
show_title "使用帮助"
|
||||
printf "%s%s用法:%s %s [选项]\n\n" "${BOLD}" "${GREEN}" "${NC}" "$0"
|
||||
printf "%s%s选项:%s\n" "${BOLD}" "${BLUE}" "${NC}"
|
||||
printf " %s-w, --workspace%s WORKSPACE %s指定工作区%s\n" "${GREEN}" "${NC}" "${CYAN}" "${NC}"
|
||||
printf " %s-t, --target%s DIR %s指定目标 Git 仓库路径%s\n" "${GREEN}" "${NC}" "${CYAN}" "${NC}"
|
||||
printf " %s-b, --branch%s BRANCH %s指定分支名称%s\n" "${GREEN}" "${NC}" "${CYAN}" "${NC}"
|
||||
printf " %s-s, --sync-structure%s %s同步项目结构%s\n" "${GREEN}" "${NC}" "${CYAN}" "${NC}"
|
||||
printf " %s-p, --parallel%s %s并行编译%s\n" "${GREEN}" "${NC}" "${CYAN}" "${NC}"
|
||||
printf " %s-d, --dry-run%s %s干运行模式%s\n" "${GREEN}" "${NC}" "${CYAN}" "${NC}"
|
||||
printf " %s--debug%s %s调试模式(显示详细日志)%s\n" "${GREEN}" "${NC}" "${CYAN}" "${NC}"
|
||||
printf " %s-h, --help%s %s显示帮助信息%s\n" "${GREEN}" "${NC}" "${CYAN}" "${NC}"
|
||||
|
||||
show_separator
|
||||
}
|
||||
|
||||
# 命令行参数解析
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-w|--workspace)
|
||||
SELECTED_WORKSPACE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-t|--target)
|
||||
TARGET_GIT_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
-b|--branch)
|
||||
BRANCH="$2"
|
||||
shift 2
|
||||
;;
|
||||
-s|--sync-structure)
|
||||
SYNC_STRUCTURE=true
|
||||
shift
|
||||
;;
|
||||
-p|--parallel)
|
||||
PARALLEL_BUILD=true
|
||||
shift
|
||||
;;
|
||||
-d|--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
--debug)
|
||||
DEBUG_MODE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "未知参数: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# 初始化插件目录
|
||||
init_plugins() {
|
||||
if [[ -z "$PLUGINS_DIR" ]]; then
|
||||
log_error "插件目录未初始化,请确保已找到项目根目录"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$PLUGINS_DIR" ]]; then
|
||||
mkdir -p "$PLUGINS_DIR" || {
|
||||
log_error "创建插件目录失败: $PLUGINS_DIR"
|
||||
return 1
|
||||
}
|
||||
log_info "已创建插件目录: $PLUGINS_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# 加载插件
|
||||
load_plugins() {
|
||||
if [[ -z "$PLUGINS_DIR" ]]; then
|
||||
log_error "插件目录未初始化,请确保已找到项目根目录"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -d "$PLUGINS_DIR" ]]; then
|
||||
for plugin in "$PLUGINS_DIR"/*.sh; do
|
||||
if [[ -f "$plugin" ]]; then
|
||||
if ! source "$plugin"; then
|
||||
log_error "加载插件失败: $(basename "$plugin")"
|
||||
continue
|
||||
fi
|
||||
log_info "已加载插件: $(basename "$plugin")"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# 插件钩子函数
|
||||
run_hook() {
|
||||
local hook_name="$1"
|
||||
shift
|
||||
|
||||
# 检查是否存在对应的钩子函数
|
||||
if declare -F "hook_${hook_name}" > /dev/null; then
|
||||
"hook_${hook_name}" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# 导出函数
|
||||
export -f check_dependencies
|
||||
export -f detect_os
|
||||
export -f parse_workspaces
|
||||
export -f show_steps
|
||||
export -f build_workspace
|
||||
export -f check_build_result
|
||||
export -f parallel_build_workspaces
|
||||
export -f show_option_list
|
||||
export -f show_multi_select_list
|
||||
export -f show_help
|
||||
export -f parse_args
|
||||
export -f init_plugins
|
||||
export -f load_plugins
|
||||
export -f run_hook
|
||||
709
frontend/scripts/temp/file-operations.sh
Normal file
709
frontend/scripts/temp/file-operations.sh
Normal file
@@ -0,0 +1,709 @@
|
||||
#!/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
|
||||
515
frontend/scripts/temp/git-operations.sh
Normal file
515
frontend/scripts/temp/git-operations.sh
Normal file
@@ -0,0 +1,515 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ===================================================
|
||||
# Git 操作脚本 - git-operations.sh
|
||||
# 用于处理 Git 仓库管理、同步和提交相关功能
|
||||
# ===================================================
|
||||
|
||||
# 依赖文件操作脚本的函数
|
||||
source "$(dirname "$0")/file-operations.sh"
|
||||
|
||||
# Git 操作模块
|
||||
prepare_git_repo() {
|
||||
local git_url="$1"
|
||||
local branch="$2"
|
||||
local alias="$3"
|
||||
local target_dir="$GIT_DIR/${alias:-$(basename "$git_url" .git)}"
|
||||
|
||||
# 检查目标目录是否存在
|
||||
if [[ ! -d "$target_dir" ]]; then
|
||||
log_info "目标目录不存在,尝试克隆仓库"
|
||||
if ! git clone "$git_url" "$target_dir"; then
|
||||
log_error "克隆仓库失败"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 切换到目标目录
|
||||
cd "$target_dir" || {
|
||||
log_error "无法切换到目标目录"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 检查是否是 Git 仓库
|
||||
if [[ ! -d ".git" ]]; then
|
||||
log_error "目标目录不是有效的 Git 仓库"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 清理未提交的更改
|
||||
if [[ -n "$(git status --porcelain)" ]]; then
|
||||
log_warn "发现未提交的更改,正在清理..."
|
||||
git reset --hard HEAD
|
||||
git clean -fd
|
||||
fi
|
||||
|
||||
# 拉取最新代码
|
||||
log_info "拉取最新代码..."
|
||||
if ! git pull; then
|
||||
log_error "拉取代码失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 切换到指定分支
|
||||
if [[ -n "$branch" ]]; then
|
||||
log_info "切换到分支: $branch"
|
||||
if ! git checkout "$branch"; then
|
||||
log_error "切换分支失败"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Git 仓库准备完成"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 获取源项目的最新提交信息
|
||||
get_source_commit_info() {
|
||||
cd "$PROJECT_ROOT" || return 1
|
||||
local commit_hash=$(git rev-parse HEAD)
|
||||
local commit_msg=$(git log -1 --pretty=%B)
|
||||
echo "$commit_hash|$commit_msg"
|
||||
}
|
||||
|
||||
# 提交更改
|
||||
commit_changes() {
|
||||
local commit_info
|
||||
commit_info=$(get_source_commit_info) || {
|
||||
log_error "获取源项目提交信息失败"
|
||||
return 1
|
||||
}
|
||||
|
||||
local commit_hash=$(echo "$commit_info" | cut -d'|' -f1)
|
||||
local commit_msg=$(echo "$commit_info" | cut -d'|' -f2)
|
||||
|
||||
# 添加所有更改
|
||||
git add .
|
||||
|
||||
# 提交更改
|
||||
if git commit -m "sync: $commit_msg (from $commit_hash)"; then
|
||||
log_info "提交成功"
|
||||
return 0
|
||||
else
|
||||
log_error "提交失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 推送更改到远程仓库
|
||||
push_changes() {
|
||||
local current_branch=$(git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
# 检查是否有远程仓库
|
||||
if ! git remote | grep -q origin; then
|
||||
log_error "未找到远程仓库 origin"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 推送到远程仓库
|
||||
log_info "正在推送更改到远程仓库..."
|
||||
if git push origin "$current_branch"; then
|
||||
log_info "推送成功"
|
||||
return 0
|
||||
else
|
||||
log_error "推送失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 选择 Git 项目目录
|
||||
select_git_dirs() {
|
||||
local git_dirs=()
|
||||
local selected_indices=()
|
||||
local SYNC_MAPPINGS=()
|
||||
|
||||
# 检查配置文件是否存在
|
||||
if [[ ! -f "$SYNC_CONFIG_FILE" ]]; then
|
||||
log_error "配置文件不存在: $SYNC_CONFIG_FILE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查是否安装了 yq
|
||||
if ! command -v yq &> /dev/null; then
|
||||
log_error "未安装 yq,无法读取配置文件"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查工作区是否存在
|
||||
if ! yq e ".workspaces.$SELECTED_WORKSPACE" "$SYNC_CONFIG_FILE" &> /dev/null; then
|
||||
log_error "工作区 $SELECTED_WORKSPACE 不存在于配置文件中"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 获取工作区的所有 Git 仓库配置
|
||||
local count
|
||||
count=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings | length" "$SYNC_CONFIG_FILE")
|
||||
|
||||
if [[ $count -eq 0 ]]; then
|
||||
log_error "工作区 $SELECTED_WORKSPACE 未配置任何 Git 仓库"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 构建显示列表和映射信息
|
||||
local all_mappings=()
|
||||
for ((i=0; i<count; i++)); do
|
||||
local git_url
|
||||
local branch
|
||||
local alias
|
||||
local sync_dir
|
||||
local git_dir
|
||||
|
||||
git_url=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].source.git_url" "$SYNC_CONFIG_FILE")
|
||||
branch=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].source.branch" "$SYNC_CONFIG_FILE")
|
||||
alias=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].source.alias" "$SYNC_CONFIG_FILE")
|
||||
sync_dir=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].target.sync_dir" "$SYNC_CONFIG_FILE")
|
||||
git_dir=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].target.git_dir" "$SYNC_CONFIG_FILE")
|
||||
|
||||
if [[ -n "$git_url" ]]; then
|
||||
# 格式化显示信息
|
||||
local display_name
|
||||
if [[ -n "$alias" ]]; then
|
||||
display_name="[$alias] $git_url ($branch) -> $sync_dir:$git_dir"
|
||||
else
|
||||
display_name="$git_url ($branch) -> $sync_dir:$git_dir"
|
||||
fi
|
||||
git_dirs+=("$display_name")
|
||||
# 存储完整映射信息,稍后使用
|
||||
all_mappings+=("$git_url|$branch|$sync_dir|$git_dir|$alias")
|
||||
fi
|
||||
done
|
||||
|
||||
# 默认全选
|
||||
for i in "${!git_dirs[@]}"; do
|
||||
selected_indices+=($i)
|
||||
done
|
||||
|
||||
local cursor_index=0
|
||||
local max_index=$((${#git_dirs[@]}-1))
|
||||
|
||||
# 设置当前步骤
|
||||
CURRENT_STEP=2
|
||||
|
||||
# 主循环 - 处理用户输入并更新选择
|
||||
while true; do
|
||||
# 显示步骤状态
|
||||
show_steps
|
||||
|
||||
# 显示菜单标题
|
||||
printf "%s%s选择目标 Git 仓库%s\n\n" "${BOLD}" "${CYAN}" "${NC}"
|
||||
|
||||
# 显示Git仓库列表
|
||||
for i in "${!git_dirs[@]}"; 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\n" \
|
||||
"${BOLD}" "${CYAN}" "${GREEN}" "${git_dirs[$i]}" "${NC}"
|
||||
else
|
||||
# 未选中项
|
||||
printf "%s%s❯ %s[ ] %s%s\n" \
|
||||
"${BOLD}" "${CYAN}" "${DIM}" "${git_dirs[$i]}" "${NC}"
|
||||
fi
|
||||
else
|
||||
# 非光标位置项
|
||||
if [[ "$is_selected" == "true" ]]; then
|
||||
# 选中项
|
||||
printf " %s[✓] %s%s\n" \
|
||||
"${GREEN}" "${git_dirs[$i]}" "${NC}"
|
||||
else
|
||||
# 未选中项
|
||||
printf " %s[ ] %s%s\n" \
|
||||
"${DIM}" "${git_dirs[$i]}" "${NC}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 显示操作提示
|
||||
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 "请至少选择一个 Git 仓库"
|
||||
sleep 2 # 暂停显示错误信息
|
||||
continue
|
||||
fi
|
||||
|
||||
# 根据选中的索引获取对应的映射信息
|
||||
SYNC_MAPPINGS=()
|
||||
for idx in "${selected_indices[@]}"; do
|
||||
SYNC_MAPPINGS+=("${all_mappings[$idx]}")
|
||||
done
|
||||
|
||||
# 更新步骤状态
|
||||
STEP_GIT_REPOS="${#selected_indices[@]} 个仓库"
|
||||
CURRENT_STEP=3
|
||||
|
||||
return 0
|
||||
;;
|
||||
"QUIT") # 退出
|
||||
log_error "操作已取消"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# 读取工作区配置
|
||||
read_workspace_config() {
|
||||
local workspace="$1"
|
||||
|
||||
if ! command -v yq &> /dev/null; then
|
||||
log_warn "未安装 yq,将使用默认配置"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 使用 yq 解析 YAML
|
||||
local mappings
|
||||
mappings=$(yq e ".workspaces.$workspace.sync_mappings" "$SYNC_CONFIG_FILE")
|
||||
if [[ "$mappings" != "null" ]]; then
|
||||
# 解析每个映射
|
||||
local count
|
||||
count=$(yq e ".workspaces.$workspace.sync_mappings | length" "$SYNC_CONFIG_FILE")
|
||||
for ((i=0; i<count; i++)); do
|
||||
local git_url
|
||||
local branch
|
||||
local sync_dir
|
||||
local git_dir
|
||||
local alias
|
||||
|
||||
git_url=$(yq e ".workspaces.$workspace.sync_mappings[$i].source.git_url" "$SYNC_CONFIG_FILE")
|
||||
branch=$(yq e ".workspaces.$workspace.sync_mappings[$i].source.branch" "$SYNC_CONFIG_FILE")
|
||||
sync_dir=$(yq e ".workspaces.$workspace.sync_mappings[$i].target.sync_dir" "$SYNC_CONFIG_FILE")
|
||||
git_dir=$(yq e ".workspaces.$workspace.sync_mappings[$i].target.git_dir" "$SYNC_CONFIG_FILE")
|
||||
alias=$(yq e ".workspaces.$workspace.sync_mappings[$i].source.alias" "$SYNC_CONFIG_FILE")
|
||||
|
||||
if [[ -n "$git_url" && -n "$sync_dir" ]]; then
|
||||
# 存储映射信息,添加别名
|
||||
SYNC_MAPPINGS+=("$git_url|$branch|$sync_dir|$git_dir|$alias")
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 更新工作区配置
|
||||
update_workspace_config() {
|
||||
local workspace="$1"
|
||||
|
||||
if ! command -v yq &> /dev/null; then
|
||||
log_warn "未安装 yq,无法更新配置"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 创建备份
|
||||
if [[ -f "$SYNC_CONFIG_FILE" ]]; then
|
||||
cp "$SYNC_CONFIG_FILE" "${SYNC_CONFIG_FILE}.bak"
|
||||
fi
|
||||
|
||||
# 清空现有映射
|
||||
yq e -i ".workspaces.$workspace.sync_mappings = []" "$SYNC_CONFIG_FILE"
|
||||
|
||||
# 添加新的映射
|
||||
for mapping in "${SYNC_MAPPINGS[@]}"; do
|
||||
IFS='|' read -r git_url branch sync_dir git_dir alias <<< "$mapping"
|
||||
yq e -i ".workspaces.$workspace.sync_mappings += [{\"source\": {\"git_url\": \"$git_url\", \"branch\": \"$branch\", \"alias\": \"$alias\"}, \"target\": {\"sync_dir\": \"$sync_dir\", \"git_dir\": \"$git_dir\"}}]" "$SYNC_CONFIG_FILE"
|
||||
done
|
||||
|
||||
# 删除备份
|
||||
rm -f "${SYNC_CONFIG_FILE}.bak"
|
||||
log_info "工作区配置已更新"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 读取配置
|
||||
read_config() {
|
||||
if [[ -z "$SYNC_CONFIG_FILE" ]]; then
|
||||
log_error "配置文件未初始化,请确保已找到项目根目录"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -f "$SYNC_CONFIG_FILE" ]]; then
|
||||
# 使用 yq 解析 YAML(如果安装了的话)
|
||||
if command -v yq &> /dev/null; then
|
||||
SELECTED_WORKSPACE=$(yq e '.config.workspace' "$SYNC_CONFIG_FILE")
|
||||
TARGET_GIT_DIR=$(yq e '.config.target_git_dir' "$SYNC_CONFIG_FILE")
|
||||
BRANCH=$(yq e '.config.branch' "$SYNC_CONFIG_FILE")
|
||||
SYNC_STRUCTURE=$(yq e '.config.sync_structure' "$SYNC_CONFIG_FILE")
|
||||
PARALLEL_BUILD=$(yq e '.config.parallel_build' "$SYNC_CONFIG_FILE")
|
||||
DRY_RUN=$(yq e '.config.dry_run' "$SYNC_CONFIG_FILE")
|
||||
else
|
||||
log_warn "未安装 yq,将使用默认配置"
|
||||
fi
|
||||
else
|
||||
log_error "配置文件不存在: $SYNC_CONFIG_FILE"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 保存配置
|
||||
save_config() {
|
||||
if [[ -z "$SYNC_CONFIG_FILE" ]]; then
|
||||
log_error "配置文件未初始化,请确保已找到项目根目录"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v yq &> /dev/null; then
|
||||
log_warn "未安装 yq,无法保存配置"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 创建备份
|
||||
if [[ -f "$SYNC_CONFIG_FILE" ]]; then
|
||||
cp "$SYNC_CONFIG_FILE" "${SYNC_CONFIG_FILE}.bak"
|
||||
fi
|
||||
|
||||
# 保存配置
|
||||
if ! yq e -i ".config.workspace = \"$SELECTED_WORKSPACE\"" "$SYNC_CONFIG_FILE" \
|
||||
&& yq e -i ".config.target_git_dir = \"$TARGET_GIT_DIR\"" "$SYNC_CONFIG_FILE" \
|
||||
&& yq e -i ".config.branch = \"$BRANCH\"" "$SYNC_CONFIG_FILE" \
|
||||
&& yq e -i ".config.sync_structure = $SYNC_STRUCTURE" "$SYNC_CONFIG_FILE" \
|
||||
&& yq e -i ".config.parallel_build = $PARALLEL_BUILD" "$SYNC_CONFIG_FILE" \
|
||||
&& yq e -i ".config.dry_run = $DRY_RUN" "$SYNC_CONFIG_FILE"; then
|
||||
log_error "保存配置失败"
|
||||
# 恢复备份
|
||||
if [[ -f "${SYNC_CONFIG_FILE}.bak" ]]; then
|
||||
mv "${SYNC_CONFIG_FILE}.bak" "$SYNC_CONFIG_FILE"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 删除备份
|
||||
rm -f "${SYNC_CONFIG_FILE}.bak"
|
||||
log_info "配置已保存"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 导出函数
|
||||
export -f prepare_git_repo
|
||||
export -f get_source_commit_info
|
||||
export -f commit_changes
|
||||
export -f push_changes
|
||||
export -f select_git_dirs
|
||||
export -f read_workspace_config
|
||||
export -f update_workspace_config
|
||||
export -f read_config
|
||||
export -f save_config
|
||||
306
frontend/scripts/temp/sync-project.sh
Normal file
306
frontend/scripts/temp/sync-project.sh
Normal file
@@ -0,0 +1,306 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ===================================================
|
||||
# 项目同步脚本 - sync-project.sh
|
||||
# 用于同步 Turborepo 项目工作区的编译结果到 Git 仓库
|
||||
# ===================================================
|
||||
|
||||
# 脚本目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# 全局变量
|
||||
PROJECT_ROOT="" # 项目根目录
|
||||
SELECTED_WORKSPACE="" # 选中的工作区
|
||||
PARALLEL_BUILD=false # 是否并行编译
|
||||
DRY_RUN=false # 是否干运行
|
||||
OS="" # 操作系统类型
|
||||
DEBUG_MODE=false # 调试模式
|
||||
|
||||
# 相关文件和目录变量(初始化为空,后续赋值)
|
||||
SYNC_DIR="" # 同步配置目录
|
||||
GIT_DIR="" # Git 仓库目录
|
||||
SYNC_CONFIG_FILE="" # 同步配置文件
|
||||
HISTORY_FILE="" # 历史记录文件
|
||||
PLUGINS_DIR="" # 插件目录
|
||||
WORKSPACE_CONFIG_DIR="" # 工作区配置目录
|
||||
|
||||
# 步骤状态变量
|
||||
CURRENT_STEP=1
|
||||
STEP_WORKSPACE=""
|
||||
STEP_GIT_REPOS=""
|
||||
STEP_SYNC_MODE=""
|
||||
|
||||
# 操作历史相关
|
||||
MAX_HISTORY=10
|
||||
|
||||
# 加载拆分后的脚本
|
||||
source "$SCRIPT_DIR/file-operations.sh"
|
||||
source "$SCRIPT_DIR/git-operations.sh"
|
||||
source "$SCRIPT_DIR/build-operations.sh"
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
# 检查参数
|
||||
if [[ $# -gt 0 ]]; then
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == "--help" || "$arg" == "-h" ]]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 初始化
|
||||
detect_os
|
||||
check_dependencies # 检查依赖,包括 yq
|
||||
|
||||
# 首先查找项目根目录
|
||||
find_project_root || exit 1
|
||||
|
||||
# 初始化同步目录结构
|
||||
init_sync_dirs || exit 1
|
||||
|
||||
# 在找到项目根目录后,初始化插件系统
|
||||
init_plugins
|
||||
load_plugins
|
||||
|
||||
# 读取配置
|
||||
read_config
|
||||
|
||||
# 解析命令行参数
|
||||
parse_args "$@"
|
||||
|
||||
# 运行前置钩子
|
||||
run_hook "pre_main"
|
||||
|
||||
# 如果是干运行模式,只显示将要执行的操作
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "干运行模式 - 将显示将要执行的操作"
|
||||
log_info "工作区: $SELECTED_WORKSPACE"
|
||||
log_info "并行编译: $PARALLEL_BUILD"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 解析工作区(如果未通过参数指定)
|
||||
if [[ -z "$SELECTED_WORKSPACE" ]]; then
|
||||
parse_workspaces || {
|
||||
log_error "工作区选择失败"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
# 验证工作区是否有效
|
||||
if [[ -z "$SELECTED_WORKSPACE" ]]; then
|
||||
log_error "未选择有效的工作区"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证工作区目录是否存在
|
||||
if [[ ! -d "$PROJECT_ROOT/apps/$SELECTED_WORKSPACE" ]]; then
|
||||
log_error "工作区目录不存在: $PROJECT_ROOT/apps/$SELECTED_WORKSPACE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "当前选择的工作区: $SELECTED_WORKSPACE"
|
||||
|
||||
# 编译工作区
|
||||
build_workspace || {
|
||||
log_error "编译失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 检查编译结果
|
||||
check_build_result || {
|
||||
log_error "编译结果检查失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 选择 Git 项目目录
|
||||
select_git_dirs || {
|
||||
log_error "Git 仓库选择失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 询问是否同步源码
|
||||
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
|
||||
|
||||
# 设置当前步骤
|
||||
CURRENT_STEP=3
|
||||
|
||||
while true; do
|
||||
# 显示步骤状态
|
||||
show_steps
|
||||
|
||||
# 显示选项列表标题
|
||||
printf "%s%s选择同步方式%s\n\n" "${BOLD}" "${CYAN}" "${NC}"
|
||||
|
||||
# 显示同步选项
|
||||
for i in "${!sync_options[@]}"; do
|
||||
if [[ $i -eq $selected_index ]]; then
|
||||
printf "%s%s❯ %s%s\n" \
|
||||
"${BOLD}" "${GREEN}" "${sync_options[$i]}" "${NC}"
|
||||
else
|
||||
printf " %s%s%s\n" \
|
||||
"${DIM}" "${sync_options[$i]}" "${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# 显示操作提示
|
||||
printf "\n%s%s使用上下箭头选择,回车确认,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" # 空字符通常是回车键
|
||||
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="ENTER"
|
||||
;;
|
||||
*) # 其他按键
|
||||
key_pressed="OTHER"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# 调试信息
|
||||
[[ "$DEBUG_MODE" == "true" ]] && log_debug "按键被解析为: $key_pressed"
|
||||
|
||||
# 处理操作
|
||||
case "$key_pressed" in
|
||||
"UP") # 上箭头
|
||||
if [[ $selected_index -gt 0 ]]; then
|
||||
selected_index=$((selected_index-1))
|
||||
else
|
||||
# 如果已经是第一项,跳到最后一项
|
||||
selected_index=$max_index
|
||||
fi
|
||||
;;
|
||||
"DOWN") # 下箭头
|
||||
if [[ $selected_index -lt $max_index ]]; then
|
||||
selected_index=$((selected_index+1))
|
||||
else
|
||||
# 如果已经是最后一项,回到第一项
|
||||
selected_index=0
|
||||
fi
|
||||
;;
|
||||
"ENTER") # 回车
|
||||
case $selected_index in
|
||||
0) # 仅同步编译结果
|
||||
STEP_SYNC_MODE="仅同步编译结果"
|
||||
CURRENT_STEP=4
|
||||
sync_files || {
|
||||
log_error "同步编译结果失败"
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
1) # 同步编译结果和源码
|
||||
STEP_SYNC_MODE="同步编译结果和源码"
|
||||
CURRENT_STEP=4
|
||||
sync_files || {
|
||||
log_error "同步编译结果失败"
|
||||
exit 1
|
||||
}
|
||||
sync_source_code || {
|
||||
log_error "同步源码失败"
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
esac
|
||||
break
|
||||
;;
|
||||
"QUIT") # 退出
|
||||
log_error "操作已取消"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 运行工作区选择后钩子
|
||||
run_hook "post_workspace_select" "$SELECTED_WORKSPACE"
|
||||
|
||||
# 设置当前步骤
|
||||
CURRENT_STEP=4
|
||||
|
||||
# 显示当前步骤
|
||||
show_steps
|
||||
printf "%s%s正在执行同步操作,请稍候...%s\n\n" "${BOLD}" "${CYAN}" "${NC}"
|
||||
|
||||
# 准备 Git 仓库
|
||||
for mapping in "${SYNC_MAPPINGS[@]}"; do
|
||||
IFS='|' read -r git_url branch sync_dir git_dir alias <<< "$mapping"
|
||||
prepare_git_repo "$git_url" "$branch" "$alias" || {
|
||||
log_error "准备 Git 仓库失败"
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
# 提交和推送
|
||||
for mapping in "${SYNC_MAPPINGS[@]}"; do
|
||||
IFS='|' read -r git_url branch sync_dir git_dir alias <<< "$mapping"
|
||||
local repo_dir="$GIT_DIR/${alias:-$(basename "$git_url" .git)}"
|
||||
cd "$repo_dir" || {
|
||||
log_error "无法切换到 Git 仓库目录"
|
||||
exit 1
|
||||
}
|
||||
|
||||
commit_changes || {
|
||||
log_error "提交更改失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
push_changes || {
|
||||
log_error "推送更改失败"
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
# 保存配置
|
||||
save_config || log_warn "保存配置失败,但操作已完成"
|
||||
|
||||
# 记录操作历史
|
||||
record_history "同步工作区 $SELECTED_WORKSPACE"
|
||||
|
||||
# 运行完成钩子
|
||||
run_hook "post_main"
|
||||
|
||||
# 设置最后步骤状态,显示完成
|
||||
CURRENT_STEP=5
|
||||
show_steps
|
||||
printf "%s%s所有操作已完成!%s\n" "${BOLD}" "${GREEN}" "${NC}"
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
||||
1379
frontend/scripts/tools/file-handle.sh
Normal file
1379
frontend/scripts/tools/file-handle.sh
Normal file
File diff suppressed because it is too large
Load Diff
860
frontend/scripts/tools/git-handle.sh
Normal file
860
frontend/scripts/tools/git-handle.sh
Normal file
@@ -0,0 +1,860 @@
|
||||
#!/bin/bash
|
||||
|
||||
#######################################
|
||||
# Git操作处理脚本
|
||||
#
|
||||
# 此脚本提供了一系列与Git操作相关的函数,包括仓库检查、
|
||||
# 代码拉取推送、分支管理、标签管理、日志查看等Git常用
|
||||
# 操作的封装,使Git操作更加便捷和安全。
|
||||
#
|
||||
# 作者: chudong
|
||||
# 版本: 1.0.0
|
||||
#######################################
|
||||
|
||||
# 导入通知处理脚本
|
||||
GIT_TOOL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$GIT_TOOL_DIR/notice-handle.sh"
|
||||
|
||||
#######################################
|
||||
# 检查是否为Git项目
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# 返回值:
|
||||
# 0 - 是Git项目
|
||||
# 1 - 不是Git项目
|
||||
#######################################
|
||||
check_git_repository() {
|
||||
local repo_path="${1:-.}"
|
||||
|
||||
if [ ! -d "$repo_path" ]; then
|
||||
notice_error "路径 '$repo_path' 不存在或不是一个目录"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到指定目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 检查是否存在.git目录
|
||||
if [ -d ".git" ] || git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
cd "$current_dir"
|
||||
notice_success "'$repo_path' 是一个有效的Git仓库"
|
||||
return 0
|
||||
else
|
||||
cd "$current_dir"
|
||||
notice_error "'$repo_path' 不是一个Git仓库"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 拉取Git项目更新
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 分支名称,默认为当前分支
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_pull() {
|
||||
local repo_path="${1:-.}"
|
||||
local branch="$2"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 如果没有指定分支,则拉取当前分支
|
||||
if [ -z "$branch" ]; then
|
||||
notice_info "拉取当前分支的最新代码..."
|
||||
git pull
|
||||
else
|
||||
notice_info "拉取 '$branch' 分支的最新代码..."
|
||||
git pull origin "$branch"
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "Git拉取成功"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "Git拉取失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 推送Git项目变更
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 分支名称,默认为当前分支
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_push() {
|
||||
local repo_path="${1:-.}"
|
||||
local branch="$2"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 如果没有指定分支,则推送当前分支
|
||||
if [ -z "$branch" ]; then
|
||||
# 获取当前分支名称
|
||||
local current_branch=$(git symbolic-ref --short HEAD 2>/dev/null)
|
||||
if [ -z "$current_branch" ]; then
|
||||
notice_error "无法获取当前分支名称"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
notice_info "推送到当前分支 '$current_branch'..."
|
||||
git push origin "$current_branch"
|
||||
else
|
||||
notice_info "推送到 '$branch' 分支..."
|
||||
git push origin "$branch"
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "Git推送成功"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "Git推送失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 提交Git变更
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 提交信息
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_commit() {
|
||||
local repo_path="${1:-.}"
|
||||
local commit_message="$2"
|
||||
|
||||
if [ -z "$commit_message" ]; then
|
||||
notice_error "请提供提交信息"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 检查是否有变更需要提交
|
||||
if ! git diff --quiet || ! git diff --staged --quiet; then
|
||||
# 添加所有变更
|
||||
git add .
|
||||
|
||||
# 提交变更
|
||||
git commit -m "$commit_message"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "Git提交成功: $commit_message"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "Git提交失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
notice_warning "没有变更需要提交"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 获取Git提交记录日志
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 记录数量,默认为10
|
||||
# $3: 格式化输出格式,默认为 "%h - %an, %ar : %s"
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
# 标准输出: Git日志信息
|
||||
#######################################
|
||||
git_log() {
|
||||
local repo_path="${1:-.}"
|
||||
local count="${2:-10}"
|
||||
local format="${3:-%h - %an, %ar : %s}"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
notice_info "获取最近 $count 条提交记录:"
|
||||
git log -n "$count" --pretty=format:"$format"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "获取Git提交记录失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 创建Git分支
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 新分支名称
|
||||
# $3: 基于的分支名称,默认为当前分支
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_create_branch() {
|
||||
local repo_path="${1:-.}"
|
||||
local new_branch="$2"
|
||||
local base_branch="$3"
|
||||
|
||||
if [ -z "$new_branch" ]; then
|
||||
notice_error "请提供新分支名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 如果没有指定基础分支,则基于当前分支创建
|
||||
if [ -z "$base_branch" ]; then
|
||||
git checkout -b "$new_branch"
|
||||
else
|
||||
git checkout -b "$new_branch" "$base_branch"
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "成功创建并切换到分支 '$new_branch'"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "创建分支 '$new_branch' 失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 切换Git分支
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 目标分支名称
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_checkout_branch() {
|
||||
local repo_path="${1:-.}"
|
||||
local target_branch="$2"
|
||||
|
||||
if [ -z "$target_branch" ]; then
|
||||
notice_error "请提供目标分支名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
git checkout "$target_branch"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "已切换到分支 '$target_branch'"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "切换到分支 '$target_branch' 失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 删除Git分支
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 要删除的分支名称
|
||||
# $3: 是否强制删除 ("force" 表示强制删除未合并的分支)
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_delete_branch() {
|
||||
local repo_path="${1:-.}"
|
||||
local branch_name="$2"
|
||||
local force="$3"
|
||||
|
||||
if [ -z "$branch_name" ]; then
|
||||
notice_error "请提供要删除的分支名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 检查是否为当前分支
|
||||
local current_branch=$(git symbolic-ref --short HEAD 2>/dev/null)
|
||||
if [ "$current_branch" = "$branch_name" ]; then
|
||||
notice_error "无法删除当前所在的分支 '$branch_name',请先切换到其他分支"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 根据是否强制删除选择不同的命令
|
||||
if [ "$force" = "force" ]; then
|
||||
git branch -D "$branch_name"
|
||||
else
|
||||
git branch -d "$branch_name"
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "已删除分支 '$branch_name'"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
if [ "$force" != "force" ]; then
|
||||
notice_error "删除分支 '$branch_name' 失败,可能该分支尚未合并,请添加 'force' 参数强制删除"
|
||||
else
|
||||
notice_error "删除分支 '$branch_name' 失败"
|
||||
fi
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 查看Git分支列表
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 分支类型 ("all" 表示所有分支,"remote" 表示远程分支,默认为本地分支)
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
# 标准输出: 分支列表
|
||||
#######################################
|
||||
git_list_branches() {
|
||||
local repo_path="${1:-.}"
|
||||
local branch_type="${2:-local}"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 根据分支类型选择不同的命令
|
||||
case "$branch_type" in
|
||||
"all")
|
||||
notice_info "所有分支列表:"
|
||||
git branch -a
|
||||
;;
|
||||
"remote")
|
||||
notice_info "远程分支列表:"
|
||||
git branch -r
|
||||
;;
|
||||
*)
|
||||
notice_info "本地分支列表:"
|
||||
git branch
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "获取分支列表失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 获取Git当前分支名称
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# 返回值:
|
||||
# 0 - 成功,并打印分支名称
|
||||
# 1 - 失败
|
||||
# 标准输出: 当前分支名称
|
||||
#######################################
|
||||
git_current_branch() {
|
||||
local repo_path="${1:-.}"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
local branch_name=$(git symbolic-ref --short HEAD 2>/dev/null)
|
||||
|
||||
if [ -n "$branch_name" ]; then
|
||||
notice_info "当前分支: $branch_name"
|
||||
echo "$branch_name"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "无法获取当前分支名称,可能处于分离的HEAD状态"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 查看Git仓库状态
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
# 标准输出: 仓库状态信息
|
||||
#######################################
|
||||
git_status() {
|
||||
local repo_path="${1:-.}"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
notice_info "Git仓库状态:"
|
||||
git status
|
||||
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 创建Git标签
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 标签名称
|
||||
# $3: 标签消息,默认为标签名称
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_create_tag() {
|
||||
local repo_path="${1:-.}"
|
||||
local tag_name="$2"
|
||||
local tag_message="${3:-$tag_name}"
|
||||
|
||||
if [ -z "$tag_name" ]; then
|
||||
notice_error "请提供标签名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
git tag -a "$tag_name" -m "$tag_message"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "已创建标签 '$tag_name'"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "创建标签 '$tag_name' 失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 删除Git标签
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 标签名称
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_delete_tag() {
|
||||
local repo_path="${1:-.}"
|
||||
local tag_name="$2"
|
||||
|
||||
if [ -z "$tag_name" ]; then
|
||||
notice_error "请提供标签名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
git tag -d "$tag_name"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "已删除标签 '$tag_name'"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "删除标签 '$tag_name' 失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 列出Git标签
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
# 标准输出: 标签列表
|
||||
#######################################
|
||||
git_list_tags() {
|
||||
local repo_path="${1:-.}"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
notice_info "标签列表:"
|
||||
git tag
|
||||
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 比较两个提交之间的差异
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 起始提交或分支
|
||||
# $3: 目标提交或分支,默认为当前分支
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
# 标准输出: 差异内容
|
||||
#######################################
|
||||
git_diff() {
|
||||
local repo_path="${1:-.}"
|
||||
local start_commit="$2"
|
||||
local end_commit="$3"
|
||||
|
||||
if [ -z "$start_commit" ]; then
|
||||
notice_error "请提供起始提交或分支"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 如果没有指定目标提交,则与当前分支比较
|
||||
if [ -z "$end_commit" ]; then
|
||||
notice_info "比较 '$start_commit' 与当前分支的差异:"
|
||||
git diff "$start_commit"
|
||||
else
|
||||
notice_info "比较 '$start_commit' 与 '$end_commit' 的差异:"
|
||||
git diff "$start_commit" "$end_commit"
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "比较差异失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 合并分支
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# $2: 要合并的源分支
|
||||
# $3: 目标分支,默认为当前分支
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_merge() {
|
||||
local repo_path="${1:-.}"
|
||||
local source_branch="$2"
|
||||
local target_branch="$3"
|
||||
|
||||
if [ -z "$source_branch" ]; then
|
||||
notice_error "请提供要合并的源分支"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 如果指定了目标分支,则先切换到目标分支
|
||||
if [ -n "$target_branch" ]; then
|
||||
git checkout "$target_branch"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
notice_error "切换到目标分支 '$target_branch' 失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
notice_info "合并分支 '$source_branch'..."
|
||||
git merge "$source_branch"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "成功合并分支 '$source_branch'"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "合并分支 '$source_branch' 失败,可能存在冲突"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 解决冲突后继续合并
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_merge_continue() {
|
||||
local repo_path="${1:-.}"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
# 检查是否有未解决的冲突
|
||||
if git diff --name-only --diff-filter=U | grep -q .; then
|
||||
notice_error "仍有未解决的冲突,请先解决冲突"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 提交解决的冲突
|
||||
git add .
|
||||
git commit --no-edit
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "成功完成合并"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "完成合并失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 取消合并
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
git_merge_abort() {
|
||||
local repo_path="${1:-.}"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
git merge --abort
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
notice_success "已取消合并"
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
else
|
||||
notice_error "取消合并失败"
|
||||
cd "$current_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 获取远程仓库信息
|
||||
# 参数:
|
||||
# $1: 项目路径,默认为当前目录
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
# 标准输出: 远程仓库信息
|
||||
#######################################
|
||||
git_remote_info() {
|
||||
local repo_path="${1:-.}"
|
||||
|
||||
if ! check_git_repository "$repo_path"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存当前目录
|
||||
local current_dir=$(pwd)
|
||||
|
||||
# 切换到仓库目录
|
||||
cd "$repo_path" || return 1
|
||||
|
||||
notice_info "远程仓库信息:"
|
||||
git remote -v
|
||||
|
||||
cd "$current_dir"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 使用示例
|
||||
#######################################
|
||||
|
||||
# # 检查是否为Git仓库
|
||||
# check_git_repository "/path/to/repo"
|
||||
|
||||
# # 代码拉取与推送
|
||||
# git_pull "/path/to/repo" "main"
|
||||
# git_push "/path/to/repo" "feature/branch"
|
||||
# git_commit "/path/to/repo" "修复了某个问题"
|
||||
|
||||
# # 查看日志和状态
|
||||
# git_log "/path/to/repo" 5 "%h - %an, %ar : %s"
|
||||
# git_status "/path/to/repo"
|
||||
|
||||
# # 分支管理
|
||||
# git_create_branch "/path/to/repo" "feature/new-feature" "main"
|
||||
# git_checkout_branch "/path/to/repo" "develop"
|
||||
# git_delete_branch "/path/to/repo" "old-branch" "force"
|
||||
# git_list_branches "/path/to/repo" "all"
|
||||
# git_current_branch "/path/to/repo"
|
||||
|
||||
# # 标签管理
|
||||
# git_create_tag "/path/to/repo" "v1.0.0" "第一个正式版本"
|
||||
# git_delete_tag "/path/to/repo" "v0.9.9"
|
||||
# git_list_tags "/path/to/repo"
|
||||
|
||||
# # 比较与合并
|
||||
# git_diff "/path/to/repo" "v1.0.0" "v1.1.0"
|
||||
# git_merge "/path/to/repo" "feature/completed" "main"
|
||||
# git_merge_continue "/path/to/repo"
|
||||
# git_merge_abort "/path/to/repo"
|
||||
|
||||
# # 远程仓库
|
||||
# git_remote_info "/path/to/repo"
|
||||
76
frontend/scripts/tools/help.md
Normal file
76
frontend/scripts/tools/help.md
Normal file
@@ -0,0 +1,76 @@
|
||||
以下是根据你提供的脚本生成的说明文档:
|
||||
|
||||
### 脚本概述
|
||||
这些脚本主要包含了Git操作、文件处理、通知处理以及通用工具处理等功能,旨在提供便捷、安全的操作封装,使各项任务更加高效。
|
||||
|
||||
### 各脚本功能及函数说明
|
||||
|
||||
#### 1. `git-handle.sh`
|
||||
此脚本提供了与Git操作相关的函数,如仓库检查、代码拉取推送、分支管理等。
|
||||
|
||||
| 函数名 | 参数 | 返回值 | 使用方法 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| `check_git_repository` | `$1: 项目路径,默认为当前目录` | `0 - 是Git项目<br>1 - 不是Git项目` | `check_git_repository /path/to/repo` |
|
||||
| `git_list_branches` | `$1: 项目路径,默认为当前目录<br>$2: 分支类型 ("all" 表示所有分支,"remote" 表示远程分支,默认为本地分支)` | `0 - 成功<br>1 - 失败` | `git_list_branches /path/to/repo all` |
|
||||
| `git_current_branch` | `$1: 项目路径,默认为当前目录` | `0 - 成功,并打印分支名称<br>1 - 失败` | `git_current_branch /path/to/repo` |
|
||||
|
||||
#### 2. `file-handle.sh`
|
||||
该脚本提供了文件和文件夹的创建、删除、读写、权限设置等操作。
|
||||
|
||||
| 函数名 | 参数 | 返回值 | 使用方法 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| `create_file` | `$1: 文件路径` | `0 - 成功<br>1 - 失败` | `create_file /path/to/file.txt` |
|
||||
| `create_directory` | `$1: 文件夹路径` | `0 - 成功<br>1 - 失败` | `create_directory /path/to/directory` |
|
||||
| `delete_file` | `$1: 文件路径` | `0 - 成功<br>1 - 失败` | `delete_file /path/to/file.txt` |
|
||||
| `delete_directory` | `$1: 文件夹路径<br>$2: 是否强制删除 ("force" 表示强制删除)` | `0 - 成功<br>1 - 失败` | `delete_directory /path/to/directory force` |
|
||||
| `rename_directory` | `$1: 旧文件夹路径<br>$2: 新文件夹路径` | `0 - 成功<br>1 - 失败` | `rename_directory old/directory/path new/directory/path` |
|
||||
| `rename_file` | `$1: 旧文件路径<br>$2: 新文件路径` | `0 - 成功<br>1 - 失败` | `rename_file old/file/path.txt new/file/path.txt` |
|
||||
| `copy_file` | `$1: 源文件路径<br>$2: 目标文件路径<br>$3: 是否强制覆盖 ("force" 表示强制覆盖)` | `0 - 成功<br>1 - 失败` | `copy_file source/file.txt target/file.txt force` |
|
||||
| `copy_directory` | `$1: 源文件夹路径<br>$2: 目标文件夹路径<br>$3: 是否强制覆盖 ("force" 表示强制覆盖)` | `0 - 成功<br>1 - 失败` | `copy_directory source/dir target/dir force` |
|
||||
| `file_exists` | `$1: 文件路径` | `0 - 文件存在<br>1 - 文件不存在` | `file_exists /path/to/file.txt` |
|
||||
| `directory_exists` | `$1: 文件夹路径` | `0 - 文件夹存在<br>1 - 文件夹不存在` | `directory_exists /path/to/directory` |
|
||||
| `get_file_size` | `$1: 文件路径<br>$2: 单位 (B, K, M, G)` | `0 - 成功<br>1 - 失败` | `get_file_size /path/to/file.txt M` |
|
||||
| `read_file_content` | `$1: 文件路径` | `0 - 成功<br>1 - 失败` | `read_file_content /path/to/file.txt` |
|
||||
| `write_file_content` | `$1: 文件路径<br>$2: 要写入的内容<br>$3: 是否覆盖已存在的文件 ("force" 表示强制覆盖)` | `0 - 成功<br>1 - 失败` | `write_file_content /path/to/file.txt "Hello, World!" force` |
|
||||
| `append_file_content` | `$1: 文件路径<br>$2: 要追加的内容` | `0 - 成功<br>1 - 失败` | `append_file_content /path/to/file.txt "New line"` |
|
||||
| `find_files` | `$1: 目录路径<br>$2: 文件名模式(支持通配符)<br>$3: 是否递归搜索 ("recursive" 表示递归)` | `0 - 成功<br>1 - 失败` | `find_files /path/to/search "*.txt" recursive` |
|
||||
| `find_directories` | `$1: 目录路径<br>$2: 文件夹名模式(支持通配符)<br>$3: 是否递归搜索 ("recursive" 表示递归)` | `0 - 成功<br>1 - 失败` | `find_directories /path/to/search "data*" recursive` |
|
||||
| `set_file_permission` | `$1: 文件路径<br>$2: 权限模式 (例如: "755", "644")` | `0 - 成功<br>1 - 失败` | `set_file_permission /path/to/file.txt "755"` |
|
||||
| `compare_files` | `$1: 第一个文件路径<br>$2: 第二个文件路径` | `0 - 文件内容相同<br>1 - 文件内容不同或出错` | `compare_files file1.txt file2.txt` |
|
||||
| `get_file_type` | `$1: 文件路径` | `0 - 成功<br>1 - 失败` | `get_file_type /path/to/file` |
|
||||
| `check_file_checksum` | `$1: 文件路径<br>$2: 校验和算法 (md5, sha1, sha256, sha512)<br>$3: 期望的校验和值 (可选)` | `0 - 校验成功或校验和已输出<br>1 - 校验失败或出错` | `check_file_checksum /path/to/file.txt sha256` |
|
||||
| `get_file_mtime` | `$1: 文件路径` | `0 - 成功<br>1 - 失败` | `get_file_mtime /path/to/file.txt` |
|
||||
| `encrypt_file` | `$1: 文件路径<br>$2: 加密后文件路径<br>$3: 密码` | `0 - 成功<br>1 - 失败` | `encrypt_file /path/to/file.txt /path/to/file.enc "password123"` |
|
||||
| `watch_file_changes` | `$1: 要监控的文件路径<br>$2: 检查间隔时间(秒,默认为 1)<br>$3: 运行最大时间(秒,默认为 60)` | `0 - 文件被修改或创建<br>1 - 超时或出错` | `watch_file_changes /path/to/file.txt 2 120` |
|
||||
|
||||
#### 3. `notice-handle.sh`
|
||||
该脚本提供了在终端中显示不同类型通知的函数,以及交互式的用户输入、确认、单选和多选菜单功能。
|
||||
|
||||
| 函数名 | 参数 | 返回值 | 使用方法 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| `notice_info` | `$1: 消息内容` | `0 - 成功<br>1 - 失败` | `notice_info "这是一条信息通知"` |
|
||||
| `notice_success` | `$1: 消息内容` | `0 - 成功<br>1 - 失败` | `notice_success "操作成功完成"` |
|
||||
| `notice_warning` | `$1: 消息内容` | `0 - 成功<br>1 - 失败` | `notice_warning "请注意这个警告"` |
|
||||
| `notice_error` | `$1: 消息内容` | `0 - 成功<br>1 - 失败` | `notice_error "发生了错误"` |
|
||||
| `notice_debug` | `$1: 消息内容` | `0 - 成功<br>1 - 失败` | `DEBUG=true notice_debug "这是调试信息"` |
|
||||
| `notice_system` | `$1: 通知类型<br>$2: 消息内容` | `0 - 成功<br>1 - 失败` | `notice_system "系统通知" "这是一条系统通知消息"` |
|
||||
| `notice_progress` | `$1: 当前进度<br>$2: 总进度<br>$3: 描述信息` | `0 - 成功<br>1 - 失败` | `notice_progress 5 10 "文件下载中"` |
|
||||
| `notice_prompt` | `$1: 提示文本<br>$2: 用于存储用户输入的变量名<br>$3: 默认值 (可选)` | `0 - 成功<br>1 - 失败` | `notice_prompt "请输入您的名字" "user_name"` |
|
||||
| `notice_confirm` | `$1: 提示文本<br>$2: 默认选项 (可选,"y"或"n",默认为"n")` | `0 - 用户确认是<br>1 - 用户确认否` | `notice_confirm "是否继续?"` |
|
||||
| `notice_select_menu` | `$1: 菜单标题<br>$2: 用于存储选择结果的变量名<br>$3...$n: 菜单选项` | `0 - 用户选择了一个选项<br>1 - 用户取消选择 (按q)<br>2 - 参数错误` | `notice_select_menu "请选择一个操作系统" "selected_os" "Linux" "MacOS" "Windows" "其他"` |
|
||||
| `notice_multi_select_menu` | `$1: 菜单标题<br>$2: 用于存储选择结果的变量名 (将存储为选中项的索引,用逗号分隔)<br>$3...$n: 菜单选项` | `0 - 用户完成选择<br>1 - 用户取消选择 (按q)<br>2 - 参数错误` | `notice_multi_select_menu "请选择您喜欢的编程语言" "selected_langs" "Python" "JavaScript" "Bash" "Go" "Rust" "Java"` |
|
||||
|
||||
#### 4. `other-handle.sh`
|
||||
此脚本提供了通用工具函数,如系统环境检测、依赖检测、配置文件解析等。
|
||||
|
||||
| 函数名 | 参数 | 返回值 | 使用方法 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| `check_environment` | 无 | `0 - 成功检测<br>1 - 检测失败` | `check_environment` |
|
||||
| `parse_yaml_config` | `$1: YAML配置文件路径<br>$2: 用于存储解析后配置的关联数组名称` | `0 - 成功解析配置<br>1 - 解析失败` | `declare -A CONFIG; parse_yaml_config "config.yaml" CONFIG` |
|
||||
| `parse_arguments` | `$@: 所有命令行参数` | `0 - 成功解析参数<br>1 - 解析参数时出错` | `parse_arguments --name="示例项目" --debug -v -o output.txt input1.txt input2.txt` |
|
||||
| `yq_get_value` | `$1: YAML文件路径<br>$2: YAML路径表达式` | `0 - 成功获取值<br>1 - 失败` | `yq_get_value "example.yaml" ".name"` |
|
||||
| `yq_has_path` | `$1: YAML文件路径<br>$2: YAML路径表达式` | `0 - 路径存在<br>1 - 路径不存在` | `yq_has_path "example.yaml" ".version"` |
|
||||
| `run_examples` | 无 | 无 | `run_examples` |
|
||||
|
||||
### 总结
|
||||
这些脚本涵盖了Git操作、文件处理、通知显示和通用工具等多个方面,通过封装常用操作,提高了脚本的可维护性和易用性。在使用时,可根据具体需求调用相应的函数,并按照函数说明提供正确的参数。
|
||||
694
frontend/scripts/tools/notice-handle.sh
Normal file
694
frontend/scripts/tools/notice-handle.sh
Normal file
@@ -0,0 +1,694 @@
|
||||
#!/bin/bash
|
||||
|
||||
#######################################
|
||||
# 通知处理脚本
|
||||
#
|
||||
# 此脚本提供了一系列用于在终端中显示不同类型通知的函数,
|
||||
# 包括普通信息、成功、警告、错误、调试信息等,以及交互式
|
||||
# 的用户输入、确认、单选和多选菜单功能。
|
||||
#
|
||||
# 作者: chudong
|
||||
# 版本: 1.0.0
|
||||
#######################################
|
||||
|
||||
# 定义颜色
|
||||
RED='\033[0;31m' # 错误
|
||||
GREEN='\033[0;32m' # 成功
|
||||
YELLOW='\033[0;33m' # 警告
|
||||
BLUE='\033[0;34m' # 信息
|
||||
PURPLE='\033[0;35m' # 调试
|
||||
CYAN='\033[0;36m' # 提示
|
||||
NC='\033[0m' # 无颜色
|
||||
|
||||
#######################################
|
||||
# 普通信息通知
|
||||
# 参数:
|
||||
# $1: 消息内容
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
notice_info() {
|
||||
local message="$1"
|
||||
|
||||
if [ -z "$message" ]; then
|
||||
echo "${BLUE}[信息] 未提供消息内容${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "${BLUE}[信息] $message${NC}"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 成功信息通知
|
||||
# 参数:
|
||||
# $1: 消息内容
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
notice_success() {
|
||||
local message="$1"
|
||||
|
||||
if [ -z "$message" ]; then
|
||||
echo "${GREEN}[成功] 未提供消息内容${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "${GREEN}[成功] $message${NC}"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 警告信息通知
|
||||
# 参数:
|
||||
# $1: 消息内容
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
notice_warning() {
|
||||
local message="$1"
|
||||
|
||||
if [ -z "$message" ]; then
|
||||
echo "${YELLOW}[警告] 未提供消息内容${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "${YELLOW}[警告] $message${NC}"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 错误信息通知
|
||||
# 参数:
|
||||
# $1: 消息内容
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
notice_error() {
|
||||
local message="$1"
|
||||
|
||||
if [ -z "$message" ]; then
|
||||
echo "${RED}[错误] 未提供消息内容${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "${RED}[错误] $message${NC}"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 调试信息通知(仅在调试模式下显示)
|
||||
# 参数:
|
||||
# $1: 消息内容
|
||||
# 环境变量:
|
||||
# DEBUG: 设置为 "true" 时启用调试输出
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败(仅在调试模式下且未提供消息内容时)
|
||||
#######################################
|
||||
notice_debug() {
|
||||
local message="$1"
|
||||
local debug_mode="${DEBUG:-false}"
|
||||
|
||||
if [ "$debug_mode" = "true" ]; then
|
||||
if [ -z "$message" ]; then
|
||||
echo "${PURPLE}[调试] 未提供消息内容${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "${PURPLE}[调试] $message${NC}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 系统通知(使用系统原生通知功能)
|
||||
# 参数:
|
||||
# $1: 通知标题
|
||||
# $2: 通知消息内容
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
notice_system() {
|
||||
local title="$1"
|
||||
local message="$2"
|
||||
|
||||
if [ -z "$title" ] || [ -z "$message" ]; then
|
||||
echo "${RED}[错误] 系统通知需要标题和消息内容${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检测系统类型,使用适当的系统通知方法
|
||||
if [ "$(uname)" = "Darwin" ]; then
|
||||
# MacOS
|
||||
osascript -e "display notification \"$message\" with title \"$title\""
|
||||
elif [ "$(uname)" = "Linux" ] && command -v notify-send &> /dev/null; then
|
||||
# Linux 并且有 notify-send
|
||||
notify-send "$title" "$message"
|
||||
else
|
||||
# 回退到普通输出
|
||||
echo "${CYAN}[系统通知] $title: $message${NC}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 进度条通知
|
||||
# 参数:
|
||||
# $1: 当前进度值
|
||||
# $2: 总进度值
|
||||
# $3: 进度描述 (可选,默认为"处理中")
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
notice_progress() {
|
||||
local current="$1"
|
||||
local total="$2"
|
||||
local description="${3:-处理中}"
|
||||
|
||||
if [ -z "$current" ] || [ -z "$total" ]; then
|
||||
echo "${RED}[错误] 进度条需要当前值和总值${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 计算百分比
|
||||
local percent=$((current * 100 / total))
|
||||
local completed=$((percent / 2))
|
||||
local remaining=$((50 - completed))
|
||||
|
||||
# 创建进度条
|
||||
local bar="["
|
||||
for ((i=0; i<completed; i++)); do
|
||||
bar+="#"
|
||||
done
|
||||
|
||||
for ((i=0; i<remaining; i++)); do
|
||||
bar+="-"
|
||||
done
|
||||
|
||||
bar+="] $percent%"
|
||||
|
||||
# 显示进度条
|
||||
echo -n "${CYAN}$description: $bar\r${NC}"
|
||||
|
||||
# 如果完成了,添加换行
|
||||
if [ "$current" -ge "$total" ]; then
|
||||
echo ""
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 用户输入提示
|
||||
# 参数:
|
||||
# $1: 提示文本
|
||||
# $2: 用于存储用户输入的变量名
|
||||
# $3: 默认值 (可选)
|
||||
# 返回值:
|
||||
# 0 - 成功
|
||||
# 1 - 失败
|
||||
# 注意:
|
||||
# 此函数通过 eval 设置调用者环境中的变量,
|
||||
# 必须通过 source 方式调用脚本才能生效
|
||||
#######################################
|
||||
notice_prompt() {
|
||||
local prompt="$1"
|
||||
local variable_name="$2"
|
||||
local default_value="$3"
|
||||
|
||||
if [ -z "$prompt" ] || [ -z "$variable_name" ]; then
|
||||
echo "${RED}[错误] 用户输入提示需要提示文本和变量名${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local input=""
|
||||
|
||||
# 显示带有默认值的提示(如果有)
|
||||
if [ -n "$default_value" ]; then
|
||||
echo -n "${CYAN}$prompt [${default_value}]: ${NC}"
|
||||
read input
|
||||
|
||||
# 如果用户未输入任何内容,使用默认值
|
||||
if [ -z "$input" ]; then
|
||||
input="$default_value"
|
||||
fi
|
||||
else
|
||||
echo -n "${CYAN}$prompt: ${NC}"
|
||||
read input
|
||||
fi
|
||||
|
||||
# 为调用脚本设置变量
|
||||
# 注意:这只在 source 调用此脚本时有效
|
||||
eval "$variable_name=\"$input\""
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 确认消息通知(询问用户是/否)
|
||||
# 参数:
|
||||
# $1: 提示文本
|
||||
# $2: 默认选项 (可选,"y"或"n",默认为"n")
|
||||
# 返回值:
|
||||
# 0 - 用户确认是
|
||||
# 1 - 用户确认否
|
||||
#######################################
|
||||
notice_confirm() {
|
||||
local message="$1"
|
||||
local default="${2:-n}" # 默认为否
|
||||
|
||||
if [ -z "$message" ]; then
|
||||
echo "${RED}[错误] 确认消息需要提示文本${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local prompt=""
|
||||
|
||||
# 根据默认值设置提示
|
||||
if [ "$default" = "y" ]; then
|
||||
prompt="[Y/n]"
|
||||
else
|
||||
prompt="[y/N]"
|
||||
fi
|
||||
|
||||
# 询问用户
|
||||
while true; do
|
||||
echo -n "${CYAN}$message $prompt ${NC}"
|
||||
read response
|
||||
|
||||
# 如果用户未输入任何内容,使用默认值
|
||||
if [ -z "$response" ]; then
|
||||
response="$default"
|
||||
fi
|
||||
|
||||
# 检查响应
|
||||
case "$response" in
|
||||
[Yy]*)
|
||||
return 0
|
||||
;;
|
||||
[Nn]*)
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
echo "${YELLOW}请输入 y 或 n${NC}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 单选菜单函数,支持键盘方向键控制
|
||||
# 参数:
|
||||
# $1: 菜单标题
|
||||
# $2: 用于存储选择结果的变量名
|
||||
# $3...$n: 菜单选项
|
||||
# 使用方法:
|
||||
# notice_select_menu "请选择一个选项" "selected_option" "选项1" "选项2" "选项3"
|
||||
# echo "用户选择了: $selected_option"
|
||||
# 返回值:
|
||||
# 0 - 用户选择了一个选项
|
||||
# 1 - 用户取消选择 (按q)
|
||||
# 2 - 参数错误
|
||||
#######################################
|
||||
notice_select_menu() {
|
||||
local title="$1"
|
||||
local return_var="$2"
|
||||
|
||||
# 检查参数
|
||||
if [ -z "$title" ] || [ -z "$return_var" ] || [ $# -lt 3 ]; then
|
||||
echo "${RED}[错误] 单选菜单需要标题、返回变量名和至少一个选项${NC}"
|
||||
return 2
|
||||
fi
|
||||
|
||||
# 移除前两个参数,剩下的是菜单选项
|
||||
shift 2
|
||||
local options=("$@")
|
||||
local selected=0
|
||||
local option_count=${#options[@]}
|
||||
local result=""
|
||||
|
||||
# 检查是否支持 tput (但不强制依赖)
|
||||
local has_tput=false
|
||||
if command -v tput &>/dev/null; then
|
||||
has_tput=true
|
||||
fi
|
||||
|
||||
# 隐藏光标 (如果支持)
|
||||
if [ "$has_tput" = true ]; then
|
||||
tput civis 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 保存终端设置
|
||||
local saved_tty=""
|
||||
if [ -t 0 ]; then
|
||||
saved_tty=$(stty -g)
|
||||
fi
|
||||
|
||||
# 设置终端为非规范模式,禁用回显
|
||||
stty -icanon -echo 2>/dev/null || true
|
||||
|
||||
while true; do
|
||||
# 清屏和显示标题
|
||||
clear 2>/dev/null || printf "\033c" || echo -e "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
|
||||
echo "${CYAN}$title${NC}"
|
||||
echo "${CYAN}(使用方向键↑↓移动,回车确认,q退出)${NC}"
|
||||
echo ""
|
||||
|
||||
# 显示选项
|
||||
for i in "${!options[@]}"; do
|
||||
if [ $i -eq $selected ]; then
|
||||
echo "${GREEN}>> $((i+1)). ${options[$i]} <<${NC}"
|
||||
else
|
||||
echo " $((i+1)). ${options[$i]}"
|
||||
fi
|
||||
done
|
||||
|
||||
# 读取用户输入
|
||||
local key=""
|
||||
IFS= read -r -s -n1 key
|
||||
|
||||
# 处理特殊键
|
||||
if [[ $key == $'\e' ]]; then
|
||||
if read -r -s -n2 -t 0.1 key2; then
|
||||
key+="$key2"
|
||||
fi
|
||||
|
||||
case "$key" in
|
||||
$'\e[A') # 上箭头
|
||||
((selected--))
|
||||
if [ $selected -lt 0 ]; then
|
||||
selected=$((option_count - 1))
|
||||
fi
|
||||
;;
|
||||
$'\e[B') # 下箭头
|
||||
((selected++))
|
||||
if [ $selected -ge $option_count ]; then
|
||||
selected=0
|
||||
fi
|
||||
;;
|
||||
$'\e[C'|$'\e[D') # 左右箭头 (忽略)
|
||||
;;
|
||||
*) # 其他ESC序列 (忽略)
|
||||
;;
|
||||
esac
|
||||
continue
|
||||
fi
|
||||
|
||||
# 处理其他键
|
||||
case "$key" in
|
||||
"q"|"Q")
|
||||
# 恢复终端设置
|
||||
if [ -n "$saved_tty" ]; then
|
||||
stty "$saved_tty" 2>/dev/null || true
|
||||
fi
|
||||
# 显示光标 (如果支持)
|
||||
if [ "$has_tput" = true ]; then
|
||||
tput cnorm 2>/dev/null || true
|
||||
fi
|
||||
echo ""
|
||||
return 1
|
||||
;;
|
||||
"w"|"W") # 向上兼容原始的 w/s 控制
|
||||
((selected--))
|
||||
if [ $selected -lt 0 ]; then
|
||||
selected=$((option_count - 1))
|
||||
fi
|
||||
;;
|
||||
"s"|"S") # 向上兼容原始的 w/s 控制
|
||||
((selected++))
|
||||
if [ $selected -ge $option_count ]; then
|
||||
selected=0
|
||||
fi
|
||||
;;
|
||||
"") # 回车键
|
||||
result="$selected"
|
||||
break
|
||||
;;
|
||||
[0-9]) # 数字键 - 直接跳转
|
||||
local num=$((key - 1))
|
||||
if [ $num -ge 0 ] && [ $num -lt $option_count ]; then
|
||||
selected=$num
|
||||
result="$selected"
|
||||
break
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 恢复终端设置
|
||||
if [ -n "$saved_tty" ]; then
|
||||
stty "$saved_tty" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 显示光标 (如果支持)
|
||||
if [ "$has_tput" = true ]; then
|
||||
tput cnorm 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 设置返回变量
|
||||
eval "$return_var=\"$result\""
|
||||
|
||||
echo ""
|
||||
echo "${GREEN}已选择: ${options[$result]}${NC}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 多选菜单函数,支持键盘方向键控制
|
||||
# 参数:
|
||||
# $1: 菜单标题
|
||||
# $2: 用于存储选择结果的变量名 (将存储为选中项的索引,用逗号分隔)
|
||||
# $3...$n: 菜单选项
|
||||
# 使用方法:
|
||||
# notice_multi_select_menu "请选择多个选项" "selected_options" "选项1" "选项2" "选项3"
|
||||
# echo "用户选择了索引: $selected_options"
|
||||
# IFS=',' read -ra SELECTED_INDICES <<< "$selected_options"
|
||||
# for i in "${SELECTED_INDICES[@]}"; do
|
||||
# echo "选中了选项: ${原选项数组[$i]}"
|
||||
# done
|
||||
# 返回值:
|
||||
# 0 - 用户完成选择
|
||||
# 1 - 用户取消选择 (按q)
|
||||
# 2 - 参数错误
|
||||
#######################################
|
||||
notice_multi_select_menu() {
|
||||
local title="$1"
|
||||
local return_var="$2"
|
||||
|
||||
# 检查参数
|
||||
if [ -z "$title" ] || [ -z "$return_var" ] || [ $# -lt 3 ]; then
|
||||
echo "${RED}[错误] 多选菜单需要标题、返回变量名和至少一个选项${NC}"
|
||||
return 2
|
||||
fi
|
||||
|
||||
# 移除前两个参数,剩下的是菜单选项
|
||||
shift 2
|
||||
local options=("$@")
|
||||
local cursor=0
|
||||
local option_count=${#options[@]}
|
||||
local result=""
|
||||
|
||||
# 初始化选中状态数组 (0表示未选中,1表示已选中)
|
||||
local selected_status=()
|
||||
for ((i=0; i<option_count; i++)); do
|
||||
selected_status[$i]=0
|
||||
done
|
||||
|
||||
# 检查是否支持 tput (但不强制依赖)
|
||||
local has_tput=false
|
||||
if command -v tput &>/dev/null; then
|
||||
has_tput=true
|
||||
fi
|
||||
|
||||
# 隐藏光标 (如果支持)
|
||||
if [ "$has_tput" = true ]; then
|
||||
tput civis 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 保存终端设置
|
||||
local saved_tty=""
|
||||
if [ -t 0 ]; then
|
||||
saved_tty=$(stty -g)
|
||||
fi
|
||||
|
||||
# 设置终端为非规范模式,禁用回显
|
||||
stty -icanon -echo 2>/dev/null || true
|
||||
|
||||
while true; do
|
||||
# 清屏和显示标题
|
||||
clear 2>/dev/null || printf "\033c" || echo -e "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
|
||||
echo "${CYAN}$title${NC}"
|
||||
echo "${CYAN}(使用方向键↑↓移动,空格键选择/取消,回车确认,q退出)${NC}"
|
||||
echo ""
|
||||
|
||||
# 显示选项
|
||||
for i in "${!options[@]}"; do
|
||||
if [ $i -eq $cursor ]; then
|
||||
if [ ${selected_status[$i]} -eq 1 ]; then
|
||||
echo "${GREEN}>> [X] ${options[$i]} <<${NC}"
|
||||
else
|
||||
echo "${GREEN}>> [ ] ${options[$i]} <<${NC}"
|
||||
fi
|
||||
else
|
||||
if [ ${selected_status[$i]} -eq 1 ]; then
|
||||
echo " [X] ${options[$i]}"
|
||||
else
|
||||
echo " [ ] ${options[$i]}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 读取用户输入
|
||||
local key=""
|
||||
IFS= read -r -s -n1 key
|
||||
|
||||
# 处理特殊键
|
||||
if [[ $key == $'\e' ]]; then
|
||||
if read -r -s -n2 -t 0.1 key2; then
|
||||
key+="$key2"
|
||||
fi
|
||||
|
||||
case "$key" in
|
||||
$'\e[A') # 上箭头
|
||||
((cursor--))
|
||||
if [ $cursor -lt 0 ]; then
|
||||
cursor=$((option_count - 1))
|
||||
fi
|
||||
;;
|
||||
$'\e[B') # 下箭头
|
||||
((cursor++))
|
||||
if [ $cursor -ge $option_count ]; then
|
||||
cursor=0
|
||||
fi
|
||||
;;
|
||||
$'\e[C') # 右箭头 (忽略)
|
||||
;;
|
||||
$'\e[D') # 左箭头 (忽略)
|
||||
;;
|
||||
*) # 其他ESC序列 (忽略)
|
||||
;;
|
||||
esac
|
||||
continue
|
||||
fi
|
||||
|
||||
# 处理其他键
|
||||
case "$key" in
|
||||
"q"|"Q")
|
||||
# 恢复终端设置
|
||||
if [ -n "$saved_tty" ]; then
|
||||
stty "$saved_tty" 2>/dev/null || true
|
||||
fi
|
||||
# 显示光标 (如果支持)
|
||||
if [ "$has_tput" = true ]; then
|
||||
tput cnorm 2>/dev/null || true
|
||||
fi
|
||||
echo ""
|
||||
return 1
|
||||
;;
|
||||
" ") # 空格键
|
||||
# 切换选中状态
|
||||
if [ ${selected_status[$cursor]} -eq 0 ]; then
|
||||
selected_status[$cursor]=1
|
||||
else
|
||||
selected_status[$cursor]=0
|
||||
fi
|
||||
;;
|
||||
"") # 回车键
|
||||
# 构建结果字符串 (选中项的索引,用逗号分隔)
|
||||
local first=true
|
||||
for i in "${!selected_status[@]}"; do
|
||||
if [ ${selected_status[$i]} -eq 1 ]; then
|
||||
if [ "$first" = true ]; then
|
||||
result="$i"
|
||||
first=false
|
||||
else
|
||||
result="$result,$i"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
break
|
||||
;;
|
||||
[0-9]) # 数字键 - 直接跳转
|
||||
local num=$((key))
|
||||
if [ $num -lt $option_count ]; then
|
||||
cursor=$num
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 恢复终端设置
|
||||
if [ -n "$saved_tty" ]; then
|
||||
stty "$saved_tty" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 显示光标 (如果支持)
|
||||
if [ "$has_tput" = true ]; then
|
||||
tput cnorm 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 设置返回变量
|
||||
eval "$return_var=\"$result\""
|
||||
|
||||
echo ""
|
||||
echo "${GREEN}已选择索引: $result${NC}"
|
||||
|
||||
# 显示已选项
|
||||
if [ -n "$result" ]; then
|
||||
echo "${GREEN}已选择选项:${NC}"
|
||||
IFS=',' read -ra SELECTED_INDICES <<< "$result"
|
||||
for i in "${SELECTED_INDICES[@]}"; do
|
||||
echo "${GREEN} - ${options[$i]}${NC}"
|
||||
done
|
||||
else
|
||||
echo "${YELLOW}未选择任何选项${NC}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 使用示例
|
||||
#######################################
|
||||
|
||||
# notice_info "这是一条信息通知"
|
||||
# notice_success "操作成功完成"
|
||||
# notice_warning "请注意这个警告"
|
||||
# notice_error "发生了错误"
|
||||
# DEBUG=true notice_debug "这是调试信息"
|
||||
# notice_system "系统通知" "这是一条系统通知消息"
|
||||
|
||||
# # 进度条示例
|
||||
# for i in {1..10}; do
|
||||
# notice_progress $i 10 "文件下载中"
|
||||
# sleep 0.5
|
||||
# done
|
||||
|
||||
# # 用户输入示例
|
||||
# notice_prompt "请输入您的名字" "user_name"
|
||||
# echo "您好, $user_name!"
|
||||
|
||||
# # 确认示例
|
||||
# if notice_confirm "是否继续?"; then
|
||||
# echo "用户选择继续"
|
||||
# else
|
||||
# echo "用户选择取消"
|
||||
# fi
|
||||
|
||||
# # 单选菜单示例
|
||||
# notice_select_menu "请选择一个操作系统" "selected_os" "Linux" "MacOS" "Windows" "其他"
|
||||
# echo "您选择了: $selected_os"
|
||||
|
||||
# # 多选菜单示例
|
||||
# notice_multi_select_menu "请选择您喜欢的编程语言" "selected_langs" "Python" "JavaScript" "Bash" "Go" "Rust" "Java"
|
||||
# echo "选择的索引: $selected_langs"
|
||||
611
frontend/scripts/tools/other-handle.sh
Normal file
611
frontend/scripts/tools/other-handle.sh
Normal file
@@ -0,0 +1,611 @@
|
||||
#!/bin/bash
|
||||
|
||||
#######################################
|
||||
# 通用工具处理脚本
|
||||
#
|
||||
# 此脚本提供了一系列通用工具函数,包括系统环境检测、
|
||||
# 依赖检测、工作区检测、配置文件解析、命令行参数解析
|
||||
# 以及yq工具的常用操作方法。
|
||||
#
|
||||
# 作者: chudong
|
||||
# 版本: 1.0.0
|
||||
#######################################
|
||||
|
||||
# 引入通知处理脚本
|
||||
OTHER_TOOL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$OTHER_TOOL_DIR/notice-handle.sh"
|
||||
|
||||
#######################################
|
||||
# 检测系统环境信息
|
||||
# 全局变量:
|
||||
# OS_TYPE - 操作系统类型 (Linux/MacOS/Windows/Unknown)
|
||||
# OS_NAME - 操作系统名称 (如 Ubuntu, Debian, macOS等)
|
||||
# OS_VERSION - 操作系统版本
|
||||
# ARCH - 系统架构 (x86_64, arm64等)
|
||||
# SHELL_TYPE - Shell类型 (bash, zsh等)
|
||||
# 返回值:
|
||||
# 0 - 成功检测
|
||||
# 1 - 检测失败
|
||||
#######################################
|
||||
check_environment() {
|
||||
notice_info "检测系统环境..."
|
||||
|
||||
# 检测操作系统类型
|
||||
case "$(uname -s)" in
|
||||
Linux*)
|
||||
OS_TYPE="Linux"
|
||||
if [ -f /etc/os-release ]; then
|
||||
OS_NAME=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"')
|
||||
OS_VERSION=$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release | tr -d '"')
|
||||
else
|
||||
OS_NAME="Unknown Linux"
|
||||
OS_VERSION="Unknown"
|
||||
fi
|
||||
;;
|
||||
Darwin*)
|
||||
OS_TYPE="MacOS"
|
||||
OS_NAME="macOS"
|
||||
OS_VERSION=$(sw_vers -productVersion)
|
||||
;;
|
||||
CYGWIN*|MINGW*|MSYS*)
|
||||
OS_TYPE="Windows"
|
||||
OS_NAME="Windows"
|
||||
OS_VERSION="Unknown"
|
||||
;;
|
||||
*)
|
||||
OS_TYPE="Unknown"
|
||||
OS_NAME="Unknown"
|
||||
OS_VERSION="Unknown"
|
||||
notice_error "无法识别的操作系统类型: $(uname -s)"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# 检测系统架构
|
||||
ARCH=$(uname -m)
|
||||
|
||||
# 检测Shell类型
|
||||
SHELL_TYPE=$(basename "$SHELL")
|
||||
|
||||
# 输出检测结果
|
||||
notice_success "系统环境检测完成"
|
||||
notice_info "操作系统类型: $OS_TYPE"
|
||||
notice_info "操作系统版本: $OS_VERSION"
|
||||
notice_info "系统架构: $ARCH"
|
||||
notice_info "Shell类型: $SHELL_TYPE"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 检测yq依赖是否安装
|
||||
# 参数:
|
||||
# $1: 最低版本要求 (可选,格式如 "4.0.0")
|
||||
# 全局变量:
|
||||
# YQ_PATH - yq命令的完整路径
|
||||
# YQ_VERSION - yq的版本号
|
||||
# 返回值:
|
||||
# 0 - yq已安装且版本符合要求
|
||||
# 1 - yq未安装或版本不符合要求
|
||||
#######################################
|
||||
check_yq_dependency() {
|
||||
local min_version="${1:-0.0.0}"
|
||||
local yq_cmd
|
||||
|
||||
notice_info "检测yq依赖..."
|
||||
|
||||
# 检查yq命令是否可用
|
||||
yq_cmd=$(command -v yq)
|
||||
if [ -z "$yq_cmd" ]; then
|
||||
notice_error "yq 未安装"
|
||||
notice_info "请安装yq: https://github.com/mikefarah/yq#install"
|
||||
return 1
|
||||
fi
|
||||
|
||||
YQ_PATH="$yq_cmd"
|
||||
YQ_VERSION=$("$YQ_PATH" --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
||||
|
||||
# 检查版本要求
|
||||
if [ "$min_version" != "0.0.0" ]; then
|
||||
# 简单版本比较函数
|
||||
version_lt() {
|
||||
[ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ] && [ "$1" != "$2" ]
|
||||
}
|
||||
|
||||
if version_lt "$YQ_VERSION" "$min_version"; then
|
||||
notice_error "yq版本过低: $YQ_VERSION (需要 $min_version 或更高)"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
notice_success "yq依赖检测通过 (版本: $YQ_VERSION, 路径: $YQ_PATH)"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 检测当前目录是否为工作区根目录
|
||||
# 参数:
|
||||
# $@: 表示工作区的标志文件或目录 (默认为 package.json, .git等)
|
||||
# 全局变量:
|
||||
# WORKSPACE_ROOT - 工作区根目录的绝对路径
|
||||
# 返回值:
|
||||
# 0 - 在工作区根目录中
|
||||
# 1 - 不在工作区根目录中
|
||||
#######################################
|
||||
check_workspace() {
|
||||
local markers=("$@")
|
||||
local current_dir
|
||||
local parent_dir
|
||||
|
||||
notice_info "检测工作区..."
|
||||
|
||||
# 如果未指定标志,使用默认值
|
||||
if [ ${#markers[@]} -eq 0 ]; then
|
||||
markers=("package.json" ".git" "pnpm-workspace.yaml" "turbo.json")
|
||||
fi
|
||||
|
||||
# 获取当前目录的绝对路径
|
||||
current_dir=$(pwd)
|
||||
parent_dir="$current_dir"
|
||||
|
||||
while [ "$parent_dir" != "/" ]; do
|
||||
for marker in "${markers[@]}"; do
|
||||
if [ -e "$parent_dir/$marker" ]; then
|
||||
WORKSPACE_ROOT="$parent_dir"
|
||||
notice_success "找到工作区根目录: $WORKSPACE_ROOT"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
parent_dir=$(dirname "$parent_dir")
|
||||
done
|
||||
|
||||
notice_error "未找到工作区根目录"
|
||||
return 1
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 解析YAML配置文件
|
||||
# 参数:
|
||||
# $1: YAML配置文件路径
|
||||
# $2: 用于存储解析后配置的关联数组名称
|
||||
# 返回值:
|
||||
# 0 - 成功解析配置
|
||||
# 1 - 解析失败
|
||||
# 使用示例:
|
||||
# declare -A CONFIG
|
||||
# parse_yaml_config "config.yaml" CONFIG
|
||||
# echo "用户名: ${CONFIG[username]}"
|
||||
#######################################
|
||||
parse_yaml_config() {
|
||||
local config_file="$1"
|
||||
local config_var="$2"
|
||||
|
||||
notice_info "解析配置文件: $config_file"
|
||||
|
||||
# 检查文件是否存在
|
||||
if [ ! -f "$config_file" ]; then
|
||||
notice_error "配置文件不存在: $config_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查yq依赖
|
||||
if ! check_yq_dependency "4.0.0"; then
|
||||
notice_error "解析YAML需要yq 4.0.0或更高版本"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查文件格式是否为YAML
|
||||
if ! "$YQ_PATH" e '.' "$config_file" > /dev/null 2>&1; then
|
||||
notice_error "无效的YAML文件: $config_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 将YAML转换为扁平的键值对
|
||||
local key_values
|
||||
key_values=$("$YQ_PATH" e 'to_entries | .[] | select(.value != null) | [.key, .value] | join("=")' "$config_file")
|
||||
|
||||
# 解析键值对到关联数组
|
||||
while IFS= read -r line; do
|
||||
if [ -n "$line" ]; then
|
||||
IFS='=' read -r key value <<< "$line"
|
||||
# 使用eval给指定的数组变量赋值
|
||||
eval "$config_var[\$key]=\$value"
|
||||
fi
|
||||
done <<< "$key_values"
|
||||
|
||||
notice_success "配置文件解析完成"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 解析命令行参数
|
||||
# 参数:
|
||||
# $@: 所有命令行参数
|
||||
# 全局变量:
|
||||
# PARAMS - 关联数组,存储所有解析后的参数
|
||||
# POSITIONAL - 数组,存储所有位置参数
|
||||
# 返回值:
|
||||
# 0 - 成功解析参数
|
||||
# 1 - 解析参数时出错
|
||||
# 使用示例:
|
||||
# parse_arguments "$@"
|
||||
# echo "输出文件: ${PARAMS[output]}"
|
||||
# echo "调试模式: ${PARAMS[debug]}"
|
||||
# echo "第一个位置参数: ${POSITIONAL[0]}"
|
||||
#######################################
|
||||
parse_arguments() {
|
||||
# 初始化全局参数数组
|
||||
declare -gA PARAMS
|
||||
declare -ga POSITIONAL=()
|
||||
|
||||
notice_info "解析命令行参数..."
|
||||
|
||||
# 如果没有参数,返回成功
|
||||
if [ $# -eq 0 ]; then
|
||||
notice_warning "没有提供命令行参数"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 解析所有参数
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
# 长选项 (--option=value 或 --option value)
|
||||
--*=*)
|
||||
key="${1#--}"
|
||||
key="${key%%=*}"
|
||||
value="${1#*=}"
|
||||
PARAMS["$key"]="$value"
|
||||
shift
|
||||
;;
|
||||
--*)
|
||||
key="${1#--}"
|
||||
if [[ "$2" != -* && $# -gt 1 ]]; then
|
||||
PARAMS["$key"]="$2"
|
||||
shift 2
|
||||
else
|
||||
# 布尔标志
|
||||
PARAMS["$key"]="true"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
# 短选项 (-o value 或 -o)
|
||||
-*)
|
||||
if [ ${#1} -gt 2 ]; then
|
||||
# 组合短选项 (-abc)
|
||||
flags="${1:1}"
|
||||
for ((i=0; i<${#flags}; i++)); do
|
||||
flag="${flags:$i:1}"
|
||||
PARAMS["$flag"]="true"
|
||||
done
|
||||
shift
|
||||
else
|
||||
# 单个短选项 (-a value 或 -a)
|
||||
key="${1:1}"
|
||||
if [[ "$2" != -* && $# -gt 1 ]]; then
|
||||
PARAMS["$key"]="$2"
|
||||
shift 2
|
||||
else
|
||||
# 布尔标志
|
||||
PARAMS["$key"]="true"
|
||||
shift
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
# 位置参数
|
||||
*)
|
||||
POSITIONAL+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 输出解析结果
|
||||
if [ ${#PARAMS[@]} -gt 0 ]; then
|
||||
notice_info "命名参数:"
|
||||
for key in "${!PARAMS[@]}"; do
|
||||
notice_info " - $key: ${PARAMS[$key]}"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ ${#POSITIONAL[@]} -gt 0 ]; then
|
||||
notice_info "位置参数:"
|
||||
for ((i=0; i<${#POSITIONAL[@]}; i++)); do
|
||||
notice_info " - $i: ${POSITIONAL[$i]}"
|
||||
done
|
||||
fi
|
||||
|
||||
notice_success "命令行参数解析完成"
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# yq常用操作方法集合
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# 从YAML文件获取指定路径的值
|
||||
# 参数:
|
||||
# $1: YAML文件路径
|
||||
# $2: YAML路径表达式
|
||||
# 返回值:
|
||||
# 0 - 成功获取值
|
||||
# 1 - 失败
|
||||
# 标准输出: 查询到的值
|
||||
#######################################
|
||||
yq_get_value() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
notice_error "文件不存在: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$YQ_PATH" e "$path" "$file"
|
||||
return $?
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 设置YAML文件中指定路径的值
|
||||
# 参数:
|
||||
# $1: YAML文件路径
|
||||
# $2: YAML路径表达式
|
||||
# $3: 要设置的值
|
||||
# 返回值:
|
||||
# 0 - 成功设置值
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
yq_set_value() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
local value="$3"
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
notice_error "文件不存在: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$YQ_PATH" e "$path = $value" -i "$file"
|
||||
return $?
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 删除YAML文件中指定路径的值
|
||||
# 参数:
|
||||
# $1: YAML文件路径
|
||||
# $2: YAML路径表达式
|
||||
# 返回值:
|
||||
# 0 - 成功删除值
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
yq_delete_path() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
notice_error "文件不存在: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$YQ_PATH" e "del($path)" -i "$file"
|
||||
return $?
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 合并两个YAML文件
|
||||
# 参数:
|
||||
# $1: 目标文件路径
|
||||
# $2: 源文件路径
|
||||
# 返回值:
|
||||
# 0 - 成功合并文件
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
yq_merge_files() {
|
||||
local target_file="$1"
|
||||
local source_file="$2"
|
||||
|
||||
if [ ! -f "$target_file" ] || [ ! -f "$source_file" ]; then
|
||||
notice_error "文件不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$YQ_PATH" eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' "$target_file" "$source_file" > "${target_file}.tmp" \
|
||||
&& mv "${target_file}.tmp" "$target_file"
|
||||
|
||||
return $?
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 检查YAML文件中是否存在某个路径
|
||||
# 参数:
|
||||
# $1: YAML文件路径
|
||||
# $2: YAML路径表达式
|
||||
# 返回值:
|
||||
# 0 - 路径存在
|
||||
# 1 - 路径不存在或文件不存在
|
||||
#######################################
|
||||
yq_has_path() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
notice_error "文件不存在: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 若路径存在,则值不为null,返回true
|
||||
if [ "$("$YQ_PATH" e "$path" "$file")" != "null" ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 为YAML数组添加元素
|
||||
# 参数:
|
||||
# $1: YAML文件路径
|
||||
# $2: 数组路径表达式
|
||||
# $3: 要添加的值
|
||||
# 返回值:
|
||||
# 0 - 成功添加元素
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
yq_array_append() {
|
||||
local file="$1"
|
||||
local array_path="$2"
|
||||
local value="$3"
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
notice_error "文件不存在: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$YQ_PATH" e "$array_path += [$value]" -i "$file"
|
||||
return $?
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 获取YAML数组长度
|
||||
# 参数:
|
||||
# $1: YAML文件路径
|
||||
# $2: 数组路径表达式
|
||||
# 返回值:
|
||||
# 0 - 成功获取长度
|
||||
# 1 - 失败
|
||||
# 标准输出: 数组长度
|
||||
#######################################
|
||||
yq_array_length() {
|
||||
local file="$1"
|
||||
local array_path="$2"
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
notice_error "文件不存在: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$YQ_PATH" e "$array_path | length" "$file"
|
||||
return $?
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 将YAML转换为JSON
|
||||
# 参数:
|
||||
# $1: YAML文件路径
|
||||
# $2: 输出JSON文件路径 (可选,默认为与YAML文件同名但扩展名为.json)
|
||||
# 返回值:
|
||||
# 0 - 成功转换
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
yq_to_json() {
|
||||
local yaml_file="$1"
|
||||
local json_file="${2:-${yaml_file%.*}.json}"
|
||||
|
||||
if [ ! -f "$yaml_file" ]; then
|
||||
notice_error "YAML文件不存在: $yaml_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$YQ_PATH" e -o=json '.' "$yaml_file" > "$json_file"
|
||||
notice_success "转换完成: $yaml_file -> $json_file"
|
||||
return $?
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 将JSON转换为YAML
|
||||
# 参数:
|
||||
# $1: JSON文件路径
|
||||
# $2: 输出YAML文件路径 (可选,默认为与JSON文件同名但扩展名为.yaml)
|
||||
# 返回值:
|
||||
# 0 - 成功转换
|
||||
# 1 - 失败
|
||||
#######################################
|
||||
yq_from_json() {
|
||||
local json_file="$1"
|
||||
local yaml_file="${2:-${json_file%.*}.yaml}"
|
||||
|
||||
if [ ! -f "$json_file" ]; then
|
||||
notice_error "JSON文件不存在: $json_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$YQ_PATH" e -p=json -o=yaml '.' "$json_file" > "$yaml_file"
|
||||
notice_success "转换完成: $json_file -> $yaml_file"
|
||||
return $?
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 使用示例
|
||||
#######################################
|
||||
|
||||
# 运行所有示例函数
|
||||
run_examples() {
|
||||
notice_info "运行示例..."
|
||||
|
||||
# 1. 环境检测示例
|
||||
check_environment
|
||||
echo ""
|
||||
|
||||
# 2. yq依赖检测示例
|
||||
check_yq_dependency
|
||||
echo ""
|
||||
|
||||
# 3. 工作区检测示例
|
||||
check_workspace
|
||||
echo ""
|
||||
|
||||
# 4. 配置文件解析示例(假设有配置文件)
|
||||
if [ -f "config.yaml" ]; then
|
||||
declare -A CONFIG
|
||||
parse_yaml_config "config.yaml" CONFIG
|
||||
echo "解析到的配置项:"
|
||||
for key in "${!CONFIG[@]}"; do
|
||||
echo " $key: ${CONFIG[$key]}"
|
||||
done
|
||||
echo ""
|
||||
else
|
||||
notice_warning "示例配置文件 config.yaml 不存在,跳过解析示例"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 5. 命令行参数解析示例
|
||||
parse_arguments --name="示例项目" --debug -v -o output.txt input1.txt input2.txt
|
||||
echo ""
|
||||
|
||||
# 6. yq操作示例(假设有YAML文件)
|
||||
if [ -f "example.yaml" ]; then
|
||||
notice_info "yq操作示例:"
|
||||
echo "- 获取值示例:"
|
||||
yq_get_value "example.yaml" ".name"
|
||||
echo ""
|
||||
|
||||
echo "- 检查路径示例:"
|
||||
if yq_has_path "example.yaml" ".version"; then
|
||||
echo "version 字段存在"
|
||||
else
|
||||
echo "version 字段不存在"
|
||||
fi
|
||||
echo ""
|
||||
else
|
||||
notice_warning "示例YAML文件 example.yaml 不存在,跳过yq操作示例"
|
||||
fi
|
||||
|
||||
notice_success "示例运行完成"
|
||||
}
|
||||
|
||||
# 如果直接运行此脚本,显示帮助
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
if [ "$1" = "--examples" ] || [ "$1" = "-e" ]; then
|
||||
run_examples
|
||||
else
|
||||
notice_info "此脚本提供以下功能:"
|
||||
echo " 1. 系统环境检测 (check_environment)"
|
||||
echo " 2. yq依赖检测 (check_yq_dependency)"
|
||||
echo " 3. 工作区检测 (check_workspace)"
|
||||
echo " 4. YAML配置文件解析 (parse_yaml_config)"
|
||||
echo " 5. 命令行参数解析 (parse_arguments)"
|
||||
echo " 6. yq常用操作 (yq_*)"
|
||||
echo ""
|
||||
echo "使用 --examples 或 -e 参数可以运行示例"
|
||||
fi
|
||||
fi
|
||||
Reference in New Issue
Block a user