Dev.to · 3 min read

仓库能推之后:内置流水线 YAML 怎么写

仓库能推之后:内置流水线 YAML 怎么写

这篇讲代码能推上 AngusGit 之后,怎么写一份让每次推送都自动构建和测试的 .anguscicd.yaml。看完你可以写齐顶层字段,把多个 Job(流水线里的一组步骤)按顺序串起来,配好什么时候自动运行,并在脚本里引用凭证(平台上保存的令牌、密码),而不把令牌写进仓库文件。 开始之前先弄清三件事。这份文件用的是 AngusGit 自己的语法,和 GitLab CI 的 .gitlab-ci.yml、GitHub Actions 的 workflow 文件都不通用。流水线功能只在团队版和企业版中提供,社区版没有 CI/CD 菜单。运行器(Runner,真正执行任务的机器)要先接好,否则推送之后任务会一直停在等待中。登录 AngusGit,侧栏进入 CI/CD → 运行器,点 新增 Runner。控制面地址 改成构建机访问得到的地址。名称写这台机器,例如 build-linux-01。点 生成令牌,令牌 1 小时内有效,明文只显示一次。任务要在容器里跑时,页签选 Docker 一键拉起,标签填 linux,docker。任务要直接在构建机上跑时,页签选 Zip 安装,标签不要带 docker,机器上先装好 JDK 21。把命令复制到构建机执行,回到列表看到这台运行器是 空闲,再写 YAML。 下文的 YAML 都取自示例仓库 xlliu/test-cicd-example,克隆地址是 https://git.anguskit.com/xlliu/test-cicd-example.git,可以对照着看。文中用到仓库里的两份文件:.anguscicd/01-basic-build.yaml(name: basic-build-test)在控制台里显示为 test-cicd-example · 01 Basic Build,.anguscicd/04-monorepo-change.yaml(name: monorepo-change-analysis)显示为 test-cicd-example · 04 Monorepo Change。 1. 文件放在哪 流水线配置文件和代码一起放在仓库里,有两种放法: 根目录的 .anguscicd.yaml,适合一个仓库只有一条流水线。 .anguscicd/ 目录下的任意 .yaml 或 .yml 文件,例如 .anguscicd/ci.yaml、.anguscicd/release.yaml,适合一个仓库有多条流水线,每个文件对应一条。 文件名必须写对:像 .gitlab-ci.yml、.angus-ci.yml 这类名字,平台不会把它当成流水线配置。 2. 一份最小能跑的配置 示例仓库里的 .anguscicd/01-basic-build.yaml 就是一份能在运行器上完整跑完的配置。每当有提交推送到 main 或 develop 分支,它就在 node:20-alpine 容器里依次做三件事:打印环境信息、运行 apps/hello-node、执行这个模块的单元测试。文件里没有拉取代码(checkout)的步骤,因为平台在 Job 开始前会自动检出代码。 version: "1" name: basic-build-test triggers: - on: push branches: - main - develop env: TZ: Asia/Shanghai APP_NAME: AngusCICD pipeline: build: runner: mode: DOCKER image: node:20-alpine labels: ["linux", "docker"] env: MODULE: hello-node steps: - name: Show environment run: | echo "TZ=$TZ APP_NAME=$APP_NAME MODULE=$MODULE" node -v pwd ls -la timeout: 60 - name: Run app run: node apps/hello-node/src/index.js timeout: 60 - name: Unit test run: node --test apps/hello-node/src/index.test.js timeout: 120 下表逐个解释这些字段: 你要写的字段 写成什么样 作用 version "1" 配置格式的版本,不写会收到警告 name 例如 basic-build-test 流水线名称,必填 triggers 事件列表 什么时候自动运行,例如 on: push 且匹配 main 分支 pipeline Job 映射 所有 Job 的集合,必填,1~100 个 build Job 标识 自己起的 Job 名字,needs 引用的就是它 runner.mode DOCKER 或 NATIVE 执行模式:容器里跑,或直接在宿主机上跑 runner.image 镜像地址 容器镜像,DOCKER 模式下必填 runner.labels 字符串数组 带齐这些标签的在线运行器才会接这个 Job steps 步骤数组 按顺序执行,每个步骤必须有 name 和 run timeout 秒数(1–86400) 这一步最多跑多少秒,可选 表里有两处需要展开说明。一是 name:控制台列表里的显示名会带上仓库名,所以你看到的是 test-cicd-example · 01 Basic Build 这种形式。二是 runner.mode,也就是执行模式,只有两种:DOCKER 表示在 runner.image 指定的镜像启动的容器里执行命令,运行环境由镜像决定;NATIVE 表示直接在运行器所在的机器(宿主机)上执行,用的是那台机器上已经装好的工具。 这份示例用的是公开镜像 node:20-alpine,所以运行器要能拉到它。如果内网构建机访问不了公网,就换成你们内网镜像仓库里的镜像,写上完整的镜像名即可。同一仓库的 apps/hello-java 是一个 Java 模块,本地可以用 mvn -B -ntp test 验证;放进流水线时,把镜像换成带 JDK 和 Maven 的镜像。 前面说过平台会自动检出代码:Job 开始时,平台会把触发这次运行的那个提交检出到工作目录,命令直接在仓库根目录执行。这种检出是浅克隆,只下载这一个提交,不带历史记录。 3. 扩展成多个 Job:先检查,再按目录构建 一个 Job 够用之后,可以把流程拆成多个 Job。同一仓库的 .anguscicd/04-monorepo-change.yaml 就拆成了三个:lint 负责检查,每次都跑;build-service-a 和 build-service-b 分别构建两个服务,它们都写了 needs: ["lint"],还用 modules 标出了自己关心的目录。这条流水线由目标分支是 main 的拉取请求触发。以分支 feature/change-service-a 为例,它只改动了 apps/service-a。 version: "1" name: monorepo-change-analysis triggers: - on: pull_request branches: - main smart: changeAnalysis: true cacheStrategy: aggressive pipeline: lint: runner: mode: DOCKER image: node:20-alpine steps: - name: Lint all run: | node apps/hello-node/src/index.js echo "lint ok" timeout: 120 build-service-a: needs: ["lint"] modules: ["apps/service-a/", "apps/shared-lib/"] runner: mode: DOCKER image: node:20-alpine steps: - name: Build service-a run: node apps/service-a/src/index.js timeout: 120 build-service-b: needs: ["lint"] modules: ["apps/service-b/", "apps/shared-lib/"] runner: mode: DOCKER image: node:20-alpine steps: - name: Build service-b run: node apps/service-b/src/index.js timeout: 120 这份配置有三点要注意: needs: ["lint"] 决定了先后顺序。 没写 needs 的 Job 会同时开始;写了之后,两个构建都要等 lint 成功才开始,lint 一旦失败,它们就不会运行。 每个 Job 各自检出代码。 两个 Job 可能被分到不同的运行器上,工作目录互不共享,一个 Job 生成的文件另一个 Job 看不到。这份示例的命令不安装依赖,直接运行仓库里的脚本,所以不受影响。 触发器是 pull_request。 这里的 branches: [main] 指拉取请求的目标分支,也就是说,凡是要合进 main 的拉取请求都会跑这条流水线,结果显示在拉取请求页的状态检查(页面上标明流水线是否通过的那一项)里,详细用法见文末参考里的合并请求触发流水线一文。另外,modules 要配合顶部的 smart.changeAnalysis 使用:一次改动只涉及某一个目录时,就跳过另一个不相关的构建。 4. 触发器怎么写 triggers 决定流水线什么时候自动运行。它是一个列表,每一项用 on 写事件类型,还可以加 branches 限定分支。常用的有下面四种: on 的值 可配的过滤项 什么时候运行 push branches 用 git push 把提交推送到匹配的分支时 pull_request branches(目标分支) 发起拉取请求、往源分支推送新提交、重新打开拉取请求时 tag 无或标签规则 用 git push 推送标签时 workflow_dispatch 输入参数 inputs 在控制台点 触发 手动运行时 triggers 也可以留空,保存时会提示「未配置触发器,流水线只能手动触发」。 有一点要提前知道:on: push 只响应 git push。目前在拉取请求页点合并按钮把代码合进 main,不会触发 on: push。所以,如果你的 main 开了分支保护(只允许通过拉取请求合并,不能直接推送),main 上的构建有两种办法:一是用 pull_request 触发器在合并前完成;二是合并后在控制台打开这条流水线,点 触发 手动运行一次。 5. 用到密码和令牌时:先建凭证,再在 Job 里声明 发布制品、调用内部接口时往往需要令牌。令牌如果直接写进 YAML,能看到仓库的人就都能看到它,所以要先把它存成凭证,再在 Job 里引用,步骤如下。 在控制台建凭证 打开 CI/CD → 配置,切到 凭证 标签,点 新建凭证,进入 创建凭证 页面。名称 就是 YAML 里引用这条凭证时用的名字,例如 NPM_TOKEN,页面上的提示是「流水线 YAML 里 secrets / credentials 引用的名字。建议大写加下划线,创建后不可改。」类型 默认是 凭证文本,适合存一段 Token。可见性 默认是 指定仓库,这时还要在 绑定仓库 里选上仓库;如果改成 全部仓库,本租户下所有流水线都能使用这条凭证。值 里粘贴令牌,保存后就无法再查看明文。需要的话,再填 过期时间。 在 Job 里声明要用哪些凭证 在需要令牌的 Job 下加一个 credentials 字段,列出要用的凭证名称。没有列出的凭证,这个 Job 拿不到。 在命令里引用 声明过的凭证会以同名环境变量的形式交给这个 Job,所以命令里直接写 $NPM_TOKEN 就能用。下面的 publish Job 就是这样写的。 publish: needs: [test] credentials: ["NPM_TOKEN"] runner: mode: DOCKER image: node:20-alpine labels: ["linux", "docker"] steps: - name: publish run: | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc npm publish 在 env 或条件里,还可以写成 ${{ secrets.NPM_TOKEN }} 或 ${{ credentials.NPM_TOKEN }},两种写法效果一样,前提同样是这个 Job 的 credentials 里列出了这条凭证。 上图是 创建凭证 页面的填写示例:名称填 NPM_TOKEN,类型选 凭证文本,可见性选 指定仓库 并绑定 xlliu/test-cicd-example。值 必须先填真实令牌再保存,因为保存后无法再查看明文;如果还没准备好令牌,点 取消 回到凭证列表即可。 6. 让流水线出现在控制台里 YAML 写好之后,流水线还要在 CI/CD → 流水线 里建立起来,才会运行。有两种方式: 推送到默认分支。 用 git push 把包含 .anguscicd.yaml 的提交直接推到默认分支,平台会自动识别并建立流水线。 在控制台新建。 点 新建流水线 → 空白创建,填写 流水线名称,选择 仓库,再切到 YAML 把配置粘进去,最后点 创建流水线。保存前平台会做一次运行前检查:看到「检查通过,可以保存。」就可以保存;有错误时会提示「请先修复错误」。 如果 main 开了分支保护、不能直接推送,第二种方式最省事。要注意,控制台新建的流水线只保存在平台里,不会往仓库提交文件;如果之后仓库里也放了同路径的 YAML,运行时以触发分支上的文件为准。 如果你手上已经有 GitLab CI 或 GitHub Actions 的配置,新建流水线 菜单里还有 导入 GitHub Actions 和 导入 GitLab CI 两项。它们能自动转换一部分语法,得到的是一份需要人工核对的初稿(转换出来还要手改的第一版),导入后要逐项检查。 7. 写错了会看到什么 平台对字段的校验很严格,遇到认不出的字段会直接报错。最常见的几类情况如下: 你看到的 原因 怎么处理 提示 stages、variables、jobs 等是未知字段 用了 GitLab CI 或 GitHub Actions 的写法 Job 放在 pipeline 下;全局变量写在顶层 env;先后顺序用 needs 表达 提示步骤缺少 name 每个步骤都必须有名字 给每个步骤加 name 在 Job 下写了 image 报错 镜像属于运行器配置 写在 runner.image 下 Job 一直排队不开始 没有在线运行器同时满足 mode 和 labels 核对运行器的模式和标签 Job 失败,提示 Credential not visible or expired 凭证不存在、对这个仓库不可见,或已过期 检查凭证名称、可见性和过期时间 命令里引用的 $CI_COMMIT_SHA 等变量是空的 GitLab 的预定义变量不会注入 改用 GIT_COMMIT_SHA、GIT_BRANCH Job 一直排队时,回到 CI/CD → 运行器,看开头接上的那台是不是 空闲,标签和 runner.mode 是否一致。如果要在脚本里读仓库地址、分支或提交号,直接用平台自动提供的 GIT_REPO_URL、GIT_BRANCH、GIT_COMMIT_SHA,不要再写 GitLab 那套 CI_*。 常见问题 能不能先在本地或控制台试跑一下,不真正执行? 没有试运行模式:控制台新建时的运行前检查只校验配置,不执行命令。比较稳妥的做法是先建一条只有 echo 命令的流水线,确认运行器能接到任务、能跑完,再逐步加入真实的步骤。 多个 Job 之间怎么传文件,比如把构建产物交给部署 Job? Job 之间不共享工作目录,YAML 里也没有专门传递制品(构建产物)的字段。常见做法是在构建 Job 里把产物上传到制品仓库,部署 Job 再从制品仓库下载。如果构建和部署本来就是连续的几步,也可以把它们写进同一个 Job,按步骤顺序执行。 依赖每次都重新下载,很慢,怎么办? 在 Job 下配置 cache:key 用来区分不同的缓存,paths 写要缓存的目录,例如 Node 项目缓存 node_modules。下次运行时,只要 key 相同,平台就会先恢复这些目录,再执行步骤。 小结 写 AngusGit 流水线,可以从一份最小配置起步:写齐 version、name、triggers,再在 pipeline 下放一个 Job。每个 Job 用 runner 选择执行模式(DOCKER 容器或 NATIVE 宿主机),用 steps 列出命令,每个步骤都要有 name,代码检出由平台自动完成。多个 Job 默认同时运行,需要先后顺序时用 needs;用到令牌时,先在控制台建凭证,再在 Job 的 credentials 里列出,命令里用 $名字 引用。最后要记住,on: push 只响应 git push,在拉取请求页合并不会触发它,所以受保护的 main 分支要靠 pull_request 触发器在合并前把关。 结论: 在 .anguscicd.yaml 里写齐 version: "1"、name、triggers 和 pipeline 下的 Job(每个 Job 都要有 runner,每个步骤都要有 name),再用 needs 安排 Job 的先后顺序,用 credentials 引入令牌等敏感凭证,就可以让每次推送自动构建和测试。 参考文章 AngusGit 手册:流水线基础 AngusGit 手册:流水线进阶 AngusGit 手册:凭证与制品 流水线运行器怎么接到构建机:在那台机器上执行接入命令 流水线任务在哪跑:Docker 容器还是宿主机 合并请求怎么触发流水线:红了还能不能合 运行器不接任务:标签、在线和并发对不上

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Programming & Dev News