github-actionsgithub-apigithub-enterprisegithub-api-v3workflow-dispatch

Get run id after triggering a github workflow dispatch event


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.


Solution

  • 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:

    example

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