Skip to content
Merged
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
49 changes: 21 additions & 28 deletions pydocteur/github_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

import requests
from requests.auth import HTTPBasicAuth
from github import GithubException
from github import Github

from pydocteur.settings import GH_TOKEN
from pydocteur.settings import GH_USERNAME
from pydocteur.settings import REPOSITORY_NAME

logger = logging.getLogger("pydocteur")
gh = Github(GH_TOKEN)
gh = Github(GH_TOKEN if GH_TOKEN else None)


def get_rest_api(url: str) -> requests.Response:
Expand All @@ -24,37 +23,31 @@ def get_graphql_api(query: str) -> requests.Response:
return resp


def get_pull_request_from_checks(commit_sha):
prs_for_commit = gh.search_issues(f"type:pr repo:{REPOSITORY_NAME} sha:{commit_sha}")
if prs_for_commit.totalCount != 1:
logger.error("Should be exactly one PR for this sha: %s, found %s", commit_sha, prs_for_commit.totalCount)
return prs_for_commit[0]


def get_pull_request(payload):
logger.debug("Getting repository")
gh_repo = gh.get_repo(REPOSITORY_NAME)
logger.info("Trying to find PR number from payload")
logger.info("Trying to find PR from %s", payload.get("action", "A payload with: " + ", ".join(payload.keys())))

is_run = payload.get("check_run", False)
is_suite = payload.get("check_suite", False)
head_sha = payload.get("check_suite", {}).get("head_sha")
if head_sha:
return get_pull_request_from_checks(head_sha)

if is_run or is_suite:
logger.info("Payload is from checks, ignoring")
return None
pr_number = payload.get("pull_request", {}).get("number")
if pr_number:
return gh_repo.get_pull(pr_number)

try:
try:
pr_number = payload["pull_request"]["number"]
logger.debug(f"Found PR {pr_number} first try")
except KeyError:
issue_number = payload["issue"]["number"]
logger.debug(f"Found issue {issue_number} from payload")
try:
repo = gh_repo.get_pull(issue_number)
logger.info(f"Found PR #{repo.number}")
return repo
except GithubException:
logger.debug(f"Found issue {issue_number}, returning None")
return None
except Exception: # noqa
logger.warning("Unknown payload, returning None")
logger.debug(payload)
return None
return gh_repo.get_pull(pr_number)
issue_number = payload.get("issue", {}).get("number")
if issue_number:
return gh_repo.get_pull(issue_number)

logger.warning("Unknown payload, (action: %s)", payload.get("action", ""))
return None


def get_trad_team_members() -> set:
Expand Down
8 changes: 5 additions & 3 deletions pydocteur/pr_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ def sort_reviews_key(review):
return review.user.login, review.submitted_at

last_reviews = []
for author, reviews in groupby(sorted(pr_reviews, key=sort_reviews_key), key=lambda review: review.user.login):
for _, reviews in groupby(sorted(pr_reviews, key=sort_reviews_key), key=lambda review: review.user.login):
last_reviews.append(list(reviews)[-1])
is_approved = all(review.state == "APPROVED" for review in last_reviews)
logger.info(
f"is_approved for PR #{pr.number} is {is_approved}: "
+ ", ".join(f"{review.user.login} has {review.state}" for review in last_reviews)
"is_pr_approved(%s): %s (%s)",
pr.number,
is_approved,
", ".join(f"{review.user.login} has {review.state}" for review in last_reviews),
)
return is_approved

Expand Down
266 changes: 266 additions & 0 deletions tests/cassettes/test_get_pr_from_check_run.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
User-Agent:
- PyGithub/Python
method: GET
uri: https://api.github.com/repos/python/python-docs-fr
response:
body:
string: !!binary |
H4sIAAAAAAAAA+1Y32/iOBD+Vyper2AIzbZEqvYqbbe6k1rudru6VV+Qkxji4sSR7cBB1P/9xnZ+
Qe+g1A/30pcWjL/Pnyczk5kpezTuBRNvOLn0x/55L+Mxmeml3v2X2/WU/c6iu8kW//y2irKlf/98
4z9sf2wf/ry+7sFmnBLYmW9UwrN+zCPZnwtYnxeMzXZ+RK/25IKusAL4HDNJznt8nRHRC8oe4wua
NazAptWMfM+fXI329G2my8nmyfta4J95Et+xVfh8O7p//uFPv9xqfRhOwGJWCAZ8iVK5DBCyi3I8
WFCVFGEhiYh4pkimBhFPUYGqsz6vri+AYyEqFmMUWNhjy2lFZNHAJqvLwt5EpWzveHuq2Wxtou3F
GeNrQO5L/W9y1GAaPM0WJ+MBUyKuEgJWAukv+sJUqlOEmP0l0v/AcTSDBKMLEp8gpkKAFO0FLyUS
JOeGqghlJGiuKM9OEbWDAx4uFjijW3wqD+AkwLWcU443+wFHVuBWpwAtoEQmOqKNNoEgEaErMOfJ
ZHtI4FKbXAfstGMNbWSqyAzHqQ47E40vb/Pc10Edk+ZpwTFfBcmi5EwJnElmTH/G52fgbGd/mIxx
BhmjSMFC5rcBKJlzsWwywsFAMxauBLzWoXmOmP0AAcQWwEHOkmwcWDS6RPC3CosIohyHXGDFj0X6
IXE7NCXqftXuoghOHUQbONAknLtY0MCBhkpZkDd57qErGxaJ6uDIijS0ueotIXGI2OJBJ5aSLjJC
HCzXUJSoTqUheH6UuJDWDCWyn8wTxgsHmRoNJCHjoQMLvMWQoSiRTLB9baiZmzLNqRl2KAWZO8rU
DA2lEk7P2EjUFA0hvLkUPG4HjTUDKitLMpwtCrxw4Wwo4Enrd+sCb49WF4fipOUAQl0tCRoWrmms
ZdEq7Yse4trFlC1JS2lqh8O1yMGrdyoQc/k0pcde6Yf4KoIdJ3cm1X65T6y/H68+jknVDCVqM65N
6BX3+61aZfRaY/eEqhh3cIOaAZW/5FglOjvBQTkW5P2CKwJUhhjqo8FgUCYEm2o3JcIpVi0eiLCI
Eij03q+xrBmgfkmxMmX0XEuMoaxmHMcONm0ogM4+vPfrtPjuM8+hYXQQZ+BdvpQyIhXPXHJoy9Fl
zriicxq9pZE4FFo7NOVnSbOInGPGzsFLFY0o+C20ZvrZQclIXGxj8XAF6NVtO8EIuLCDtQWxDCWy
7V5McsY3jrmmQ6LDVRCYDcQzrKCN8Iajy/7Q73veozcMLoaBf/UEe4o87u7xhv2R1x96jyM/GF0G
F5d6T17IpKVpt3wKhsPAMzSQOitPhk8wHnjVn+91F7rbB5iUSQv7tQUFtqn/d1DEwCX34uat5632
32HHgCAy4SnJoZLoTD/0kGZgNQ6guUVzgfRt6BY2jf1PV+OdgiHiRQaPYOzB8horqGXh9dxdrAsN
OOEeL8kc4k6fi+XMBnkvUKKA6Y5eyQV/JpGS3bU2rXQ2rumS7gB1MdS0hba3q0R4Y5gJpVQIXo15
MkgFTSqFiU01XYqpxCEj7QLPSVZprC/kAxWjEckk2KLUzR/cysxG4ErVNGtafZV5/Lcdkz1Mb75/
v/32+Nv0QTulHjVZDd1JWvTp8Y49P/3lb58eb6570F/bFjMw+jtaeoFWUZu6snxM5rhgamZbABA1
HkzgLEXSfGZdSvElgebdHtyddXwM1HZnfx8DtY+B2v8zUMuIWsNYqU42JvC7TU+dVUcv/wAuj38g
kxcAAA==
headers:
Accept-Ranges:
- bytes
Content-Length:
- '1315'
X-GitHub-Request-Id:
- 998E:DAEA:42E9D86:4D53DD3:5FC7BDEC
X-Ratelimit-Limit:
- '60'
X-Ratelimit-Remaining:
- '57'
X-Ratelimit-Reset:
- '1606929236'
X-Ratelimit-Used:
- '3'
access-control-allow-origin:
- '*'
access-control-expose-headers:
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
cache-control:
- public, max-age=60, s-maxage=60
content-encoding:
- gzip
content-security-policy:
- default-src 'none'
content-type:
- application/json; charset=utf-8
date:
- Wed, 02 Dec 2020 16:16:44 GMT
etag:
- W/"24d977b9e4582f04b3f9ae01a0d39a62359d71ace777aa7e07d8cd5eaeaedef4"
last-modified:
- Wed, 02 Dec 2020 15:17:47 GMT
referrer-policy:
- origin-when-cross-origin, strict-origin-when-cross-origin
server:
- GitHub.com
status:
- 200 OK
strict-transport-security:
- max-age=31536000; includeSubdomains; preload
vary:
- Accept, Accept-Encoding, Accept, X-Requested-With, Accept-Encoding
x-content-type-options:
- nosniff
x-frame-options:
- deny
x-github-media-type:
- github.v3; format=json
x-xss-protection:
- 1; mode=block
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
User-Agent:
- PyGithub/Python
method: GET
uri: https://api.github.com/search/issues?q=type%3Apr+repo%3Apython%2Fpython-docs-fr+sha%3A390a392633a4692ee96ebd8d122cfdcd602224e0&per_page=1
response:
body:
string: !!binary |
H4sIAAAAAAAAA61V227jNhD9FYELv8UWJd8SAUWRvWSxD3barNKmWSwEWhrJTChSS1JOHMO/sa99
6Qf2EzqUL3WMhbdx90k2OefMcDg8Z0GsskwkqaqlJVFwQrhMVVkJsJBoMLWwhkQ5EwZwy0KJ/z4t
SK0FicjU2spEvs8q3im4ndaTDmJ9DZUyfjW3UyXXn3amUtPOtc+NqcH4QW8QkBPSRHKr9Dw5lhFZ
BJuAMEcz7Nbkr7gWvmQlLJEbz1OCtD+IfcOGxDD7cbQrLiSd2lLs9WHnXr59I1UtxOY+eEaiYdjv
DU77Yf+ESJVB4tbI6O27x0vxOpi8f/x4e3MR3N6M6Tg+D8d3993R0/0T5pZ1OQGNI4RXe0IstwIQ
GGuW1anlSnoZeDn74hcgQTPRqRSCauMgCyJUwaXLw2XGQQjccnkHQUj7/dPweSm/Dn67GYv07o/H
0d15MI6LOYazGbNM7w9Bs2i66+l02VIlLXa+GdTa3yT4efZTD0kKvaZpDu3qOzTnjs74OyUfbv9O
YK6EUA+I3q/3+UN6nsDforCw1W8uiyMYELXwlZ0CtguP4Ma84OZ7M75XTINY+O6DI+I4DLZaQ/ai
gtYYLOcBp2K5aLSjIasnJtW8cpPzsi49QyKT0gWT/Im9nAmRBgkakXrRqRoEIv/LG99r6wqy8CvN
Zyydu1ZoSIHPsLFH0O1hkc3OK/cur93DwzajpCcsK93ja0R+uZHTRubdMwj73dMgHJ7RPUG4HsTv
L/jt7w/zcTzqjeIP3cv43In6wTdzyBtW2uu3Lmjr7KJ13mudDVohZbVVJegCkNqpMhb/959/ffV2
11MlFOoIoXQwGfYxMIOcoXVtnSuD7TQ5UZqC98uV98CF8CbQMHlNiowsPzeDbF0aVYFELqHSe9zZ
mCAzhhcSMECicqLwrP87Y0RwyQUYq+R2f6v50RDtRANyZwnD0khIQ9oOaDukcRBEtBt1+7euf1W2
HxO2aRgHg4jSKBy6mFQos6ZZV1Gj2+oEi1Epb2YdE7y5HMdXH15fx5dXiGEowzNI3HHQ2ZlxIStw
pln+b6+cIWDAF/RpXPwfXu+ItlZ/WBu/b00Zz/Ojra3j0NiCitl0ejxLAyf4RCYqm7v+ulsw3iu0
POo5etC50iVezIyzZOW8CauqTZ9NqjTORdChy8/LfwBesmWBegkAAA==
headers:
Accept-Ranges:
- bytes
Transfer-Encoding:
- chunked
X-GitHub-Request-Id:
- 998E:DAEA:42E9DD5:4D53E2C:5FC7BDEC
X-Ratelimit-Limit:
- '10'
X-Ratelimit-Remaining:
- '8'
X-Ratelimit-Reset:
- '1606925835'
X-Ratelimit-Used:
- '2'
access-control-allow-origin:
- '*'
access-control-expose-headers:
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
cache-control:
- no-cache
content-encoding:
- gzip
content-security-policy:
- default-src 'none'
content-type:
- application/json; charset=utf-8
date:
- Wed, 02 Dec 2020 16:16:44 GMT
referrer-policy:
- origin-when-cross-origin, strict-origin-when-cross-origin
server:
- GitHub.com
status:
- 200 OK
strict-transport-security:
- max-age=31536000; includeSubdomains; preload
vary:
- Accept, Accept-Encoding, Accept, X-Requested-With, Accept-Encoding
x-content-type-options:
- nosniff
x-frame-options:
- deny
x-github-media-type:
- github.v3; format=json
x-xss-protection:
- 1; mode=block
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
User-Agent:
- PyGithub/Python
method: GET
uri: https://api.github.com/search/issues?q=type%3Apr+repo%3Apython%2Fpython-docs-fr+sha%3A390a392633a4692ee96ebd8d122cfdcd602224e0
response:
body:
string: !!binary |
H4sIAAAAAAAAA61V227jNhD9FYELv8UWJd8SAUWRvWSxD3barNKmWSwEWhrJTChSS1JOHMO/sa99
6Qf2EzqUL3WMhbdx90k2OefMcDg8Z0GsskwkqaqlJVFwQrhMVVkJsJBoMLWwhkQ5EwZwy0KJ/z4t
SK0FicjU2spEvs8q3im4ndaTDmJ9DZUyfjW3UyXXn3amUtPOtc+NqcH4QW8QkBPSRHKr9Dw5lhFZ
BJuAMEcz7Nbkr7gWvmQlLJEbz1OCtD+IfcOGxDD7cbQrLiSd2lLs9WHnXr59I1UtxOY+eEaiYdjv
DU77Yf+ESJVB4tbI6O27x0vxOpi8f/x4e3MR3N6M6Tg+D8d3993R0/0T5pZ1OQGNI4RXe0IstwIQ
GGuW1anlSnoZeDn74hcgQTPRqRSCauMgCyJUwaXLw2XGQQjccnkHQUj7/dPweSm/Dn67GYv07o/H
0d15MI6LOYazGbNM7w9Bs2i66+l02VIlLXa+GdTa3yT4efZTD0kKvaZpDu3qOzTnjs74OyUfbv9O
YK6EUA+I3q/3+UN6nsDforCw1W8uiyMYELXwlZ0CtguP4Ma84OZ7M75XTINY+O6DI+I4DLZaQ/ai
gtYYLOcBp2K5aLSjIasnJtW8cpPzsi49QyKT0gWT/Im9nAmRBgkakXrRqRoEIv/LG99r6wqy8CvN
Zyydu1ZoSIHPsLFH0O1hkc3OK/cur93DwzajpCcsK93ja0R+uZHTRubdMwj73dMgHJ7RPUG4HsTv
L/jt7w/zcTzqjeIP3cv43In6wTdzyBtW2uu3Lmjr7KJ13mudDVohZbVVJegCkNqpMhb/959/ffV2
11MlFOoIoXQwGfYxMIOcoXVtnSuD7TQ5UZqC98uV98CF8CbQMHlNiowsPzeDbF0aVYFELqHSe9zZ
mCAzhhcSMECicqLwrP87Y0RwyQUYq+R2f6v50RDtRANyZwnD0khIQ9oOaDukcRBEtBt1+7euf1W2
HxO2aRgHg4jSKBy6mFQos6ZZV1Gj2+oEi1Epb2YdE7y5HMdXH15fx5dXiGEowzNI3HHQ2ZlxIStw
pln+b6+cIWDAF/RpXPwfXu+ItlZ/WBu/b00Zz/Ojra3j0NiCitl0ejxLAyf4RCYqm7v+ulsw3iu0
POo5etC50iVezIyzZOW8CauqTZ9NqjTORdChy8/LfwBesmWBegkAAA==
headers:
Accept-Ranges:
- bytes
Transfer-Encoding:
- chunked
X-GitHub-Request-Id:
- 998E:DAEA:42E9E4A:4D53EB6:5FC7BDEC
X-Ratelimit-Limit:
- '10'
X-Ratelimit-Remaining:
- '7'
X-Ratelimit-Reset:
- '1606925835'
X-Ratelimit-Used:
- '3'
access-control-allow-origin:
- '*'
access-control-expose-headers:
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
cache-control:
- no-cache
content-encoding:
- gzip
content-security-policy:
- default-src 'none'
content-type:
- application/json; charset=utf-8
date:
- Wed, 02 Dec 2020 16:16:45 GMT
referrer-policy:
- origin-when-cross-origin, strict-origin-when-cross-origin
server:
- GitHub.com
status:
- 200 OK
strict-transport-security:
- max-age=31536000; includeSubdomains; preload
vary:
- Accept, Accept-Encoding, Accept, X-Requested-With, Accept-Encoding
x-content-type-options:
- nosniff
x-frame-options:
- deny
x-github-media-type:
- github.v3; format=json
x-xss-protection:
- 1; mode=block
status:
code: 200
message: OK
version: 1
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os

os.environ["GH_TOKEN"] = "dummy"
os.environ["REPOSITORY_NAME"] = "dummy"
os.environ["GH_USERNAME"] = "dummy"
os.environ["GH_TOKEN"] = ""
os.environ["REPOSITORY_NAME"] = "python/python-docs-fr"
os.environ["GH_USERNAME"] = "PyDocTeur"
27 changes: 27 additions & 0 deletions tests/test_find_pr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pytest
from pydocteur.github_api import get_pull_request


@pytest.mark.vcr()
def test_get_pr_from_check_suite():
payload = {
"action": "completed",
"check_suite": {
"id": 1487327954,
"node_id": "MDg6Q2hlY2tSdW4xNDg3MzI3OTU0",
"head_sha": "390a392633a4692ee96ebd8d122cfdcd602224e0",
"external_id": "d133e080-687a-5f71-4d25-419e05f66c84",
"url": "https://api.github.com/repos/python/python-docs-fr/check-runs/1487327954",
"html_url": "https://github.com/python/python-docs-fr/runs/1487327954",
"details_url": "https://github.com/python/python-docs-fr/runs/1487327954",
"status": "completed",
"conclusion": "success",
"started_at": "2020-12-02T16:00:51Z",
"completed_at": "2020-12-02T16:05:22Z",
"output": {
"annotations_count": 0,
},
},
}
pr = get_pull_request(payload)
assert pr.number == 1461