How to use Github Action to automate your workflow.
記錄一下這幾年來、使用 Github Action 的一些心得:如何透過自動/手動觸發、來執行一些自動化的工作。
Concepts
主要來說,Github Action 是一個 CI/CD 的工具:透過撰寫正確的 YAML 檔案、定義觸發流程跟執行的指令, 來自動化執行一連串的工作。整個 YAML 檔案稱為 workflow,而每個 workflow 可以包含一個或多個 job,而每個 job 可以包含一個或多個 step,另外可以使用 on 來定義觸發的事件。
以下是一個複雜的例子,完整的語法可以參考 官方文件 查詢所有可以使用的指令。
# define the human-readable name of the workflow and show it in the Actions tab
name: Your first but not last Workflow
# define how to trigger the workflow by the following events
on:
# when push to the `main` branch or tag with pattern `v1.*`
push:
branches:
- main
tags:
- "v1.*"
# when create a pull request with:
#
# 1. the `.py` file modified
# 2. all the files inside the `src` directory
# 3. but not the files inside the `src/ignore` directory
pull_request:
paths:
- "*.py"
- "src/**"
- "!src/ignore/**"
# trigger on every day at 00:00 UTC
schedule:
- cron: "0 0 * * *"
# or manually trigger on the Actions tab
workflow_dispatch:
# with some necessary or optional inputs
inputs:
# define the necessary input with the default value and description
server:
description: "Your first input"
required: true
default: "localhost"
# define the optional input with the options value and description
options:
description: "Your second input"
required: false
default: "option_1"
options: ["option_1", "option_2", "option_3"]
# the global environment variables and may be overridden by job env
env:
# define the global environment variable
DEBUG: true
# define the environment variable from the input or use the default value
SERVER: ${{ inputs.server || 'example.com' }}
jobs:
your_first_job:
name: Your First Job on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 60 # define the job-level timeout (60 minutes)
env:
DEBUG: false # override the global environment variable
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v2
with:
python-version: "3.x"
- name: whoami
run: |
whoami
- name: whois
run: |
whois ${{ env.SERVER }}
on
on
是用來定義觸發的事件,可以是一個或多個事件,例如:push、pull_request、fork 等。
事件可以指定單一事件,像是 on: push
,也可以指定多個事件,像是 on: [push, pull_request]
。
如果需要更細膩的設定,可以根據不同的事件來指定更多的條件。像是當 push 到特定的分支時才觸發。
或者當 pull request 修改到特定的檔案時才觸發。
on:
push:
branches:
- master
- develop
pull_request:
paths:
- "src/**"
- "!src/ignore/**"
可以參考 官方文件 查詢所有可以使用的事件。
jobs
jobs
定義了一個或多個工作,每個工作可以包含一個或多個步驟。每個工作預設是平行執行 (parallel) 的,
如果需要串行執行,可以使用 needs
來定義依賴的工作。每一個job 都需要透過 runs-on
指定執行的環境。
這邊可以指定
Github-hosted runner
或者是
self-hosted runner
。每一個 job 都有自己的 job id,
可以透過這個 id 來指定依賴的工作或拿到特定 job 的結果。
概念上,每一個 job 都被視為是獨立的執行環境,所以每一個 job 都會有自己的環境變數、工作目錄等等。 不同的 job 之間是無法共享環境變數、工作目錄等參數。如果需要、可以透過 outputs 將結果傳遞給其他的 job,另外的 job 可以透過 needs 來拿到其他 job 的結果。
在 job 下會有一個或多個 step,可以在 job 下定義全域的環境變數等參數。job 下的 step 會繼承這些參數 ,但是可以透過 step 自己的參數來覆蓋全域的參數。
jobs:
your_first_job:
runs-on: ubuntu-latest
# the job-level environment variables and may be overridden by step env
env:
YOUR_ENV: "YOUR_VALUE"
# the job-level timeout and may be overridden by step timeout
timeout-minutes: 10
outputs:
your_output: ${{ steps.your_step_id.outputs.your_output }}
steps:
- name: Your First Step
id: your_step_id
run: |
echo "Hello, World!"
echo "your_output=THE SECRET RESULT" >> $GITHUB_OUTPUT
- name: Your Second Step
run: echo "Hello, World!"
your_second_job:
runs-on: ubuntu-latest
needs: your_first_job
steps:
- name: Your Another Step
run: echo "Hello, World!"
- name: your_first_job Output
run: echo "${{ needs.your_first_job.outputs.your_output }}"
strategy
如果同一個 job 會需要執行多次、每次執行的參數都不一樣,可以使用 strategy
來定義這些參數。
在 matrix
下可以定義多個變數,每個變數都代表一個維度。在 steps
下可以使用 ${{ matrix.YOUR_VARIABLE }}
來取得這些變數。以下面的例子則會執行 9 次,每次執行的環境變數都不一樣。
jobs:
your_first_job:
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [10, 12, 14]
steps:
- name: Your Step
run: |
echo "Run on ${{ matrix.os }} with node ${{ matrix.node }}"
steps
steps
定義了一個或多個步驟,每個步驟都是一個指令。每個步驟需要使用 uses 或 run 來定義執行的指令。
uses 是用來引用其他的 action,而 run 則是直接執行指令。每個步驟都有自己的環境變數、工作目錄等參數,
沒有設定的話會繼承 job 的參數。
每個 step 可以使用 id 指定一個唯一的 id,這樣可以在其他的 step 或 job 中使用這個 id 來取得這個步驟的結果。
此時可以透過 ${{ steps.your_step_id.outputs.your_output }}
來取得這個步驟的結果。如果需要重複傳遞結果,
可以透過複寫 $GITHUB_ENV
來覆蓋全域的環境變數,其他的 job 可以透過 ${{ env.your_output }}
來取得這個結果。
jobs:
your_first_job:
runs-on: ubuntu-latest
steps:
- name: Your First Step
id: your_step_id
run: |
echo "Hello, World!"
echo "your_output=THE SECRET RESULT" >> $GITHUB_OUTPUT
- name: Your Second Step
run: |
echo "${{ steps.your_step_id.outputs.your_output }}"
echo "your_output=THE NEW SECRET RESULT" >> $GITHUB_ENV
uses
再重複利用的情境時、可以使用 uses
來引用其他的 action。這樣可以將重複的部分抽出來、讓 workflow 更加簡潔。
而這個 workflow 可以是同個 repository 的 workflow,也可以是其他 repository 的 workflow。
如果是同個 repository,使用./the/path/of/your/workflow.yaml@v1
來引用。如果是其他 repository 使用
your-org/your-repo/another/workflow.yaml@v1
。如果需要傳遞參數,可以使用 with
來傳遞參數,而 workflow
可以透過 ${{ inputs.input_key }}
來取得這些參數。另外也可以使用 env
來傳遞環境變數。
jobs:
your_first_job:
uses: ./the/path/of/your/workflow.yaml@v1
your_second_job:
uses: your-org/your-repo/another/workflow.yaml@v1
with:
input_key: "YOUR INPUT VALUE"