第三章:Jenkins Pipeline 基础——安装、语法与SDK环境配置

讲到CI流水线,Jenkins几乎成了行业标配。我记得2015年刚接触它时,还是个Java老项目,配置全靠Web界面点来点去。后来Pipeline as Code普及了,才真正把CI从“玄学”变成了“工程学”。这一章,我就带你搭建Jenkins环境,讲清楚Pipeline的核心语法,再顺手把Android SDK配置好。不绕弯子,直接上干货。

本章知识主线:安装Jenkins → 掌握Pipeline语法(声明式为主) → 配置Android SDK环境。三步走完,你就能跑出第一条Android CI流水线。

3.1 Jenkins 安装与初始化

安装方式很多,我推荐用Docker,隔离性好,升级也方便。你想想看,要是直接装在宿主机器上,环境变量一团乱,哪天想换个版本还得去翻安装包,太折腾了。

3.1.1 Docker 安装(我的首选)

直接拉取官方LTS镜像,挂载一个本地卷用于持久化数据:

docker run \
  --name jenkins-blue \
  -d \
  -p 8080:8080 \
  -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkins/jenkins:lts

个人习惯:我会额外挂载 /var/run/docker.sock,这样Jenkins容器内部就能直接调用宿主机的Docker引擎。为什么?因为在流水线里你经常需要动态创建容器来跑编译,没有这个映射,你就得在容器里再装一个Docker,多此一举。

3.1.2 首次启动配置

启动后,访问 http://localhost:8080。Jenkins会让你输入一个初始密码,在容器里执行:

docker exec jenkins-blue cat /var/jenkins_home/secrets/initialAdminPassword

复制出来,粘贴进去,然后选「安装推荐的插件」。这一步大概等5-10分钟。安装完成后创建一个管理员用户,嗯,这里要注意——密码别太简单,你后面会频繁用它登录。

我曾经踩过的坑:有的团队为了省事,把JENKINS_HOME映射到了宿主机的一个NFS目录,结果插件安装时文件锁冲突,导致界面卡死。所以建议映射到本地SSD盘,或者用命名卷(named volume),别用网络存储。

3.2 Pipeline 语法:声明式 vs 脚本式

Jenkins Pipeline有两种写法:声明式(Declarative)和脚本式(Scripted)。我个人的建议是:新项目一律用声明式。别问为什么,说白了就是——结构清晰、模板化、IDE友好,团队成员一看就懂。脚本式虽然灵活,但写起来容易失控,像一盘散沙。

3.2.1 声明式Pipeline的核心结构

最基本的骨架长这样:

pipeline {
    agent any
    
    stages {
        stage('Checkout') {
            steps {
                echo '拉取代码...'
                checkout scm
            }
        }
        stage('Build') {
            steps {
                echo '开始构建...'
                sh './gradlew assembleDebug'
            }
        }
        stage('Test') {
            steps {
                echo '运行单元测试...'
                sh './gradlew testDebugUnitTest'
            }
        }
        stage('Archive') {
            steps {
                echo '归档产物...'
                archiveArtifacts artifacts: 'app/build/outputs/**/*.apk'
            }
        }
    }
    
    post {
        always {
            echo '无论成功失败都会执行'
            cleanWs()
        }
        success {
            echo '构建成功'
        }
        failure {
            echo '构建失败,发个告警'
        }
    }
}

必知概念:

  • agent:指定运行环境。可以用any,也可以用agent { label 'android' }绑定特定节点。
  • stages:流水线的阶段容器,一个阶段可以包含多个步骤。
  • steps:具体执行的操作,比如运行Shell命令、调用Gradle、归档文件。
  • post:构建结束后的回调,always success failure unstable 分别对应不同状态。

3.2.2 声明式 vs 脚本式:一张表看清区别

维度 声明式(Declarative) 脚本式(Scripted)
语法风格 结构化、有固定schema 自由Groovy代码
可读性 高,新人上手快 低,需要熟悉Groovy
错误检查 语法校验严格,错误提前暴露 运行时才知道对错
代码复用 通过Shared Libraries 支持函数、闭包
推荐场景 90%的CI场景 复杂的自定义逻辑

我的一贯做法:主体用声明式,遇到需要动态解析JSON或循环生成Stage时,在script { }块里夹一段脚本式代码。比如动态生成并行测试环境,这样既保持了结构清晰,又不失灵活性。

3.3 配置 Android SDK 环境

Pipeline写好之后,你得让Jenkins知道去哪找Android SDK。环境配置不到位,Gradle编译时会直接报错,连sdk.dir都找不到。你在项目中遇到过这种情况吗?多半是SDK路径没配对。

3.3.1 在Jenkins节点上安装SDK

我习惯在Jenkins主节点(或构建Agent节点)上手动安装Command Line Tools:

cd /opt
wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip
unzip commandlinetools-linux-*.zip -d android-sdk
cd android-sdk/cmdline-tools
mv tools latest
# 设置环境变量(写入 /etc/profile 或 /home/jenkins/.bashrc)
export ANDROID_HOME=/opt/android-sdk
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH
# 安装所需SDK组件
sdkmanager "platforms;android-34" "build-tools;34.0.0" "ndk;25.2.9519653"

避坑指南:我曾经在一台CI节点上直接apt install android-sdk,结果系统自带的SDK版本太老,Gradle不兼容。后来一律改用Command Line Tools手动装,版本自己控制。还有,别忘记安装platform-tools——如果你后续要做设备测试,adb必不可少。

3.3.2 在Pipeline中注入SDK路径

有两种方式:一种是写到全局环境变量(Manage Jenkins → Configure System),另一种是直接在Pipeline里指定。我推荐后者,因为同一个Jenkins实例可能跑多个安卓项目,有的用SDK 33,有的用34,灵活点好。

pipeline {
    agent any
    
    environment {
        ANDROID_HOME = '/opt/android-sdk'
        ANDROID_SDK_ROOT = '/opt/android-sdk'
        PATH = "${env.ANDROID_HOME}/tools/bin:${env.ANDROID_HOME}/platform-tools:${env.PATH}"
    }
    
    stages {
        stage('Check Environment') {
            steps {
                sh '''
                    echo "ANDROID_HOME: $ANDROID_HOME"
                    ls $ANDROID_HOME/platforms/
                    ./gradlew --version
                '''
            }
        }
        stage('Build') {
            steps {
                sh './gradlew clean assembleDebug'
            }
        }
    }
}

关键说明:很多新手会把ANDROID_HOME和ANDROID_SDK_ROOT只设一个,其实Gradle查找SDK时两个都会尝试。保持一致的话,能避免诡异的环境感知问题。另外,如果项目中用了local.properties,记得把它加到.gitignore里,不然本地路径会覆盖Jenkins上的路径。

3.3.3 验证环境是否就绪

跑一条简单的Pipeline验证一下,在构建日志里看到类似这样的输出就对了:

[Pipeline] echo
ANDROID_HOME: /opt/android-sdk
[Pipeline] sh
+ ls /opt/android-sdk/platforms/
android-34
[Pipeline] sh
+ ./gradlew --version

------------------------------------------------------------
Gradle 8.4
------------------------------------------------------------

如果sdk.dir找不到、或者Gradle版本不匹配,日志里会直接飘红。建议先手动跑一遍./gradlew assembleDebug确保本地能通过,再把脚本交到Jenkins手上。

本章知识结构一览

下面这张图概括了从Jenkins启动到Android SDK就绪的完整关系和流程:

Jenkins Pipeline 基础搭建流程 Docker部署 安装Jenkins LTS 首次初始化 插件安装+管理员账号 Pipeline语法 声明式 vs 脚本式 编写脚本 Stage/Steps/Post Android SDK安装 cmdline-tools 手动部署 platforms/build-tools/ndk 环境变量注入 environment {} ANDROID_HOME / PATH 验证构建 sh './gradlew assembleDebug' 检查日志与产物 说明: 1. 上半部分:Jenkins安装与Pipeline脚本编写流程。 2. 下半部分:Android SDK环境配置与构建验证环节。 3. 虚线箭头表示从Pipeline编写完成后,返回配置SDK环境,形成闭环。 4. 声明式Pipeline是推荐写法,脚本式仅用于特殊场景。 5. SDK版本必须与Gradle、AGP版本匹配,否则编译时会有兼容性问题。

高效小技巧:把ANDROID_HOME、GRADLE_USER_HOME这些变量放到全局工具配置(Manage Jenkins → Global Tool Configuration)里,然后在Pipeline中用tool name: 'android-sdk-34', type: 'com.cloudbees.jenkins.plugins.customtools.CustomTool'引用,可读性更佳。不过要注意,Custom Tools插件需要额外安装。