2025-09-21 19:52:30 +08:00

243 lines
6.9 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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