307 lines
9.5 KiB
Bash
Executable File
307 lines
9.5 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
setup_jenkins_container() {
|
|
show_banner "配置 Jenkins Docker 容器"
|
|
|
|
log_info "停止现有 Jenkins 容器..."
|
|
docker stop ${JENKINS_CONTAINER_NAME} 2>/dev/null || true
|
|
docker rm ${JENKINS_CONTAINER_NAME} 2>/dev/null || true
|
|
|
|
log_info "启动新的 Jenkins 容器(挂载 SSH 密钥)..."
|
|
|
|
if [ ! -d ~/.ssh ]; then
|
|
log_error "~/.ssh 目录不存在!"
|
|
log_info "请先生成 SSH 密钥: ssh-keygen -t rsa -b 4096"
|
|
exit 1
|
|
fi
|
|
|
|
docker run -d \
|
|
--name ${JENKINS_CONTAINER_NAME} \
|
|
--restart unless-stopped \
|
|
--user root \
|
|
-p "${JENKINS_PORT}:8080" \
|
|
-p "50000:50000" \
|
|
-v jenkins_data:/var/jenkins_home \
|
|
-v ~/.ssh:/var/jenkins_home/.ssh:ro \
|
|
-e JAVA_OPTS="-Djenkins.install.runSetupWizard=false" \
|
|
-e TZ="Asia/Shanghai" \
|
|
--network ${DOCKER_NETWORK} \
|
|
${JENKINS_IMAGE}
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log_success "Jenkins 容器已启动"
|
|
log_info "容器名称: ${JENKINS_CONTAINER_NAME}"
|
|
log_info "访问地址: ${JENKINS_URL}"
|
|
else
|
|
log_error "Jenkins 容器启动失败!"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
install_jenkins_tools() {
|
|
show_banner "安装 Jenkins 必要工具"
|
|
|
|
log_info "在 Jenkins 容器中安装 rsync, curl, openssh-client..."
|
|
|
|
docker exec -u root ${JENKINS_CONTAINER_NAME} bash -c "
|
|
apt-get update && \
|
|
apt-get install -y \
|
|
rsync \
|
|
curl \
|
|
openssh-client \
|
|
jq \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& echo '✅ 工具安装完成'
|
|
"
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log_success "工具安装成功"
|
|
|
|
echo ""
|
|
log_info "验证安装结果:"
|
|
docker exec ${JENKINS_CONTAINER_NAME} bash -c '
|
|
echo "--- rsync ---"
|
|
rsync --version | head -1 || echo "❌ 未安装"
|
|
echo ""
|
|
echo "--- curl ---"
|
|
curl --version | head -1 || echo "❌ 未安装"
|
|
echo ""
|
|
echo "--- ssh ---"
|
|
ssh -V || echo "❌ 未安装"
|
|
echo ""
|
|
echo "--- jq ---"
|
|
jq --version || echo "❌ 未安装"
|
|
'
|
|
else
|
|
log_error "工具安装失败!请手动执行:"
|
|
echo "docker exec -u root ${JENKINS_CONTAINER_NAME} bash -c 'apt-get update && apt-get install -y rsync curl openssh-client'"
|
|
exit 1
|
|
fi
|
|
|
|
test_ssh_connection
|
|
}
|
|
|
|
test_ssh_connection() {
|
|
show_banner "测试 SSH 连接到生产服务器"
|
|
|
|
log_info "测试连接到 ${SERVER_USER}@${SERVER_IP}..."
|
|
|
|
local result=$(docker exec ${JENKINS_CONTAINER_NAME} ssh \
|
|
-o StrictHostKeyChecking=no \
|
|
-o ConnectTimeout=5 \
|
|
-o BatchMode=yes \
|
|
${SERVER_USER}@${SERVER_IP} \
|
|
"hostname && whoami && pwd" 2>&1)
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log_success "SSH 连接成功!"
|
|
echo "$result" | while IFS= read -r line; do
|
|
log_info " $line"
|
|
done
|
|
else
|
|
log_warning "SSH 连接失败(部署阶段将无法工作)"
|
|
log_error "错误信息: $result"
|
|
log_info "可能原因:"
|
|
log_info " 1. SSH 密钥未正确挂载到容器"
|
|
log_info " 2. 生产服务器的 authorized_keys 不包含公钥"
|
|
log_info ""
|
|
log_info "解决方案:"
|
|
log_info " 1. 在宿主机上测试: ssh ${SERVER_USER}@${SERVER_IP}"
|
|
log_info " 2. 如果宿主机可以免密登录,检查 docker run 的 -v 参数"
|
|
log_info " 3. 复制公钥到服务器: ssh-copy-id ${SERVER_USER}@${SERVER_IP}"
|
|
|
|
read -p "是否继续配置(跳过 SSH 测试)?(y/N): " continue_without_ssh
|
|
if [[ ! "$continue_without_ssh" =~ ^[Yy]$ ]]; then
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
setup_jenkins_credentials() {
|
|
show_banner "配置 Jenkins 凭据"
|
|
|
|
create_gitea_credentials
|
|
}
|
|
|
|
create_gitea_credentials() {
|
|
log_info "创建 Gitea 访问凭据..."
|
|
|
|
local credentials_xml="<com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl>
|
|
<scope>GLOBAL</scope>
|
|
<id>gitea-credentials</id>
|
|
<description>Gitea 仓库访问凭据</description>
|
|
<username>${GITEA_USERNAME}</username>
|
|
<password>${GITEA_PASSWORD}</password>
|
|
</com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl>"
|
|
|
|
local result=$(jenkins_api_call "POST" "/credential-store/domain/createCredential" "
|
|
{
|
|
\"credentials\": {
|
|
\"scope\": \"GLOBAL\",
|
|
\"id\": \"gitea-credentials\",
|
|
\"username\": \"${GITEA_USERNAME}\",
|
|
\"password\": \"${GITEA_PASSWORD}\",
|
|
\"description\": \"Gitea 仓库访问凭据\",
|
|
\"\$class\": \"com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl\"
|
|
}
|
|
}
|
|
")
|
|
|
|
if echo "$result" | grep -q "error"; then
|
|
log_warning "凭据可能已存在,尝试更新..."
|
|
else
|
|
log_success "Gitea 凭据创建成功 (ID: gitea-credentials)"
|
|
fi
|
|
}
|
|
|
|
setup_jenkins_global_tools() {
|
|
show_banner "配置全局工具"
|
|
|
|
configure_nodejs
|
|
}
|
|
|
|
configure_nodejs() {
|
|
log_info "配置 Node.js ${NODE_VERSION}..."
|
|
|
|
jenkins_api_call "POST" "/pluginManager/plugins/nodescript/configure" > /dev/null 2>&1 || true
|
|
|
|
jenkins_api_call "POST" "/toolLocation/updateNodeJS" "
|
|
{
|
|
\"nodeJSHome\": \"/var/jenkins_home/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/NodeJS_${NODE_VERSION}\",
|
|
\"name\": \"${NODE_VERSION}\",
|
|
\"properties\": [
|
|
{
|
|
\"installSource\": {
|
|
\"installers\": [
|
|
{
|
|
\"id\": \"${NODE_VERSION}.x\"
|
|
}
|
|
]
|
|
},
|
|
\"stapler-class\": \"hudson.tools.InstallSourceProperty\"
|
|
}
|
|
]
|
|
}" > /dev/null 2>&1
|
|
|
|
log_success "Node.js 配置完成 (版本: ${NODE_VERSION})"
|
|
}
|
|
|
|
install_required_plugins() {
|
|
show_banner "安装 Jenkins 插件"
|
|
|
|
local plugins=(
|
|
"generic-webhook-trigger"
|
|
"nodejs"
|
|
"htmlpublisher"
|
|
"workspace-cleanup"
|
|
"git"
|
|
"credentials-binding"
|
|
"ssh-credentials"
|
|
"timestamper"
|
|
"ansicolor"
|
|
)
|
|
|
|
log_info "需要安装的插件: ${#plugins[@]} 个"
|
|
|
|
local plugin_list=$(IFS=','; echo "${plugins[*]}")
|
|
|
|
local install_result=$(jenkins_api_CALL "POST" "/pluginManager/installNecessaryPlugins" "
|
|
{
|
|
\"<dynamic-install>\": [${plugin_list}]
|
|
}" 2>/dev/null) || true
|
|
|
|
jenkins_api_call "POST" "/scriptText" "script=
|
|
import jenkins.model.*
|
|
import hudson.model.*
|
|
|
|
def plugins = [
|
|
'generic-webhook-trigger',
|
|
'nodejs',
|
|
'htmlpublisher',
|
|
'workspace-cleanup',
|
|
'timestamper',
|
|
'ansicolor'
|
|
]
|
|
|
|
def pm = Jenkins.instance.pluginManager
|
|
def uc = Jenkins.instance.updateCenter
|
|
|
|
pm.plugins.each { plugin ->
|
|
if (plugins.contains(plugin.shortName)) {
|
|
println \"✅ 已安装: \${plugin.shortName}\"
|
|
}
|
|
}
|
|
|
|
plugins.each { pluginName ->
|
|
def plugin = pm.getPlugin(pluginName)
|
|
if (!plugin) {
|
|
println \"📦 安装中: \${pluginName}\"
|
|
uc.getPlugin(pluginName).deploy(true).get()
|
|
}
|
|
}
|
|
|
|
println '✅ 插件安装完成'
|
|
|
|
return 'success'
|
|
" > /dev/null 2>&1
|
|
|
|
log_success "插件安装请求已提交(可能需要重启)"
|
|
log_info "建议手动重启 Jenkins 使插件生效:"
|
|
log_info " docker restart ${JENKINS_CONTAINER_NAME}"
|
|
}
|
|
|
|
create_pipeline_job() {
|
|
show_banner "创建 Pipeline 任务"
|
|
|
|
local job_config="<?xml version='1.0' encoding='UTF-8'?>
|
|
<flow-definition>
|
|
<description>Novalon Website CI/CD Pipeline (自动生成)</description>
|
|
<keepDependencies>false</keepDependencies>
|
|
<properties>
|
|
<hudson.model.ParametersDefinitionProperty>
|
|
<parameterDefinitions>
|
|
<hudson.model.BooleanParameterDefinition>
|
|
<name>DEPLOY_TO_PRODUCTION</name>
|
|
<description>是否部署到生产环境(仅在 main 分支且所有测试通过后可用)</description>
|
|
<defaultValue>false</defaultValue>
|
|
</hudson.model.BooleanParameterDefinition>
|
|
</parameterDefinitions>
|
|
</hudson.model.ParametersDefinitionProperty>
|
|
</properties>
|
|
<definition class=\"org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition\" plugin=\"workflow-cps@\">
|
|
<scm class=\"hudson.plugins.git.GitSCM\" plugin=\"git@\">
|
|
<configVersion>2</configVersion>
|
|
<userRemoteConfigs>
|
|
<hudson.plugins.git.UserRemoteConfig>
|
|
<url>${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}.git</url>
|
|
<credentialsId>gitea-credentials</credentialsId>
|
|
</hudson.plugins.git.UserRemoteConfig>
|
|
</userRemoteConfigs>
|
|
<branches>
|
|
<hudson.plugins.git.BranchSpec>
|
|
<name>*/main</name>
|
|
</hudson.plugins.git.BranchSpec>
|
|
</branches>
|
|
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
|
|
<submoduleCfg class=\"list\"/>
|
|
<extensions/>
|
|
</scm>
|
|
<scriptPath>Jenkinsfile</scriptPath>
|
|
<lightweight>true</lightweight>
|
|
</definition>
|
|
<triggers/>
|
|
<disabled>false</disabled>
|
|
</flow-definition>"
|
|
|
|
local result=$(jenkins_api_call "POST" "/createItem?name=${JOB_NAME}" "$job_config")
|
|
|
|
if [ $? -eq 0 ] && ! echo "$result" | grep -q "error"; then
|
|
log_success "Pipeline 任务创建成功: ${JOB_NAME}"
|
|
log_info "任务地址: ${JENKINS_URL}/job/${JOB_NAME}/"
|
|
else
|
|
log_warning "任务可能已存在,尝试更新配置..."
|
|
jenkins_api_call "POST" "/job/${JOB_NAME}/config.xml" "$job_config" > /dev/null
|
|
log_success "Pipeline 任务更新成功: ${JOB_NAME}"
|
|
fi
|
|
}
|