Get run id after triggering a github workflow dispatch event
Asked Answered
S

7

21

I am triggering a workflow run via github's rest api. But github doesn't send any data in the response body (204). How do i get the run id of the trigger request made? I know about the getRunsList api, which would return runs for a workflow id, then i can get the latest run, but this can cause issues when two requests are submitted at almost the same time.

Scampi answered 7/10, 2021 at 10:17 Comment(2)
A github support ticket for this issue was closed with response: "This is not currently supported. Github has this in their internal feature list. Some workaround needs to be followed, but will be buggy." For workaround please find the accepted answer below.Scampi
See also this discussion in the GitHub community.Muldrow
R
25

This is not currently possible to get the run_id associated to the dispatch API call in the dispatch response itself, but there is a way to find this out if you can edit your worflow file a little.

You need to dispatch the workflow with an input like this:

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

Also edit your workflow yaml file with an optionnal input (named id here). Also, place it as the first job, a job which has a single step with the same name as the input id value (this is how we will get the id back using the API!):

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 }}

The trick here is to use name: ${{github.event.inputs.id}}

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

Then the flow is the following:

  • run the dispatch API call along with the input named id in this case with a random value

    POST https://api.github.com/repos/$OWNER/$REPO/actions/workflows/$WORKFLOW/dispatches
    
  • in a loop get the runs that have been created since now minus 5 minutes (the delta is to avoid any issue with timings):

    GET https://api.github.com/repos/$OWNER/$REPO/actions/runs?created=>$run_date_filter
    

example

  • in the run API response, you will get a jobs_url that you will call:

    GET https://api.github.com/repos/$OWNER/$REPO/actions/runs/[RUN_ID]/jobs
    
  • the job API call above returns the list of jobs, as you have declared the id jobs as 1st job it will be in first position. It also gives you the steps with the name of the steps. Something like this:

{
  "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"
}

The name of the id step is returning your input value, so you can safely confirm that it is this run that was triggered by your dispatch call

Here is an implementation of this flow in , it will return the workflow run 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 link

Sample output

$ 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

It would have been easier if there was a way to retrieve the workflow inputs via API but there is no way to do this at this moment

Note that in the worflow file, I use ${{github.event.inputs.id}} because ${{inputs.id}} doesn't work. It seems inputs is not being evaluated when we use it as the step name

Ragsdale answered 8/10, 2021 at 18:47 Comment(1)
Thanks for this solutions, I mention some consideration for the steps mentioned above. I had some problems related to the steps not being listed in the rest API response if the workflow is queued and not being processed yet, which I could solve checking the workflow run state and fetching that workflow run data again until the state has changed. Additionally the job steps will appear only if they were executed, pending steps will not be listed, causing some errors if it's in the "Set up job" step before setting our custom id. I've applied same solution to retry until it has at least 2 stepsGulley
L
2

Get WORKFLOWID

gh workflow list --repo <repo-name>

Trigger workflow of type workflow_dispatch

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

It doesnot return the run-id which is required get the status of execution

Get latest run-id WORKFLOW_RUNID

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

Get workflow run details

gh run view --repo <repo> $WORKFLOW_RUNID

This is workaround that we do. It is not perfect, but should work.

License answered 14/4, 2022 at 16:16 Comment(0)
C
0

inspired by the comment above, made a /bin/bash script which gets your $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= generates a random 8 digit number
now, later, date_filter= use for time filter, now - 5 minutes \

  • generates a random ID
  • POST job and trigger workflow
  • GET action/runs descending and gets first .workflow_run[].id
  • keeps looping until script matches random ID from step 1
  • echo 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"

output:

1...
2...
3...
Your run_id is 67530050
Campobello answered 24/9, 2022 at 13:41 Comment(0)
M
0

I recommend using the convictional/trigger-workflow-and-wait action:

- name: Example
  uses: convictional/[email protected]
  with:
    owner: my-org
    repo: other-repo
    workflow_file_name: other-workflow.yaml
    github_token: ${{ secrets.GH_TOKEN }}
    client_payload: '{"key1": "value1", "key2": "value2"}'

This takes care of waiting for the other job and returning a success or failure according to whether the other workflow succeeded or failed. It does so in a robust way that handles multiple runs being triggered at almost the same time.

Muldrow answered 7/12, 2022 at 4:4 Comment(1)
This is not robust. If the case of a matrix job using this action to run a workflow with different input options you'll get the typical racy results that GitHub's API forces us to have. There's nothing that actually ties a workflow dispatch to a run id in this action.Endurable
S
0

If you're looking for a reliable way to get the run ID after dispatching a workflow, check out the return-dispatch GitHub Action. It simplifies the process and provides a straightforward solution.

simple usage-

- 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

next you can read the run_id

- name: Use the output run ID
  run: echo ${{steps.return_dispatch.outputs.run_id}}
Supporting answered 23/5, 2023 at 19:4 Comment(0)
T
0

Affilitiation: I acknowledge that I am a major contributor to this GitHub Action.

lasith-kg/dispatch-workflow@v1 is a GitHub Action that dispatches workflows either using repository_dispatch or workflow_dispatch event and has an optional flag discover: true. When this flag is enabled, it injects a unique UUID into the Workflow Run Name so that it can be correlated to the initial dispatch event. Once the workflow is discovered, the Run ID and Run URL is available from the step outputs.

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 }}"

Naturally, the receiving workflow needs to be modified to intercept this UUID in the run-name

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
Theoretics answered 8/8, 2023 at 0:27 Comment(0)
C
-1

the whole idea is to know which run was dispatched, when id was suggested to use on dispatch, this id is expected to be found in the response of the GET call to this url "actions/runs" so now user is able to identify the proper run to monitor. The injected id is not part of the response, so extracting another url to find your id is not helpful since this is the point where id needed to identify the run for monitoring

Chant answered 19/5, 2022 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.