大家好,我是老王。
前几天跟朋友聊天,他说最近搞了项目,一堆简单的H5项目放到一个仓库进行管理,一个目录是项目,都是从其他历史代码里复制过来用的,技术栈不一样,编译部署方式还不一样,有的甚至不需要编译就是一个html文件。
我很奇怪。
问他,为什么要放到一个仓库呢?直接用历史代码fork出来搞一下不行吗。
他说,统一管理,方便后续快速复制…巴拉巴拉….
好吧。
一个仓库,每个目录都是独立部署上线,并且部署的位置不同,使用的技术栈也不同,CICD给他带来了很大的负担。最后写了一个比较复杂的脚本来控制,很是麻烦。
我建议他用看看能不能用GitLab CICD去解决目录更新后的编译部署问题。
也是这篇文章的由来。
通过本文你可以了解
-
目录级CICD的实现思路及脚本
-
文件级CICD的实现思路
文章内容默认读者具备GitLab CICD相关基础知识,如果不了解,可以根据基础概念中提到的内容自行查阅学习。相关的资料非常多。这里就不再赘述。
基础概念
-
持续集成(Continuous Integration,CI)。持续集成是一种开发实践,旨在确保团队成员对代码的修改能够快速、频繁地集成到共享存储库中,并通过自动化的构建和测试流程进行验证。主要目标是减少集成问题,促进团队成员之间的协作,并提高软件质量。
-
持续交付/持续部署(Continuous Delivery/Continuous Deployment,CD):
-
持续交付(Continuous Delivery,CD): 一种软件开发实践,目标是确保软件随时都是可发布的状态。即使完成了开发和测试,也要保持软件能够随时进行部署到生产环境的准备状态,但实际的部署动作可能需要手动触发。
-
持续部署(Continuous Deployment,CD): 是持续交付的一种更进一步的实践,自动化了将通过测试的代码部署到生产环境的过程。这种方式减少了人为干预的需要,通过自动化流程实现了快速部署。
-
仓库(Repository):
代码存储的地方,可以包含项目的所有文件和版本历史。
- CI/CD Pipeline(流水线):
一系列自动化任务组成的工作流程,用于构建、测试和部署代码。CICD的最顶层组件,5-9都是服务于流水线,或者是流水线的组成部分。
- Job(任务):
流水线中的一个单独步骤或任务,例如构建代码、运行测试或部署应用程序。
- Stage(阶段):
一组相关的任务集合,按顺序执行。例如,构建、测试和部署可以是不同的阶段。
- Artifact(构件):
流水线任务生成的输出文件或数据,可以用于后续任务或部署操作。
- Trigger(触发器):
启动流水线的事件或条件,例如代码推送、定时触发或外部触发器。
- Variables(变量):
流水线中可用的环境变量,用于传递参数或配置任务。
- .gitlab-ci.yml(配置文件)
.gitlab-ci.yml 文件的作用是定义和配置项目的 CI/CD 流程,通过配置,控制流水线如何执行、包含那些任务等。其提供了丰富的配置项,就包含了诸如:5-9的配置信息。文档参考:docs.gitlab.cn/jh/ci/yaml/
实现目录级CICD需要满足的条件
-
目录中文件变更能够触发(trigger)CICD
-
不同目录有各自的配置,以支持不同的编译部署方式
-
支持多个目录同时有变更时,能够并行Job,同时部署多个目录
思路
-
在每个项目目录添加.gitlab-ci.yml 配置文件,进行个性化配置,支持不同项目的编译部署;
-
在根目录.gitlab-ci.yml 配置文件中,include其他工程目录的.gitlab-ci.yml配置文件;
-
根据目录变化,执行相应目录.gitlab-ci.yml 配置文件中的job。这里有两种方式
a. 在根目录.gitlab-ci.yml 配置文件中处理,根据changes目录,来include该目录的yml配置文件
b. 在根目录include所有项目目录配置文件,changes目录监听放在项目目录yml文件处理
代码和解释
目录结构
├── .gitlab-ci.yml # 根目录配置文件
├── test1
│ ├── .test1.yml # test1项目 配置文件
│ └── index.html
└── test2
├── .test2.yml # test2项目 配置文件
├── assets
│ └── 1.js
└── index.html
为了方便阅读和理解,我将项目中的 .gitlab-ci.yml 配置文件重命名为对应项目的名称。是否重命名取决于个人喜好。
# 根目录配置文件
include:
- local: '/test1/.test1.yml'
- local: '/test2/.test2.yml'
根目录配置,include项目中的配置文件。
# .test1.yml
variables:
DIR_NAME: 'test1'
test1:
rules:
- if: $CI_COMMIT_BRANCH == 'main' || $CI_COMMIT_BRANCH == 'test'
changes: # 在这里监听响应目录变化
- $DIR_NAME/**/*
stage: deploy
tags:
- Web
script: # 这里编写需要执行的脚本
- echo "Deploying $DIR_NAME"
test1配置文件中,定义一个job test1 ,在script 里 编写要执行的脚本
# .test2.yml
variables:
DIR_NAME: 'test1'
test2:
rules:
- if: $CI_COMMIT_BRANCH == 'main' || $CI_COMMIT_BRANCH == 'test'
changes:
- $DIR_NAME/**/*
stage: deploy
tags:
- Web
script:
- echo "Deploying $DIR_NAME"
test2配置文件中,定义一个job test2 ,在script 里 编写要执行的脚本
这里需要注意。项目之间的Job名要不同。因为include有合并逻辑,可以理解成两个对象合并,相同的key会被后面include的key覆盖,不同的key会一起保留。所以如果要并行执行Job就需要不同的Job Name。
以上示例代码项目,触发流水线之后,如果2个项目都有修改就会如图所示
如果想在根目录的配置文件中检测文件或目录的变化,可以在include中使用rules[2]。同时,项目中的配置changes就不需要了。
include:
- local: '/test1/.gitlab-ci.yml'
rules:
- if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "test"'
changes:
- test1/**/*
- local: '/test2/.gitlab-ci.yml'
rules:
- if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "test"'
changes:
- test2/**/*
文件级变化的CICD
了解了上面目录级变化的CICD,文件级也就不难处理了。changes除了可以监听目录级别以外,还可以监听某些文件。
当然,如果需要根据变化的文件进行部署的话,也可以通过 git diff 命令获取到变化的文件,在对文件进行后续操作就可以了。
可以尝试在script中执行一些脚本。
script:
- |
# 获取差异
CHANGED_FILES=$(git diff --name-only HEAD HEAD~1)
echo $CHANGED_FILES
# .gitlab-ci.yml
# test1/index.html
# test2/assets/1.js
# test2/index.html
# 可以看到已经获取到了变化的文件完整路径
# 使用 for 循环逐行读取并处理
for line in $CHANGED_FILES; do
# 对每一行进行处理
# 提取每一行的第一级目录
dir=$(echo "$line")
echo "Directory: $dir"
# 这里可以添加更多的命令来处理每个文件
done
# 如果需要,可以将这些文件夹名称作为环境变量传递给后续的作业
export FOLDERS="$FOLDERS"
$CHANGED_FOLDERS = "$FOLDERS"
总结
CICD的核心逻辑就是 触发事件 → 执行脚本
只要能找到满足你需求的触发器,并且具备一定的脚本编写能力,几乎可以解决所有 CI/CD 的问题。
参考资料
[1] include: archives.docs.gitlab.com/16.6/ee/ci/…
[2] 在include中使用rules: archives.docs.gitlab.com/16.6/ee/ci/…
公众号: 老王聊前端