如何通过GitLab实现单仓库多项目的自动化部署?

728 阅读6分钟

大家好,我是老王。

前几天跟朋友聊天,他说最近搞了项目,一堆简单的H5项目放到一个仓库进行管理,一个目录是项目,都是从其他历史代码里复制过来用的,技术栈不一样,编译部署方式还不一样,有的甚至不需要编译就是一个html文件。

我很奇怪。

问他,为什么要放到一个仓库呢?直接用历史代码fork出来搞一下不行吗。

他说,统一管理,方便后续快速复制…巴拉巴拉….

好吧。

一个仓库,每个目录都是独立部署上线,并且部署的位置不同,使用的技术栈也不同,CICD给他带来了很大的负担。最后写了一个比较复杂的脚本来控制,很是麻烦。

我建议他用看看能不能用GitLab CICD去解决目录更新后的编译部署问题。

也是这篇文章的由来。

通过本文你可以了解

  1. 目录级CICD的实现思路及脚本

  2. 文件级CICD的实现思路

文章内容默认读者具备GitLab CICD相关基础知识,如果不了解,可以根据基础概念中提到的内容自行查阅学习。相关的资料非常多。这里就不再赘述。

基础概念

  1. 持续集成(Continuous Integration,CI)。持续集成是一种开发实践,旨在确保团队成员对代码的修改能够快速、频繁地集成到共享存储库中,并通过自动化的构建和测试流程进行验证。主要目标是减少集成问题,促进团队成员之间的协作,并提高软件质量。

  2. 持续交付/持续部署(Continuous Delivery/Continuous Deployment,CD):

  3. 持续交付(Continuous Delivery,CD): 一种软件开发实践,目标是确保软件随时都是可发布的状态。即使完成了开发和测试,也要保持软件能够随时进行部署到生产环境的准备状态,但实际的部署动作可能需要手动触发。

  4. 持续部署(Continuous Deployment,CD): 是持续交付的一种更进一步的实践,自动化了将通过测试的代码部署到生产环境的过程。这种方式减少了人为干预的需要,通过自动化流程实现了快速部署。

  5. 仓库(Repository):

代码存储的地方,可以包含项目的所有文件和版本历史。

  1. CI/CD Pipeline(流水线):

一系列自动化任务组成的工作流程,用于构建、测试和部署代码。CICD的最顶层组件,5-9都是服务于流水线,或者是流水线的组成部分。

  1. Job(任务):

流水线中的一个单独步骤或任务,例如构建代码、运行测试或部署应用程序。

  1. Stage(阶段):

一组相关的任务集合,按顺序执行。例如,构建、测试和部署可以是不同的阶段。

  1. Artifact(构件):

流水线任务生成的输出文件或数据,可以用于后续任务或部署操作。

  1. Trigger(触发器):

启动流水线的事件或条件,例如代码推送、定时触发或外部触发器。

  1. Variables(变量):

流水线中可用的环境变量,用于传递参数或配置任务。

  1. .gitlab-ci.yml(配置文件)

.gitlab-ci.yml 文件的作用是定义和配置项目的 CI/CD 流程,通过配置,控制流水线如何执行、包含那些任务等。其提供了丰富的配置项,就包含了诸如:5-9的配置信息。文档参考:docs.gitlab.cn/jh/ci/yaml/

实现目录级CICD需要满足的条件

  1. 目录中文件变更能够触发(trigger)CICD

  2. 不同目录有各自的配置,以支持不同的编译部署方式

  3. 支持多个目录同时有变更时,能够并行Job,同时部署多个目录

思路

  1. 在每个项目目录添加.gitlab-ci.yml 配置文件,进行个性化配置,支持不同项目的编译部署;

  2. 在根目录.gitlab-ci.yml 配置文件中,include其他工程目录的.gitlab-ci.yml配置文件;

  3. 根据目录变化,执行相应目录.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个项目都有修改就会如图所示

Untitled.png

如果想在根目录的配置文件中检测文件或目录的变化,可以在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/…

公众号: 老王聊前端