共用相同的 Workflow,簡單介紹 Action Composition

程式技術
sharkHead
共用相同的 Workflow,簡單介紹 Action Composition

GitHub Action 是一個很方便的 CI/CD 工具,你可以用它來自動化程式部署前的多項任務,例如測試、靜態分析和排版檢查。但如果任務太多,用來描述 CI/CD 流程的定義檔案可能會變得冗長,並包含重複的部分。

舉個例子,假設我有一個 CI/CD 流程需要打包 4 個 Docker 映像檔,為了提高效率,這 4 個映像的打包會分別在不同的 job 中同時進行。這也意味著,我需要在定義檔中重複寫 4 次 Docker Hub 登入操作。

# 超級長的 workflow 定義檔案
name: Build images

# ...

jobs:
  build-hello-1-image:
    # 登入 Docker Hub 並打包映像檔
    name: Start to build images and publish to docker hub
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}

      - name: Build and push image to Docker Hub
        uses: docker/build-push-action@v5
        with:
          file: dockerfiles/hello-1.Dockerfile
          platforms: linux/amd64,linux/arm64
          push: true
          tags: |
            allen/hello-1:latest
            allen/hello-1:1.0.0
          cache-from: type=registry,ref=allen/hello-1:buildcache
          cache-to: type=registry,ref=allen/hello-1:buildcache,mode=max

  # 上面的流程改一下 image 名稱後,重複三次... 😰
  build-hello-2-image:
    # ...

  build-hello-3-image:
    # ...

  build-hello-4-image:
    # ...

為了避免反覆書寫相同的流程定義,GitHub Action 提供了一種叫做 Composite Action 的功能。你可以將重複的定義抽出來,形成一個獨立的 action。透過重複使用這個 action,你能夠更有效的組織你的流程定義,使檔案的內容更加精簡。

首先我們在 .github 資料夾中建立一個 actions 資料夾,這個資料夾底下可以放入各種 action 的定義檔案。

接下來建立一個用來登入 Docker Hub 並打包 Docker 映像檔的 action,在 actions 底下,我們建立一個 build-image 資料夾, 並在裡面建立一個 action.yaml 檔案,這個檔案就是我們的 action 定義檔案。

此時的資料夾結構如下:

.github
└── actions
    └── build-image
        └── action.yaml

action.yml 的內容如下:

# .github/actions/build-image/action.yaml
name: Login to Docker Hub and build images

description: A GitHub Action to login to Docker Hub, then build images and publish to docker hub

# Composite Action 的輸入參數,可以用來設定 Docker Hub 的帳號密碼以及映像檔的名稱
inputs:
  registry_username:
    description: “Username for image registry”
    required: true
  registry_password:
    description: “Password for image registry”
    required: true
  file:
    description: “Path to the Dockerfile to build”
    required: true
  image_name:
    description: “Name of the image to push”
    required: true
  image_tag:
    description: “Tag of the image to push”
    required: true

# Composite Action 的執行步驟
runs:
  using: "composite"
  steps:
    - name: Set up QEMU
      uses: docker/setup-qemu-action@v3

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3

    - name: Login to Docker Hub
      uses: docker/login-action@v3
      with:
        # 使用剛剛的輸入參數
        username: ${{ inputs.registry_username }}
        password: ${{ inputs.registry_password }}

    - name: Build and push image to Docker Hub
      uses: docker/build-push-action@v5
      with:
        file: ${{ inputs.file }}
        platforms: linux/amd64,linux/arm64
        push: true
        tags: |
          ${{ inputs.image_name }}:latest
          ${{ inputs.image_name }}:${{ inputs.image_tag }}
        cache-from: type=registry,ref=${{ inputs.image_name }}:buildcache
        cache-to: type=registry,ref=${{ inputs.image_name }}:buildcache,mode=max

可以看到 Composite Action 就好像 Terraform 的模組,你可以設定需要提供哪些參數,並在內部使用外部傳入的參數。

接下來我們修改原本的 workflow 定義檔案,把映像檔的打包步驟改為使用剛剛建立的 action:

# .github/workflows/build-image.yml
name: Build images

# ...

jobs:
  build-hello-1-image:
    name: Start to build images and publish to docker hub
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      # 使用 Composite Action 並傳入參數
      # 流程不變,但可以少打很多行字 👍
      - name: Build and push image to Docker Hub
        uses: ./.github/actions/build-image
        with:
          registry_username: ${{ secrets.REGISTRY_USERNAME }}
          registry_password: ${{ secrets.REGISTRY_PASSWORD }}
          file: dockerfiles/hello-1.Dockerfile
          image_name: allen/hello
          image_tag: 1.0.0

  # 下面的流程也同樣使用 Composite Action,並傳入不同的輸入參數
  build-hello-2-image:
    # ...

  build-hello-3-image:
    # ...

  build-hello-4-image:
    # ...

上述的做法是將 action 與 workflow 放在同一個專案底下,如果想讓不同專案共用相同的 action,你可以單獨建立一個 GitHub 專案來放常常使用到的 action。

然後在 workflow 中使用 uses 來指定專案中的 action:

# ...

jobs:
  hello_world_job:
    runs-on: ubuntu-latest
    name: A job to say hello
    steps:
      - uses: actions/checkout@v4

      # 使用 GitHub 上的 action
      - uses: my-github-account/reusable-actions/hello-world-composite-action@v1

有了 Action Composition,我們就可以將流程模組化,除了可以少寫很多行字,流程上的管理也會更輕鬆,當有流程的內容需要修改時,我們只需要修改 action 的內容即可,而不用修改每一個專案中 workflow。

參考資料

sharkHead
written by
sharkHead

後端打工仔,在下班後喜歡研究各種不同的技術。稍微擅長 PHP,並偶爾涉獵前端開發。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。

0 則留言