Retrieve workflow ID for a given PR

Hi everyone! I need to get the artefacts from the last workflow run for a PR associated with a commit SHA.

The steps I envisage are:

1. Retrieve PR by commit SHA
2. Get last workflow ID for that PR
3. Get artefacts by workflow ID

I can do 1 with /repos/{owner}/{repo}/commits/{commit_sha}/pulls and 3 with /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts, but I’m missing how to do step 2.

I have looked at the example response data for /repos/{owner}/{repo}/commits/{commit_sha}/pulls but it doesn’t contain any references to workflow runs that it triggered.

Is there an API that allows me to retrieve a workflow ID by using a PR’s ID?

Thanks in advance!

1 Like

I’ve been trying to do gymnastics like this for months.

The GraphQL API lets you get from a CheckRun to a WorkflowRun.

Here’s a check-run:
https://api.github.com/repos/jsoref/examples-testing/check-runs/3456649098

{
  "id": 3456649098,
  "name": "Spell checking",
  "node_id": "MDg6Q2hlY2tSdW4zNDU2NjQ5MDk4",
...
}

Taking the node_id and posting to https://api.github.com/graphql

{
  node(id: "MDg6Q2hlY2tSdW4zNDU2NjQ5MDk4")  {
    __typename
    ... on CheckRun {
      checkSuite {
        commit {
          commitUrl
        }
        workflowRun {
          url
        }
      }
      url
    }
  }
}

yields:

{
  "data": {
    "node": {
      "__typename": "CheckRun",
      "checkSuite": {
        "commit": {
          "commitUrl": "https://github.com/jsoref/examples-testing/commit/a826ccb09b685930af00723ee937dc11716b32b7"
        },
        "workflowRun": {
          "url": "https://github.com/jsoref/examples-testing/actions/runs/1180203998"
        }
      },
      "url": "https://github.com/jsoref/examples-testing/runs/3456649098"
    }
  }
}

(I’ve left the top url field for show, you can see it has an artifact.)

Taking the workflowRun.url:

and switching to the v3 API form (i.e. adding api. to the domain and repos/ at the beginning of the path):
https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998

{
  "id": 1180203998,
  "name": "Spell checking",
  "node_id": "WFR_kwLODlzVTc5GWHve",
  "head_branch": "IFooBar",
  "head_sha": "a826ccb09b685930af00723ee937dc11716b32b7",
  "run_number": 381,
  "event": "pull_request_target",
  "status": "completed",
  "conclusion": "failure",
  "workflow_id": 713920,
  "check_suite_id": 3625854331,
  "check_suite_node_id": "MDEwOkNoZWNrU3VpdGUzNjI1ODU0MzMx",
  "url": "https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998",
  "html_url": "https://github.com/jsoref/examples-testing/actions/runs/1180203998",
  "pull_requests": [
    {
      "url": "https://api.github.com/repos/jsoref/examples-testing/pulls/8",
      "id": 699280813,
      "number": 8,
      "head": {
        "ref": "IFooBar",
        "sha": "a57d9e2366f613a3a44231d4f8d79a09ec3433b3",
        "repo": {
          "id": 240964941,
          "url": "https://api.github.com/repos/jsoref/examples-testing",
          "name": "examples-testing"
        }
      },
      "base": {
        "ref": "youtube",
        "sha": "7a74808e65a9536f9b7e47995d474bbf20e3ed93",
        "repo": {
          "id": 240964941,
          "url": "https://api.github.com/repos/jsoref/examples-testing",
          "name": "examples-testing"
        }
      }
    }
  ],
  "created_at": "2021-08-29T22:23:17Z",
  "updated_at": "2021-08-29T22:23:55Z",
  "jobs_url": "https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998/jobs",
  "logs_url": "https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998/logs",
  "check_suite_url": "https://api.github.com/repos/jsoref/examples-testing/check-suites/3625854331",
  "artifacts_url": "https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998/artifacts",
  "cancel_url": "https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998/cancel",
  "rerun_url": "https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998/rerun",
  "workflow_url": "https://api.github.com/repos/jsoref/examples-testing/actions/workflows/713920",
  "head_commit": {
    "id": "a826ccb09b685930af00723ee937dc11716b32b7",
    "tree_id": "4bc11cc5e7440d14c4092efba348074979944ab4",
    "message": "go back",
    "timestamp": "2021-08-29T22:23:12Z",
    "author": {
      "name": "Josh Soref",
      "email": "jsoref@users.noreply.github.com"
    },
    "committer": {
      "name": "Josh Soref",
      "email": "jsoref@users.noreply.github.com"
    }
  },
  "repository": {
    "id": 240964941,
    "node_id": "MDEwOlJlcG9zaXRvcnkyNDA5NjQ5NDE=",
    "name": "examples-testing",
    "full_name": "jsoref/examples-testing",
    "private": false,
    "owner": {
      "login": "jsoref",
      "id": 2119212,
      "node_id": "MDQ6VXNlcjIxMTkyMTI=",
      "avatar_url": "https://avatars.githubusercontent.com/u/2119212?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/jsoref",
      "html_url": "https://github.com/jsoref",
      "followers_url": "https://api.github.com/users/jsoref/followers",
      "following_url": "https://api.github.com/users/jsoref/following{/other_user}",
      "gists_url": "https://api.github.com/users/jsoref/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/jsoref/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/jsoref/subscriptions",
      "organizations_url": "https://api.github.com/users/jsoref/orgs",
      "repos_url": "https://api.github.com/users/jsoref/repos",
      "events_url": "https://api.github.com/users/jsoref/events{/privacy}",
      "received_events_url": "https://api.github.com/users/jsoref/received_events",
      "type": "User",
      "site_admin": false
    },
    "html_url": "https://github.com/jsoref/examples-testing",
    "description": "This repository will be trashed periodically -- it's for testing / debugging the examples repository",
    "fork": true,
    "url": "https://api.github.com/repos/jsoref/examples-testing",
    "forks_url": "https://api.github.com/repos/jsoref/examples-testing/forks",
    "keys_url": "https://api.github.com/repos/jsoref/examples-testing/keys{/key_id}",
    "collaborators_url": "https://api.github.com/repos/jsoref/examples-testing/collaborators{/collaborator}",
    "teams_url": "https://api.github.com/repos/jsoref/examples-testing/teams",
    "hooks_url": "https://api.github.com/repos/jsoref/examples-testing/hooks",
    "issue_events_url": "https://api.github.com/repos/jsoref/examples-testing/issues/events{/number}",
    "events_url": "https://api.github.com/repos/jsoref/examples-testing/events",
    "assignees_url": "https://api.github.com/repos/jsoref/examples-testing/assignees{/user}",
    "branches_url": "https://api.github.com/repos/jsoref/examples-testing/branches{/branch}",
    "tags_url": "https://api.github.com/repos/jsoref/examples-testing/tags",
    "blobs_url": "https://api.github.com/repos/jsoref/examples-testing/git/blobs{/sha}",
    "git_tags_url": "https://api.github.com/repos/jsoref/examples-testing/git/tags{/sha}",
    "git_refs_url": "https://api.github.com/repos/jsoref/examples-testing/git/refs{/sha}",
    "trees_url": "https://api.github.com/repos/jsoref/examples-testing/git/trees{/sha}",
    "statuses_url": "https://api.github.com/repos/jsoref/examples-testing/statuses/{sha}",
    "languages_url": "https://api.github.com/repos/jsoref/examples-testing/languages",
    "stargazers_url": "https://api.github.com/repos/jsoref/examples-testing/stargazers",
    "contributors_url": "https://api.github.com/repos/jsoref/examples-testing/contributors",
    "subscribers_url": "https://api.github.com/repos/jsoref/examples-testing/subscribers",
    "subscription_url": "https://api.github.com/repos/jsoref/examples-testing/subscription",
    "commits_url": "https://api.github.com/repos/jsoref/examples-testing/commits{/sha}",
    "git_commits_url": "https://api.github.com/repos/jsoref/examples-testing/git/commits{/sha}",
    "comments_url": "https://api.github.com/repos/jsoref/examples-testing/comments{/number}",
    "issue_comment_url": "https://api.github.com/repos/jsoref/examples-testing/issues/comments{/number}",
    "contents_url": "https://api.github.com/repos/jsoref/examples-testing/contents/{+path}",
    "compare_url": "https://api.github.com/repos/jsoref/examples-testing/compare/{base}...{head}",
    "merges_url": "https://api.github.com/repos/jsoref/examples-testing/merges",
    "archive_url": "https://api.github.com/repos/jsoref/examples-testing/{archive_format}{/ref}",
    "downloads_url": "https://api.github.com/repos/jsoref/examples-testing/downloads",
    "issues_url": "https://api.github.com/repos/jsoref/examples-testing/issues{/number}",
    "pulls_url": "https://api.github.com/repos/jsoref/examples-testing/pulls{/number}",
    "milestones_url": "https://api.github.com/repos/jsoref/examples-testing/milestones{/number}",
    "notifications_url": "https://api.github.com/repos/jsoref/examples-testing/notifications{?since,all,participating}",
    "labels_url": "https://api.github.com/repos/jsoref/examples-testing/labels{/name}",
    "releases_url": "https://api.github.com/repos/jsoref/examples-testing/releases{/id}",
    "deployments_url": "https://api.github.com/repos/jsoref/examples-testing/deployments"
  },
  "head_repository": {
    "id": 240964941,
    "node_id": "MDEwOlJlcG9zaXRvcnkyNDA5NjQ5NDE=",
    "name": "examples-testing",
    "full_name": "jsoref/examples-testing",
    "private": false,
    "owner": {
      "login": "jsoref",
      "id": 2119212,
      "node_id": "MDQ6VXNlcjIxMTkyMTI=",
      "avatar_url": "https://avatars.githubusercontent.com/u/2119212?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/jsoref",
      "html_url": "https://github.com/jsoref",
      "followers_url": "https://api.github.com/users/jsoref/followers",
      "following_url": "https://api.github.com/users/jsoref/following{/other_user}",
      "gists_url": "https://api.github.com/users/jsoref/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/jsoref/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/jsoref/subscriptions",
      "organizations_url": "https://api.github.com/users/jsoref/orgs",
      "repos_url": "https://api.github.com/users/jsoref/repos",
      "events_url": "https://api.github.com/users/jsoref/events{/privacy}",
      "received_events_url": "https://api.github.com/users/jsoref/received_events",
      "type": "User",
      "site_admin": false
    },
    "html_url": "https://github.com/jsoref/examples-testing",
    "description": "This repository will be trashed periodically -- it's for testing / debugging the examples repository",
    "fork": true,
    "url": "https://api.github.com/repos/jsoref/examples-testing",
    "forks_url": "https://api.github.com/repos/jsoref/examples-testing/forks",
    "keys_url": "https://api.github.com/repos/jsoref/examples-testing/keys{/key_id}",
    "collaborators_url": "https://api.github.com/repos/jsoref/examples-testing/collaborators{/collaborator}",
    "teams_url": "https://api.github.com/repos/jsoref/examples-testing/teams",
    "hooks_url": "https://api.github.com/repos/jsoref/examples-testing/hooks",
    "issue_events_url": "https://api.github.com/repos/jsoref/examples-testing/issues/events{/number}",
    "events_url": "https://api.github.com/repos/jsoref/examples-testing/events",
    "assignees_url": "https://api.github.com/repos/jsoref/examples-testing/assignees{/user}",
    "branches_url": "https://api.github.com/repos/jsoref/examples-testing/branches{/branch}",
    "tags_url": "https://api.github.com/repos/jsoref/examples-testing/tags",
    "blobs_url": "https://api.github.com/repos/jsoref/examples-testing/git/blobs{/sha}",
    "git_tags_url": "https://api.github.com/repos/jsoref/examples-testing/git/tags{/sha}",
    "git_refs_url": "https://api.github.com/repos/jsoref/examples-testing/git/refs{/sha}",
    "trees_url": "https://api.github.com/repos/jsoref/examples-testing/git/trees{/sha}",
    "statuses_url": "https://api.github.com/repos/jsoref/examples-testing/statuses/{sha}",
    "languages_url": "https://api.github.com/repos/jsoref/examples-testing/languages",
    "stargazers_url": "https://api.github.com/repos/jsoref/examples-testing/stargazers",
    "contributors_url": "https://api.github.com/repos/jsoref/examples-testing/contributors",
    "subscribers_url": "https://api.github.com/repos/jsoref/examples-testing/subscribers",
    "subscription_url": "https://api.github.com/repos/jsoref/examples-testing/subscription",
    "commits_url": "https://api.github.com/repos/jsoref/examples-testing/commits{/sha}",
    "git_commits_url": "https://api.github.com/repos/jsoref/examples-testing/git/commits{/sha}",
    "comments_url": "https://api.github.com/repos/jsoref/examples-testing/comments{/number}",
    "issue_comment_url": "https://api.github.com/repos/jsoref/examples-testing/issues/comments{/number}",
    "contents_url": "https://api.github.com/repos/jsoref/examples-testing/contents/{+path}",
    "compare_url": "https://api.github.com/repos/jsoref/examples-testing/compare/{base}...{head}",
    "merges_url": "https://api.github.com/repos/jsoref/examples-testing/merges",
    "archive_url": "https://api.github.com/repos/jsoref/examples-testing/{archive_format}{/ref}",
    "downloads_url": "https://api.github.com/repos/jsoref/examples-testing/downloads",
    "issues_url": "https://api.github.com/repos/jsoref/examples-testing/issues{/number}",
    "pulls_url": "https://api.github.com/repos/jsoref/examples-testing/pulls{/number}",
    "milestones_url": "https://api.github.com/repos/jsoref/examples-testing/milestones{/number}",
    "notifications_url": "https://api.github.com/repos/jsoref/examples-testing/notifications{?since,all,participating}",
    "labels_url": "https://api.github.com/repos/jsoref/examples-testing/labels{/name}",
    "releases_url": "https://api.github.com/repos/jsoref/examples-testing/releases{/id}",
    "deployments_url": "https://api.github.com/repos/jsoref/examples-testing/deployments"
  }
}

which includes:

  "artifacts_url": "https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998/artifacts",

And:
https://api.github.com/repos/jsoref/examples-testing/actions/runs/1180203998/artifacts
Has an artifact.

I personally needed the WorkflowRun's event (pull_request_target) and the workflow_url (to get path, name, and state).

Fwiw, afaict for my case, I actually must switch back and forth between the v3 and v4 api, as the v4 api content for the WorkflowRun is really bare. In your case, it might be possible to work solely with the v4 api.

Thanks for responding @jsoref, ‘gymnastics’ is definitely the right word! I’m afraid I still can’t see how to find the missing link between a PR and workflow runs though, as the PR doesn’t return check runs from what I can tell. I’ve just been delving into the GraphQL schema and can’t find a link there at all.

It’s frustrating! The only thing I can think of at the moment is to loop over all workflow runs and find a way to filter them by the related commit SHA or PR number. This is obviously hugely wasteful when the GraphQL layer should (to my mind) be able to do the legwork for us.

I spoke to GH Support who confirmed that the only way to do this is to filter the workflow runs.

This is the approach:

  1. Use a given SHA to retrieve PR:
    /repos/{owner}/{repo}/commits/{commit_sha}/pulls

  2. Get all relevant workflows
    /repos/{owner}/{repo}/actions/runs

Query params:
branch: pr.head.ref (where pr is the PR data object from step 1)
event: "pull_request"

  1. Filter workflow runs for that particular commit using the commit SHA
    where workflow_run.head_sha === commit_sha

  2. Retrieve artefacts by using the workflow run ID from step 3
    /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts

2 Likes