243 lines
6.9 KiB
Bash
243 lines
6.9 KiB
Bash
#!/bin/bash
|
|
|
|
# Jenkins Workspace Cleanup Script
|
|
# Description: 安全清理Jenkins工作区,保留最近构建和重要文件
|
|
# Author: System Admin
|
|
# Date: $(date +%Y-%m-%d)
|
|
|
|
set -euo pipefail
|
|
|
|
# 配置变量 - 根据实际情况修改
|
|
JENKINS_HOME="${JENKINS_HOME:-/usr/local/docker/jenkins/jenkins_home}" # Jenkins主目录
|
|
WORKSPACE_DIR="${JENKINS_HOME}/workspace" # 工作区目录
|
|
DRY_RUN=false # 干运行模式
|
|
RETENTION_DAYS=1 # 保留最近N天的构建
|
|
MIN_FREE_SPACE_GB=20 # 最小保留磁盘空间(GB)
|
|
EXCLUDE_PATTERNS=(".*/builds/.*" ".*/lastStable.*" ".*/lastSuccessful.*" ".*/nextBuildNumber.*") # 排除模式
|
|
LOG_FILE="/var/log/jenkins-cleanup.log" # 日志文件
|
|
|
|
# 日志函数
|
|
log() {
|
|
local message="$1"
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo "[${timestamp}] ${message}" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# 检查目录是否存在
|
|
check_directories() {
|
|
if [ ! -d "$JENKINS_HOME" ]; then
|
|
log "错误: Jenkins目录不存在: $JENKINS_HOME"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -d "$WORKSPACE_DIR" ]; then
|
|
log "错误: 工作区目录不存在: $WORKSPACE_DIR"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# 检查磁盘空间
|
|
check_disk_space() {
|
|
local available_space_gb=$(df -BG "$JENKINS_HOME" | awk 'NR==2 {print $4}' | sed 's/G//')
|
|
|
|
if [ "$available_space_gb" -ge "$MIN_FREE_SPACE_GB" ]; then
|
|
log "磁盘空间充足 (${available_space_gb}GB可用),跳过清理"
|
|
return 1
|
|
fi
|
|
|
|
log "磁盘空间不足 (${available_space_gb}GB可用),需要清理"
|
|
return 0
|
|
}
|
|
|
|
# 安全删除目录
|
|
safe_delete() {
|
|
local dir_path="$1"
|
|
|
|
if [ "$DRY_RUN" = true ]; then
|
|
log "干运行: 将删除目录 - ${dir_path}"
|
|
return 0
|
|
fi
|
|
|
|
# 检查目录是否重要(包含排除模式)
|
|
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
|
|
if [[ "$dir_path" =~ $pattern ]]; then
|
|
log "跳过重要目录: ${dir_path}"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
# 检查目录是否在最近修改时间内
|
|
local last_modified=$(stat -c %Y "$dir_path")
|
|
local current_time=$(date +%s)
|
|
local age_days=$(( (current_time - last_modified) / 86400 ))
|
|
|
|
if [ "$age_days" -le "$RETENTION_DAYS" ]; then
|
|
log "跳过最近修改的目录 (${age_days}天前): ${dir_path}"
|
|
return 0
|
|
fi
|
|
|
|
# 执行删除
|
|
if rm -rf "$dir_path" 2>/dev/null; then
|
|
log "已删除: ${dir_path} (${age_days}天未修改)"
|
|
return 0
|
|
else
|
|
log "警告: 删除失败: ${dir_path}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 清理工作区
|
|
clean_workspace() {
|
|
log "开始清理Jenkins工作区..."
|
|
log "工作区目录: ${WORKSPACE_DIR}"
|
|
log "保留最近 ${RETENTION_DAYS} 天的构建"
|
|
log "最小保留空间: ${MIN_FREE_SPACE_GB}GB"
|
|
|
|
local total_size_before=$(du -sh "$WORKSPACE_DIR" 2>/dev/null | cut -f1)
|
|
log "清理前工作区大小: ${total_size_before}"
|
|
|
|
# 查找并清理旧的工作区目录
|
|
local cleaned_count=0
|
|
local error_count=0
|
|
|
|
find "$WORKSPACE_DIR" -maxdepth 2 -type d -name "*" | while read -r dir_path; do
|
|
# 跳过根目录和一级目录
|
|
if [ "$dir_path" = "$WORKSPACE_DIR" ] || [ "$(dirname "$dir_path")" = "$WORKSPACE_DIR" ]; then
|
|
continue
|
|
fi
|
|
|
|
if safe_delete "$dir_path"; then
|
|
((cleaned_count++)) || true
|
|
else
|
|
((error_count++)) || true
|
|
fi
|
|
done
|
|
|
|
local total_size_after=$(du -sh "$WORKSPACE_DIR" 2>/dev/null | cut -f1)
|
|
|
|
log "清理完成: 删除了 ${cleaned_count} 个目录, ${error_count} 个错误"
|
|
log "清理后工作区大小: ${total_size_after}"
|
|
log "释放空间: $(echo "$total_size_before -> $total_size_after")"
|
|
}
|
|
|
|
# 清理构建历史(可选)
|
|
clean_builds() {
|
|
local builds_dir="${JENKINS_HOME}/jobs"
|
|
local max_builds_to_keep=20 # 每个作业保留的最大构建数
|
|
|
|
log "开始清理旧的构建历史..."
|
|
|
|
find "$builds_dir" -name "builds" -type d 2>/dev/null | while read -r builds_path; do
|
|
local job_dir=$(dirname "$builds_path")
|
|
local job_name=$(basename "$job_dir")
|
|
|
|
# 获取所有构建目录并按编号排序
|
|
local build_dirs=$(find "$builds_path" -maxdepth 1 -type d -name "[0-9]*" | sort -Vr)
|
|
local build_count=$(echo "$build_dirs" | wc -l)
|
|
|
|
if [ "$build_count" -gt "$max_builds_to_keep" ]; then
|
|
local to_delete_count=$((build_count - max_builds_to_keep))
|
|
local deleted_count=0
|
|
|
|
echo "$build_dirs" | tail -n "$to_delete_count" | while read -r build_dir; do
|
|
if safe_delete "$build_dir"; then
|
|
((deleted_count++)) || true
|
|
fi
|
|
done
|
|
|
|
log "作业 ${job_name}: 保留 ${max_builds_to_keep} 个构建,删除 ${deleted_count} 个旧构建"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# 显示帮助信息
|
|
show_help() {
|
|
cat << EOF
|
|
使用方式: $0 [选项]
|
|
|
|
选项:
|
|
-d, --dry-run 干运行模式,只显示将要执行的操作
|
|
-a, --all 清理工作区和构建历史
|
|
-w, --workspace 只清理工作区(默认)
|
|
-b, --builds 只清理构建历史
|
|
-r DAYS, --retention-days DAYS 设置保留天数(默认: 7)
|
|
-h, --help 显示此帮助信息
|
|
|
|
示例:
|
|
$0 # 默认清理工作区
|
|
$0 --dry-run # 干运行模式
|
|
$0 --all # 清理工作区和构建历史
|
|
$0 --retention-days 30 # 保留最近30天的构建
|
|
EOF
|
|
}
|
|
|
|
# 解析命令行参数
|
|
MODE="workspace"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-d|--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
-a|--all)
|
|
MODE="all"
|
|
shift
|
|
;;
|
|
-w|--workspace)
|
|
MODE="workspace"
|
|
shift
|
|
;;
|
|
-b|--builds)
|
|
MODE="builds"
|
|
shift
|
|
;;
|
|
-r|--retention-days)
|
|
RETENTION_DAYS="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
*)
|
|
log "错误: 未知选项 $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# 主执行函数
|
|
main() {
|
|
log "=== Jenkins 工作区清理开始 ==="
|
|
log "模式: ${MODE}, 干运行: ${DRY_RUN}, 保留天数: ${RETENTION_DAYS}"
|
|
|
|
check_directories
|
|
|
|
# 检查磁盘空间,如果充足且不是强制清理,则跳过
|
|
if ! check_disk_space && [ "$MODE" != "all" ]; then
|
|
log "磁盘空间充足,跳过清理"
|
|
return 0
|
|
fi
|
|
|
|
case "$MODE" in
|
|
"workspace")
|
|
clean_workspace
|
|
;;
|
|
"builds")
|
|
clean_builds
|
|
;;
|
|
"all")
|
|
clean_workspace
|
|
clean_builds
|
|
;;
|
|
esac
|
|
|
|
log "=== Jenkins 工作区清理完成 ==="
|
|
}
|
|
|
|
# 执行主函数
|
|
main
|
|
|
|
exit 0 |