[JIRA] (JENKINS-51225) Support for GitHub Checks API

0 views
Skip to first unread message

dan@danbovey.uk (JIRA)

unread,
May 9, 2018, 11:13:01 AM5/9/18
to jenkinsc...@googlegroups.com
Dan Bovey created an issue
 
Jenkins / New Feature JENKINS-51225
Support for GitHub Checks API
Issue Type: New Feature New Feature
Assignee: Unassigned
Components: github-api-plugin
Created: 2018-05-09 15:12
Priority: Minor Minor
Reporter: Dan Bovey

My main use case for this would be for use in the github-branch-source-plugin to post to the new GitHub Checks API instead of/as well as the Commit Status API.

 

See: https://help.github.com/articles/about-status-checks/#checks

Add Comment Add Comment
 
This message was sent by Atlassian JIRA (v7.3.0#73011-sha1:3c73d0e)
Atlassian logo

dan@danbovey.uk (JIRA)

unread,
May 9, 2018, 11:14:02 AM5/9/18
to jenkinsc...@googlegroups.com
Dan Bovey updated an issue
Change By: Dan Bovey
My main use case for this would be for use in the github-branch-source-plugin to GitHub Organisation Folder/Branch Source Plugin. When building a pull request, it should post to the new GitHub Checks API instead of/as well as the Commit Status API.

 

See: [https://help.github.com/articles/about-status-checks/#checks]

stevengfoster@gmail.com (JIRA)

unread,
Aug 9, 2018, 10:11:03 AM8/9/18
to jenkinsc...@googlegroups.com
Steven Foster commented on New Feature JENKINS-51225
 
Re: Support for GitHub Checks API

I'd love this too. It seems to require the use of Github Apps over OAuth, which means it needs a bigger change underneath. Github Apps have the advantage of not taking up a user seat in github enterprise though

This message was sent by Atlassian JIRA (v7.10.1#710002-sha1:6efc396)

iseric@gmail.com (JIRA)

unread,
Oct 3, 2018, 4:14:02 PM10/3/18
to jenkinsc...@googlegroups.com

It would be awesome to see a community member create a groovy script or simple Java shared library example of how this might work until it's eventually added to the standard plug-in out-of-the-box. ... I wouldn't know where to start (other than reading the ReST API docs and manually handling the HTTP request/response in a Jenkins pipeline script).

This message was sent by Atlassian Jira (v7.11.2#711002-sha1:fdc329d)

ianfixes@gmail.com (JIRA)

unread,
Dec 16, 2019, 8:52:05 AM12/16/19
to jenkinsc...@googlegroups.com
Ian Katz commented on New Feature JENKINS-51225

I can probably take on a proof of concept for this work as a groovy script.  It would end up being something like the "junit" pipeline step, which reads a file from the workspace.

What I would need to know before I can start is how to read the stored GitHub credentials and/or instantiate a REST client with them.  Is the proper way to do that described somewhere?

This message was sent by Atlassian Jira (v7.13.6#713006-sha1:cc4451f)
Atlassian logo

j3p0uk@hotmail.com (JIRA)

unread,
Dec 16, 2019, 12:43:03 PM12/16/19
to jenkinsc...@googlegroups.com

For interacting with the GitHub API, there are a number of libraries to help with this in a number of languages:

https://developer.github.com/v3/libraries/

Typically if credentials are supplied by a credentials binding, they would be available in the environment.  You might find that the libraries expect particular environment variables (similar to how AWS' boto3 library can work), and as such you might just need a withCredentials() {} wrapper around the build step with the correct definitions.

It really feels like this needs to be an update tot he actual GitHub status notification plugin though, or a new plugin that can be used as an alternative, as great as a build step would be, it would become incumbent on folks to use it correctly in both happy-path and failure-paths of their Jenkinsfiles.

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:24:05 AM12/18/19
to jenkinsc...@googlegroups.com
Ian Katz updated an issue
 
Change By: Ian Katz
Attachment: Screen Shot 2019-12-18 at 8.19.18 AM.png

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:25:11 AM12/18/19
to jenkinsc...@googlegroups.com
Ian Katz commented on New Feature JENKINS-51225
 
Re: Support for GitHub Checks API

I got this working. First off, you can't use the GitHub Checks API with a user/password combo, nor with a token. You MUST set up a GitHub app to be able to generate the proper tokens. Here's the process I used:

  1. set up a new GitHub app at https://github.com/organizations/YOUR_ORG/settings/apps/new. You can also do this from a user account, provided that the user account is listed as an owner of every repo for which you want to write status checks. (If you mess this up (like I did) you can use the "transfer app" option to move it to the right place.) The only required field should be the homepage URL, and you can put in a bogus one. Your app should be private, not public. Install that app.
  2. GitHub will save a PRIVATE KEY to your computer; save it somewhere that your build machine can access it (we'll say /path/to/key.pem. You will also need your app ID from https://github.com/organizations/YOUR_ORG/settings/apps/your-app-name
  3. Generate a JWT, which consists of 3 base64-encoded chunks separated by . characters. For each of these chunks, delete all newlines and =, change all / to _, and change all + to -.
    1. The first chunk is {{ {"alg":"RS256"}

      }} , base64-encoded. Literally eyJhbGciOiJSUzI1NiJ9

    1. The second chunk is the base64-encoded result of a JSON object with 3 fields: iat, which holds the current unix epochal time (what you'd get from date -u '+%s'), exp, the expiration time (maximum of 600 more than iat, i.e. 10 minutes), and the issuer iss which should be set to your app ID
    2. The 3rd chunk is calculated with the shell as follows, as I'm not sure how to accomplish it in a pure-jenkins-groovy way:
      echo "$CHUNK1.$CHUNK2" | openssl dgst -sha256 -sign /path/to/key.pem -binary | openssl base64 

      Note that openssl base64 will split the signature over several lines, so just strip out the newlines

  1. With the JWT, we can get the installation ID.
    curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/app/installations

    This should return a JSON array with only one installation. Get its installation ID (the ["id"] field), which will be an integer e.g. {{ 8675309 }}.

  1. With the installation ID, we can get an installation token. This will have specific permissions for accesing the checks API on specific repositories (by default, all repositories – so we leave this blank).
    curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" -d '{"permissions":{ "checks":"write"}}' https://api.github.com/app/installations/8675309/access_tokens

    This will give you a response with a ["token"] field, something like v1.fedcba9876543210.

  1. With the installation token, we can do a quick sanity check that we can "see" the repositories we want to access.
    curl -s -H "Authorization: token v1.fedcba9876543210" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/installation/repositories

    . You should see all your repositories in there

  1. Finally, with the installation token, we can upload an actual check run (assuming you have permissions to access YOUR_ORG/YOUR_REPO!
    curl -s -H "Authorization: token v1.fedcba9876543210" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" -d @check-run-definition.json https://api.github.com/repos/YOUR_ORG/YOUR_REPO/check-runs

Where check-run-definition.json contains

{
  "name": "my check name",
  "head_sha": "SOME_ACTUAL_SHA",
  "external_id": "42",
  "details_url": "http://jenkins.your-org.com/foo",
  "started_at": "2019-12-18T07:57:00-05:00",
  "completed_at": "2019-12-18T07:58:00-05:00",
  "conclusion": "failure",
  "output": {
    "title": "My Output title",
    "summary": "my output summary _checking_ `markdown` **support**",
    "text": "my output text _checking_ `markdown` **support**",
    "annotations": [
      {
        "path": "Jenkinsfile",
        "start_line": 1,
        "end_line": 1,
        "start_column": 3,
        "end_column": 4,
        "annotation_level": "warning",
        "message": "my message  _checking_ `markdown` **support** (it isn't)",
        "title": "my title _checking_ `markdown` **support** (it isn't)",
        "raw_details": "my raw details _checking_ `markdown` **support** (it isn't)"
      }
    ]
  }
}

Results:

 

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:27:20 AM12/18/19
to jenkinsc...@googlegroups.com
Ian Katz edited a comment on New Feature JENKINS-51225
I got this working. First off, you can't use the GitHub Checks API with a user/password combo, nor with a token. You MUST set up a GitHub app to be able to generate the proper tokens. Here's the process I used:
# set up a new GitHub app at [https://github.com/organizations/YOUR_ORG/settings/apps/new]. You can also do this from a user account, provided that the user account is listed as an owner of every repo for which you want to write status checks. (If you mess this up (like I did) you can use the "transfer app" option to move it to the right place.) The only required field should be the homepage URL, and you can put in a bogus one. Your app should be private, not public. Install that app.
# GitHub will save a PRIVATE KEY to your computer; save it somewhere that your build machine can access it (we'll say {{/path/to/key.pem}}. You will also need your app ID from [https://github.com/organizations/YOUR_ORG/settings/apps/your-app-name]
# [Generate a JWT|https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app], which consists of 3 base64-encoded chunks separated by {{.}} characters. For each of these chunks, delete all newlines and {{=}}, change all {{/}} to {{_}}, and change all {{+}} to {{-}}.
## The first chunk is \{{ {"alg":"RS256"}}} , base64-encoded. Literally {{eyJhbGciOiJSUzI1NiJ9}}

## The second chunk is the base64-encoded result of a JSON object with 3 fields: {{iat}}, which holds the current unix epochal time (what you'd get from {{date -u '+%s'}}), {{exp}}, the expiration time (maximum of 600 more than {{iat}}, i.e. 10 minutes), and the issuer {{iss}} which should be set to your app ID
## The 3rd chunk is calculated with the shell as follows, as I'm not sure how to accomplish it in a pure-jenkins-groovy way:
{code:java}
echo "$CHUNK1.$CHUNK2" | openssl dgst -sha256 -sign /path/to/key.pem -binary | openssl base64 {code}

Note that {{openssl base64}} will split the signature over several lines, so just strip out the newlines

# With the JWT, we can [get the installation ID|https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation].
{code:java}
curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/app/installations{code}

This should return a JSON array with only one installation. Get its installation ID (the {{["id"]}} field), which will be an integer e.g. \{{ 8675309 }}.

# With the installation ID, we can get an installation token. This will have specific permissions for accesing the checks API on specific repositories (by default, all repositories – so we leave this blank).
{code:java}
curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" -d '{"permissions":{ "checks":"write"}}' https://api.github.com/app/installations/8675309/access_tokens{code}
This will give you a response with a {{["token"]}} field, something like {{v1.fedcba9876543210}}.

# With the installation token, we can do a quick sanity check that we can "see" the repositories we want to access.
{code:java}
curl -s -H "Authorization: token v1.fedcba9876543210" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/installation/repositories{code}

. You should see all your repositories in there

# Finally, with the installation token, we can [upload an actual check run|https://developer.github.com/v3/checks/runs/#create-a-check-run] (assuming you have permissions to access {{YOUR_ORG/YOUR_REPO}}!
{code:java}
curl -s -H "Authorization: token v1.fedcba9876543210" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" -d @check-run-definition.json https://api.github.com/repos/YOUR_ORG/YOUR_REPO/check-runs{code}

Where {{check-run-definition.json}} contains
{code:java}

{
  "name": "my check name",
  "head_sha": "SOME_ACTUAL_SHA",
  "external_id": "42",
  "details_url": "http://jenkins.your-org.com/foo",
  "started_at": "2019-12-18T07:57:00-05:00",
  "completed_at": "2019-12-18T07:58:00-05:00",
  "conclusion": "failure",
  "output": {
    "title": "My Output title",
    "summary": "my output summary _checking_ `markdown` **support**",
    "text": "my output text _checking_ `markdown` **support**",
    "annotations": [
      {
        "path": "Jenkinsfile",
        "start_line": 1,
        "end_line": 1,
        "start_column": 3,
        "end_column": 4,
        "annotation_level": "warning",
        "message": "my message  _checking_ `markdown` **support** (it isn't)",
        "title": "my title _checking_ `markdown` **support** (it isn't)",
        "raw_details": "my raw details _checking_ `markdown` **support** (it isn't)"
      }
    ]
  }
}
{code}
Results:

  !Screen Shot 2019-12-18 at 8.19.18 AM.png|width=
821 302 ,height= 739 272 ,thumbnail!

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:27:22 AM12/18/19
to jenkinsc...@googlegroups.com
  !Screen Shot 2019-12-18 at 8.19.18 AM.png|width=302,height=272,thumbnail!

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:27:28 AM12/18/19
to jenkinsc...@googlegroups.com

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:28:17 AM12/18/19
to jenkinsc...@googlegroups.com
Ian Katz edited a comment on New Feature JENKINS-51225
I got this working. First off, you can't use the GitHub Checks API with a user/password combo, nor with a token. You MUST set up a GitHub app to be able to generate the proper tokens. Here's the process I used:
# set up a new GitHub app at [https://github.com/organizations/YOUR_ORG/settings/apps/new]. You can also do this from a user account, provided that the user account is listed as an owner of every repo for which you want to write status checks. (If you mess this up (like I did) you can use the "transfer app" option to move it to the right place.) The only required field should be the homepage URL, and you can put in a bogus one. Your app should be private, not public. Install that app.
# GitHub will save a PRIVATE KEY to your computer; save it somewhere that your build machine can access it (we'll say {{/path/to/key.pem}}. You will also need your app ID from [https://github.com/organizations/YOUR_ORG/settings/apps/your-app-name]
# [Generate a JWT|https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app], which consists of 3 base64-encoded chunks separated by {{.}} characters. For each of these chunks, delete all newlines and {{=}}, change all {{/}} to {{_}}, and change all {{+}} to {{-}}.
## The first chunk is {{{"alg":"RS256"\}}} , base64-encoded. Literally {{eyJhbGciOiJSUzI1NiJ9}}
## The second chunk is the base64-encoded result of a JSON object with 3 fields: {{iat}}, which holds the current unix epochal time (what you'd get from {{date -u '+%s'}}), {{exp}}, the expiration time (maximum of 600 more than {{iat}}, i.e. 10 minutes), and the issuer {{iss}} which should be set to your app ID
## The 3rd chunk is calculated with the shell as follows, as I'm not sure how to accomplish it in a pure-jenkins-groovy way:
{code:java}
echo "$CHUNK1.$CHUNK2" | openssl dgst -sha256 -sign /path/to/key.pem -binary | openssl base64 {code}
Note that {{openssl base64}} will split the signature over several lines, so just strip out the newlines
# With the JWT, we can [get the installation ID|https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation].
{code:java}
curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/app/installations{code}
This should return a JSON array with only one installation. Get its installation ID (the {{["id"]}} field), which will be an integer e.g. \{{ 8675309 }}.
# With the installation ID, we can get an installation token. This will have specific permissions for accesing the checks API on specific repositories (by default, all repositories – so we leave this blank).
{code:java}
curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" -d '{"permissions":{ "checks":"write"}}' https://api.github.com/app/installations/8675309/access_tokens{code}
This will give you a response with a {{["token"]}} field, something like {{v1.fedcba9876543210}}.
# With the installation token, we can do a quick sanity check that we can "see" the repositories we want to access.
{code:java}
curl -s -H "Authorization: token v1.fedcba9876543210" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/installation/repositories{code}
. You should see all your repositories in there .

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:28:17 AM12/18/19
to jenkinsc...@googlegroups.com
{{ {"alg":"RS256"\}}} , base64-encoded. Literally {{eyJhbGciOiJSUzI1NiJ9}}

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:32:05 AM12/18/19
to jenkinsc...@googlegroups.com
Ian Katz edited a comment on New Feature JENKINS-51225
I got this working. First off, you can't use the GitHub Checks API with a user/password combo, nor with a token. You MUST set up a GitHub app to be able to generate the proper tokens. Here's the process I used:
# set up a new GitHub app at [https://github.com/organizations/YOUR_ORG/settings/apps/new]. You can also do this from a user account, provided that the user account is listed as an owner of every repo for which you want to write status checks. (If you mess this up (like I did) you can use the "transfer app" option to move it to the right place.) The only required field should be the homepage URL, and you can put in a bogus one. Your app should be private, not public. Install that app.
# GitHub will save a PRIVATE KEY to your computer; save it somewhere that your build machine can access it (we'll say {{/path/to/key.pem}}. You will also need your app ID from [https://github.com/organizations/YOUR_ORG/settings/apps/your-app-name]
# [Generate a JWT|https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app], which consists of 3 base64-encoded chunks separated by {{.}} characters. For each of these chunks, delete all newlines and {{=}}, change all {{/}} to {{_}}, and change all {{+}} to {{-}}.
## The first chunk is {{{"alg":"RS256"\}}} , base64-encoded. Literally {{eyJhbGciOiJSUzI1NiJ9}}
## The second chunk is the base64-encoded result of a JSON object with 3 fields: {{iat}}, which holds the current unix epochal time (what you'd get from {{date -u '+%s'}}), {{exp}}, the expiration time (maximum of 600 more than {{iat}}, i.e. 10 minutes), and the issuer {{iss}} which should be set to your app ID
## The 3rd chunk is calculated with the shell as follows, as I'm not sure how to accomplish it in a pure-jenkins-groovy way:
{code:java}
echo "$CHUNK1.$CHUNK2" | openssl dgst -sha256 -sign /path/to/key.pem -binary | openssl base64 {code}
Note that {{openssl base64}} will split the signature over several lines, so just strip out the newlines
# With the JWT, we can [get the installation ID|https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation].
{code:java}
curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/app/installations{code}
This should return a JSON array with only one installation. Get its installation ID (the {{["id"]}} field), which will be an integer e.g. \{{ 8675309 }}.
# With the installation ID, we can get an installation token. This will have specific permissions for accesing the checks API on specific repositories (by default, all repositories – so we leave this blank).
{code:java}
curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" -d '{"permissions":{ "checks":"write"}}' https://api.github.com/app/installations/8675309/access_tokens{code}
This will give you a response with a {{["token"]}} field, something like {{v1.fedcba9876543210}}.
# With the installation token, we can do a quick sanity check that we can "see" the repositories we want to access.
{code:java}
curl -s -H "Authorization: token v1.fedcba9876543210" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/installation/repositories{code}
You should see all your repositories in there.


My plan is to make a function / build-step that accepts those annotation objects, possibly in the form of a standardized file format.  That gives me the flexibility to annotate a lot of my own lines.

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:46:05 AM12/18/19
to jenkinsc...@googlegroups.com

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:47:18 AM12/18/19
to jenkinsc...@googlegroups.com

ianfixes@gmail.com (JIRA)

unread,
Dec 18, 2019, 8:47:18 AM12/18/19
to jenkinsc...@googlegroups.com

ianfixes@gmail.com (JIRA)

unread,
Mar 4, 2020, 10:06:04 AM3/4/20
to jenkinsc...@googlegroups.com
Ian Katz commented on New Feature JENKINS-51225

Update: I have this working in my pipeline scripts library. There are 2 gotchas for anyone looking do this on their own:

1. It's probably not a good idea to hard-code the annotation_level as you map from one static analysis format (e.g. rubocop, eslint) to GitHub annotation format! I use a closure to allow my pipeline scripts to decide how to register all the reports. For example, bumping everything up to an error or down to a warning. This also lets me run multiple linting runs with multiple configurations, to allow some soft-launching of lint rules in a non-punitive fashion

2. Merge Commits are tricky. For the source control plugin I'm using, if the target branch and branch-under-test have diverged in the context of a pull request, jenkins will automatically merge the latest target branch into the branch under test, creating a merged commit and then test that. What this means is that the SHA1 reported by Jenkins isn't one that GitHub will recognize and more importantly the file/line numbers may not align with the unmerged commit. Fortunately, the workaround for the first part is straightforward: get the "true commit" by running git log --pretty='%H %h %ae' -n 3 and look for the first author that isn't "nobody@nowhere". That gets you the commit to send to GitHub. That also tells you whether you need the workaround for the second part: you have your choice of simply suppressing the annotations, or just converting to a markdown table in the summary.

This message was sent by Atlassian Jira (v7.13.12#713012-sha1:6e07c38)
Atlassian logo

fred.vogt@gmail.com (JIRA)

unread,
Mar 5, 2020, 12:04:11 PM3/5/20
to jenkinsc...@googlegroups.com

Oleg Nenashev - youtube - GSoC Office Hours for 3/4/2020 - mentioned a future project meeting for adding the Github checks API.

ianfixes@gmail.com (JIRA)

unread,
Mar 6, 2020, 4:52:04 PM3/6/20
to jenkinsc...@googlegroups.com
Ian Katz commented on New Feature JENKINS-51225

Sorry – one other point. When uploading the annotations, the name field appears to be the primary key. So if you have the same check running in 2 builds (e.g. the branch build and the PR build), then the second check will completely overwrite the first.

Although this sounds OK in theory, note that if you are implementing the above recommendation of summarizing the annotations to table if Jenkins is not testing the "true" commit, then you run the risk of overwriting the actual annotations report with the summary report.

The solution here is to recognize PR builds (which I do from checking the branch name) and alter the report name (e.g. with <original name> (PR)) to disambiguate.

Reply all
Reply to author
Forward
0 new messages