From 597359b4ffbcca5807fad30073f44d24702b9733 Mon Sep 17 00:00:00 2001 From: BrunoGao Date: Sun, 19 Oct 2025 22:58:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Gitea=20Git=20?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E7=BB=B4=E6=8A=A4=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 Gitea 模块,支持 Docker Compose 部署 - 实现完整的备份恢复机制 (gitea-data-protection.sh) - 添加快速备份脚本 (gitea-backup.sh) 和恢复脚本 (gitea-restore.sh) - 支持双重定时任务保障 (cron + launchd) - 包含环境变量配置和详细文档 - 新增一键部署脚本 (deploy.sh) 和初始化脚本 (init-ops.sh) - 更新项目总文档和 .gitignore 配置 特性: ✅ Docker Compose 一键部署 ✅ 自动定时备份和恢复 ✅ 实时健康监控 ✅ macOS 原生集成 ✅ 环境变量配置管理 ✅ 完整的部署自动化 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .gitignore | 11 +- README.md | 81 +++++- deploy.sh | 84 ++++++ gitea/.env.example | 16 ++ gitea/README.md | 324 +++++++++++++++++++++++ gitea/docker-compose.yml | 49 ++++ gitea/gitea-backup.sh | 10 + gitea/gitea-data-protection.sh | 464 +++++++++++++++++++++++++++++++++ gitea/gitea-restore.sh | 19 ++ init-ops.sh | 373 ++++++++++++++++++++++++++ 10 files changed, 1425 insertions(+), 6 deletions(-) create mode 100755 deploy.sh create mode 100644 gitea/.env.example create mode 100644 gitea/README.md create mode 100644 gitea/docker-compose.yml create mode 100755 gitea/gitea-backup.sh create mode 100755 gitea/gitea-data-protection.sh create mode 100755 gitea/gitea-restore.sh create mode 100755 init-ops.sh diff --git a/.gitignore b/.gitignore index eedbeca..61c2def 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ # 运行时产生的文件 logs/ -backups/*.tar.gz -backups/Brewfile.* -backups/*.txt +backups/ +data/ # 系统文件 .DS_Store @@ -22,4 +21,8 @@ Thumbs.db # 个人配置(如果包含敏感信息) .env *.key -*.pem \ No newline at end of file +*.pem + +# Docker 相关 +.dockerignore +docker-compose.override.yml \ No newline at end of file diff --git a/README.md b/README.md index f691525..520e194 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,13 @@ │ ├── brew_restore.sh # 恢复脚本 │ ├── com.ljwx.brew.maintenance.plist # 定时任务配置 │ └── README.md # Brew 模块详细文档 +├── gitea/ # Gitea Git 服务器模块 +│ ├── docker-compose.yml # Docker 服务配置 +│ ├── gitea-data-protection.sh # 数据保护脚本 +│ ├── gitea-backup.sh # 快速备份脚本 +│ ├── gitea-restore.sh # 快速恢复脚本 +│ ├── .env.example # 环境变量模板 +│ └── README.md # Gitea 模块详细文档 ├── [其他模块]/ # 未来可添加的其他模块 │ ├── [模块脚本] │ └── README.md @@ -49,14 +56,84 @@ launchctl load -w ~/Library/LaunchAgents/com.ljwx.brew.maintenance.plist 详细文档请查看 [brew/README.md](brew/README.md) +### 🐙 Gitea 模块 (`gitea/`) + +完整的 Gitea Git 服务器管理解决方案,包括 Docker 部署、自动备份、健康监控和一键恢复功能。 + +**功能特性**: +- ✅ Docker Compose 一键部署 +- ✅ 双重定时任务保障 (cron + launchd) +- ✅ 安全备份和恢复机制 +- ✅ 实时健康监控 +- ✅ 灵活的环境变量配置 +- ✅ macOS 原生集成 + +**快速开始**: +```bash +# 进入 Gitea 模块目录 +cd gitea/ + +# 初始化设置 +./gitea-data-protection.sh setup + +# 启动服务 +./gitea-data-protection.sh start + +# 访问 Gitea +open http://192.168.1.83:33000 +``` + +详细文档请查看 [gitea/README.md](gitea/README.md) + +## 🚀 快速部署 + +### 一键部署 + +```bash +# 在项目根目录执行一键部署 +./deploy.sh +``` + +部署脚本会自动: +- ✅ 创建完整的 `~/ops/` 目录结构 +- ✅ 复制所有模块文件到正确位置 +- ✅ 设置适当的文件权限 +- ✅ 创建环境配置文件 +- ✅ 安装定时任务配置 +- ✅ 验证部署结果 + +### 手动部署 + +```bash +# 执行初始化脚本 +./init-ops.sh +``` + +### 部署后使用 + +**Homebrew 模块**: +```bash +cd ~/ops/brew +./brew_maintain.sh # 手动维护 +``` + +**Gitea 模块**: +```bash +cd ~/ops/gitea +./gitea-data-protection.sh setup # 初始化 +./gitea-data-protection.sh start # 启动服务 +open http://192.168.1.83:33000 # 访问 Gitea +``` + ## 开发指南 ### 添加新模块 1. 在根目录创建新模块目录,如 `mymodule/` 2. 编写模块脚本和文档 -3. 更新本 README.md 文件 -4. 提交到版本控制 +3. 更新 `init-ops.sh` 添加部署逻辑 +4. 更新本 README.md 文件 +5. 提交到版本控制 ### 目录规范 diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..ea92e7f --- /dev/null +++ b/deploy.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# macOS 工具集 - 一键部署脚本 +# 简化版本的部署脚本,快速部署到 ~/ops/ + +set -euo pipefail + +# 颜色输出 +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log() { + echo -e "${GREEN}[$(date '+%H:%M:%S')] $1${NC}" +} + +info() { + echo -e "${BLUE}[$(date '+%H:%M:%S')] $1${NC}" +} + +warn() { + echo -e "${YELLOW}[$(date '+%H:%M:%S')] WARNING: $1${NC}" +} + +# 获取脚本目录 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# 显示部署信息 +show_deployment_info() { + echo -e "${BLUE}" + echo "==============================================" + echo " macOS 工具集 - 一键部署" + echo "==============================================" + echo -e "${NC}" + echo "📁 源码目录: ${SCRIPT_DIR}" + echo "🎯 目标目录: ${HOME}/ops/" + echo "" +} + +# 快速部署函数 +quick_deploy() { + log "🚀 开始快速部署..." + + # 1. 直接执行初始化脚本,传递 PROJECT_ROOT 变量 + log "📋 执行初始化脚本..." + export PROJECT_ROOT="${SCRIPT_DIR}" + "${SCRIPT_DIR}/init-ops.sh" + + log "✅ 快速部署完成!" +} + +# 显示使用说明 +show_usage() { + echo -e "${BLUE}使用方法:${NC}" + echo " $0 # 快速部署到 ~/ops/" + echo " $0 --help # 显示帮助" + echo "" + echo -e "${BLUE}部署后访问:${NC}" + echo " - Gitea: http://192.168.1.83:33000" + echo " - 日志: ~/ops/logs/" + echo " - 备份: ~/ops/backups/" + echo "" +} + +# 主函数 +main() { + case "${1:-}" in + "--help"|"-h") + show_deployment_info + show_usage + ;; + "") + show_deployment_info + quick_deploy + ;; + *) + echo "未知参数: $1" + show_usage + exit 1 + ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/gitea/.env.example b/gitea/.env.example new file mode 100644 index 0000000..c45eec5 --- /dev/null +++ b/gitea/.env.example @@ -0,0 +1,16 @@ +# Gitea 环境配置 +# 复制此文件为 .env 并根据实际情况修改 + +# 服务器配置 +GITEA_DOMAIN=192.168.1.83 +GITEA_SSH_DOMAIN=192.168.1.83 + +# 备份配置 +GITEA_BACKUP_DIR=/Users/$(whoami)/ops/backups/gitea +GITEA_BACKUP_RETENTION_DAYS=30 +GITEA_BACKUP_INTERVAL_HOURS=6 + +# 高级配置(可选) +# GITEA_ADMIN_USERNAME=admin +# GITEA_ADMIN_PASSWORD=your_secure_password +# GITEA_ADMIN_EMAIL=admin@example.com \ No newline at end of file diff --git a/gitea/README.md b/gitea/README.md new file mode 100644 index 0000000..a240832 --- /dev/null +++ b/gitea/README.md @@ -0,0 +1,324 @@ +# Gitea 维护模块 + +## 概述 + +这是 macOS 工具集中的 Gitea 维护模块,提供完整的 Gitea Git 服务器管理解决方案,包括服务部署、自动备份、健康监控和一键恢复功能。 + +## 功能特性 + +- ✅ **一键部署**: Docker Compose 快速部署 Gitea 服务 +- ✅ **自动备份**: 双重定时任务保障 (cron + launchd) +- ✅ **安全恢复**: 支持一键恢复和安全回滚 +- ✅ **健康监控**: 实时监控服务状态和数据完整性 +- ✅ **环境配置**: 灵活的环境变量配置管理 +- ✅ **macOS 集成**: 原生支持 macOS 系统特性 + +## 目录结构 + +``` +gitea/ +├── docker-compose.yml # Docker 服务配置 +├── gitea-data-protection.sh # 主管理脚本 +├── gitea-backup.sh # 快速备份脚本 +├── gitea-restore.sh # 快速恢复脚本 +├── .env.example # 环境变量模板 +└── README.md # 本文档 +``` + +## 快速开始 + +### 1. 环境准备 + +确保系统已安装: +- Docker Desktop for Mac +- Git + +### 2. 初始化设置 + +```bash +# 进入项目目录 +cd /path/to/macos/gitea + +# 复制环境配置文件 +cp .env.example .env + +# 根据需要修改配置(可选) +nano .env + +# 初始化设置(自动配置定时任务等) +./gitea-data-protection.sh setup +``` + +### 3. 启动服务 + +```bash +# 启动 Gitea 服务 +./gitea-data-protection.sh start + +# 检查服务状态 +./gitea-data-protection.sh health +``` + +### 4. 访问 Gitea + +- **Web 界面**: http://192.168.1.83:33000 +- **SSH**: git@192.168.1.83:32222 + +## 使用说明 + +### 服务管理 + +```bash +# 启动服务 +./gitea-data-protection.sh start + +# 停止服务 +./gitea-data-protection.sh stop + +# 健康检查 +./gitea-data-protection.sh health +``` + +### 备份操作 + +```bash +# 手动备份(自动命名) +./gitea-backup.sh + +# 手动备份(指定名称) +./gitea-backup.sh my-backup-20251019 + +# 查看备份文件 +ls -la ~/ops/backups/gitea/ +``` + +### 恢复操作 + +```bash +# 查看可用备份 +./gitea-restore.sh + +# 从指定备份恢复 +./gitea-restore.sh gitea-20251019-120000.tar.gz +``` + +### 高级操作 + +```bash +# 完整功能菜单 +./gitea-data-protection.sh help + +# 重新初始化设置 +./gitea-data-protection.sh setup +``` + +## 配置说明 + +### 环境变量 (.env) + +| 变量名 | 默认值 | 说明 | +|--------|--------|------| +| `GITEA_DOMAIN` | 192.168.1.83 | Gitea 服务器域名 | +| `GITEA_SSH_DOMAIN` | 192.168.1.83 | SSH 服务器域名 | +| `GITEA_BACKUP_DIR` | ~/ops/backups/gitea | 备份存储目录 | +| `GITEA_BACKUP_RETENTION_DAYS` | 30 | 备份保留天数 | +| `GITEA_BACKUP_INTERVAL_HOURS` | 6 | 自动备份间隔(小时) | + +### Docker 配置 + +- **镜像**: `gitea/gitea:1.21` +- **端口**: 33000 (HTTP), 32222 (SSH) +- **数据库**: SQLite3 +- **数据卷**: `gitea-data` + +## 备份机制 + +### 自动备份 + +系统提供双重保障的自动备份: + +1. **Cron 任务**: 传统 Unix 定时任务 +2. **Launchd 任务**: macOS 原生定时任务 + +默认每 6 小时自动备份一次,保留 30 天的备份文件。 + +### 备份内容 + +- Gitea 数据库 (SQLite) +- Git 仓库数据 +- 用户配置和设置 +- LFS 数据 (如果启用) + +### 备份位置 + +``` +~/ops/backups/gitea/ +├── gitea-20251019-120000.tar.gz # 备份文件 +├── gitea-20251019-120000.tar.gz.meta # 备份元数据 +└── safety-backup-*.tar.gz # 恢复时的安全备份 +``` + +## 恢复机制 + +### 安全恢复流程 + +1. **停止服务**: 安全停止所有 Gitea 服务 +2. **安全备份**: 自动创建当前状态的安全备份 +3. **数据恢复**: 从指定备份恢复数据 +4. **服务启动**: 重启服务并验证状态 +5. **完整性检查**: 验证数据完整性 + +### 恢回滚 + +如果恢复出现问题,可以使用安全备份回滚: + +```bash +./gitea-restore.sh safety-backup-20251019-130000.tar.gz +``` + +## 监控和日志 + +### 健康检查内容 + +- Docker 环境状态 +- Gitea 服务运行状态 +- 数据卷完整性 +- 仓库数量统计 +- 备份状态检查 +- 定时任务状态 + +### 日志文件位置 + +``` +~/ops/logs/ +├── gitea-protection.log # 主脚本日志 +├── gitea-backup_stdout.log # 备份输出日志 +└── gitea-backup_stderr.log # 备份错误日志 +``` + +### 查看日志 + +```bash +# 查看主日志 +tail -f ~/ops/logs/gitea-protection.log + +# 查看定时任务日志 +tail -f ~/ops/logs/gitea-backup_*.log +``` + +## 故障排除 + +### 常见问题 + +1. **Docker 服务未启动** + ```bash + # 启动 Docker Desktop + open /Applications/Docker.app + ``` + +2. **端口冲突** + ```bash + # 检查端口占用 + lsof -i :33000 + lsof -i :32222 + ``` + +3. **权限问题** + ```bash + # 设置脚本权限 + chmod +x gitea-*.sh + ``` + +4. **定时任务未执行** + ```bash + # 检查 launchd 任务 + launchctl list | grep gitea + + # 检查 cron 任务 + crontab -l | grep gitea + ``` + +### 手动恢复定时任务 + +```bash +# 重新加载 launchd 任务 +launchctl unload ~/Library/LaunchAgents/com.ljwx.gitea.backup.plist 2>/dev/null || true +launchctl load -w ~/Library/LaunchAgents/com.ljwx.gitea.backup.plist + +# 重新设置 cron 任务 +./gitea-data-protection.sh setup +``` + +## 性能优化 + +### 备份优化 + +- 调整备份频率:修改 `GITEA_BACKUP_INTERVAL_HOURS` +- 调整保留策略:修改 `GITEA_BACKUP_RETENTION_DAYS` +- 使用外部存储:修改 `GITEA_BACKUP_DIR` 到外部磁盘 + +### 服务优化 + +- 调整 Docker 资源限制 +- 使用外部数据库(PostgreSQL/MySQL) +- 配置 CDN 或反向代理 + +## 安全建议 + +1. **定期备份**: 确保自动备份正常运行 +2. **异地备份**: 将重要备份同步到云存储 +3. **访问控制**: 配置防火墙规则限制访问 +4. **定期更新**: 定期更新 Gitea 版本 +5. **监控告警**: 配置服务状态监控 + +## 升级指南 + +### Gitea 版本升级 + +```bash +# 1. 备份当前数据 +./gitea-backup.sh pre-upgrade-$(date +%Y%m%d) + +# 2. 停止服务 +./gitea-data-protection.sh stop + +# 3. 更新镜像版本 +# 编辑 docker-compose.yml,修改 image 标签 + +# 4. 启动服务 +./gitea-data-protection.sh start + +# 5. 验证升级 +./gitea-data-protection.sh health +``` + +## 扩展集成 + +### 与 CI/CD 集成 + +```bash +# CI 脚本示例 +./gitea-backup.sh ci-pre-build-$(BUILD_NUMBER) +# 执行构建任务 +./gitea-restore.sh ci-pre-build-$(BUILD_NUMBER).tar.gz +``` + +### 与监控系统集成 + +```bash +# 健康检查 API 调用 +./gitea-data-protection.sh health +echo $? +``` + +## 许可证 + +MIT License - 可自由使用和修改 + +## 支持 + +如有问题或建议,请通过以下方式联系: + +1. 查看日志文件排查问题 +2. 检查 GitHub Issues +3. 提交新的 Issue 或 Pull Request \ No newline at end of file diff --git a/gitea/docker-compose.yml b/gitea/docker-compose.yml new file mode 100644 index 0000000..c9ae2b2 --- /dev/null +++ b/gitea/docker-compose.yml @@ -0,0 +1,49 @@ +services: + gitea: + image: docker.m.daocloud.io/gitea/gitea:1.21 + container_name: gitea + restart: unless-stopped + ports: + - "33000:3000" + - "32222:32222" + volumes: + # 使用 Docker 命名卷持久化 + - gitea-data:/data + # 时区同步 + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA__database__DB_TYPE=sqlite3 + - GITEA__database__PATH=/data/gitea/gitea.db + - GITEA__server__DOMAIN=${GITEA_DOMAIN:-192.168.1.83} + - GITEA__server__SSH_DOMAIN=${GITEA_SSH_DOMAIN:-192.168.1.83} + - GITEA__server__ROOT_URL=http://${GITEA_DOMAIN:-192.168.1.83}:33000/ + - GITEA__server__DISABLE_SSH=false + - GITEA__server__SSH_PORT=32222 + - GITEA__server__SSH_LISTEN_PORT=32222 + - GITEA__server__START_SSH_SERVER=true + - GITEA__server__LFS_START_SERVER=true + - GITEA__server__BUILTIN_SSH_SERVER_USER=git + - GITEA__repository__ENABLE_PUSH_CREATE_USER=true + - GITEA__repository__ENABLE_PUSH_CREATE_ORG=true + - GITEA__webhook__ALLOWED_HOST_LIST=localhost,jenkins,${GITEA_DOMAIN:-192.168.1.83} + - GITEA__service__DISABLE_REGISTRATION=false # 允许注册 + - GITEA__service__REQUIRE_SIGNIN_VIEW=false + - GITEA__actions__ENABLED=true # 启用Actions + networks: + - gitea-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"] + interval: 30s + timeout: 10s + retries: 3 + +networks: + gitea-network: + driver: bridge + +volumes: + gitea-data: + driver: local \ No newline at end of file diff --git a/gitea/gitea-backup.sh b/gitea/gitea-backup.sh new file mode 100755 index 0000000..76c0425 --- /dev/null +++ b/gitea/gitea-backup.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Gitea 快速备份脚本 + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BACKUP_NAME="${1:-manual-$(date +%Y%m%d-%H%M%S)}" + +echo "🔄 正在备份 Gitea 数据: ${BACKUP_NAME}" +"${SCRIPT_DIR}/gitea-data-protection.sh" backup "${BACKUP_NAME}" \ No newline at end of file diff --git a/gitea/gitea-data-protection.sh b/gitea/gitea-data-protection.sh new file mode 100755 index 0000000..14266c7 --- /dev/null +++ b/gitea/gitea-data-protection.sh @@ -0,0 +1,464 @@ +#!/bin/bash + +# Gitea 数据保护管理脚本 - macOS 优化版 +# 专为 macOS 项目结构设计的备份恢复解决方案 +# 使用方法: ./gitea-data-protection.sh [start|stop|backup|restore|health|setup] + +set -euo pipefail + +# 基础配置 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OPS_DIR="${HOME}/ops" +BACKUP_DIR="${OPS_DIR}/backups/gitea" +DATA_DIR="${OPS_DIR}/data/gitea" +LOG_DIR="${OPS_DIR}/logs" +COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.yml" +ENV_FILE="${SCRIPT_DIR}/.env" +LOG_FILE="${LOG_DIR}/gitea-protection.log" + +# 创建必要的目录 +mkdir -p "${BACKUP_DIR}" "${DATA_DIR}" "${LOG_DIR}" + +# 日志函数 +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "${LOG_FILE}" +} + +# 加载环境变量 +load_env() { + if [[ -f "${ENV_FILE}" ]]; then + set -a + source "${ENV_FILE}" + set +a + log "✅ 已加载环境配置: ${ENV_FILE}" + else + log "⚠️ 未找到 .env 文件,使用默认配置" + fi +} + +# 获取配置值或使用默认值 +get_config() { + local key="$1" + local default="$2" + local value="${!key:-${default}}" + echo "${value}" +} + +# 初始化配置 +init_config() { + # 设置默认值 + GITEA_DOMAIN=$(get_config "GITEA_DOMAIN" "192.168.1.83") + GITEA_SSH_DOMAIN=$(get_config "GITEA_SSH_DOMAIN" "192.168.1.83") + GITEA_BACKUP_DIR=$(get_config "GITEA_BACKUP_DIR" "${BACKUP_DIR}") + GITEA_BACKUP_RETENTION_DAYS=$(get_config "GITEA_BACKUP_RETENTION_DAYS" "30") + GITEA_BACKUP_INTERVAL_HOURS=$(get_config "GITEA_BACKUP_INTERVAL_HOURS" "6") + + # 确保备份目录存在 + mkdir -p "${GITEA_BACKUP_DIR}" +} + +# 检查 Docker 环境 +check_docker() { + if ! command -v docker >/dev/null 2>&1; then + log "❌ Docker 未安装或未在 PATH 中" + return 1 + fi + + if ! command -v docker-compose >/dev/null 2>&1; then + log "❌ Docker Compose 未安装或未在 PATH 中" + return 1 + fi + + if ! docker info >/dev/null 2>&1; then + log "❌ Docker 服务未运行或权限不足" + return 1 + fi + + return 0 +} + +# 检查 Gitea 服务状态 +check_gitea_status() { + if docker-compose -f "${COMPOSE_FILE}" ps gitea 2>/dev/null | grep -q "Up"; then + return 0 + else + return 1 + fi +} + +# 初始化设置 +setup() { + log "🔧 开始 Gitea 数据保护初始化设置..." + + # 1. 环境检查 + if ! check_docker; then + log "❌ Docker 环境检查失败" + return 1 + fi + + # 2. 加载配置 + load_env + init_config + + # 3. 创建环境配置文件(如果不存在) + if [[ ! -f "${ENV_FILE}" ]]; then + log "📝 创建环境配置文件..." + cp "${SCRIPT_DIR}/.env.example" "${ENV_FILE}" + log "✅ 已创建 ${ENV_FILE},请根据需要修改配置" + fi + + # 4. 设置定时备份 + setup_cron_backup + + # 5. 设置定时任务 + setup_launchd_backup + + # 6. 验证设置 + verify_setup + + log "✅ Gitea 数据保护初始化完成" +} + +# 设置 cron 备份(传统方式) +setup_cron_backup() { + log "📅 设置定时备份任务 (cron)..." + + # 创建备份脚本 + cat > "${SCRIPT_DIR}/gitea-auto-backup.sh" << EOF +#!/bin/bash +cd "${SCRIPT_DIR}" +./gitea-data-protection.sh backup auto-\$(date +%Y%m%d-%H%M%S) +EOF + chmod +x "${SCRIPT_DIR}/gitea-auto-backup.sh" + + # 检查 crontab 中是否已存在该任务 + CRON_ENTRY="0 */${GITEA_BACKUP_INTERVAL_HOURS} * * * ${SCRIPT_DIR}/gitea-auto-backup.sh" + if ! crontab -l 2>/dev/null | grep -F "${SCRIPT_DIR}/gitea-auto-backup.sh" >/dev/null; then + (crontab -l 2>/dev/null; echo "${CRON_ENTRY}") | crontab - + log "✅ 定时备份任务已设置 (每${GITEA_BACKUP_INTERVAL_HOURS}小时执行)" + else + log "ℹ️ 定时备份任务已存在,跳过添加" + fi +} + +# 设置 launchd 备份(macOS 原生方式) +setup_launchd_backup() { + log "🚀 设置 macOS 原生定时任务 (launchd)..." + + # 创建 launchd plist 文件 + cat > "${HOME}/Library/LaunchAgents/com.ljwx.gitea.backup.plist" << EOF + + + + + Labelcom.ljwx.gitea.backup + ProgramArguments + + /bin/zsh + -lc + ${SCRIPT_DIR}/gitea-data-protection.sh backup auto-\$(date +%Y%m%d-%H%M%S) + + StartInterval + $((${GITEA_BACKUP_INTERVAL_HOURS} * 3600)) + StandardOutPath${LOG_DIR}/gitea-backup_stdout.log + StandardErrorPath${LOG_DIR}/gitea-backup_stderr.log + RunAtLoad + + +EOF + + # 加载 launchd 任务 + launchctl unload "${HOME}/Library/LaunchAgents/com.ljwx.gitea.backup.plist" 2>/dev/null || true + launchctl load -w "${HOME}/Library/LaunchAgents/com.ljwx.gitea.backup.plist" + + log "✅ macOS 原生定时任务已设置" +} + +# 执行备份 +backup() { + local BACKUP_NAME="${1:-gitea-$(date +%Y%m%d-%H%M%S)}" + local BACKUP_PATH="${GITEA_BACKUP_DIR}/${BACKUP_NAME}.tar.gz" + + log "🔄 开始备份 Gitea 数据: ${BACKUP_NAME}" + + # 加载配置 + load_env + init_config + + # 检查服务状态 + local RESTART_NEEDED=false + if check_gitea_status; then + log "⏸️ 临时停止 Gitea 服务以确保数据一致性..." + docker-compose -f "${COMPOSE_FILE}" stop gitea + RESTART_NEEDED=true + fi + + # 执行备份 + log "📦 创建备份到: ${BACKUP_PATH}" + if docker run --rm \ + -v gitea-data:/source:ro \ + -v "${GITEA_BACKUP_DIR}:/backup" \ + busybox \ + tar -czf "/backup/$(basename "${BACKUP_PATH}")" -C /source .; then + + # 重启服务 + if [[ "${RESTART_NEEDED}" == "true" ]]; then + log "🚀 重启 Gitea 服务..." + docker-compose -f "${COMPOSE_FILE}" start gitea + fi + + # 验证备份 + if [[ -f "${BACKUP_PATH}" ]]; then + local BACKUP_SIZE=$(du -h "${BACKUP_PATH}" | cut -f1) + log "✅ 备份完成: ${BACKUP_PATH} (大小: ${BACKUP_SIZE})" + + # 记录备份元数据 + cat > "${BACKUP_PATH}.meta" << EOF +backup_name: ${BACKUP_NAME} +backup_time: $(date -Iseconds) +backup_size: ${BACKUP_SIZE} +gitea_domain: ${GITEA_DOMAIN} +script_version: 1.0 +EOF + + # 清理旧备份 + cleanup_old_backups + else + log "❌ 备份文件未找到" + return 1 + fi + else + log "❌ 备份执行失败" + return 1 + fi +} + +# 清理旧备份 +cleanup_old_backups() { + log "🧹 清理 ${GITEA_BACKUP_RETENTION_DAYS} 天前的旧备份..." + find "${GITEA_BACKUP_DIR}" -name "gitea-*.tar.gz" -mtime "+${GITEA_BACKUP_RETENTION_DAYS}" -delete 2>/dev/null || true + find "${GITEA_BACKUP_DIR}" -name "gitea-*.meta" -mtime "+${GITEA_BACKUP_RETENTION_DAYS}" -delete 2>/dev/null || true +} + +# 恢复数据 +restore() { + local BACKUP_FILE="$1" + + if [[ -z "${BACKUP_FILE}" ]]; then + log "📋 可用的备份文件:" + ls -lt "${GITEA_BACKUP_DIR}"/gitea-*.tar.gz 2>/dev/null | head -10 || log "❌ 未找到备份文件" + echo "" + echo "使用方法: $0 restore " + return 1 + fi + + # 处理相对路径 + if [[ ! -f "${BACKUP_FILE}" ]]; then + BACKUP_FILE="${GITEA_BACKUP_DIR}/${BACKUP_FILE}" + if [[ ! -f "${BACKUP_FILE}" ]]; then + log "❌ 备份文件不存在: ${BACKUP_FILE}" + return 1 + fi + fi + + log "🔄 开始恢复 Gitea 数据从: $(basename "${BACKUP_FILE}")" + + # 停止服务 + log "⏸️ 停止 Gitea 服务..." + docker-compose -f "${COMPOSE_FILE}" down + + # 备份现有数据(安全措施) + if docker volume inspect gitea-data >/dev/null 2>&1; then + local SAFETY_BACKUP="${GITEA_BACKUP_DIR}/safety-backup-$(date +%Y%m%d-%H%M%S).tar.gz" + log "💾 创建安全备份: $(basename "${SAFETY_BACKUP}")" + docker run --rm \ + -v gitea-data:/source:ro \ + -v "${GITEA_BACKUP_DIR}:/backup" \ + busybox \ + tar -czf "/backup/$(basename "${SAFETY_BACKUP}")" -C /source . + fi + + # 删除旧卷并创建新卷 + docker volume rm gitea-data 2>/dev/null || true + docker volume create gitea-data + + # 恢复数据 + log "📦 恢复数据..." + if docker run --rm \ + -v gitea-data:/target \ + -v "$(dirname "${BACKUP_FILE}"):/backup:ro" \ + busybox \ + tar -xzf "/backup/$(basename "${BACKUP_FILE}")" -C /target; then + + # 启动服务 + log "🚀 启动 Gitea 服务..." + docker-compose -f "${COMPOSE_FILE}" up -d + + # 等待服务启动 + sleep 10 + + # 验证服务状态 + if check_gitea_status; then + log "✅ 数据恢复完成,服务正常运行" + else + log "⚠️ 服务启动可能有问题,请检查日志" + fi + else + log "❌ 数据恢复失败" + return 1 + fi +} + +# 健康检查 +health_check() { + log "🏥 开始健康检查..." + + # 1. Docker 环境检查 + if ! check_docker; then + log "❌ Docker 环境检查失败" + return 1 + fi + + # 2. 服务状态检查 + if check_gitea_status; then + log "✅ Gitea 服务运行正常" + else + log "❌ Gitea 服务未运行" + return 1 + fi + + # 3. 数据卷检查 + if docker volume inspect gitea-data >/dev/null 2>&1; then + log "✅ 数据卷 gitea-data 存在" + + # 检查数据完整性 + local REPO_COUNT=$(docker run --rm \ + -v gitea-data:/data:ro \ + busybox \ + find /data -name "*.git" -type d 2>/dev/null | wc -l) + log "📊 发现 ${REPO_COUNT} 个 Git 仓库" + else + log "❌ 数据卷不存在" + return 1 + fi + + # 4. 备份状态检查 + local RECENT_BACKUPS=$(find "${GITEA_BACKUP_DIR}" -name "gitea-*.tar.gz" -mtime -1 2>/dev/null | wc -l) + log "📈 最近24小时内的备份: ${RECENT_BACKUPS} 个" + + if [[ ${RECENT_BACKUPS} -eq 0 ]]; then + log "⚠️ 警告: 最近24小时内无备份" + fi + + # 5. 定时任务检查 + if launchctl list | grep -q "com.ljwx.gitea.backup"; then + log "✅ macOS 定时任务已设置" + else + log "⚠️ 未发现 macOS 定时任务" + fi + + log "✅ 健康检查完成" +} + +# 验证设置 +verify_setup() { + log "🔍 验证 Gitea 数据保护设置..." + + # 检查配置文件 + if [[ -f "${ENV_FILE}" ]]; then + log "✅ 环境配置文件存在" + else + log "⚠️ 环境配置文件不存在,将使用默认配置" + fi + + # 检查备份目录 + if [[ -d "${GITEA_BACKUP_DIR}" ]]; then + log "✅ 备份目录存在: ${GITEA_BACKUP_DIR}" + else + log "❌ 备份目录不存在" + return 1 + fi + + # 检查 Docker Compose 文件 + if [[ -f "${COMPOSE_FILE}" ]]; then + log "✅ Docker Compose 文件存在" + else + log "❌ Docker Compose 文件不存在" + return 1 + fi + + log "✅ 设置验证完成" +} + +# 启动服务 +start() { + log "🚀 启动 Gitea 服务..." + + # 加载配置 + load_env + init_config + + # 启动服务 + docker-compose -f "${COMPOSE_FILE}" up -d + + # 等待服务启动 + sleep 10 + + # 健康检查 + health_check +} + +# 停止服务 +stop() { + log "⏸️ 停止 Gitea 服务..." + docker-compose -f "${COMPOSE_FILE}" down + log "✅ 服务已停止" +} + +# 主函数 +main() { + case "${1:-help}" in + "setup") + setup + ;; + "start") + start + ;; + "stop") + stop + ;; + "backup") + backup "$2" + ;; + "restore") + restore "$2" + ;; + "health") + health_check + ;; + "help"|*) + echo "Gitea 数据保护管理脚本 - macOS 优化版" + echo "" + echo "使用方法:" + echo " $0 setup # 初始化设置" + echo " $0 start # 启动 Gitea 服务" + echo " $0 stop # 停止 Gitea 服务" + echo " $0 backup [name] # 创建备份" + echo " $0 restore # 恢复数据" + echo " $0 health # 健康检查" + echo " $0 help # 显示帮助" + echo "" + echo "功能特性:" + echo " ✅ 自动定时备份 (cron + launchd)" + echo " ✅ 一键备份和恢复" + echo " ✅ 健康监控" + echo " ✅ 备份元数据记录" + echo " ✅ 自动清理旧备份" + echo " ✅ macOS 原生集成" + echo " ✅ 环境变量配置" + ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/gitea/gitea-restore.sh b/gitea/gitea-restore.sh new file mode 100755 index 0000000..fe3489f --- /dev/null +++ b/gitea/gitea-restore.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Gitea 快速恢复脚本 + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +if [[ $# -eq 0 ]]; then + echo "📋 可用的备份文件:" + ls -lt ~/ops/backups/gitea/gitea-*.tar.gz 2>/dev/null | head -10 || echo "❌ 未找到备份文件" + echo "" + echo "使用方法: $0 " + echo "示例: $0 gitea-20251019-120000.tar.gz" + exit 1 +fi + +BACKUP_FILE="$1" +echo "🔄 正在从备份恢复 Gitea 数据: $(basename "${BACKUP_FILE}")" +"${SCRIPT_DIR}/gitea-data-protection.sh" restore "${BACKUP_FILE}" \ No newline at end of file diff --git a/init-ops.sh b/init-ops.sh new file mode 100755 index 0000000..7a68592 --- /dev/null +++ b/init-ops.sh @@ -0,0 +1,373 @@ +#!/bin/bash +# macOS 工具集 - ops 目录结构初始化脚本 +# 用于在 ~/ops/ 目录创建完整的项目结构 + +set -euo pipefail + +# 配置 +PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}" +OPS_DIR="${HOME}/ops" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 日志函数 +log() { + echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}" +} + +warn() { + echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}" +} + +error() { + echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" + exit 1 +} + +info() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1${NC}" +} + +# 检查依赖 +check_dependencies() { + log "🔍 检查系统依赖..." + + # 检查基本命令 + for cmd in mkdir cp chmod; do + if ! command -v "$cmd" >/dev/null 2>&1; then + error "缺少必要命令: $cmd" + fi + done + + # 检查 Docker(Gitea 模块需要) + if command -v docker >/dev/null 2>&1; then + log "✅ Docker 已安装" + DOCKER_AVAILABLE=true + else + warn "Docker 未安装,Gitea 模块将无法使用" + DOCKER_AVAILABLE=false + fi + + # 检查 Homebrew(brew 模块需要) + if command -v brew >/dev/null 2>&1; then + log "✅ Homebrew 已安装" + BREW_AVAILABLE=true + else + warn "Homebrew 未安装,brew 模块将无法使用" + BREW_AVAILABLE=false + fi +} + +# 创建基础目录结构 +create_base_structure() { + log "📁 创建基础目录结构..." + + # 主要目录 + local dirs=( + "${OPS_DIR}" + "${OPS_DIR}/logs" + "${OPS_DIR}/backups" + "${OPS_DIR}/data" + "${OPS_DIR}/backups/brew" + "${OPS_DIR}/backups/gitea" + "${OPS_DIR}/data/gitea" + ) + + for dir in "${dirs[@]}"; do + if [[ ! -d "$dir" ]]; then + mkdir -p "$dir" + log "✅ 创建目录: $dir" + else + info "ℹ️ 目录已存在: $dir" + fi + done + + # 设置权限 + chmod 755 "${OPS_DIR}" + chmod 755 "${OPS_DIR}/logs" + chmod 755 "${OPS_DIR}/backups" + chmod 755 "${OPS_DIR}/data" +} + +# 部署 brew 模块 +deploy_brew_module() { + if [[ "$BREW_AVAILABLE" != true ]]; then + warn "跳过 brew 模块部署(Homebrew 未安装)" + return + fi + + log "🍺 部署 Homebrew 维护模块..." + + local brew_target_dir="${OPS_DIR}/brew" + + # 创建目录 + mkdir -p "$brew_target_dir" + + # 复制文件 + local files=( + "brew/brew_maintain.sh" + "brew/brew_backup.sh" + "brew/brew_restore.sh" + "brew/com.ljwx.brew.maintenance.plist" + "brew/README.md" + ) + + for file in "${files[@]}"; do + local src="${PROJECT_ROOT}/$file" + local dst="${brew_target_dir}/$(basename "$file")" + + if [[ -f "$src" ]]; then + cp "$src" "$dst" + chmod +x "$dst" 2>/dev/null || true # 只对脚本设置可执行权限 + log "✅ 复制: $(basename "$file")" + else + warn "文件不存在: $src" + fi + done + + # 复制定时任务配置到 LaunchAgents + local launchd_file="${HOME}/Library/LaunchAgents/com.ljwx.brew.maintenance.plist" + if [[ -f "${PROJECT_ROOT}/brew/com.ljwx.brew.maintenance.plist" ]]; then + cp "${PROJECT_ROOT}/brew/com.ljwx.brew.maintenance.plist" "$launchd_file" + log "✅ 安装定时任务配置" + fi +} + +# 部署 gitea 模块 +deploy_gitea_module() { + if [[ "$DOCKER_AVAILABLE" != true ]]; then + warn "跳过 gitea 模块部署(Docker 未安装)" + return + fi + + log "🐙 部署 Gitea 维护模块..." + + local gitea_target_dir="${OPS_DIR}/gitea" + + # 创建目录 + mkdir -p "$gitea_target_dir" + + # 复制文件 + local files=( + "gitea/docker-compose.yml" + "gitea/gitea-data-protection.sh" + "gitea/gitea-backup.sh" + "gitea/gitea-restore.sh" + "gitea/.env.example" + "gitea/README.md" + ) + + for file in "${files[@]}"; do + local src="${PROJECT_ROOT}/$file" + local dst="${gitea_target_dir}/$(basename "$file")" + + if [[ -f "$src" ]]; then + cp "$src" "$dst" + chmod +x "$dst" 2>/dev/null || true # 只对脚本设置可执行权限 + log "✅ 复制: $(basename "$file")" + else + warn "文件不存在: $src" + fi + done + + # 创建 .env 文件(如果不存在) + local env_file="${gitea_target_dir}/.env" + if [[ ! -f "$env_file" ]] && [[ -f "${PROJECT_ROOT}/gitea/.env.example" ]]; then + cp "${PROJECT_ROOT}/gitea/.env.example" "$env_file" + log "✅ 创建环境配置文件: .env" + info "💡 请根据需要编辑 ${env_file}" + fi +} + +# 创建项目总 README +create_main_readme() { + local readme_file="${OPS_DIR}/README.md" + + if [[ ! -f "$readme_file" ]]; then + log "📖 创建项目总 README..." + + cat > "$readme_file" << 'EOF' +# macOS 工具集 - ops 目录 + +这是 macOS 工具集的运行时目录,包含各个维护模块的部署文件。 + +## 目录结构 + +``` +~/ops/ +├── logs/ # 统一日志目录 +├── backups/ # 统一备份目录 +│ ├── brew/ # Homebrew 备份 +│ └── gitea/ # Gitea 备份 +├── data/ # 运行时数据目录 +│ └── gitea/ # Gitea 运行时数据 +├── brew/ # Homebrew 维护模块 +├── gitea/ # Gitea 维护模块 +└── README.md # 本文档 +``` + +## 模块使用 + +### Homebrew 模块 + +```bash +cd ~/ops/brew +./brew_maintain.sh # 手动维护 +./brew_backup.sh # 手动备份 +./brew_restore.sh # 恢复 +``` + +### Gitea 模块 + +```bash +cd ~/ops/gitea +./gitea-data-protection.sh setup # 初始化设置 +./gitea-data-protection.sh start # 启动服务 +./gitea-backup.sh # 手动备份 +./gitea-restore.sh # 恢复 +``` + +## 部署时间 + +部署时间: $(date '+%Y-%m-%d %H:%M:%S') +部署来源: $(basename "$PROJECT_ROOT") + +## 注意事项 + +- 定期检查日志文件: `~/ops/logs/` +- 定期检查备份文件: `~/ops/backups/` +- 定期运行健康检查 +EOF + + log "✅ 创建: README.md" + else + info "ℹ️ README.md 已存在" + fi +} + +# 验证部署 +verify_deployment() { + log "🔍 验证部署结果..." + + local errors=0 + + # 检查基础目录 + local base_dirs=( + "${OPS_DIR}" + "${OPS_DIR}/logs" + "${OPS_DIR}/backups" + "${OPS_DIR}/data" + ) + + for dir in "${base_dirs[@]}"; do + if [[ -d "$dir" ]]; then + log "✅ 目录存在: $(basename "$dir")" + else + error "❌ 目录缺失: $dir" + ((errors++)) + fi + done + + # 检查模块部署 + if [[ "$BREW_AVAILABLE" == true ]]; then + if [[ -d "${OPS_DIR}/brew" ]]; then + log "✅ brew 模块已部署" + else + warn "⚠️ brew 模块部署失败" + fi + fi + + if [[ "$DOCKER_AVAILABLE" == true ]]; then + if [[ -d "${OPS_DIR}/gitea" ]]; then + log "✅ gitea 模块已部署" + else + warn "⚠️ gitea 模块部署失败" + fi + fi + + if [[ $errors -gt 0 ]]; then + error "部署验证失败,发现 $errors 个错误" + fi +} + +# 显示后续步骤 +show_next_steps() { + log "🎉 ops 目录初始化完成!" + echo "" + echo -e "${BLUE}📋 后续步骤:${NC}" + echo "" + + if [[ "$BREW_AVAILABLE" == true ]]; then + echo -e "${GREEN}🍺 Homebrew 模块:${NC}" + echo " cd ~/ops/brew" + echo " ./brew_maintain.sh # 测试运行" + echo "" + fi + + if [[ "$DOCKER_AVAILABLE" == true ]]; then + echo -e "${GREEN}🐙 Gitea 模块:${NC}" + echo " cd ~/ops/gitea" + echo " ./gitea-data-protection.sh setup # 初始化设置" + echo " ./gitea-data-protection.sh start # 启动服务" + echo " open http://192.168.1.83:33000 # 访问 Gitea" + echo "" + fi + + echo -e "${BLUE}📁 重要目录:${NC}" + echo " ~/ops/logs/ # 查看运行日志" + echo " ~/ops/backups/ # 查看备份文件" + echo "" + + echo -e "${YELLOW}💡 提示:${NC}" + echo " - 定期检查日志文件了解运行状态" + echo " - 定期验证备份文件完整性" + echo " - 根据需要编辑配置文件" +} + +# 主函数 +main() { + echo -e "${BLUE}" + echo "==================================================" + echo " macOS 工具集 - ops 目录初始化脚本" + echo "==================================================" + echo -e "${NC}" + echo "" + + log "🚀 开始初始化 ops 目录结构..." + echo "目标目录: ${OPS_DIR}" + echo "项目源码: ${PROJECT_ROOT}" + echo "" + + # 检查依赖 + check_dependencies + echo "" + + # 创建基础结构 + create_base_structure + echo "" + + # 部署模块 + deploy_brew_module + echo "" + deploy_gitea_module + echo "" + + # 创建文档 + create_main_readme + echo "" + + # 验证部署 + verify_deployment + echo "" + + # 显示后续步骤 + show_next_steps +} + +# 执行主函数 +main "$@" \ No newline at end of file