踩了些坑,记录下
我的场景是一台Mac mini做master,另一台Mac mini做slave
- Jenkins的插件中心安装
ssh插件 - 节点列表中增加从节点
- 从节点电脑需要安装和master同版本的Java环境
- 确保从节点使用固定的内网ip,需要在系统设置->共享->打开远程登录
- 从节点配置使用userName password登录(使用ssh公钥也行)
- 从节点环境变量
我需要执行的Job都是一些shell脚本,在从节点中执行时,默认无法访问到所有环境变量,比如会出现command not found,需要再shell中手动导出下
#从节点 .zshrc文件中 需要配置好的一些环境变量
export PATH="/opt/homebrew/bin:$PATH"
#export PATH=$PATH:/Users/flutter/bin:$PATH
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
# Jenkins的Job命中中,先导入环境变量
source ~/.bash_profile
source ~/.zshrc
# todo ..
- 打包脚本遇到签名错误,如这种
...xxxKitExtension.appex: errSecInternalComponent Command CodeSign failed with a nonzero exit code
解决方法是在脚本增加钥匙串登录命令(下面的判断可以不要,我是有需要才加的)
# 登录从节点钥匙串
if [ $(hostname) == "ios_slave_1.local" ]; then
echo "登录从节点钥匙串"
security unlock-keychain -p "password" ~/Library/Keychains/login.keychain
fi
问题1: 打包机磁盘占用很高
- 仅保留有限的构建日志,job配置里 ->
丢弃旧的构建 -> 保持构建的最大个数(50-100即可) - 脚本定期清理构建产物, 我设置job每周执行一次
定时构建 -> 日程表(H 5 * * 6)
echo "清理7天前的内网包."
# 设置根目录
root_dir="xxxx/itms-services"
# 计算7天前的时间(秒)
# $(date +%s) 获取当前时间的秒数
threshold=$(( $(date +%s) - 7 * 24 * 60 * 60 ))
# 递归遍历目录 (3层)
find "$root_dir" -type d -maxdepth 3 -mindepth 3 -print0 | while IFS= read -r -d '' dir; do
# 获取文件夹的修改时间戳(秒)
mtime=$(stat -f %m "$dir") # macOS 上使用 %m 获取修改时间
# 检查时间戳是否大于阈值(即是否早于7天前)
if [ "$mtime" -lt "$threshold" ]; then
rm -rf "$dir"
else
echo "step this dir"
fi
printf "\n"
done
问题2: 清理废弃job残留工作空间的磁盘目录
直接贴到 Manage Jenkins-> Script Console里点运行,查看输出;
确认删除的话,修改DRY_RUN = false后再次运行脚本
import jenkins.model.Jenkins
import hudson.FilePath
def DRY_RUN = true // 先跑一遍只打印,确认无误后改 false
def jenkins = Jenkins.instance
def existingJobs = jenkins.getAllItems(hudson.model.AbstractItem.class)*.fullName as Set
def deleteCount = 0
def failCount = 0
def cleanWorkspace = { String nodeName, FilePath wsRoot ->
println ""
println "========== 节点: ${nodeName} =========="
if (wsRoot == null || !wsRoot.exists()) {
println " (workspace 目录不存在,跳过)"
return
}
def found = false
wsRoot.listDirectories().each { dir ->
def baseName = dir.name.split('@')[0]
if (!existingJobs.contains(baseName)) {
found = true
println ""
println " ${DRY_RUN ? '[DRY-RUN] ' : ''}发现废弃 workspace:"
println " 目录: ${dir.remote}"
println " 对应 job: ${baseName} (已不存在)"
if (!DRY_RUN) {
try {
dir.deleteRecursive()
deleteCount++
println " => 已删除"
} catch (e) {
failCount++
println " => 删除失败: ${e.message}"
}
} else {
deleteCount++
}
}
}
if (!found) {
println " (无废弃 workspace)"
}
}
// agents
jenkins.nodes.each { node ->
def computer = node.toComputer()
if (computer == null || computer.offline) {
println ""
println "========== 节点: ${node.displayName} =========="
println " (节点离线,跳过)"
return
}
if (!computer.idle) {
println ""
println "========== 节点: ${node.displayName} =========="
println " (节点有构建在跑,跳过)"
return
}
cleanWorkspace(node.displayName, node.rootPath?.child("workspace"))
}
// master (built-in node)
cleanWorkspace("master", jenkins.rootPath.child("workspace"))
println ""
println "===================================="
println DRY_RUN ? "DRY-RUN 完成,共发现 ${deleteCount} 个待删除目录" \
: "执行完成: 删除 ${deleteCount} 个,失败 ${failCount} 个"
println "===================================="