Julia 教程 / CI/CD 与工程最佳实践
CI/CD 与工程最佳实践
现代软件开发离不开自动化。本文介绍如何为 Julia 项目配置 CI/CD 流水线,包括自动测试、代码质量检查、文档发布和版本管理。
1. GitHub Actions 配置 Julia CI
1.1 基本 CI 配置
# .github/workflows/CI.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
- '1.10'
- '1.11'
- 'nightly'
os:
- ubuntu-latest
- macOS-latest
- windows-latest
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
if: matrix.version == '1.11' && matrix.os == 'ubuntu-latest'
- uses: codecov/codecov-action@v4
if: matrix.version == '1.11' && matrix.os == 'ubuntu-latest'
with:
files: lcov.info
1.2 配置解读
| 步骤 | 作用 |
|---|---|
checkout | 拉取代码 |
setup-julia | 安装指定版本 Julia |
cache | 缓存包和编译产物 |
julia-buildpkg | 构建包 |
julia-runtest | 运行测试 |
julia-processcoverage | 处理覆盖率数据 |
codecov-action | 上传到 Codecov |
2. 多版本矩阵测试
2.1 矩阵策略
strategy:
fail-fast: false
matrix:
version: ['1.9', '1.10', '1.11']
os: [ubuntu-latest, windows-latest]
include:
- version: 'nightly'
os: ubuntu-latest
exclude:
- version: '1.9'
os: windows-latest
2.2 允许失败配置
jobs:
test:
continue-on-error: ${{ matrix.version == 'nightly' }}
strategy:
matrix:
version: ['1.10', '1.11', 'nightly']
3. 代码覆盖率
3.1 Codecov 集成
# 在 CI 中添加覆盖率步骤
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: lcov.info
fail_ci_if_error: false
3.2 本地查看覆盖率
using Pkg
Pkg.test("MyPkg"; coverage=true)
# 生成覆盖率报告
using Coverage
coverage = process_folder("src")
LCOV.writefile("lcov.info", coverage)
3.3 覆盖率徽章
# 在 README.md 中添加
[](https://codecov.io/gh/user/MyPkg.jl)
4. 文档自动发布
4.1 GitHub Pages 配置
# .github/workflows/Documentation.yml
name: Documentation
on:
push:
branches: [main]
tags: ['*']
pull_request:
branches: [main]
jobs:
docs:
name: Documentation
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
with:
version: '1.11'
- uses: julia-actions/cache@v2
- name: Install dependencies
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
- name: Build and deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: julia --project=docs/ docs/make.jl
4.2 Documenter.jl 配置
# docs/make.jl
using Documenter
using MyPkg
makedocs(;
modules=[MyPkg],
sitename="MyPkg.jl",
format=Documenter.HTML(;
prettyurls=get(ENV, "CI", nothing) == "true",
canonical="https://user.github.io/MyPkg.jl",
),
pages=[
"首页" => "index.md",
"教程" => [
"快速入门" => "tutorial.md",
"高级用法" => "advanced.md",
],
"API 参考" => "api.md",
],
)
deploydocs(;
repo="github.com/user/MyPkg.jl",
devbranch="main",
)
5. JuliaFormatter — 代码格式化
5.1 安装与使用
using Pkg
Pkg.add("JuliaFormatter")
using JuliaFormatter
# 格式化单个文件
format("src/MyPkg.jl")
# 格式化整个目录
format("src/")
format("test/")
# 使用 .JuliaFormatter.toml 配置
5.2 配置文件
# .JuliaFormatter.toml
style = "default"
indent = 4
margin = 92
always_for_in = true
whitespace_typedefs = true
whitespace_ops_in_indices = true
remove_extra_newlines = true
short_to_long_function_def = 0
always_use_return = false
whitespace_in_kwargs = true
5.3 CI 中检查格式
# .github/workflows/Format.yml
name: Format
on:
pull_request:
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
with:
version: '1.11'
- run: julia -e 'using Pkg; Pkg.add("JuliaFormatter")'
- run: julia -e 'using JuliaFormatter; format(".", verbose=true)'
- name: Check formatting
run: |
if [ -n "$(git diff)" ]; then
echo "Code is not formatted. Please run JuliaFormatter."
git diff
exit 1
fi
6. Linting — JET.jl 静态分析
6.1 JET.jl 使用
using Pkg
Pkg.add("JET")
using JET
# 分析整个包
report_package("MyPkg")
# 分析特定文件
report_file("src/core.jl")
# 分析函数调用
report_call(my_function, (Int, Float64))
6.2 常见检查结果
| 检查类型 | 说明 | 示例 |
|---|---|---|
MethodError | 方法不存在 | 调用未定义的函数 |
UndefVarError | 未定义变量 | 拼写错误 |
TypeError | 类型错误 | 传递错误类型的参数 |
NoMethodError | 无匹配方法 | 类型不匹配的调用 |
6.3 CI 集成
- name: JET static analysis
run: |
julia -e '
using Pkg
Pkg.add("JET")
using JET
report_package("MyPkg"; target_defined_modules=true)
'
7. Docker 构建 Julia 镜像
7.1 基本 Dockerfile
FROM julia:1.11-slim
WORKDIR /app
# 先复制依赖文件(利用 Docker 缓存层)
COPY Project.toml Manifest.toml ./
RUN julia -e 'using Pkg; Pkg.activate("."); Pkg.instantiate(); Pkg.precompile()'
# 复制应用代码
COPY . .
# 构建
RUN julia -e 'using Pkg; Pkg.activate("."); Pkg.build()'
CMD ["julia", "--project=.", "main.jl"]
7.2 多阶段构建
# 阶段一:构建
FROM julia:1.11 AS builder
WORKDIR /app
COPY Project.toml Manifest.toml ./
RUN julia -e 'using Pkg; Pkg.activate("."); Pkg.instantiate(); Pkg.precompile()'
COPY . .
RUN julia -e 'using PackageCompiler; create_sysimage(["MyApp"]; sysimage_path="sys.so", project=".")'
# 阶段二:运行
FROM debian:bookworm-slim
COPY --from=builder /app /app
COPY --from=builder /usr/local/julia /usr/local/julia
ENV PATH="/usr/local/julia/bin:${PATH}"
WORKDIR /app
CMD ["julia", "--project=.", "--sysimage=sys.so", "main.jl"]
7.3 优化技巧
| 技巧 | 说明 |
|---|---|
| 利用缓存层 | 先复制 Project.toml,再复制源码 |
| 多阶段构建 | 减少最终镜像大小 |
| 系统镜像 | PackageCompiler 创建预编译镜像 |
| slim 基础镜像 | julia:1.11-slim 比完整镜像小很多 |
8. 版本发布自动化
8.1 TagBot
# .github/workflows/TagBot.yml
name: TagBot
on:
issue_comment:
types: [created]
workflow_dispatch:
inputs:
lookback:
default: 3
permissions:
actions: read
contents: write
issues: read
pull-requests: read
jobs:
TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
runs-on: ubuntu-latest
steps:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
8.2 发布流程
# 1. 更新版本号
# 编辑 Project.toml: version = "1.2.0"
# 2. 提交
git add -A
git commit -m "Release v1.2.0"
# 3. 注册(触发自动打标签)
# 在 GitHub issue 中 @JuliaRegistrator register
# 4. TagBot 自动创建 GitHub Release
9. 项目结构最佳实践
9.1 推荐结构
MyPkg/
├── .github/
│ └── workflows/
│ ├── CI.yml
│ ├── Documentation.yml
│ ├── FormatCheck.yml
│ └── TagBot.yml
├── .JuliaFormatter.toml
├── .gitignore
├── Project.toml
├── README.md
├── LICENSE
├── src/
│ ├── MyPkg.jl
│ ├── core.jl
│ ├── utils.jl
│ └── types.jl
├── test/
│ ├── runtests.jl
│ ├── test_core.jl
│ └── test_utils.jl
└── docs/
├── make.jl
├── Project.toml
└── src/
├── index.md
└── api.md
9.2 .gitignore
# Julia
Manifest.toml
*.jl.cov
*.jl.*.cov
*.jl.mem
# 编译产物
*.so
*.dylib
*.dll
# 系统
.DS_Store
*.swp
10. 代码审查清单
10.1 PR 提交前检查
## 功能
- [ ] 功能按设计工作
- [ ] 边界情况已处理
- [ ] 异常情况有合理错误信息
## 代码质量
- [ ] 代码已格式化 (JuliaFormatter)
- [ ] JET.jl 无严重警告
- [ ] 无不必要的全局变量
- [ ] 类型标注(提升性能)
## 测试
- [ ] 新功能有测试覆盖
- [ ] 边界值已测试
- [ ] 所有测试通过
## 文档
- [ ] 公共 API 有文档字符串
- [ ] README 已更新
- [ ] CHANGELOG 已更新
## 版本
- [ ] 版本号已递增(如需要)
- [ ] [compat] 已更新(如有新依赖)
10.2 自动化检查脚本
# scripts/check.jl
using Pkg
println("=== 格式化检查 ===")
using JuliaFormatter
is_formatted = format("src/", verbose=true, check=true)
println(is_formatted ? "✅ 格式正确" : "❌ 需要格式化")
println("\n=== 静态分析 ===")
using JET
report = report_package("MyPkg")
n_issues = length(report)
println(n_issues == 0 ? "✅ 无问题" : "⚠️ 发现 $n_issues 个问题")
println("\n=== 测试 ===")
Pkg.test("MyPkg")
println("✅ 测试通过")
进阶:完整 CI/CD 流水线
# .github/workflows/Release.yml
name: Release
on:
push:
tags: ['v*']
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
- uses: julia-actions/julia-runtest@v1
release:
needs: test
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Create Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
docs:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
- run: julia --project=docs/ docs/make.jl
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
业务场景
场景一:开源库维护
配置完整的 CI/CD 流水线:PR 触发测试+格式检查+静态分析,合并到 main 触发文档更新,注册触发自动打标签和发布。
场景二:团队协作
强制 CI 通过才能合并 PR。代码审查清单确保质量。JuliaFormatter 统一代码风格,减少格式争论。
场景三:生产部署
使用 Docker 多阶段构建减小镜像体积。PackageCompiler 创建系统镜像加速启动。GitHub Actions 自动构建和推送镜像。
总结
| 主题 | 关键要点 |
|---|---|
| CI | GitHub Actions + julia-actions |
| 多版本测试 | matrix 策略,覆盖多个 Julia 版本 |
| 覆盖率 | Codecov 集成 |
| 文档 | Documenter.jl + GitHub Pages |
| 格式化 | JuliaFormatter + CI 检查 |
| 静态分析 | JET.jl |
| Docker | 多阶段构建,利用缓存层 |
| 发布 | TagBot 自动化 |