Apache Arrow Crossbow 打包与集成测试系统:架构原理与 arc<hour> 实战指南
2026/9/14 5:48:33 网站建设 项目流程

Apache Arrow Crossbow 打包与集成测试系统:架构原理与 arc 实战指南

【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow

Apache Arrow 的 Crossbow 是一套基于 Git 分支作为任务队列的持续集成调度系统,它把"打包"(conda 包、Python Wheel、Linux 发行版包、Homebrew Formula 等)和"集成测试"(Pandas、Dask、Spark、HDFS、Turbodbc 等)自动化为可复现、可追踪的构建任务。本文以仓库文档 crossbow.rst 为骨架,结合dev/tasksdev/archery下的真实实现,完整讲解 Crossbow 的安装、提交、状态查询、产物下载与报告机制,并深入剖析其"Git 分支即任务队列"的调度原理,帮助你像 Arrow 维护者一样按需触发任意平台的构建与测试。

Crossbow 是什么:dev/tasks目录的使命

Crossbow 的出发点位于arrow/dev/tasks目录,它承载了 Apache Arrow 打包与集成测试的全套自动化。按功能划分,它主要覆盖两大类别:

打包(Packages)

  • C++ 与 Python 的 conda-forge 包:覆盖 Linux、macOS、Windows;
  • Python Wheel:覆盖 Linux(manylinux / musllinux)、macOS、Windows;
  • C++ 与 GLib 的 Linux 发行版包:面向多个发行版(apt、yum 体系);
  • Java(面向 Gandiva)相关构建。

集成测试(Integration tests)

  • 各类 Docker 容器测试(C++、Python、R、Ruby、GLib 等镜像);
  • Pandas、Dask、Turbodbc、HDFS、Spark 的兼容性测试。

这些任务并不是"一次性脚本",而是以结构化配置存在:任务定义集中在 dev/tasks/tasks.yml,CI 工作流模板分散在dev/tasks下的子目录(如 python-wheels、docker-tests、verify-rc、r 等),真正的调度逻辑则由archery crossbow子命令实现,代码位于 dev/archery/archery/crossbow/。

架构总览:Executor、Queue 与 Scheduler

Crossbow 的架构可以拆成三个角色:

Executors:公网 CI 执行器

单个构建任务最终在公共 CI 服务上执行,当前仓库所依赖的执行器组合为:

  • Linux:GitHub Actions、Travis CI、Azure Pipelines;
  • macOS:GitHub Actions、Azure Pipelines;
  • Windows:GitHub Actions、Azure Pipelines。

从 tasks.yml 的实际任务看,绝大多数任务标注为ci: github,即默认走 GitHub Actions;Travis、CircleCI、Azure 的配置模板(.travis.yml.circleci/config.yml)在 core.py 中仍作为"跳过分支"的默认骨架保留,用于让不参与某次构建的 CI 服务自动跳过对应分支。

Queue:作为任务队列的 Git 仓库

由于 CI 服务按"分支推送"触发,Crossbow 把调度抽象成一个额外的 Git 仓库——队列仓库(Queue Repository),任何人都可以托管一个队列仓库(惯例命名为<ghuser>/crossbow)。

在 Crossbow 中,"一个任务"本质上就是队列仓库里的一个 Git 提交:它位于某个特定分支上,分支内包含触发对应构建所需的配置文件(如.travis.ymlazure-pipelines.yml,或 GitHub Actions 用的crossbow.yml)。CI 服务监听到队列仓库出现新的匹配分支,就会拉取并执行其中的构建。

Scheduler:archery crossbow调度器

调度器负责三件事:版本生成任务渲染任务提交。版本号基于setuptools_scm从当前 Git 提交推导;任务定义与任务组来自tasks.yml;渲染通过 Jinja2 模板完成。在 core.py 中可以看到,渲染时使用jinja2.Environment加载模板并以StrictUndefined模式执行——这意味着模板中任何未定义的参数都会直接报错,从而保证"渲染即校验"。

安装 Crossbow:从零到archery crossbow --help

以下步骤以 GitHub 为例(理论上任意 Git 服务器均可)。如果你使用官方的ursacomputing/crossbow队列仓库,可直接跳到第 3 步;否则需要先完成前两步。

1. 创建队列仓库

新建一个 GitHub 仓库作为你的任务队列,例如crossbow。参考仓库创建流程即可(GitHub 的仓库创建向导)。

2. 为队列仓库启用 Azure Pipelines 集成

在 Azure Pipelines 中为新建的队列仓库启用构建集成,这样队列仓库上的分支推送事件才能触发 Azure 上的构建。

3. 克隆 Crossbow 仓库

将官方ursacomputing/crossbow(或你刚创建的仓库)克隆到 arrow 仓库旁边:

git clone https://github.com/<user>/crossbow crossbow

默认情况下脚本会在arrow目录旁寻找名为crossbow的克隆,该路径也可以通过命令行参数(--queue-path)覆盖。从 cli.py 可以看到,队列路径默认值就是ArrowSources.find().path.parent / "crossbow",同时支持通过CROSSBOW_QUEUE_PATH环境变量指定。

重要提示:Crossbow 只支持基于 GitHub Token 的认证。虽然代码内部会把 SSH 协议的远程地址重写为 HTTPS(见 core.py 的_git_ssh_to_https),但文档仍建议直接使用 HTTPS 仓库地址,避免不必要的坑。

4. 创建 Personal Access Token

在 GitHub 上创建一个 Personal Access Token,只需要repoworkflow两个权限(其他权限不需要)。

5. 导出 Token

将 Token 导出为环境变量:

export GH_TOKEN=<token>

或者在使用 CLI 时通过--github-token参数传入。此外 cli.py 也支持CROSSBOW_GITHUB_TOKEN环境变量;三个来源的优先级顺序为--github-token>CROSSBOW_GITHUB_TOKEN>GH_TOKEN。如果最终未提供 Token,core.py 的push()会直接抛出RuntimeError提示缺少凭据。

6. 安装 Python

需要 Python 3.11 及以上版本。文档建议优先使用 Miniconda 管理环境(参见 conda 官方安装指南)。

7. 安装 archery(含 Crossbow 子命令)

pip install -e "arrow/dev/archery[crossbow]"

[crossbow]extra 会额外引入pygithubpygit2等 Crossbow 依赖(在 dev/archery/setup.py 中声明)。-e表示可编辑安装,便于跟随仓库开发迭代。

8. 验证安装

archery crossbow --help

看到命令帮助即说明安装成功。整个crossbow命令组还提供了子命令级帮助,如archery crossbow submit --help

核心用法:submit 提交构建任务

基本流程

archery crossbow submit的执行流程(与 cli.py 中submit的实现一一对应):

  1. 自动检测当前仓库:脚本检测当前 checkout 的 arrow 仓库及其 remote,因此天然支持 fork。例如在 kszucs 的 fork 下执行,构建的就是 kszucs/arrow 而非上游 apache/arrow:

    git clone https://github.com/kszucs/arrow git clone https://github.com/kszucs/crossbow cd arrow/dev/tasks archery crossbow submit --help # 查看可用选项 archery crossbow submit conda-win conda-linux conda-osx
  2. 读取 HEAD 并生成版本号:脚本取当前 checkout 分支的 HEAD 提交,基于setuptools_scm推导版本号。因此要构建某个特定分支,请先 checkout 再提交:

    git checkout ARROW-<ticket number> archery crossbow submit --dry-run conda-linux conda-osx

    注意:目标分支必须先推送到远程,因为脚本(以及后续 CI 上的克隆)会拉取该分支。

  3. 渲染构建配置:读取tasks.yml中对应的任务定义,用参数替换后渲染出 CI 配置(如crossbow.yml.travis.yml等)。

  4. 为每个任务创建分支:按任务创建以 job id 为前缀的分支,例如 Linux 上构建 conda recipes 会创建crossbow@build-<id>-conda-linux。从源码看,实际分支命名格式为<job.branch>-<task.ci>-<task_name>(core.py),即在任务分支末尾附加 CI 类型与任务名,便于 Travis/CircleCI 使用分支名做跳过匹配。

  5. 推送分支触发构建:将修改后的分支推送到 GitHub 触发 CI,认证使用上文安装步骤中的 GitHub OAuth Token。

常用参数一览

以下参数来自submit命令的实现(cli.py),可配合--help对照:

参数说明
tasks(位置参数)任务名列表,支持多个,如conda-win conda-linux
--group, -gtasks.yml中定义的任务组批量提交,可多次指定
--param, -p附加的任务参数,格式key=value,用于渲染 CI 模板
--job-prefix分支名的任意前缀,默认build(如 nightly 用--job-prefix nightly
--config-path, -c任务配置文件 YAML,默认dev/tasks/tasks.yml
--arrow-version, -v显式指定目标版本
--arrow-remote, -r显式指定要克隆的 GitHub remote(不本地校验),如https://github.com/apache/arrow
--arrow-branch, -b显式指定分支名,如ARROW-1949
--arrow-sha, -t显式指定提交 SHA 或 Tag,如f67a515apache-arrow-0.11.1
--fetch/--no-fetch是否从远程 fetch 引用,默认 fetch
--dry-run/--commit仅渲染展示,不提交(默认 dry-run 为 False,即会提交)
--no-push/--push是否推送变更,--no-push只在本地创建分支和提交

任务组机制大大简化了批量调度:例如--group conda只会挑选tasks.ymlconda组列出的任务。完整任务组定义见 dev/tasks/tasks.yml 顶部的groups:段,其中包括wheelhomebrewpackagingtestcppc-glibpythonrrubyvcpkgintegrationexamplefuzzverify-rc系列以及nightlynightly-testsnightly-packagingnightly-release等。

示例:多种提交方式

提交多个指定任务:

archery crossbow submit debian-stretch conda-linux-gcc-py37-r40 Repository: https://github.com/kszucs/arrow@tasks Commit SHA: 810a718836bb3a8cefc053055600bdcc440e6702 Version: 0.9.1.dev48+g810a7188.d20180414 Pushed branches: - debian-stretch - conda-linux-gcc-py37-r40

仅渲染不提交(dry-run):

archery crossbow submit --dry-run task_name

只跑 conda 打包任务加一个 C++ 测试任务:

archery crossbow submit --group conda test-ubuntu-24.04-cpp

跑全部 Wheel 构建:

archery crossbow submit --group wheel

此外tasks.yml中还有dockerintegrationcpp-python等多个任务组,用于运行基于 Docker 的测试矩阵。

任务配置结构速览

tasks.yml中,每个任务由"任务名 → 定义"组成,其基本结构为:

tasks: # 任意任务名: # template: 指向 jinja2 模板的路径 # params: 可选的额外参数(如镜像名、Python 版本、架构、环境变量) # artifacts: 正则模式列表,每个模式需匹配单个 GitHub release 资产, # 版本变量会替换进模式,例如: # - pyarrow-{no_rc_version}-py38(h[a-z0-9]+)_0-linux-64.tar.bz2

以真实任务为例(tasks.yml 中的 manylinux wheel):

wheel-manylinux-2-28-cp311-cp311-amd64: ci: github template: python-wheels/github.linux.yml params: arch: "amd64" linux_wheel_kind: "manylinux" linux_wheel_version: "2-28" python_abi_tag: "cp311" python_version: "3.11" wheel_platform_tag: "manylinux_2_28_x86_64" artifacts: - pyarrow-{no_rc_version}-cp311-cp311-manylinux_2_28_x86_64.whl

大量任务通过 Jinja2 的 for 循环批量生成,例如 Python 3.11–3.15 的 Wheel 矩阵、Ubuntu/Debian/Fedora 的 C++ 与 Python 容器测试、Pandas/Dask/Spark/HDFS 集成测试等,维护成本被压缩到极低。

查询构建状态:status 命令

submit会返回一个 build id(对应队列仓库中的一个分支),用它即可查询状态:

archery crossbow status <build id / branch name>

实现层面(cli.py),status会 fetch 队列仓库、按 job 名取出任务,再以ConsoleReport逐任务渲染状态。它支持以下选项:

  • --fetch/--no-fetch:查询前是否 fetch,默认 fetch;
  • --task-filter, -f:Glob 模式过滤关心的任务,可多次指定;
  • --validate/--no-validate:只要存在任一非成功任务就返回非零退出码,便于接入脚本做门禁判断。状态判断依据task.status().combined_state是否为errorfailure(cli.py)。

下载构建产物:artifacts 命令

构建完成后,产物会作为资产上传到队列仓库对应的 GitHub release,用以下命令下载:

archery crossbow artifacts <build id / branch name>

对应实现是download_artifacts(cli.py),行为要点:

  • 默认下载到<arrow 仓库>/packages/<job-name>,可用-t/--target-dir覆盖;
  • 支持--dry-run只展示过程不下载;
  • 下载前按资产大小与本地文件比对,已存在且大小一致的会跳过;
  • 下载失败自动重试(最多 5 次,每次间隔 60 秒);
  • 支持--task-filter过滤任务、--validate-patterns/--skip-pattern-validation控制资产名校验。

报告机制:邮件、聊天与 CSV

Crossbow 还内置了一套报告体系,把构建结果推送到合适的地方,便于夜间构建(nightly)无人值守时自动通知。

邮件报告:report

archery crossbow report <job-name> \ --send \ --sender-name "Arrow CI" --sender-email ci@example.com \ --recipient-email dev@example.com \ --smtp-user ci@example.com --smtp-password <pass> \ --smtp-server smtp.gmail.com --smtp-port 465

关键选项(cli.py):--send/--dry-run控制是否真正发送;--poll/--no-poll支持在任务未完成时轮询等待(--poll-max-minutes默认 180 分钟、--poll-interval-minutes默认 10 分钟);邮件主题由NightlyEmailReport生成,形如[NIGHTLY] Arrow Build Report for Job <branch>: N failed, M pending(cli.py)。

聊天报告:report_chat

archery crossbow report_chat <job-name> \ --send --webhook https://hooks.slack.com/... \ --extra-message-success "All green" \ --extra-message-failure "Please investigate"

把构建结果以文本形式发送到 Slack / Zulip 等 Webhook 地址;-s/-f两个参数分别指定成功、失败时追加的额外消息。

CSV 报告:report_csv

archery crossbow report_csv <job-name> --save

生成 CSV 格式的逐任务报告,便于导入表格工具或做历史分析。

进阶主题:夜间构建、发布验证与队列仓库维护

定时触发夜间构建

Crossbow 支持用 CI 的 cron 能力驱动周期性调度。dev/tasks/nightlies.sample.yml 给出了 Travis cron 的样板:把该文件以.travis.yml的名字放到 crossbow 仓库的某个分支上,配置该分支的每日 cron 任务,然后在script段调用:

if [ $TRAVIS_EVENT_TYPE = "cron" ]; then archery crossbow submit -g conda -g wheel -g linux else archery crossbow submit --dry-run -g conda -g wheel -g linux fi

即:cron 触发时真实提交打包任务,手工触发时仅 dry-run 演练。tasks.yml中也预置了nightlynightly-testsnightly-packagingnightly-release等组,配合--job-prefix nightly使用;latest_prefix命令(archery crossbow latest_prefix nightly)可快速取得某个前缀的最新 job。

发布候选验证:verify_release_candidate

Crossbow 深度参与 Apache Arrow 的发布流程。verify_release_candidate命令(cli.py)会自动创建(或查找)一个 PR,并通过在 PR 上添加@github-actions crossbow submit --group verify-rc-xxx --param release=<version> --param rc=<rc>形式的评论来触发验证任务:

archery crossbow verify_release_candidate \ --version 9.0.0 --rc 0 \ --verify-source --verify-binaries --verify-wheels \ --create-pr --head-branch release-9.0.0-rc0

verify-rc-sourceverify-rc-binariesverify-rc-wheels三组任务在 tasks.yml 中展开为覆盖 Linux(conda/almalinux/ubuntu 各发行版)、macOS(Intel/arm64)与 Windows 的验证矩阵,使用 verify-rc 目录下的模板执行。

队列仓库维护

队列仓库长期使用后分支会越积越多,Crossbow 提供清理命令:

archery crossbow delete_old_branches --dry-run --days 90 --maximum 1000

它删除超过指定天数(默认 90 天)的旧分支,每次最多删除--maximum(默认 1000)个,且跳过origin/pr/*引用——这是为了避免触发 GitHub 的引用保护限制(cli.py)。

Token 过期提醒

GitHub Token 有有效期,Crossbow 提供notify_token_expiration命令,在 Token 距过期不足指定天数(默认 30 天)时发送提醒邮件,主题形如[CI] Arrow Crossbow Token Expiration in <date>(cli.py)。

底层机制剖析:从提交到分支推送

理解 Crossbow 的调度本质,关键在于 core.py 中的几个核心类:

  • Repo:对本地 Git 仓库的高层封装,既用于读取 arrow 仓库的版本信息(headbranchremote_url),也用于向队列仓库推送任务(core.py)。它强制 HTTPS:如果 origin 是git@github.com形式会抛错,并要求提供 GitHub Token(CROSSBOW_GITHUB_TOKENGH_TOKEN)。
  • Queue(Repo):队列仓库专用类,负责 job 分支的自动编号(build-85这类自增 id,或build-41d017af40这类随机 hex id)、按名取 job、按模式枚举 job、查询某个前缀的最新 job(core.py)。job 元数据以job.yml形式存在分支提交里,Queue.get()读取并反序列化该文件。
  • Target:描述"构建哪个版本",可由当前仓库自动推导,也可通过--arrow-version/--arrow-remote/--arrow-branch/--arrow-sha覆盖,这正是发布流程需要精确锁定某个 RC 提交时的关键设计。
  • Job/Task:job 是一次提交的集合(一个 build id 对应一个 job),task 是其中某个具体任务;Job.from_config根据tasks.yml与命令行参数展开任务集,渲染模板后为每个任务生成独立分支并加入推送列表。

一次典型的提交链路为:Target.from_repo()确定目标 →Job.from_config()加载并展开任务 →queue.put(job)在本地队列仓库创建各任务分支与提交 →queue.push()用 OAuth Token 推送到远程 → 各 CI 服务按分支名匹配并执行 → 结果回传到队列仓库 →status/artifacts/report*命令消费结果。分支创建时以默认分支(main)的提交为父提交(core.py),这样能复用 GitHub Actions 的缓存。

小结

Crossbow 用一套极简而优雅的思路解决了多语言、多平台、多任务类型下的打包与集成测试调度问题:Git 分支即任务队列,CI 服务天然按分支触发;tasks.yml+ Jinja2 模板把成百上千的构建矩阵压缩为可维护的声明式配置;archery crossbow系列命令则覆盖了从提交、查询、下载到报告、清理、发布验证的完整生命周期。无论你是想复现 Arrow 的某次构建、为 fork 跑自己的打包任务,还是在自己的项目里借鉴这种"以 Git 分支做 CI 队列"的调度模式,crossbow.rst 连同 dev/tasks/tasks.yml、dev/archery/archery/crossbow/cli.py 与 core.py 都是最佳的起点。

【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询