Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions cherry_picker/cherry_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
import tomllib
import webbrowser

import requests
import stamina
from gidgethub import sansio
import urllib3

from . import __version__

Expand Down Expand Up @@ -458,7 +457,12 @@ def create_gh_pr(self, base_branch, head_branch, *, commit_message, gh_auth):
"""
Create PR in GitHub
"""
request_headers = sansio.create_headers(self.username, oauth_token=gh_auth)
request_headers = {
"User-Agent": self.username,
"Accept": "application/vnd.github+json",
"Authorization": f"token {gh_auth}",
"X-GitHub-Api-Version": "2022-11-28",
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a very good fix. After viewing the code from https://github.com/gidgethub/gidgethub/blob/6d64b9a0b707182fc2a86fed0f983f136f7e3f90/gidgethub/sansio.py#L158 This does in fact line up correctly. However, might I ask what the deal with X-GitHub-Api-Version is because that is not set in the original code.

@hugovk hugovk Oct 21, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not required, but GitHub recommends you send one:

You should use the X-GitHub-Api-Version header to specify an API version. For example:

curl --header "X-GitHub-Api-Version:2026-03-10" https://api.github.com/zen

Requests without the X-GitHub-Api-Version header will default to use the 2022-11-28 version.

If you specify an API version that is no longer supported, you will receive a 410 Gone response.

https://docs.github.com/en/rest/about-the-rest-api/api-versions?apiVersion=2026-03-10#specifying-an-api-version

So it's about pinning, and avoiding any surprises from changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hugovk Would it be a good idea to make it so that X-GitHub-Api-Version is configurable on the user's end as an argparse augment and then use the provided default if none are chosen?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because the API version is directly linked to the API calls in the code.

title, body = normalize_commit_message(commit_message)
if not self.prefix_commit:
title = remove_commit_prefix(title)
Expand All @@ -473,15 +477,15 @@ def create_gh_pr(self, base_branch, head_branch, *, commit_message, gh_auth):
}
url = CREATE_PR_URL_TEMPLATE.format(config=self.config)
try:
response = requests.post(
url, headers=request_headers, json=data, timeout=30
response = urllib3.request(
"POST", url, headers=request_headers, json=data, timeout=30
)
except requests.exceptions.RequestException as req_exc:
except urllib3.exceptions.HTTPError as req_exc:
raise GitHubException(f"Creating PR on GitHub failed: {req_exc}")
else:
sc = response.status_code
txt = response.text
if sc != requests.codes.created:
sc = response.status
if sc != 201:
txt = response.data.decode("utf-8", errors="replace")
raise GitHubException(
f"Unexpected response ({sc}) when creating PR on GitHub: {txt}"
)
Expand Down
21 changes: 12 additions & 9 deletions cherry_picker/test_cherry_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1335,11 +1335,10 @@ def test_cli_invoked():


@pytest.mark.parametrize("draft_pr", (True, False))
@mock.patch("requests.post")
@mock.patch("gidgethub.sansio.create_headers")
@mock.patch("urllib3.request")
@mock.patch.object(CherryPicker, "username", new_callable=mock.PropertyMock)
def test_create_gh_pr_draft_states(
mock_username, mock_create_headers, mock_post, monkeypatch, draft_pr, config
mock_username, mock_request, monkeypatch, draft_pr, config
):
config["draft_pr"] = draft_pr
mock_username.return_value = "username"
Expand All @@ -1348,15 +1347,13 @@ def test_create_gh_pr_draft_states(
cherry_picker = CherryPicker(
"origin", "xxx", [], prefix_commit=True, config=config
)
mock_create_headers.return_value = {"Authorization": "token gh-token"}

mock_response = MagicMock()
mock_response.status_code = 201
mock_response.status = 201
mock_response.json.return_value = {
"html_url": "https://github.com/octocat/Hello-World/pull/1347",
"number": 1347,
}
mock_post.return_value = mock_response
mock_request.return_value = mock_response

base_branch = "main"
head_branch = "feature-branch"
Expand All @@ -1367,9 +1364,15 @@ def test_create_gh_pr_draft_states(
base_branch, head_branch, commit_message=commit_message, gh_auth=gh_auth
)

mock_post.assert_called_once_with(
mock_request.assert_called_once_with(
"POST",
"https://api.github.com/repos/python/cpython/pulls",
headers={"Authorization": "token gh-token"},
headers={
"User-Agent": "username",
"Accept": "application/vnd.github+json",
"Authorization": "token gh_auth",
"X-GitHub-Api-Version": "2022-11-28",
},
json={
"title": "Commit message",
"body": "",
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ classifiers = [
]
dynamic = [ "version" ]
dependencies = [
"gidgethub",
"requests",
"stamina",
"urllib3>=2",
]
optional-dependencies.dev = [
"pytest",
Expand Down
Loading