Baserow CI/CD 流水线全解:5 个阶段如何把一次提交变成 Dockerhub 上的镜像
【免费下载链接】baserowBuild databases, automations, apps & agents with AI — no code. Open source platform available on cloud and self-hosted. GDPR, HIPAA, SOC 2 compliant. Best Airtable alternative.项目地址: https://gitcode.com/GitHub_Trending/ba/baserow
如果你自托管过 Baserow——这款开源自托管无代码数据库(常被称为 Airtable 的开源替代方案),大概率拉过baserow/backend:latest这类镜像,却很少关心它们是怎么来的。答案就在仓库根目录的 .gitlab-ci.yml 里:一条基于 GitLab CI 的流水线,把"推一个 commit"这件事拆成五个阶段——构建开发镜像、跑测试、构建生产镜像、推送发布、触发下游项目。本文按这条流水线的实际走向,讲清每个阶段做什么、什么分支下会跳过、以及缓存与发布环节的几个设计取舍。
先记住三根分支和两个前缀
流水线的所有行为都由分支决定,先立好坐标系:
| 分支 | 角色 | 流水线行为 |
|---|---|---|
feature branches | 从develop切出的功能分支 | 只做构建 + 测试,不构建生产镜像,不推送 |
develop | 集成所有新功能的开发分支 | 全流程执行,测试通过的生产镜像推 Dockerhub 并打develop-latest标签 |
master | 官方发布分支 | 全流程执行,且额外构建 ARM64 多平台镜像;打 tag 时才真正发布版本 |
配套的两个镜像标签前缀贯穿整个流程:
ci-latest-<commit sha>:构建阶段的产物,供同一条流水线的后续阶段使用;ci-tested-<commit sha>:测试全部通过后才打上的"合格章",发布阶段只允许推送带这个前缀的镜像。
所有具体的作业定义不在 .gitlab-ci.yml 里,而是复用 .gitlab/ci_includes/jobs.yml 中的公共模板(如.build-baserow-image、.build-final-baserow-image),主文件只负责声明阶段、变量和各类only/except规则。
阶段一:构建开发镜像,顺手当缓存用
第一个build阶段构建backend_dev和web-frontend_dev两个"开发版"镜像。这里的"开发版"指的是多阶段 Dockerfile 中--target ci的那个阶段:包含全部 Python 依赖和 Node 依赖,但不包含完整的应用代码。
为什么要专门构建它?两个用途:
- 依赖缓存。开发镜像里预装好的库可以被后续构建复用,不用每次重新安装;
- 测试环境。lint 和 test 阶段直接把这个镜像当容器跑,把 git 源码挂载进去即可,测试机不需要再装一遍环境。
构建时带BUILDKIT_INLINE_CACHE=1参数,保证镜像的所有中间层都可被下一次构建缓存。构建完的镜像同时打上ci-latest-$CI_COMMIT_SHORT_SHA(供本条流水线内部按 SHA 精确引用)和ci-latest-$分支名(供本分支下条流水线做缓存种子)两个标签。
阶段二:lint 与测试,只在相关目录变动时跑
lint和test阶段都套用了.skippable-job模板:只有RUN_WHEN_CHANGES_MADE_IN指定的目录(如backend/、premium/backend/、enterprise/backend/)有改动时才执行;若历史上同一提交(或更早提交)已成功跑过且相关文件没变化,还会直接复用上次结果跳过。
测试本身做了两层拆分:
- 后端 pytest:拆成
backend-test-group-1到group-10共 10 个并行作业,每个作业跑PYTEST_SPLIT_GROUP指定的 1/10 测试集。之所以不用 pytest-xdist 的-n参数,是因为 SaaS runner 只有单虚拟核,进程内并行反而更慢。跑完后collect-backend-coverage作业把 10 份覆盖率数据合并成一份 Cobertura 报告; - E2E 测试(Playwright + Firefox):
e2e-tests-group-1到group-4按SHARD_INDEX四分片,服务里同时拉起pgvector/pgvector:pg14、redis:6-alpine、adobe/s3mock以及后端/前端的 CI 开发镜像,完整模拟一套 Baserow 服务。仅 feature 分支跑,develop/master上靠其他覆盖。
前端则跑web-frontend-lint(eslint + stylelint)和web-frontend-test,另有docker-file-hadolint检查所有 Dockerfile、mjml-compiled-check保证邮件模板编译产物已提交。
阶段三:生产镜像只在 develop、master 或[build-all]时构建
build-final阶段构建不带 dev 目标的正式镜像:backend、web-frontend,以及 all-in-one 的baserow、Cloudron、Heroku 变体(后者需要[build-all]或BUILD_ALL_IN_ONE=true才构建,Heroku 镜像只是验证可构建性,不会推送)。
这一阶段复用阶段一的开发镜像作为--cache-from,只重新构建应用层,所以很快。构建成功且测试全部通过后,dev 和正式镜像都会补打ci-tested-$CI_COMMIT_SHORT_SHA标签——这是发布阶段的准入门槛。
在 feature 分支上,正式镜像的构建作业(如build-final-backend-image-manual)处于when: manual状态,不点不跑,默认只走阶段一、二。
阶段四:镜像何时被推到 Dockerhub
publish阶段的推送作业按分支和 tag 分三类:
| 触发条件 | 推送的标签 |
|---|---|
develop最新提交 | baserow/backend:develop-latest、baserow/web-frontend:develop-latest、baserow/baserow:develop-latest(all-in-one)、baserow/cloudron:develop-latest |
对 master 最新提交打版本 tag(如1.8.2) | baserow/backend:1.8.2+baserow/backend:latest |
| 对 master 历史提交补打 tag | 只推baserow/backend:<tag>,不动latest |
每个 publish 作业都有SKIP_IF_TAG_NOT_ON_BRANCH: master和SKIP_IF_NOT_LATEST_COMMIT_ON_BRANCH保护:非 master 上的 tag 直接失败(tag 只允许打在 master),非分支最新提交则跳过,防止把过期的ci-tested镜像推出去。tag 触发的流水线里所有 build 作业被except: tags排除——tag 流水线唯一的职责就是"把已经测过的镜像换个标签推出去",如果那个 SHA 的测试没通过或镜像不存在,publish 会直接失败。
另外,master 的 tag 流水线还会跑publish-helm-chart:打包 deploy/helm 下的 Helm chart,触发 chart 仓库的流水线完成上传,版本号取自 git tag。
阶段五:通知下游项目重建
trigger-saas-build作业在develop上把CI_COMMIT_SHA、已测试镜像地址等变量传递给依赖 Baserow 镜像的下游项目(如 saas 项目),让它们的流水线可以复用上游刚构建的产物,而不必自己再构建一遍。
用两个提交标签和一条手动流水线控制行为
不需要改配置,改提交信息即可:
[skip-ci]:该 commit 完全跳过流水线;[build-all]:在任意分支强制构建全部镜像,包括 all-in-one、cloudron、heroku 等正式变体。
更细粒度的控制来自手动流水线:在 GitLab 的 pipelines/new 页面为指定分支手动触发,可以临时覆盖任意变量。.gitlab-ci.yml 顶部就声明了这些可覆盖项:
TRIGGER_FULL_IMAGE_REBUILD=yes:所有构建加--no-cache --pull,完全从零重建;ENABLE_JOB_SKIPPING:是否允许跳过历史已成功的测试;ENABLE_COVERAGE:是否生成覆盖率报告;BUILD_ALL_IN_ONE=true:强制构建 all-in-one 镜像。
调试 CI 配置本身时(比如改了 jobs.yml 想验证),手动流水线是最直接的手段。
发布一个新版本到 Dockerhub 的完整步骤
把上面几节串起来,一次正式发布(假设版本号1.8.2)的实际操作只有四步:
- 在 GitLab 上创建并合并
develop→master的 MR,等 master 上合并提交的流水线跑完(完成构建 + 测试,产出ci-tested镜像); - 在 GitLab 界面上给该合并提交打 git tag
1.8.2; - GitLab 自动为这个 tag 新建一条流水线,publish 作业把镜像推为
baserow/*:1.8.2和baserow/*:latest; - 同时
publish-helm-chart把对应版本的 Helm chart 发布出去。
第 1 步如果没跑成功,第 3 步的流水线会失败且不推送任何东西——发布链路里没有"人工确认镜像"的环节,测试即放行。
缓存查找顺序:为什么 master 只认 develop 的缓存
构建作业找缓存的顺序是(见 .gitlab/ci_includes/jobs.yml 中.build-baserow-image的脚本):
- 非 master 分支:先拉本分支最新的
ci-latest-$分支名镜像; - 拉不到再拉
ci-latest-develop; - 都没有则完全从零构建。
master 是个例外:它只用 develop 的ci-latest镜像做缓存,不用自己的。原因写在官方文档 docs/development/ci-cd.md 里:
- master 两次发布之间可能几周没有流水线,自己的
ci-latest缓存要么早被清理,要么层内容严重过期; - 基础层若有破坏性变更,先让它在 develop 上暴露并被修好,master 复用 develop 验证过的层,避免"测试的镜像"和"发布的镜像"不是同一个;
- 全量重建的工作只在 develop 做一次,master 直接受益,不重复劳动。
缓存的安全隐患,用每日全量重建来对冲
Docker 层缓存有个众所周知的问题:FROM base_image和apt upgrade这类层一旦被缓存,即使基础镜像发布了安全补丁也永远不会重跑。Baserow 的对策是:
- 每日定时流水线:develop 分支上有一个 scheduled pipeline,设置
TRIGGER_FULL_IMAGE_REBUILD=yes,让所有构建--no-cache --pull从零重建,刷新全部ci-latest-develop缓存镜像; - 顺带跑重型测试:同一条早间流水线会激活标记为
@pytest.mark.once_per_day_in_ci的测试,这些测试平时不跑、每天只跑一次。
由于 master 和其他分支的缓存源头都指向 develop 的ci-latest镜像,一次每日重建就覆盖了所有分支的缓存安全。
临时镜像方面,ci-latest-*和ci-tested-*前缀的镜像由 GitLab 的 registry 清理规则在 7 天后自动删除(每天 11:00 CET 执行一次清理作业)。
ARM 多平台构建:为什么只有 master 做
master 上推送到 Dockerhub 的镜像同时支持linux/amd64和linux/arm64/v8,由BUILD_ARM和BUILD_ARM_ON_BRANCH=master两个变量控制。实现方式是 Docker buildx 的远程构建:CI 作业通过 SSH 连接一台专用 ARM64 服务器,把 ARM 部分的构建卸载过去(.build-baserow-image脚本里的docker buildx create --append ... --platform linux/arm64/v8 "$ARM_TARGET")。
两个设计取舍:
- 为什么不在 develop 上构建 ARM:加 ARM 会让流水线多花 5–10 分钟,把它只放在 master 上能显著加速日常开发迭代,develop 和 feature 分支的镜像只支持 AMD64;
- 为什么不用 QEMU 模拟:实测在 runner 上模拟 ARM 构建单个镜像要 1 小时以上,专用硬件是必要的。
本地怎么跑对应的测试
CI 里跑的东西本地基本都能复现,入口在 justfile 和 dev.sh:
# 后端测试(全部 / 并行 / 指定目录) just b test just b test -n=auto just b test tests/path/ # 用内存盘 PostgreSQL(端口 5433)加速 2-5 倍 just test-db start DATABASE_URL=postgres://baserow:baserow@localhost:5433/baserow just b test -n=auto just test-db down更细节的设置(--reuse-db、BASEROW_TESTS_SETUP_DB_FIXTURE等)见 docs/development/running-tests.md。E2E 测试对应e2e-tests/目录,本地可用e2e-tests/run-e2e-tests-locally.sh启动;dev.sh 也会开一个 "e2e tests" 标签页直接调起它。
常见疑问速答
develop 的develop-latest和 master 的latest有什么区别?develop-latest是开发中的滚动镜像,每次 develop 提交成功后覆盖;latest只在 master 打版本 tag 时更新,指向正式发布的最后一个版本。想尝鲜拉前者,求稳拉后者或具体版本号。
为什么 tag 打在非 master 提交上会失败?tag 流水线不做构建,只推送已存在的ci-tested镜像,而"这个提交是否在 master 上通过过完整流水线"正是SKIP_IF_TAG_NOT_ON_BRANCH检查的内容,保证发布镜像一定经过测试。
改了 CI 配置想立即验证?.gitlab/ci_util_image和.gitlab/ci_dind_image两个基础 CI 镜像的构建作业是when: manual,且只在对应目录或 .gitlab-ci.yml 变化时出现——提 MR 改完配置,在流水线页面手动点一下即可重建并推送。
CI 全流程文档在哪?仓库内的 docs/development/ci-cd.md 是最权威的版本,包括本文涉及的缓存逻辑、发布流程和 FAQ 的完整推导。
【免费下载链接】baserowBuild databases, automations, apps & agents with AI — no code. Open source platform available on cloud and self-hosted. GDPR, HIPAA, SOC 2 compliant. Best Airtable alternative.项目地址: https://gitcode.com/GitHub_Trending/ba/baserow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考