feat: 添加 Gitea Git 服务器维护模块

- 新增 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 <[email protected]>
This commit is contained in:
BrunoGao
2025-10-19 22:58:00 +08:00
co-authored by Claude
parent f9c5c54082
commit 597359b4ff
10 changed files with 1425 additions and 6 deletions
+7 -4
View File
@@ -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
*.pem
# Docker 相关
.dockerignore
docker-compose.override.yml
+79 -2
View File
@@ -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. 提交到版本控制
### 目录规范
Executable
+84
View File
@@ -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 "$@"
+16
View File
@@ -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
# [email protected]
+324
View File
@@ -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
+49
View File
@@ -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
+10
View File
@@ -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}"
+464
View File
@@ -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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.ljwx.gitea.backup</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>-lc</string>
<string>${SCRIPT_DIR}/gitea-data-protection.sh backup auto-\$(date +%Y%m%d-%H%M%S)</string>
</array>
<key>StartInterval</key>
<integer>$((${GITEA_BACKUP_INTERVAL_HOURS} * 3600))</integer>
<key>StandardOutPath</key><string>${LOG_DIR}/gitea-backup_stdout.log</string>
<key>StandardErrorPath</key><string>${LOG_DIR}/gitea-backup_stderr.log</string>
<key>RunAtLoad</key><true/>
</dict>
</plist>
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 <backup_file>"
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 <backup> # 恢复数据"
echo " $0 health # 健康检查"
echo " $0 help # 显示帮助"
echo ""
echo "功能特性:"
echo " ✅ 自动定时备份 (cron + launchd)"
echo " ✅ 一键备份和恢复"
echo " ✅ 健康监控"
echo " ✅ 备份元数据记录"
echo " ✅ 自动清理旧备份"
echo " ✅ macOS 原生集成"
echo " ✅ 环境变量配置"
;;
esac
}
main "$@"
+19
View File
@@ -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 <backup_file>"
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}"
Executable
+373
View File
@@ -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 "$@"