强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 中添加
[![Codecov](https://codecov.io/gh/user/MyPkg.jl/branch/main/graph/badge.svg)](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 自动构建和推送镜像。


总结

主题关键要点
CIGitHub Actions + julia-actions
多版本测试matrix 策略,覆盖多个 Julia 版本
覆盖率Codecov 集成
文档Documenter.jl + GitHub Pages
格式化JuliaFormatter + CI 检查
静态分析JET.jl
Docker多阶段构建,利用缓存层
发布TagBot 自动化

扩展阅读