触发 GitHub 工作流程调度事件后获取运行 ID

19

我通过GitHub的Rest API触发了工作流运行。但是GitHub在响应体(204)中不发送任何数据。

如何获得触发请求的运行ID?

我知道getRunsList API,它将返回工作流程ID的运行列表,然后我可以获取最新的运行,但是当两个请求几乎同时提交时,这可能会引起问题。


1
这个问题的Github支持票已经关闭,并回复说:“目前不支持。Github已将此列入其内部功能列表。需要遵循一些解决方法,但可能会有错误。”请在下面找到已接受的答案以获取解决方法。 - saivishnu tammineni
1
请参考 GitHub 社区中的此 讨论 - Neil Mayhew
7个回答

20

当前无法在调度响应本身中获取与调度API调用关联的run_id,但是如果您可以稍微编辑工作流文件,有一种方法可以找到这个信息。

您需要通过以下方式使用“input”来调度工作流:


curl "https://api.github.com/repos/$OWNER/$REPO/actions/workflows/$WORKFLOW/dispatches" -s \
     -H "Authorization: Token $TOKEN" \
     -d '{
        "ref":"master",
        "inputs":{
            "id":"12345678"
        }
    }'

同时编辑您的工作流yaml文件,加上一个可选的input(这里命名为id)。并将其作为第一个工作,一个工作只有一个步骤,该步骤名称与输入的id值相同(这是我们将如何使用API获取id的方式!):

name: ID Example

on:
  workflow_dispatch:
    inputs:
      id:
        description: 'run identifier'
        required: false
jobs:
  id:
    name: Workflow ID Provider
    runs-on: ubuntu-latest
    steps:
      - name: ${{github.event.inputs.id}}
        run: echo run identifier ${{ inputs.id }}

这里的技巧是使用 name: ${{github.event.inputs.id}}

https://docs.github.com/cn/actions/creating-actions/metadata-syntax-for-github-actions#inputs

然后,步骤如下:

  • 运行带有名为 idinput 的调度 API 调用,并传入一个随机值。

POST https://api.github.com/repos/$OWNER/$REPO/actions/workflows/$WORKFLOW/dispatches
在一个循环中获取自现在起5分钟前创建的运行(差值是为了避免时间差问题):
GET https://api.github.com/repos/$OWNER/$REPO/actions/runs?created=>$run_date_filter

示例

  • 在运行API响应中,您将获取一个jobs_url,您将调用它:

GET https://api.github.com/repos/$OWNER/$REPO/actions/runs/[RUN_ID]/jobs
上面的作业 API 调用返回作业列表,因为你将 id 作业声明为第一个作业,所以它将在第一个位置。它还会提供带有步骤名称的步骤信息。类似于这样:
{
  "id": 3840520726,
  "run_id": 1321007088,
  "run_url": "https://api.github.com/repos/$OWNER/$REPO/actions/runs/1321007088",
  "run_attempt": 1,
  "node_id": "CR_kwDOEi1ZxM7k6bIW",
  "head_sha": "4687a9bb5090b0aadddb69cc335b7d9e80a1601d",
  "url": "https://api.github.com/repos/$OWNER/$REPO/actions/jobs/3840520726",
  "html_url": "https://github.com/$OWNER/$REPO/runs/3840520726",
  "status": "completed",
  "conclusion": "success",
  "started_at": "2021-10-08T15:54:40Z",
  "completed_at": "2021-10-08T15:54:43Z",
  "name": "Hello world",
  "steps": [
    {
      "name": "Set up job",
      "status": "completed",
      "conclusion": "success",
      "number": 1,
      "started_at": "2021-10-08T17:54:40.000+02:00",
      "completed_at": "2021-10-08T17:54:42.000+02:00"
    },
    {
      "name": "12345678", <=============== HERE
      "status": "completed",
      "conclusion": "success",
      "number": 2,
      "started_at": "2021-10-08T17:54:42.000+02:00",
      "completed_at": "2021-10-08T17:54:43.000+02:00"
    },
    {
      "name": "Complete job",
      "status": "completed",
      "conclusion": "success",
      "number": 3,
      "started_at": "2021-10-08T17:54:43.000+02:00",
      "completed_at": "2021-10-08T17:54:43.000+02:00"
    }
  ],
  "check_run_url": "https://api.github.com/repos/$OWNER/$REPO/check-runs/3840520726",
  "labels": [
    "ubuntu-latest"
  ],
  "runner_id": 1,
  "runner_name": "Hosted Agent",
  "runner_group_id": 2,
  "runner_group_name": "GitHub Actions"
}

id 步骤的 name 返回您的输入值,因此您可以放心确认它是由您的调度调用触发的运行。

以下是在 中实现此流程的示例,它将返回工作流运行 ID:

import random
import string
import datetime
import requests
import time

# edit the following variables
owner = "YOUR_ORG" 
repo = "YOUR_REPO"
workflow = "dispatch.yaml"
token = "YOUR_TOKEN"

authHeader = { "Authorization": f"Token {token}" }

# generate a random id
run_identifier = ''.join(random.choices(string.ascii_uppercase + string.digits, k=15))
# filter runs that were created after this date minus 5 minutes
delta_time = datetime.timedelta(minutes=5)
run_date_filter = (datetime.datetime.utcnow()-delta_time).strftime("%Y-%m-%dT%H:%M") 

r = requests.post(f"https://api.github.com/repos/{owner}/{repo}/actions/workflows/{workflow}/dispatches",
    headers= authHeader,
    json= {
        "ref":"master",
        "inputs":{
            "id": run_identifier
        }
    })

print(f"dispatch workflow status: {r.status_code} | workflow identifier: {run_identifier}")
workflow_id = ""

while workflow_id == "":
        
    r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/actions/runs?created=%3E{run_date_filter}",
        headers = authHeader)
    runs = r.json()["workflow_runs"]

    if len(runs) > 0:
        for workflow in runs:
            jobs_url = workflow["jobs_url"]
            print(f"get jobs_url {jobs_url}")

            r = requests.get(jobs_url, headers= authHeader)
            
            jobs = r.json()["jobs"]
            if len(jobs) > 0:
                # we only take the first job, edit this if you need multiple jobs
                job = jobs[0]
                steps = job["steps"]
                if len(steps) >= 2:
                    second_step = steps[1] # if you have position the run_identifier step at 1st position
                    if second_step["name"] == run_identifier:
                        workflow_id = job["run_id"]
                else:
                    print("waiting for steps to be executed...")
                    time.sleep(3)
            else:
                print("waiting for jobs to popup...")
                time.sleep(3)
    else:
        print("waiting for workflows to popup...")
        time.sleep(3)

print(f"workflow_id: {workflow_id}")

gist链接

示例输出

$ python3 github_action_dispatch_runid.py
dispatch workflow status: 204 | workflow identifier: Z7YPF6DD1YP2PTM
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321463229/jobs
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321463229/jobs
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321463229/jobs
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321475221/jobs
waiting for steps to be executed...
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321463229/jobs
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321475221/jobs
waiting for steps to be executed...
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321463229/jobs
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321475221/jobs
waiting for steps to be executed...
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321463229/jobs
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321475221/jobs
get jobs_url https://api.github.com/repos/OWNER/REPO/actions/runs/1321463229/jobs
workflow_id: 1321475221

如果有一种通过API检索工作流输入的方法,那么就会更容易,但目前没有这样的方法。

请注意,在工作流文件中,我使用${{github.event.inputs.id}},因为${{inputs.id}}无法正常工作。当我们将其用作步骤名称时,似乎inputs没有被评估。


1
感谢这些解决方案,我提到了一些步骤的注意事项。如果工作流程已排队但尚未处理,则步骤未在REST API响应中列出,我遇到了相关问题,可以通过检查工作流运行状态并再次获取该工作流运行数据来解决,直到状态发生更改。此外,作业步骤仅在执行后才会出现,挂起的步骤不会列出,在设置自定义ID之前处于“设置作业”步骤中可能会导致一些错误。我已经采用了相同的解决方案,重试直到至少有2个步骤。 - Rodrigo.C

2
获取工作流ID
gh workflow list --repo <repo-name>

触发类型为workflow_dispatch的工作流程

gh workflow run $WORKFLOWID --repo <repo-name>

它没有返回所需的运行ID,以获取执行状态

获取最新的运行ID WORKFLOW_RUNID

gh run list -w $WORKFLOWID --repo <repo> -L 1 --json databaseId | jq '.[]| .databaseId'

获取工作流运行详情。
gh run view --repo <repo> $WORKFLOW_RUNID

这是我们所采用的解决办法。虽然不完美,但应该可以正常工作。


0

受上面评论的启发,我制作了一个/bin/bash脚本,可以获取您的$run_id。

name: ID Example

on:
  workflow_dispatch:
    inputs:
      id:
        description: 'run identifier'
        required: false
jobs:
  id:
    name: Workflow ID Provider
    runs-on: ubuntu-latest
    steps:
      - name: ${{github.event.inputs.id}}
        run: echo run identifier ${{ inputs.id }}

workflow_id= 会生成一个随机的8位数字
now, later, date_filter= 用于时间过滤,例如:now - 5 minutes

  • 生成一个随机ID
  • POST作业并触发工作流程
  • 获取 action/runs 并按降序排列获取第一个 .workflow_run[].id
  • 循环执行直到脚本匹配步骤1中的随机ID
  • 输出 run_id
TOKEN="" \
GH_USER="" \
REPO="" \
REF=""
WORKFLOW_ID=$(tr -dc '0-9' </dev/urandom | head -c 8) \
NOW=$(date +"%Y-%m-%dT%H:%M") \
LATER=$(date -d "-5 minutes" +"%Y-%m-%dT%H:%M") \
DATE_FILTER=$(echo "$NOW-$LATER") \
JSON=$(cat <<-EOF
{"ref":"$REF","inputs":{"id":"$WORKFLOW_ID"}}
EOF
) && \

curl -s \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.github.com/repos/$GH_USER/$REPO/actions/workflows/main.yml/dispatches" \
-d $JSON && \

INFO="null" \
COUNT=1 \
ATTEMPTS=10 && \
until [[ $CHECK -eq $WORKFLOW_ID ]] || [[ $COUNT -eq $ATTEMPTS ]];do
echo -e "$(( COUNT++ ))..."
INFO=$(curl -s \
-X GET \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.github.com/repos/$GH_USER/$REPO/actions/runs?created:<$DATE_FILTER" | jq -r '.workflow_runs[].id' | grep -m1 "")

CHECK=$(curl -s \
-X GET \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.github.com/repos/$GH_USER/$REPO/actions/runs/$INFO/jobs" | jq -r '.jobs[].steps[].name' | grep -o '[[:digit:]]*')
sleep 5s
done

echo "Your run_id is $CHECK"

输出:

1...
2...
3...
Your run_id is 67530050

0

我建议使用convictional/trigger-workflow-and-wait操作:

- name: Example
  uses: convictional/trigger-workflow-and-wait@v1.6.5
  with:
    owner: my-org
    repo: other-repo
    workflow_file_name: other-workflow.yaml
    github_token: ${{ secrets.GH_TOKEN }}
    client_payload: '{"key1": "value1", "key2": "value2"}'

这个函数会等待其他任务完成,并根据其他工作流的成功或失败返回成功或失败。它以一种健壮的方式处理几乎同时触发多次运行的情况。


这个不够健壮。如果一个矩阵作业使用此操作来运行具有不同输入选项的工作流程,您将得到典型的竞争结果,这是 GitHub API 强制我们拥有的。在此操作中,实际上没有任何将工作流程调度与运行 ID 相关联的东西。 - Brian Sidebotham

0

关联:我承认我是这个GitHub Action的主要贡献者。

lasith-kg/dispatch-workflow@v1 是一个GitHub Action,它可以使用repository_dispatchworkflow_dispatch事件来调度工作流,并且具有可选标志discover: true。当启用此标志时,它会将一个唯一的UUID注入到工作流运行名称中,以便与初始调度事件相关联。一旦发现了工作流,就可以从步骤输出中获取运行ID运行URL

steps:
  - uses: lasith-kg/dispatch-workflow@v1
    id: dispatch-with-discovery
    name: "Dispatch Workflow With Discovery"
    with:
      dispacth-method: workflow_dispatch | repository_dispatch
      ...
      discover: true
  - id: echo-run-id-url
    name: "Echo Run ID and Run URL"
    run: |
      echo "${{ steps.dispatch-with-discovery.outputs.run-id }}"
      echo "${{ steps.dispatch-with-discovery.outputs.run-url }}"

自然地,接收工作流需要修改以拦截run-name中的此UUID。

workflow_dispatch

name: Receiving Workflow
run-name: Receiving Workflow [${{ inputs.distinct_id && inputs.distinct_id || 'N/A' }}]

on:
  workflow_dispatch:
    inputs:
      distinct_id:
        description: 'Distinct ID'
        required: false

repository_dispatch

name: Receiving Workflow
run-name: >
  Receiving Workflow [${{
    github.event.client_payload.distinct_id &&
    github.event.client_payload.distinct_id || 'N/A' }}]

on:
  repository_dispatch:
    types:
      - deploy

0
如果您正在寻找一种可靠的方法,在调度工作流后获取运行 ID,请查看return-dispatch GitHub 动作。它可以简化该过程并提供直接的解决方案。
简单使用-
- name: Dispatch an action and get the run ID
    uses: codex-/return-dispatch@v1
    id: return_dispatch
    with:
      token: ${{ secrets.ACCESS_TOKEN }} # Note this is NOT GITHUB_TOKEN but a PAT
      ref:  githubActions_iOS/build # or refs/heads/target_branch
      repo: feature
      owner: mr-x
      workflow: shared.yml
      workflow_inputs: '{ "distinct_id": "123" }' # Optional
      workflow_timeout_seconds: 120 # Default: 300

接下来,您可以读取run_id

- name: Use the output run ID
  run: echo ${{steps.return_dispatch.outputs.run_id}}

-1
整个想法是要知道哪个运行被调度了,当建议使用id进行调度时,预计可以在对此URL“actions/runs”的GET调用的响应中找到该id,因此现在用户能够识别适当的运行以进行监视。注入的id不是响应的一部分,因此提取另一个URL以查找您的id是没有帮助的,因为这是需要标识运行以进行监视的id的关键点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接