9

GitLab CI CD:跨项目触发pipeline

 2 years ago
source link: https://dothinking.github.io/2022-01-27-GitLab-CI-CD%EF%BC%9A%E8%A7%A6%E5%8F%91%E8%B7%A8%E9%A1%B9%E7%9B%AEpipeline/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

GitLab CI CD:跨项目触发pipeline

发布于:2022-01-27 | 分类:devops


近期遇到一个问题,如何灵活管理分系统的版本号和最终产品的版本号。例如,对于前后端分离且分仓库管理的Web项目,前、后端维护各自的版本号(前端1.2.3,后端1.3.4),并且发布Web产品时自动根据tag(1.0.0)作为产品的版本号编译到页面上。注意问题在于:最终产品的版本号很可能与前、后端都不相同,因此通过前端或者后端仓库提交tag发布产品都不合适

基本思路:创建发布仓库

因此考虑新建一个发布仓库,仅维护汇总后的前后端更新日志和发布版本号。同时考虑到产品发布后的hotfix,也支持直接从受影响的前端或者后端仓库提交修复并部署。

综合来说,涉及两类自动化流程:

  • 从发布仓库发布产品,同时触发前、后端仓库的部署,并根据提供的tag更新产品版本号

  • 从前、后端发布hotfix,仅触发各自的部署,且不更新产品版本号,而是沿用发布仓库最新的tag

为简化表述,用A表示发布仓库,分别用B1B2表示前、后端仓库,则可用下图表示以上流程。

general-flow

具体流程

基本设定:

  • 仓库B1B2任意提交触发编译build,忽略测试test,如果是release分支则触发部署deploy

  • 仓库A仅在提交tag后触发流程

考虑到模块化和就近原则,仓库A负责获取当前最新的tag作为产品版本号,仓库B1B2负责各自的编译和发布,只不过编译前需要拿到产品版本号。参考下图具体流程:

  • 如果从仓库A提交tag触发流程,则直接获取最新版本号,然后分别触发仓库B1B2的流程。

    B1为例,注意此时直接到build这个任务;B2同理,不再赘述。

  • 如果从仓库B1提交,由于编译前需要获取产品当前版本号,因此通过trigger A任务触发仓库A的流程。

    类似上文所述A的流程,只不过此时需要根据原始仓库(即B1)确定只触发B1的流程,进而来到B1build

detail-flow

最终效果

借助 GitLab CI/CD 的基本功能,执行效果参考下图,可见完整实现了上述流程。

  • A发布版本1.1.0

result-1

  • B1release分支提交hotfix

result-2

实现细节

总结几个关键点:

  • 跨项目触发流程,例如上图中B1trigger AAtrigger B1trigger B2

  • 执行任务的时机,例如B1trigger A仅在直接提交时执行,而build仅当从上游流水pipeline触发时执行。

  • 跨项目触发流程时传递参数,例如通过B1trigger A进入A的流程时,传递表明原始仓库即B1的参数,以便选择性进入trigger B1

跨项目触发流程

这是本文的基础,具体说明参考官方文档 Multi-project pipelines

downstream-job:
  variables:
    UPSTREAM_BRANCH: $CI_COMMIT_REF_NAME
  trigger:
    project: path/to/downstream/project
    branch: stable-11-2
  • trigger关键字指定将要触发的下游项目的具体路径,同时可以指定基于的分支branch

    如果忽略分支即默认最新分支,则可以合并为trigger: path/to/downstream/project

  • variables关键字将当前流程的参数传递到下游流程的每个任务中去,例如此处$CI_COMMIT_REF_NAME表示当前分支名

    更多内置参数参考 Predefined CI/CD variables

trigger:project不支持变量输入,trigger:branch支持变量输入。

任务执行条件

GitLab CI/CD通过 only / exceptrules 关键字,以非常灵活的方式将任务加入或者排除当前流程。

本文相关的几个例子:

  • 从上游仓库触发B1不执行trigger A

    trigger_a:
        stage: trigger
        trigger: path/to/A
        except:
            - pipelines
  • 当且仅当从上游仓库触发B1时才执行build

    build:
        stage: build
        script:
            - ...
        only:
            - pipelines
  • 从上游仓库触发B1且当前分支是release时才执行deploy

    deploy:
        stage: deploy
        script:
            - echo "Deploying application only if branch=release..."
        rules: 
            - if: $CI_PIPELINE_SOURCE=="pipeline" && $SOURCE_BRANCH=="release"
    

    其中,CI_PIPELINE_SOURCE也是默认CI/CD参数,表示流程的触发源,值pipeline表明从上游流程触发而来;SOURCE_BRANCH也是从上游流程传递过来的表明当前分支的自定义参数。

  • Aget version number任务只有在tag提交或者从其他流程触发而来才执行

    get_version_number:
        stage: prepare
        script:
            - ...
        only:
            - /^([0-9]+)\.([0-9]+)\.([0-9]+)$/
            - pipelines
  • Atrigger B1任务满足以下两种情形之一即可执行:

    • 直接从A仓库tag提交

    • 从其他流程触发且自定义参数SOURCE_PROJECT的值等于B1即从B1触发的流程

    trigger_b1:
        stage: trigger
        trigger: 
            project: path/to/B1
            branch: $TARGET_BRANCH
        rules: 
            - if: $CI_COMMIT_TAG =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)$/
            - if: $CI_PIPELINE_SOURCE=="pipeline" && $SOURCE_PROJECT=="B1"

完整ymal文件

补充一点:本例的产品版本号是通过保存文件的形式在两个流程间传递的,因此需要保证各流程都在同一台gitlab-runner上执行。而这,可以通过设定相同的tags来实现。

最后,分别列出各仓库的ymal文件。

# .gitlab-ci.yml for project A

variables:
  PROJECT_B1: "B1"
  PROJECT_B2: "B2"
  VERSION_FILE: /tmp/version
  TARGET_BRANCH: "release"

stages:
  - prepare
  - trigger

check_version:
  stage: prepare
  script:
    - VERSION=$(git describe --tags $(git rev-list --tags --max-count=1))  # recent tag
    - if [ "$VERSION" = "" ]; then VERSION="1.0.0"; fi
    - VERSION="$VERSION.$(date +%Y%m%d)"
    - echo $VERSION>$VERSION_FILE
  only:
    - /^([0-9]+)\.([0-9]+)\.([0-9]+)$/
    - pipelines
  tags:
    - shell_runner


trigger_b1:
  stage: trigger
  variables:
    SOURCE_BRANCH: $TARGET_BRANCH
  trigger: 
    project: path/to/B1
    branch: $TARGET_BRANCH
  rules: 
    - if: $CI_COMMIT_TAG =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)$/
    - if: $CI_PIPELINE_SOURCE=="pipeline" && $SOURCE_PROJECT==$PROJECT_B1


trigger_b2:
  stage: trigger
  variables:
    SOURCE_BRANCH: $TARGET_BRANCH
  trigger: 
    project: path/to/B2
    branch: $TARGET_BRANCH
  rules: 
    - if: $CI_COMMIT_TAG =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)$/
    - if: $CI_PIPELINE_SOURCE=="pipeline" && $SOURCE_PROJECT==$PROJECT_B2
# .gitlab-ci.yml for project B1; same with B2

stages:
  - trigger
  - build
  - deploy

trigger_a:
    variables:  
      SOURCE_PROJECT: $CI_PROJECT_NAME
      TARGET_BRANCH: $CI_COMMIT_REF_NAME
    stage: trigger
    trigger: path/to/A
    except:
      - pipelines


build:
  stage: build
  script:
    - VERSION=$(cat $VERSION_FILE)
    - echo $VERSION
  only:
    - pipelines
  tags:
    - shell_runner


deploy:
  stage: deploy
  script:
    - echo "Deploying application only if branch=release..."
  rules: 
    - if: $CI_PIPELINE_SOURCE=="pipeline" && $SOURCE_BRANCH=="release"
  tags:
    - shell_runner

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK