Jenkins Day 3-4 学习笔记 第一个Freestyle项目 — 从零到完整 CI 流程
一、今日学习目标总览 1 2 3 4 5 6 7 8 9 10 Day 3-4 你将掌握: ├── 准备本地 Git 仓库作为练习项目 ├── 创建 Freestyle Project 并关联 Git ├── 配置源码管理(SCM) ├── 配置构建触发器(手动/定时/轮询/远程) ├── 编写 Shell 构建步骤 ├── 配置构建后操作(产物归档、邮件通知) ├── 理解构建状态和构建历史 └── 完整体验一次"代码提交→自动构建" 的 CI 流程
二、准备练习用的 Git 仓库 2.1 为什么要先准备 Git 仓库 1 2 3 4 5 6 7 8 在 Day 1-2 中,我们创建的项目没有关联任何代码仓库, 只是手动执行了一段 Shell 脚本。 真实的 CI 流程是: 代码仓库(Git)──→ Jenkins 拉取代码 ──→ 编译测试 ──→ 产出结果 所以我们需要一个 Git 仓库来模拟真实的工作场景。
2.2 在虚拟机中安装 Git(如果还没有) 1 2 3 4 5 6 7 8 9 git --versionsudo apt update && sudo apt install git -ysudo yum install git -y
2.3 配置 Git 用户信息 1 2 3 4 5 6 git config --global user.name "Your Name" git config --global user.email "your-email@example.com" git config --list
2.4 创建练习项目 我们创建一个模拟的 Java 项目来练习:
1 2 3 4 5 6 mkdir -p /home/LX/jenkins_demo/demo-java-appcd /home/LX/jenkins_demo/demo-java-app git init
创建项目结构:
1 2 3 4 mkdir -p src/main/java/com/demomkdir -p src/test/java/com/demomkdir -p scripts
Step 1:创建 Java 源文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 cat > src/main/java/com/demo/App.java << 'EOF' package com.demo; public class App { public static void main(String[] args) { System.out.println("===================================" ); System.out.println(" Demo Java Application v1.0" ); System.out.println("===================================" ); System.out.println("Application started successfully!" ); } public static int add(int a, int b) { return a + b; } public static int subtract(int a, int b) { return a - b; } } EOF
Step 2:创建简单的测试文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 cat > src/test/java/com/demo/AppTest.java << 'EOF' package com.demo; public class AppTest { public static void main(String[] args) { System.out.println("Running tests..." ); // 测试 add 方法 int result1 = App.add(2, 3); assert result1 == 5 : "add(2,3) should be 5, but got " + result1; System.out.println("✅ Test add(2,3) = 5: PASSED" ); // 测试 subtract 方法 int result2 = App.subtract(10, 4); assert result2 == 6 : "subtract(10,4) should be 6, but got " + result2; System.out.println("✅ Test subtract(10,4) = 6: PASSED" ); // 测试边界情况 int result3 = App.add(0, 0); assert result3 == 0 : "add(0,0) should be 0, but got " + result3; System.out.println("✅ Test add(0,0) = 0: PASSED" ); System.out.println("" ); System.out.println("All tests passed! ✅" ); } } EOF
Step 3:创建构建脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 cat > scripts/build.sh << 'EOF' set -eecho "========================================" echo " Step 1: 编译源代码" echo "========================================" mkdir -p build/classes javac -d build/classes \ src/main/java/com/demo/App.javaecho "✅ 编译成功" echo "" echo "========================================" echo " Step 2: 运行单元测试" echo "========================================" javac -d build/classes \ -cp build/classes \ src/test/java/com/demo/AppTest.java java -cp build/classes \ -ea \ com.demo.AppTestecho "" echo "========================================" echo " Step 3: 打包" echo "========================================" cd build/classes jar cf ../demo-app.jar .cd ../..echo "✅ 打包成功: build/demo-app.jar" echo "" echo "========================================" echo " Step 4: 验证 JAR 包" echo "========================================" java -cp build/demo-app.jar com.demo.Appecho "" echo "🎉 构建全部完成!" echo "产物文件: build/demo-app.jar" ls -lh build/demo-app.jar EOFchmod +x scripts/build.sh
Step 4:创建 README 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 cat > README.md << 'EOF' 一个用于学习 Jenkins CI 的示例 Java 项目。 demo-java-app/ ├── src/ │ ├── main/java/com/demo/ │ │ └── App.java │ └── test /java/com/demo/ │ └── AppTest.java ├── scripts/ │ └── build.sh ├── build/ └── README.md bash scripts/build.sh EOF
Step 5:创建 .gitignore 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 cat > .gitignore << 'EOF' build/ .idea/ *.iml .vscode/ *.class .DS_Store Thumbs.db *.log EOF
2.5 提交代码到本地仓库 1 2 3 4 5 6 7 8 9 10 11 git status git add . git status git commit -m "初始化项目:Java Demo App"
你应该看到:
1 2 3 4 5 6 7 [master (root-commit) abc1234] 初始化项目:Java Demo App 5 files changed, 123 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 scripts/build.sh create mode 100644 src/main/java/com /demo/App.java create mode 100644 src/test/java/com /demo/AppTest.java
2.6 先在本地验证构建脚本能正常工作 1 2 3 cd ~/demo-java-app bash scripts/build.sh
你应该看到编译、测试、打包全部成功。如果失败,检查 Java 是否安装:
1 2 3 4 5 6 7 java -version javac -versionsudo apt install openjdk-17-jdk -y sudo yum install java-17-openjdk-devel -y
确认构建成功后,清理构建产物:
2.7 多提交几个版本(模拟开发迭代) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 cat > src/main/java/com/demo/App.java << 'EOF' package com.demo; public class App { public static void main(String[] args) { System.out.println("===================================" ); System.out.println(" Demo Java Application v1.0" ); System.out.println("===================================" ); System.out.println("Application started successfully!" ); } public static int add(int a, int b) { return a + b; } public static int subtract(int a, int b) { return a - b; } public static int multiply(int a, int b) { return a * b; } } EOFcat > src/test/java/com/demo/AppTest.java << 'EOF' package com.demo; public class AppTest { public static void main(String[] args) { System.out.println("Running tests..." ); int result1 = App.add(2, 3); assert result1 == 5 : "add(2,3) should be 5, but got " + result1; System.out.println("✅ Test add(2,3) = 5: PASSED" ); int result2 = App.subtract(10, 4); assert result2 == 6 : "subtract(10,4) should be 6, but got " + result2; System.out.println("✅ Test subtract(10,4) = 6: PASSED" ); int result3 = App.multiply(3, 4); assert result3 == 12 : "multiply(3,4) should be 12, but got " + result3; System.out.println("✅ Test multiply(3,4) = 12: PASSED" ); System.out.println("" ); System.out.println("All tests passed! ✅" ); } } EOF
1 2 git add -A git commit -m "feat: 新增 multiply 方法"
1 2 3 4 5 sed -i 's/一个用于学习 Jenkins CI 的示例 Java 项目。/一个用于学习 Jenkins CI 的示例 Java 项目,支持加减乘运算。/' README.md git add -A git commit -m "docs: 更新 README 描述"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 cat > scripts/build.sh << 'EOF' set -eecho "========================================" echo " Step 1: 编译源代码" echo "========================================" mkdir -p build/classes javac -d build/classes \ src/main/java/com/demo/App.javaecho "✅ 编译成功" echo "" echo "========================================" echo " Step 2: 运行单元测试" echo "========================================" javac -d build/classes \ -cp build/classes \ src/test/java/com/demo/AppTest.java java -cp build/classes \ -ea \ com.demo.AppTestecho "" echo "========================================" echo " Step 3: 打包" echo "========================================" cd build/classes jar cf ../demo-app.jar .cd ../..echo "✅ 打包成功: build/demo-app.jar" echo "" echo "========================================" echo " Step 4: 验证 JAR 包" echo "========================================" java -cp build/demo-app.jar com.demo.Appecho "" echo "🎉 构建全部完成!" echo "产物文件: build/demo-app.jar" ls -lh build/demo-app.jarecho "" echo "========================================" echo " 构建统计" echo "========================================" echo "完成时间: $(date) " EOF
1 2 git add -A git commit -m "build: 增加构建耗时统计"
验证我们有了多个提交:
输出应该类似:
1 2 3 4 d4e5f6a build: 增加构建耗时统计 c3d4e5f docs: 更新 README 描述 b2c3d4e feat: 新增 multiply 方法 a1b2c3d 初始化项目:Java Demo App
三、创建 Freestyle 项目并关联 Git 3.1 创建项目 1 2 3 4 5 6 7 8 9 10 11 操作步骤:1. 在 Jenkins Dashboard 页面,点击左侧 [ New Item ]2. 在 "Enter an item name" 输入框中填写: demo-java-freestyle3. 选择项目类型: ○ Freestyle project ← 选这个4. 点击 [ OK ]
3.2 配置项目 — General(常规设置) 进入项目配置页面后,你会看到多个配置区域。我们从上到下逐一配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ┌─────────────────────────────────────────────────────────────┐ │ │ │ General(常规设置) │ │ │ │ Description(描述): │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Jenkins 学习项目 - Java Demo App │ │ │ │ 用于练习 Freestyle 项目的配置 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ☐ Discard old builds(丢弃旧构建) │ │ 勾选后可以配置: │ │ ├── 保持构建的天数:30 │ │ └── 保持构建的最大数量:5 │ │ (建议勾选,防止磁盘被构建日志撑满) │ │ │ │ ☐ GitHub project │ │ (如果项目在 GitHub 上,填写 GitHub 项目 URL) │ │ │ │ ☐ This project is parameterized(参数化构建) │ │ (Day 5-6 会详细讲解) │ │ │ │ ☐ Throttle builds(限流构建) │ │ (防止频繁构建) │ │ │ │ ☐ Disable this project(禁用项目) │ │ (临时禁用,不会被触发构建) │ │ │ └─────────────────────────────────────────────────────────────┘
推荐配置:
1 2 3 4 5 6 Description: Jenkins 学习项目 - Java Demo App ☑ Discard old builds 保持构建的最大数量: 5 保持构建的天数: 30
3.3 配置项目 — Source Code Management(源码管理)⭐ 重点 1 2 3 4 5 6 7 8 9 ┌─────────────────────────────────────────────────────────────┐ │ │ │ Source Code Management(源码管理) │ │ │ │ ○ None — 不关联代码仓库(Day 1 -2 用的) │ │ ● Git — 关联 Git 仓库(选这个) │ │ ○ Subversion — 关联 SVN 仓库 │ │ │ └─────────────────────────────────────────────────────────────┘
选择 Git 后,展开以下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ┌─────────────────────────────────────────────────────────────┐ │ │ │ Repositories(仓库配置) │ │ │ │ Repository URL(仓库地址): │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ file://home/你的用户名/demo-java-app │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ (这里填写本地 Git 仓库的绝对路径) │ │ │ │ Credentials(凭据): │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ - none - ← 本地仓库不需要 │ │ │ └─────────────────────────────────────────────────────┘ │ │ (如果是远程仓库,需要添加用户名密码凭据) │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Branch Specifier(分支指定): │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ │ │ */master │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ (默认是 */main ,如果你的仓库用 master 分支, │ │ │ │ │ 改成 */master ) │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Repository browser(仓库浏览器): │ │ └── Auto(自动检测) │ │ │ └─────────────────────────────────────────────────────────────┘
如何确认你的仓库路径和分支名:
1 2 3 4 5 6 7 8 9 10 cd ~/demo-java-apppwd git branch
填写说明:
1 2 3 4 5 6 7 8 9 10 11 如果你的分支是 master: Branch Specifier 填: */master 如果你的分支是 main: Branch Specifier 填: */main 如果你想拉取所有分支: Branch Specifier 填: ** 多个分支用逗号分隔: */main, */develop, */release/*
3.4 配置项目 — Build Triggers(构建触发器)⭐ 重点 这是 CI 的核心配置,决定了”什么时候自动触发构建”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ┌─────────────────────────────────────────────────────────────┐ │ │ │ Build Triggers(构建触发器) │ │ │ │ ☐ Trigger builds remotely (e .g., from scripts) │ │ 远程触发构建(通过 URL/API 触发) │ │ │ │ ☐ Build after other projects are built │ │ 其他项目构建完成后触发 │ │ │ │ ☐ Build periodically │ │ 定时构建(不管代码有没有变化) │ │ │ │ ☐ GitHub hook trigger for GITScm polling │ │ GitHub Webhook 触发(需要安装 GitHub 插件) │ │ │ │ ☐ Poll SCM │ │ 轮询代码仓库(定时检查代码是否有变化) │ │ │ └─────────────────────────────────────────────────────────────┘
3.4.1 Trigger remotely(远程触发) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 用途:通过 HTTP 请求触发构建 场景:从脚本、其他系统、Webhook 触发 配置方式: 1. 勾选 "Trigger builds remotely" 2. 填写 Authentication Token(自定义一个令牌,比如 my-build-token) 触发 URL 格式: http://<Jenkins地址>/job/<项目名>/build?token=<令牌> 示例: http://192.168.40.30:8080/job/demo-java-freestyle/build?token=my-build-token 在虚拟机中测试: curl -X POST http://192.168.40.30:8080/job/demo-java-freestyle/build?token=my-build-token
3.4.2 Build periodically(定时构建) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 用途:不管代码有没有变化,按固定时间触发构建 场景:夜间构建、每日构建报告 使用 CRON 表达式(5 个字段): ┌────────── 分钟 (0-59) │ ┌────────── 小时 (0-23) │ │ ┌────────── 日 (1-31) │ │ │ ┌────────── 月 (1-12) │ │ │ │ ┌────────── 星期几 (0-7, 0和7都是周日) │ │ │ │ │ * * * * * 常用示例: 每天早上 8:30 构建: 30 8 * * * 每天晚上 23:00 构建(夜间构建): 0 23 * * * 工作日每天 9:00 构建(周一到周五): 0 9 * * 1-5 每 30 分钟构建一次: H/30 * * * * 每周一早上 9:00 构建: H 9 * * 1 Jenkins 特殊符号 H: ├── H 表示"在指定时间范围内哈希分散" ├── H 0 * * * 表示"每天在 0:00-0:59 之间的某个随机时刻" ├── 目的:避免多个项目同时构建,分散服务器压力 └── H/15 * * * * 表示"每 15 分钟,但起始分钟由 Job 名称哈希随机决定"
3.4.3 Poll SCM(轮询代码仓库)⭐ 最常用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 用途:定时检查 Git 仓库是否有新的提交,有变化才触发构建 场景:最基础的 CI 触发方式 和 Build periodically 的区别: ├── Build periodically:不管有没有变化,时间到了就构建 └── Poll SCM:先检查有没有新提交,有才构建,没有就跳过 配置方式:1 . 勾选 "Poll SCM" 2 . 填写 CRON 表达式 推荐配置: 每 5 分钟检查一次(适合学习阶段):H /5 * * * * 每 2 分钟检查一次(快速验证):H /2 * * * * 每 15 分钟检查一次(生产环境常用):H /15 * * * * 每小时检查一次:H * * * *
**现在先勾选 Poll SCM,填写 H/2 * * * ***(每 2 分钟检查一次,方便我们快速验证效果)。
3.5 配置项目 — Build Environment(构建环境) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ┌─────────────────────────────────────────────────────────────┐ │ │ │ Build Environment(构建环境) │ │ │ │ ☐ Delete workspace before build starts │ │ 构建前清理工作空间(确保干净构建) │ │ │ │ ☐ Use secret text (s) or file (s) │ │ 使用秘密文本或文件(用于传递敏感信息) │ │ │ │ ☐ Terminate a build if it's stuck │ │ 构建卡住时中止(防止无限等待) │ │ 推荐勾选,设置超时时间:15 分钟 │ │ │ │ ☐ Add timestamps to the Console Output │ │ 在控制台输出中添加时间戳(推荐勾选) │ │ │ │ ☐ With Ant │ │ 使用 Ant 构建工具 │ │ │ └─────────────────────────────────────────────────────────────┘
推荐勾选:
1 2 3 4 5 6 7 8 9 ☑ Delete workspace before build starts (每次构建前清空工作空间,避免旧文件影响) ☑ Terminate a build if it's stuck Timeout minutes: 15 (防止构建卡死) ☑ Add timestamps to the Console Output (日志中显示时间戳,方便排查问题)
3.6 配置项目 — Build(构建步骤)⭐ 核心 这是整个配置中最核心的部分,定义了”构建时具体做什么”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ┌─────────────────────────────────────────────────────────────┐ │ │ │ Build(构建步骤) │ │ │ │ [ Add build step ▼ ] │ │ │ │ 下拉菜单中的选项: │ │ ├── Execute shell — 执行 Shell 脚本(Linux) │ │ ├── Execute Windows batch — 执行批处理脚本(Windows) │ │ ├── Invoke Ant — 调用 Ant 构建 │ │ ├── Invoke Gradle script — 调用 Gradle 构建 │ │ ├── Invoke top-level Maven targets — 调用 Maven 构建 │ │ └── ... │ │ │ └─────────────────────────────────────────────────────────────┘
点击 Add build step → 选择 Execute shell
在命令框中输入以下脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #!/bin/bash set -e echo "========================================" echo " Jenkins 构建开始" echo "========================================" echo "构建号: ${BUILD_NUMBER} " echo "工作空间: ${WORKSPACE} " echo "当前时间: $(date) " echo "========================================" echo "" cd ${WORKSPACE} bash scripts/build.shecho "" echo "========================================" echo " 构建产物信息" echo "========================================" if [ -f build/demo-app.jar ]; then echo "✅ 构建产物存在" ls -lh build/demo-app.jarelse echo "❌ 构建产物不存在" exit 1fi echo "" echo "========================================" echo " 构建完成" echo "========================================"
解释这段脚本做了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 脚本执行流程: 1. set -e └── 如果任何命令执行失败(返回非 0),脚本立即停止 Jenkins 会将这次构建标记为 FAILURE 2. 打印构建信息 └── 显示构建号、工作空间路径、当前时间 这些信息在排查问题时非常有用 3. cd ${WORKSPACE} └── 进入 Jenkins 的工作空间目录 Jenkins 在执行构建步骤前,会自动把代码拉到这个目录 ${WORKSPACE} 是 Jenkins 自动设置的环境变量 4. bash scripts/build.sh └── 执行我们项目中的构建脚本 包含:编译 → 测试 → 打包 5. 检查构建产物 └── 确认 JAR 包已成功生成 如果不存在,返回错误码 1,Jenkins 标记构建失败
3.7 配置项目 — Post-build Actions(构建后操作) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ┌─────────────────────────────────────────────────────────────┐ │ │ │ Post-build Actions(构建后操作) │ │ │ │ [ Add post-build action ▼ ] │ │ │ │ 常用选项: │ │ ├── Archive the artifacts — 归档构建产物 │ │ ├── Publish JUnit test result — 发布测试报告 │ │ ├── E-mail Notification — 邮件通知 │ │ ├── Build other projects — 触发其他项目构建 │ │ └── ... │ │ │ └─────────────────────────────────────────────────────────────┘
3.7.1 归档构建产物 1 2 3 4 5 6 7 8 9 10 11 12 点击 [ Add post-build action ] → 选择 "Archive the artifacts" Files to archive(要归档的文件): ┌─────────────────────────────────────────────────────────────┐ │ build/*.jar │ └─────────────────────────────────────────────────────────────┘ ☑ Fingerprint all archived artifacts (为所有归档产物生成指纹,用于追踪) ☑ If archived artifacts show then mark build as unstable (如果没有找到要归档的文件,将构建标记为不稳定)
3.7.2 邮件通知(基础版) 1 2 3 4 5 6 7 8 9 10 11 12 点击 [ Add post-build action ] → 选择 "E-mail Notification" Recipients(收件人): ┌─────────────────────────────────────────────────────────────┐ │ your-email@example.com │ └─────────────────────────────────────────────────────────────┘ ☑ Send e -mail for every unstable build (每次构建不稳定时发送邮件) ☑ Send separate e -mails to individuals who broke the build (向导致构建失败的人单独发送邮件)
注意: 邮件通知需要先在 Manage Jenkins → System 中配置 SMTP 服务器。如果没有配置邮件服务器,这一步可以先跳过。
3.8 保存项目配置 1 2 3 4 5 6 7 8 9 10 11 12 13 所有配置完成后,点击页面底部的 [ Save ] 按钮。 你会进入项目的主页,页面上显示: ├── 项目名称:demo-java-freestyle ├── Description:Jenkins 学习项目 - Java Demo App ├── Build History:(空的,还没有构建过) └── 左侧菜单: ├── Status — 项目状态 ├── Changes — 代码变更 ├── Workspace — 工作空间 ├── Build Now — 立即构建 ⭐ ├── Configure — 修改配置 └── Delete Project — 删除项目
四、执行第一次构建 4.1 手动触发构建 1 2 3 4 5 6 7 8 9 10 11 12 13 操作步骤:1. 在项目主页,点击左侧 [ Build Now ]2. 等几秒钟,Build History 区域会出现一个构建记录 ├── #1(构建号) ├── 蓝色圆点 = 构建中(或构建成功) ├── 黄色圆点 = 构建不稳定 └── 红色圆点 = 构建失败3. 点击构建记录 #1,进入构建详情页4. 点击 [ Console Output ] 查看实时日志
4.2 理解控制台输出 你应该看到类似这样的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 Started by user admin Building in /var /lib/jenkins/workspace/demo-java-freestyle The recommended git tool is : NONEusing credential xxx > git rev-parse --resolve-inside-dir refs/remotes/origin/master > git rev-parse origin/master^{commit}Checking out Revision abc1234 (origin/master ) Commit message: "build: 增加构建耗时统计" > git config core.sparsecheckout > git checkout -f abc1234 First time build. Skipping changelog. [timestamp] ======================================== [timestamp ] Jenkins 构建开始 [timestamp ] ======================================== [timestamp ] 构建号: 1 [timestamp ] 工作空间: /var /lib/jenkins/workspace/demo-java-freestyle [timestamp ] 当前时间: Mon Jul 14 14 :30 :00 CST 2025 [timestamp ] ======================================== [timestamp ] ======================================== [timestamp ] Step 1 : 编译源代码 [timestamp ] ======================================== [timestamp ] ✅ 编译成功 [timestamp ] ======================================== [timestamp ] Step 2 : 运行单元测试 [timestamp ] ======================================== [timestamp ] Running tests... [timestamp ] ✅ Test add (2 ,3 ) = 5 : PASSED [timestamp ] ✅ Test subtract (10 ,4 ) = 6 : PASSED [timestamp ] ✅ Test add (0 ,0 ) = 0 : PASSED [timestamp ] ✅ Test multiply (3 ,4 ) = 12 : PASSED [timestamp ] [timestamp ] All tests passed! ✅ [timestamp ] ======================================== [timestamp ] Step 3 : 打包 [timestamp ] ======================================== [timestamp ] ✅ 打包成功: build/demo-app.jar [timestamp ] ======================================== [timestamp ] Step 4 : 验证 JAR 包 [timestamp ] ======================================== [timestamp ] =================================== [timestamp ] Demo Java Application v1.0 [timestamp ] =================================== [timestamp ] Application started successfully! [timestamp ] [timestamp ] 🎉 构建全部完成! [timestamp ] ======================================== [timestamp ] 构建产物信息 [timestamp ] ======================================== [timestamp ] ✅ 构建产物存在 [timestamp ] -rw-r--r-- 1 jenkins jenkins 1.2 K ... build/demo-app.jar [timestamp ] ======================================== [timestamp ] 构建完成 [timestamp ] ======================================== Archiving artifacts Finished: SUCCESS
理解日志中的关键信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Started by user admin └── 谁触发了这次构建(admin 用户手动触发) Building in /var/lib/jenkins/workspace/demo-java-freestyle └── Jenkins 在哪个目录执行构建 > git checkout -f abc1234 └── Jenkins 从 Git 仓库拉取了哪个版本的代码Commit message: "build: 增加构建耗时统计" └── 拉取的是最新一次提交 Archiving artifacts └── 正在归档构建产物 Finished: SUCCESS └── 构建结果:成功 ✅
4.3 查看构建产物 1 2 3 4 5 6 7 8 9 10 11 构建成功后,你可以通过两种方式查看归档的产物: 方式一:在构建详情页 ├── 点击构建记录 ├── 页面中会显示 "Build Artifacts" 区域 ├── 点击 build/demo-app.jar 可以下载 └── URL 类似:http:// IP:8080 /job/ demo-java-freestyle/1/ artifact/build/ demo-app.jar 方式二:在项目主页 ├── 项目主页左侧有 "Last Successful Artifacts" └── 直接下载最近一次成功构建的产物
4.4 查看工作空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 在项目主页,点击左侧 [ Workspace ] 你会看到项目的文件结构: ├── .git/ — Git 仓库数据 ├── .gitignore ├── README.md ├── build/ — 构建产物目录 │ ├── classes/ — 编译后的 class 文件 │ ├── demo-app.jar — 打包的 JAR 文件 ├── scripts/ │ └── build.sh — 构建脚本 └── src/ ├── main/ java/ com/ demo/ │ └── App.java └── test/ java/ com/ demo/ └── AppTest.java
五、验证自动触发(Poll SCM) 5.1 触发一次新的代码提交 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 cd ~/demo-java-appcat >> src/main/java/com/demo/App.java << 'EOF' public static double divide(int a, int b) { if (b == 0) { throw new ArithmeticException("Division by zero!" ); } return (double) a / b; } EOFcat > src/main/java/com/demo/App.java << 'EOF' package com.demo; public class App { public static void main(String[] args) { System.out.println("===================================" ); System.out.println(" Demo Java Application v1.0" ); System.out.println("===================================" ); System.out.println("Application started successfully!" ); } public static int add(int a, int b) { return a + b; } public static int subtract(int a, int b) { return a - b; } public static int multiply(int a, int b) { return a * b; } public static double divide(int a, int b) { if (b == 0) { throw new ArithmeticException("Division by zero!" ); } return (double) a / b; } } EOFcat > src/test/java/com/demo/AppTest.java << 'EOF' package com.demo; public class AppTest { public static void main(String[] args) { System.out.println("Running tests..." ); // 测试 add 方法 int result1 = App.add(2, 3); assert result1 == 5 : "add(2,3) should be 5, but got " + result1; System.out.println("✅ Test add(2,3) = 5: PASSED" ); // 测试 subtract 方法 int result2 = App.subtract(10, 4); assert result2 == 6 : "subtract(10,4) should be 6, but got " + result2; System.out.println("✅ Test subtract(10,4) = 6: PASSED" ); // 测试 multiply 方法 int result3 = App.multiply(3, 4); assert result3 == 12 : "multiply(3,4) should be 12, but got " + result3; System.out.println("✅ Test multiply(3,4) = 12: PASSED" ); // 测试 divide 方法 double result4 = App.divide(10, 3); assert Math.abs(result4 - 3.333333) < 0.001 : "divide(10,3) ≈ 3.333" ; System.out.println("✅ Test divide(10,3) ≈ 3.333: PASSED" ); // 测试边界情况 int result5 = App.add(0, 0); assert result5 == 0 : "add(0,0) should be 0, but got " + result5; System.out.println("✅ Test add(0,0) = 0: PASSED" ); System.out.println("" ); System.out.println("All tests passed! ✅" ); } } EOF git add -A git commit -m "feat: 新增 divide 除法方法及测试"
5.2 等待自动触发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 我们配置了 Poll SCM 每 2 分钟检查一次。 操作步骤:1 . 提交代码后,回到 Jenkins 的项目主页2 . 等待最多 2 分钟(Poll SCM 的间隔时间)3 . 观察 Build History 区域 ├── 你会看到一个新的构建 ├── 这次触发原因不再是 "Started by user admin" └── 而是 "Started by an SCM change" 4 . 点击构建 ├── 你会看到 Jenkins 检测到了新的 Git 提交 ├── Commit message: "feat: 新增 divide 除法方法及测试" └── 测试输出中包含了 divide 方法的测试结果
5.3 理解 Poll SCM 的工作原理 1 2 3 4 5 6 7 8 9 10 11 12 13 Poll SCM 的工作机制:Jenkins 每隔 2 分钟执行一次:1 . git ls-remote <仓库地址> ← 只检查远程仓库的最新提交 SHA 2 . 和上次记录的 SHA 对比3 . 如果相同 → 不做任何事,继续等待4 . 如果不同 → 触发一次完整构建 注意: ├── Poll SCM 不会拉取完整代码来对比 ├── 只是比较提交 SHA,非常轻量 ├── 即使仓库有变化,也要等到下一个轮询周期才会触发 └── 所以最快也是"轮询间隔" 后才触发(这里是 2 分钟)
六、构建触发器进阶 6.1 远程触发构建 Step 1:配置远程触发
1 2 3 4 5 6 7 8 9 10 1. 进入项目配置页面(点击左侧 [ Configure ])2. 找到 Build Triggers 区域3. 勾选 "Trigger builds remotely (e.g., from scripts)"4. 在 Authentication Token 输入框中填写: my-secret-token-20255. 点击 [ Save ]
Step 2:通过命令触发构建
1 2 3 4 5 6 7 8 9 10 11 curl -X POST "http://localhost:8080/job/demo-java-freestyle/build?token=my-secret-token-2025" curl -X POST \ "http://localhost:8080/job/demo-java-freestyle/build?token=my-secret-token-2025" \ --user admin:你的API-Token
Step 3:验证
1 2 3 4 5 执行 curl 命令后,回到 Jenkins 项目主页, 你会看到一个新的构建被触发了。 查看控制台输出,触发原因显示: Started by remote host (触发器:远程触发)
6.2 定时构建 1 2 3 4 5 6 7 8 9 10 11 12 操作步骤:1. 进入项目配置页面2. Build Triggers 区域,勾选 "Build periodically"3. 填写 CRON 表达式: H 10 * * 1-5 含义:工作日(周一到周五)每天上午 10:00-10:59 之间的某个时刻构建4. 点击 [ Save ]
常用 CRON 速查表:
1 2 3 4 5 6 7 8 9 10 11 12 ┌──────────────────────────────┬────────────┬─────────────────────┐ │ 场景 │ CRON 表达式 │ 说明 │ ├──────────────────────────────┼────────────┼─────────────────────┤ │ 每天早上 8:30 │ 30 8 * * * │ 固定时间 │ │ 每天晚上 23:00 │ 0 23 * * * │ 夜间构建 │ │ 工作日每天 9:00 │ 0 9 * * 1-5│ 周一到周五 │ │ 每周一早上 9:00 │ 0 9 * * 1 │ 每周一次 │ │ 每小时一次 │ H * * * * │ 使用 H 分散压力 │ │ 每 15 分钟 │ H/15 * * * *│ 频繁轮询 │ │ 每天凌晨 2:00 │ 0 2 * * * │ 深夜低峰 │ │ 每月 1 号 9:00 │ 0 9 1 * * │ 月度构建 │ └──────────────────────────────┴────────────┴─────────────────────┘
6.3 触发其他项目构建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 场景:项目 A 构建成功后,自动触发项目 B 的构建 操作步骤:1 . 创建第二个项目(比如 demo-java-deploy) 2 . 在第一个项目 demo-java-freestyle 的配置中: Post-build Actions → Add post-build action → Build other projects Projects to build: demo-java-deploy Trigger only if build is stable: ☑3 . 保存后,当 demo-java-freestyle 构建成功, demo-java-deploy 会自动被触发 这种方式可以实现简单的"管道" : demo-java-freestyle ──构建成功──→ demo-java-deploy ──构建成功──→ ...
七、构建状态与构建历史 7.1 三种构建状态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Jenkins 构建状态: 🟢 SUCCESS(成功) ├── 所有构建步骤都执行成功 ├── 返回码为 0 ├── 在 UI 上显示为蓝色圆点(Blue Ocean 风格是绿色) └── 含义:代码可以正常编译、测试通过、打包成功 🟡 UNSTABLE(不稳定) ├── 构建过程本身没有出错 ├── 但某些检查不通过(比如测试失败、代码质量不达标) ├── 在 UI 上显示为黄色圆点 └── 含义:需要关注,但不是致命问题 🔴 FAILURE(失败) ├── 构建步骤执行失败 ├── 某个命令返回非 0 状态码 ├── 在 UI 上显示为红色圆点 └── 含义:必须修复,代码有问题 ⚫ ABORTED(中止) ├── 构建被用户手动取消 ├── 或被其他构建替换 ├── 在 UI 上显示为灰色圆点 └── 含义:不是因为代码问题,是被中断了
7.2 构建历史页面详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 项目主页的 Build History 区域: ┌────────────────────────────────────┐ │ Build History │ │ │ │ 🔵 │ 🔵 │ 🔵 │ │ │ [Enable auto refresh] │ ← 开启自动刷新 │ │ └────────────────────────────────────┘ 点击任意一个构建记录,进入构建详情页: ┌────────────────────────────────────┐ │ demo-java-freestyle │ │ │ Status: ✅ SUCCESS │ │ Duration: 5 sec │ │ Started: Mon Jul 14 14:30:00 │ │ Changes: 1 change │ │ │ │ Console Output ← 查看日志│ │ Edit Build Information │ │ Delete Build │ Rebuild Now │ ← 用相同参数重新构建 │ │ │ Build Artifacts: │ │ 📦 build/demo-app.jar │ ← 下载产物 │ │ └────────────────────────────────────┘
7.3 查看代码变更 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 在构建详情页,点击 "Changes" ,可以看到: ┌────────────────────────────────────┐ │ Changes │ │ │ │ Summary of changes: │ │ │ │ 📝 d4e5f6a │ │ build: 增加构建耗时统计 │ │ Author: Your Name │ │ │ │ (只显示上次构建之后的新提交) │ └────────────────────────────────────┘ 这个功能的价值: ├── 快速定位是哪次代码提交导致了构建失败 ├── 方便 Code Review └── 追溯变更历史
7.4 构建详情页的其他功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 构建详情页左侧菜单: Console Output(控制台输出) ├── 查看完整的构建日志 ├── 实时显示(构建中时也能看到) └── 这是排查问题最重要的工具 Edit Build Information(编辑构建信息) ├── 给构建添加描述 └── 标记构建状态(Good/Bad) Rebuild Now(重新构建) ├── 用相同的参数重新触发一次构建 └── 适合修复后重新验证 Delete Build(删除构建) └── 删除这个构建记录 Back to Project(返回项目) └── 回到项目主页
八、工作空间管理 8.1 理解工作空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 工作空间(Workspace)是 Jenkins 执行构建的目录: 路径格式: /var/lib/jenkins/workspace/<项目名>/ 对于我们的项目: /var/lib/jenkins/workspace/demo-java-freestyle/ 工作空间中包含: ├── Jenkins 从 Git 拉取的所有源代码 ├── 构建过程中生成的中间文件 ├── 最终的构建产物 └── 任何构建步骤产生的文件 重要概念: ├── 每个项目有独立的工作空间 ├── 默认情况下,工作空间在构建之间保留 ├── 可以配置"构建前清理工作空间" (我们已经勾选了) └── 工作空间的路径可以通过 ${WORKSPACE} 环境变量获取
8.2 在虚拟机中直接查看工作空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ls -la /var/lib/jenkins/workspace/demo-java-freestyle/ls -lh /var/lib/jenkins/workspace/demo-java-freestyle/build/ls -la /var/lib/jenkins/jobs/demo-java-freestyle/ ├── builds/ — 构建记录 │ ├── 1/ — 第 1 次构建 │ │ ├── build.xml — 构建元数据 │ │ ├── log — 构建日志 │ │ └── archive/ — 归档产物 │ ├── 2/ │ └── ... ├── config.xml — 项目配置文件(XML 格式) ├── nextBuildNumber — 下一次构建号 └── lastSuccessful — 最后一次成功构建的符号链接
8.3 清理工作空间 1 2 3 4 5 6 7 8 9 rm -rf /var/lib/jenkins/workspace/demo-java-freestyle/ java -jar jenkins-cli.jar -s http://localhost:8080/ \ clear-queue demo-java-freestyle
九、进阶练习 9.1 练习 1:模拟构建失败 1 2 3 4 5 6 7 8 9 10 11 12 cd ~/demo-java-app sed -i '/echo "✅ 编译成功"/a\ \ echo "测试构建失败..."\ exit 1 # 故意返回非 0 状态码' scripts/build.sh git add -A git commit -m "test: 故意引入构建失败"
等待 Poll SCM 触发,观察构建状态变为 红色(FAILURE) 。
1 2 3 4 5 cd ~/demo-java-app git revert HEAD --no-edit
观察要点:
1 2 3 4 5 6 构建失败时你会看到: ├── Build History 中的圆点变红 ├── 控制台输出中显示错误信息 ├── 最后一行显示 "Finished: FAILURE" ├── 如果配置了邮件通知,会收到失败邮件 └── Jenkins 会标记是哪一步失败的
9.2 练习 2:查看构建趋势 1 2 3 4 5 6 7 8 在项目主页,你会看到一个 Build History 的趋势图(如果有多个构建): ├── 蓝色/绿色 = SUCCESS ├── 黄色 = UNSTABLE ├── 红色 = FAILURE └── 灰色 = ABORTED 这个趋势图让你一眼看出项目的健康状况。
9.3 练习 3:配置项目视图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 当 Jenkins 中有多个项目时,可以用视图来组织: 1. 在 Dashboard 页面,点击左侧 [ + ] 按钮(在 My Views 旁边) 2. 选择 "New View" 3. 输入视图名称:Java Projects 4. 选择视图类型:List View 5. 配置过滤条件: ├── 勾选要显示的项目 └── 或使用正则表达式过滤:java.* 6. 点击 [ OK ] 现在你有了一个只显示 Java 相关项目的视图。
十、Day 3-4 自检清单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 环境准备 ├── [/] Git 已安装并配置了用户信息 ├── [/] Java 已安装(java -version 和 javac -version) ├── [/] 成功创建了 demo-java-app 本地 Git 仓库 ├── [/] Git 仓库中有至少 4 个提交记录 └── [/] build.sh 在本地能正常执行 Freestyle 项目配置 ├── [/] 成功创建了 demo-java-freestyle 项目 ├── [/] 正确配置了 Git 源码管理(仓库路径和分支名) ├── [/] 正确配置了构建触发器(至少配置了 Poll SCM) ├── [/] 正确配置了构建步骤(Execute shell) ├── [/] 正确配置了构建后操作(Archive artifacts) └── [/] 项目配置已保存 构建执行 ├── [/] 手动触发了第一次构建(Build Now) ├── [/] 构建状态为 SUCCESS(蓝色) ├── [/] 能查看并理解控制台输出 ├── [/] 构建产物已正确归档(build/*.jar) ├── [/] 能从 Jenkins 下载归档的产物 └── [/] 能在虚拟机中查看工作空间目录 自动触发 ├── [/] 配置了 Poll SCM(H/2 * * * *) ├── [/] 提交新代码后,Jenkins 自动触发了构建 ├── [/] 理解 Poll SCM 的工作原理 ├── [/] 能区分手动触发和自动触发的构建日志 └── [/] 配置了远程触发(Trigger remotely)并测试成功 概念理解 ├── [/] 理解四种构建状态(SUCCESS/UNSTABLE/FAILURE/ABORTED) ├── [/] 理解工作空间(Workspace)的概念 ├── [/] 理解构建触发器的类型和适用场景 ├── [/] 能读懂 CRON 表达式 ├── [/] 知道如何查看代码变更和构建趋势 └── [/] 知道 Jenkins 数据目录的结构
十一、常见问题排查(Q/A) Q1:Jenkins 拉取本地 Git 仓库报错 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 错误信息: stderr: fatal: '/home/xxx/demo-java-app' does not appear to be a git repository 原因:Jenkins 运行用户(jenkins)没有权限访问你的 home 目录 解决方案:sudo chmod -R 755 /home/你的用户名/sudo chmod -R 755 /home/你的用户名/demo-java-app/sudo cp -r ~/demo-java-app /var/lib/jenkins/sudo chown -R jenkins:jenkins /var/lib/jenkins/demo-java-app/sudo usermod -aG 你的用户组 jenkinssudo systemctl restart jenkins
Q2:构建时提示 javac: command not found 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 原因:Jenkins 的环境变量中没有包含 Java 的路径 解决方案: which javacexport JAVA_HOME =/usr/lib/jvm/java-17-openjdk-amd64export PATH =$JAVA_HOME /bin:$PATH
Q3:Poll SCM 不触发构建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 排查步骤: 1. 确认 Poll SCM 已正确配置 └── 项目配置 → Build Triggers → Poll SCM → CRON 表达式 2. 确认确实有新的 Git 提交 cd ~/demo-java-app git log --oneline └── 确认最新提交是在配置 Poll SCM 之后的 3. 查看轮询日志 └── 项目主页 → 左下角 "Polling Log" └── 这里会显示每次轮询的结果 4. 检查 Jenkins 系统日志 sudo journalctl -u jenkins -f └── 看有没有错误信息 5. 手动触发轮询 └── 项目主页 → 左侧 [ Poll Now ] └── 不用等 CRON 周期,立即检查一次 6. 确认 Git 路径正确 └── Jenkins 可能找不到 git 命令 └── Manage Jenkins → Tools → Git installations └── 确认 Git 路径配置正确(通常是 /usr/bin/git)
Q4:构建产物归档失败 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 错误信息:ERROR: No artifacts found that match the file pattern "build/*.jar" 原因: ├── 构建脚本没有生成 JAR 文件 ├── JAR 文件路径不对 └── 构建失败了,没有产物 排查: 1. 查看控制台输出,确认构建步骤是否成功 2. 在 Workspace 中确认文件是否存在 3. 确认归档路径是否正确(相对于工作空间根目录) # 在虚拟机中检查 ls -la /var/lib/jenkins/workspace/demo-java-freestyle/build/
Q5:构建触发了但没有代码变更 1 2 3 4 5 6 7 8 9 10 11 原因:Poll SCM 和 Build periodically 搞混了 ├── Poll SCM:检查到代码变化才构建 ├── Build periodically:时间到了就构建(不管有没有变化) 如果你勾选了 Build periodically, 即使代码没有变化,也会定期触发构建。 解决: ├── 只勾选 Poll SCM(推荐) └── 或者两个都勾选,但理解它们的区别
十二、下一步预告 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Day 5 -6 学习内容预告: ├── 安装和管理 Jenkins 插件 │ ├── 搜索和安装插件 │ ├── 更新和卸载插件 │ └── 常用插件推荐清单 │ ├── 配置全局工具 │ ├── JDK 配置(多版本管理) │ ├── Maven 配置 │ ├── Node.js 配置 │ └── Git 配置 │ ├── 配置凭据管理 │ ├── 用户名密码凭据 │ ├── SSH 密钥凭据 │ ├── Secret Text 凭据 │ └── 凭据作用域 │ └── 构建后操作进阶 ├── 邮件通知配置(SMTP 设置) ├── 发送 HTML 格式的构建报告 └── 构建触发其他项目
注意:如果出现问题,直接查看【jenkins DAY 3-4 错误总结与解决方案】