{{< details >}}

  • Tier: 基础版, 专业版, 旗舰版
  • Offering: JihuLab.com, 私有化部署

{{< /details >}}

这篇教程将指导你通过小而迭代的步骤配置一个越来越复杂的 CI/CD 流水线。流水线始终是完全功能的,但它在每个步骤中获得更多的功能。目标是构建、测试和部署一个文档站点。

完成本教程后,你将在 JihuLab.com 上有一个新项目,并使用 Docusaurus 运行一个文档站点。

要完成本教程,你需要:

  1. 创建一个项目来保存 Docusaurus 文件
  2. 创建初始的流水线配置文件
  3. 添加一个任务来构建站点
  4. 添加一个任务来部署站点
  5. 添加测试任务
  6. 开始使用合并请求流水线
  7. 减少重复的配置

前提条件

  • 你需要在 JihuLab.com 上有一个账户。
  • 你应该熟悉 Git。
  • Node.js 必须安装在你的本地机器上。例如,在 macOS 上,你可以使用 brew install node 安装 Node

创建一个项目来保存 Docusaurus 文件

在添加流水线配置之前,必须首先在 JihuLab.com 上设置一个 Docusaurus 项目:

  1. 在你的用户名下创建一个新项目(而不是群组):
    1. 在左侧边栏顶部,选择 创建新项目 ({{< icon name=”plus” >}}) 和 新项目/仓库
    2. 选择 创建空白项目
    3. 输入项目详情:
      • 项目名称 字段中,输入你的项目名称,例如 My Pipeline Tutorial Project
      • 选择 用 README 初始化仓库
    4. 选择 创建项目
  2. 在项目的概览页面右上角,选择 代码 以找到项目的克隆路径。复制 SSH 或 HTTP 路径并使用该路径在本地克隆项目。

    例如,要使用 SSH 克隆到你的电脑的 pipeline-tutorial 目录中:

    git clone git@gitlab.com:my-username/my-pipeline-tutorial-project.git pipeline-tutorial
    
  3. 切换到项目的目录,然后生成一个新的 Docusaurus 站点:

    cd pipeline-tutorial
    npm init docusaurus
    

    Docusaurus 初始化向导会询问有关站点的问题。使用所有默认选项。

  4. 初始化向导在 website/ 设置站点,但站点应位于项目的根目录。将文件移动到根目录并删除旧目录:

    mv website/* .
    rm -r website
    
  5. 更新 Docusaurus 配置文件以包含你的极狐GitLab 项目的详细信息。在 docusaurus.config.js 中:

    • 设置 url: 为如下格式的路径:https://<my-username>.gitlab.io/
    • baseUrl: 设置为你的项目名称,例如 /my-pipeline-tutorial-project/
  6. 提交更改,并推送到极狐GitLab:

    git add .
    git commit -m "Add simple generated Docusaurus site"
    git push origin
    

创建初始的 CI/CD 配置文件

从最简单的可能的流水线配置文件开始,以确保项目中启用 CI/CD 并且 runner 可用于运行任务。

此步骤介绍:

  • 任务:这些是流水线中运行命令的独立部分。任务在极狐GitLab 实例之外的 runner 上运行。
  • script:这是任务配置的一个部分,在这里你定义任务的命令。如果有多个命令(在数组中),它们会按顺序运行。每个命令的执行就像在 CLI 命令中运行一样。默认情况下,如果命令失败或返回错误,任务会标记为失败并且不会再运行更多命令。

在此步骤中,在项目的根目录创建一个 .gitlab-ci.yml 文件,配置如下:

test-job:
  script:
    - echo "This is my first job!"
    - date

提交并推送此更改到极狐GitLab,然后:

  1. 转到 构建 > 流水线 并确保在极狐GitLab 中运行一个包含该单一任务的流水线。
  2. 选择流水线,然后选择任务以查看任务日志,并看到 This is my first job! 消息及日期。

现在你的项目中有一个 .gitlab-ci.yml 文件,你可以使用 流水线编辑器 对流水线配置进行所有未来的更改。

添加一个任务来构建站点

CI/CD 流水线的一个常见任务是构建项目中的代码然后部署它。首先添加一个构建站点的任务。

此步骤介绍:

  • image:告诉 runner 使用哪个 Docker 容器来运行任务。runner:
    1. 下载容器镜像并启动它。
    2. 将你的极狐GitLab 项目克隆到正在运行的容器中。
    3. 一次运行一个 script 命令。
  • artifacts:任务是独立的并且不共享资源。如果你希望在一个任务中生成的文件被另一个任务使用,你必须先将它们保存为产物。然后后续的任务可以检索产物并使用生成的文件。

在此步骤中,将 test-job 替换为 build-job

  • 使用 image 配置任务以使用最新的 node 镜像运行。Docusaurus 是一个 Node.js 项目,并且 node 镜像内置了所需的 npm 命令。
  • 运行 npm install 以将 Docusaurus 安装到正在运行的 node 容器中,然后运行 npm run build 以构建站点。
  • Docusaurus 将构建的站点保存在 build/ 中,因此用 artifacts 保存这些文件。
build-job:
  image: node
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"

使用流水线编辑器将此流水线配置提交到默认分支,并检查任务日志。你可以:

  • 查看运行的 npm 命令并构建站点。
  • 验证产物是否在最后保存。
  • 在任务日志完成后,通过选择任务日志右侧的 浏览 来浏览产物文件的内容。

添加一个任务来部署站点

在验证 Docusaurus 站点在 build-job 中构建后,你可以添加一个任务来部署它。

此步骤介绍:

  • stagestages:最常见的流水线配置将任务分组到阶段中。处于同一阶段的任务可以并行运行,而后续阶段的任务则等待前一阶段的任务完成。如果一个任务失败,则整个阶段被视为失败,并且后续阶段的任务不会开始运行。
  • 极狐GitLab Pages:要托管你的静态站点,你将使用极狐GitLab Pages。

在此步骤中:

  • 添加一个任务来获取已构建的站点并部署它。使用极狐GitLab Pages 时,任务总是命名为 pages。来自 build-job 的产物会自动提取并解压缩到任务中。Pages 在 public/ 目录中寻找站点,因此添加一个 script 命令以将站点移动到该目录。
  • 添加一个 stages 部分,并为每个任务定义阶段。build-job 首先在 build 阶段运行,而 pages 在之后的 deploy 阶段运行。
stages:          # 为任务和它们的执行顺序列出阶段
  - build
  - deploy

build-job:
  stage: build   # 将该任务设置为在 `build` 阶段运行
  image: node
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"

pages:
  stage: deploy  # 将这个新任务设置为在 `deploy` 阶段运行
  script:
    - mv build/ public/
  artifacts:
    paths:
      - "public/"

使用流水线编辑器将此流水线配置提交到默认分支,并从 流水线 列表中查看流水线详细信息。验证:

  • 两个任务在不同阶段运行,分别是 builddeploy
  • pages 任务完成后出现一个 pages:deploy 任务,这是极狐GitLab 部署 Pages 站点的过程。当该任务完成时,你可以访问你的新 Docusaurus 站点。

要查看你的站点:

  • 在左侧边栏,选择 部署 > Pages
  • 确保 使用唯一域名 处于关闭状态。
  • 访问页面 下,选择链接。URL 格式应类似于:https://<my-username>.gitlab.io/<project-name>。有关更多信息,请参见 极狐GitLab Pages 默认域名

{{< alert type=”note” >}}

如果你需要 使用唯一域名,在 docusaurus.config.js 中,将 baseUrl: 设置为 /

{{< /alert >}}

添加测试任务

现在站点已经按照预期构建和部署,你可以添加测试和代码检查。例如,一个 Ruby 项目可能会运行 RSpec 测试任务。Docusaurus 是一个使用 Markdown 和生成 HTML 的静态站点,因此本教程添加了测试 Markdown 和 HTML 的任务。

此步骤介绍:

  • allow_failure:间歇性失败或预计会失败的任务可能会降低生产率或难以排除故障。使用 allow_failure 来让任务失败而不停止流水线执行。
  • dependencies:使用 dependencies 控制单个任务中的产物下载,通过列出要从中获取产物的任务。

在此步骤中:

  • 添加一个在 builddeploy 之间运行的新 test 阶段。这三个阶段是配置中未定义 stages 时的默认阶段。
  • 添加一个 lint-markdown 任务来运行 markdownlint 并检查项目中的 Markdown。markdownlint 是一个静态分析工具,检查你的 Markdown 文件是否遵循格式标准。
    • Docusaurus 生成的示例 Markdown 文件位于 blog/docs/ 中。
    • 此工具仅扫描原始 Markdown 文件,不需要在 build-job 产物中保存的生成 HTML。通过 dependencies: [] 加快任务速度,以便它不获取任何产物。
    • 一些示例 Markdown 文件违反了默认的 markdownlint 规则,因此添加 allow_failure: true 以便流水线继续执行尽管规则违反。
  • 添加一个 test-html 任务来运行 HTMLHint 并检查生成的 HTML。HTMLHint 是一个静态分析工具,扫描生成的 HTML 以查找已知问题。
  • test-htmlpages 都需要在 build-job 产物中找到的生成 HTML。任务默认从所有早期阶段的任务中获取产物,但添加 dependencies: 以确保任务在后续流水线更改后不会意外下载其他产物。
stages:
  - build
  - test               # 添加一个 `test` 阶段用于测试任务
  - deploy

build-job:
  stage: build
  image: node
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"

lint-markdown:
  stage: test
  image: node
  dependencies: []     # 不获取任何产物
  script:
    - npm install markdownlint-cli2 --global           # 将 markdownlint 安装到容器中
    - markdownlint-cli2 -v                             # 验证版本,对于故障排除很有用
    - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md"  # 检查 blog/ 和 docs/ 中的所有 markdown 文件
  allow_failure: true  # 该任务目前失败,但不要让它停止流水线。

test-html:
  stage: test
  image: node
  dependencies:
    - build-job        # 仅从 `build-job` 获取产物
  script:
    - npm install --save-dev htmlhint                  # 将 HTMLHint 安装到容器中
    - npx htmlhint --version                           # 验证版本,对于故障排除很有用
    - npx htmlhint build/                              # 检查 blog/ 和 docs/ 中的所有 markdown 文件

pages:
  stage: deploy
  dependencies:
    - build-job        # 仅从 `build-job` 获取产物
  script:
    - mv build/ public/
  artifacts:
    paths:
      - "public/"

将此流水线配置提交到默认分支,并查看流水线详细信息。

  • lint-markdown 任务失败,因为示例 Markdown 违反了默认的 markdownlint 规则,但允许失败。你可以:
    • 暂时忽略违规行为。它们不需要在教程中修复。
    • 修复 Markdown 文件违规行为。然后你可以将 allow_failure 更改为 false,或者完全删除 allow_failure,因为 allow_failure: false 是未定义时的默认行为。
    • 添加一个 markdownlint 配置文件以限制哪些规则违规需要警报。
  • 你还可以对 Markdown 文件内容进行更改,并在下次部署后在站点上查看更改。

开始使用合并请求流水线

使用上述的流水线配置,站点每次流水线成功完成时都会部署,但这不是理想的开发工作流程。更好的方法是使用功能分支和合并请求,只有在更改合并到默认分支时才部署站点。

此步骤介绍:

  • rules:为每个任务添加规则以配置其运行的流水线。你可以配置任务在 合并请求流水线计划流水线 或其他特定情况下运行。规则从上到下进行评估,如果规则匹配,任务就会被添加到流水线。
  • CI/CD 变量:使用这些环境变量在配置文件和脚本命令中配置任务行为。预定义的 CI/CD 变量 是你不需要手动定义的变量。它们会自动注入到流水线中,因此你可以使用它们来配置你的流水线。变量通常格式化为 $VARIABLE_NAME,预定义变量通常以 $CI_ 为前缀。

在此步骤中:

  • 创建一个新的功能分支,并在该分支中进行更改而不是在默认分支中。
  • 为每个任务添加 rules
    • 站点应仅为默认分支的更改部署。
    • 其他任务应在合并请求或默认分支的所有更改上运行。
  • 使用此流水线配置,你可以从功能分支工作而不运行任何任务,从而节省资源。当你准备验证更改时,创建一个合并请求,流水线会运行配置为在合并请求中运行的任务。
  • 当你的合并请求被接受并且更改合并到默认分支时,会运行一个新的流水线,其中还包含 pages 部署任务。如果没有任务失败,站点就会部署。
stages:
  - build
  - test
  - deploy

build-job:
  stage: build
  image: node
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'  # 为合并请求的源分支的所有更改运行
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH       # 为默认分支的所有更改运行

lint-markdown:
  stage: test
  image: node
  dependencies: []
  script:
    - npm install markdownlint-cli2 --global
    - markdownlint-cli2 -v
    - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md"
  allow_failure: true
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'  # 为合并请求的源分支的所有更改运行
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH       # 为默认分支的所有更改运行

test-html:
  stage: test
  image: node
  dependencies:
    - build-job
  script:
    - npm install --save-dev htmlhint
    - npx htmlhint --version
    - npx htmlhint build/
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'  # 为合并请求的源分支的所有更改运行
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH       # 为默认分支的所有更改运行

pages:
  stage: deploy
  dependencies:
    - build-job
  script:
    - mv build/ public/
  artifacts:
    paths:
      - "public/"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH      # 仅为默认分支的所有更改运行

合并你的合并请求中的更改。此操作更新默认分支。验证新流水线包含部署站点的 pages 任务。

请确保为流水线配置的所有未来更改使用功能分支和合并请求。其他项目更改,例如创建 Git 标签或添加流水线计划,不会触发流水线,除非你为这些情况也添加规则。

减少重复的配置

流水线现在包含三个任务,它们都有相同的 rulesimage 配置。与其重复这些规则,不如使用 extendsdefault 创建单一的事实来源。

此步骤介绍:

  • 隐藏任务:以 . 开头的任务永远不会被添加到流水线中。使用它们来保存你想重复使用的配置。
  • extends:使用 extends 在多个地方重复配置,通常从隐藏任务中获取。如果你更新隐藏任务的配置,所有扩展隐藏任务的任务都会使用更新后的配置。
  • default:设置默认关键字,这些关键字适用于所有未定义的任务。
  • YAML 覆盖:在使用 extendsdefault 重用配置时,你可以在任务中显式定义一个关键字来覆盖 extendsdefault 配置。

在此步骤中:

  • 添加一个 .standard-rules 隐藏任务来保存在 build-joblint-markdowntest-html 中重复的规则。
  • 使用 extends 在三个任务中重用 .standard-rules 配置。
  • 添加一个 default 部分以将 image 默认值定义为 node
  • pages 部署任务不需要默认的 node 镜像,因此显式使用 busybox,一个极其小巧且快速的镜像。
stages:
  - build
  - test
  - deploy

default:               # 添加一个默认部分以定义 `image` 关键字的默认值
  image: node

.standard-rules:       # 创建一个隐藏任务以保存通用规则
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

build-job:
  extends:
    - .standard-rules  # 在这里重用 `.standard-rules` 中的配置
  stage: build
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"

lint-markdown:
  stage: test
  extends:
    - .standard-rules  # 在这里重用 `.standard-rules` 中的配置
  dependencies: []
  script:
    - npm install markdownlint-cli2 --global
    - markdownlint-cli2 -v
    - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md"
  allow_failure: true

test-html:
  stage: test
  extends:
    - .standard-rules  # 在这里重用 `.standard-rules` 中的配置
  dependencies:
    - build-job
  script:
    - npm install --save-dev htmlhint
    - npx htmlhint --version
    - npx htmlhint build/

pages:
  stage: deploy
  image: busybox       # 用 `busybox` 覆盖默认的 `image` 值
  dependencies:
    - build-job
  script:
    - mv build/ public/
  artifacts:
    paths:
      - "public/"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

使用合并请求将此流水线配置提交到默认分支。文件更简单,但它应该与前一步具有相同的行为。

你刚刚创建了一个完整的流水线并使其更加高效。做得好!现在你可以利用这些知识,了解 .gitlab-ci.yml 关键字的其余部分,在 CI/CD YAML 语法参考 中构建自己的流水线。