osgithub 0.4.0__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- osgithub-0.6.0/PKG-INFO +19 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/README.md +47 -24
- {osgithub-0.4.0 → osgithub-0.6.0}/osgithub/github.py +14 -0
- osgithub-0.6.0/osgithub.egg-info/PKG-INFO +19 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/osgithub.egg-info/SOURCES.txt +2 -1
- osgithub-0.6.0/tests/test_github.py +737 -0
- osgithub-0.4.0/PKG-INFO +0 -15
- osgithub-0.4.0/osgithub.egg-info/PKG-INFO +0 -15
- {osgithub-0.4.0 → osgithub-0.6.0}/LICENSE +0 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/osgithub/__init__.py +0 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/osgithub.egg-info/dependency_links.txt +0 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/osgithub.egg-info/requires.txt +0 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/osgithub.egg-info/top_level.txt +0 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/pyproject.toml +0 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/setup.cfg +0 -0
- {osgithub-0.4.0 → osgithub-0.6.0}/setup.py +0 -0
osgithub-0.6.0/PKG-INFO
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: osgithub
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Home-page: https://github.com/opensafely-core/osgithub
|
|
5
|
+
Author: OpenSAFELY
|
|
6
|
+
Author-email: tech@opensafely.org
|
|
7
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: requests
|
|
11
|
+
Requires-Dist: requests-cache
|
|
12
|
+
Requires-Dist: furl
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
Dynamic: requires-dist
|
|
19
|
+
Dynamic: requires-python
|
|
@@ -3,109 +3,131 @@
|
|
|
3
3
|
A thin wrapper around the GitHub API.
|
|
4
4
|
|
|
5
5
|
## Environment
|
|
6
|
+
|
|
6
7
|
Set the following environment variables:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
- `GITHUB_USER_AGENT` - a string to identify your application
|
|
10
|
+
- `GITHUB_TOKEN` - optional; default token to use.
|
|
11
|
+
- `REQUESTS_CACHE_NAME` - optional, defaults to "http_cache"
|
|
10
12
|
|
|
11
13
|
## Usage
|
|
12
14
|
|
|
13
15
|
### Create a client
|
|
14
|
-
|
|
16
|
+
|
|
17
|
+
```py
|
|
15
18
|
from github_api_cache import GithubClient
|
|
16
19
|
# Use the default token, if one is set in the environment.
|
|
17
20
|
client = GithubClient()
|
|
18
21
|
```
|
|
19
22
|
|
|
20
23
|
### get a repo (returns a GithubRepo)
|
|
21
|
-
|
|
24
|
+
|
|
25
|
+
```py
|
|
22
26
|
repo = client.get_repo("opensafely-core/osgithub")
|
|
23
27
|
```
|
|
24
28
|
|
|
25
29
|
#### get a list of branches
|
|
26
|
-
|
|
30
|
+
|
|
31
|
+
```py
|
|
27
32
|
repo.get_branches()
|
|
28
33
|
```
|
|
34
|
+
|
|
29
35
|
And branch count:
|
|
30
|
-
|
|
36
|
+
|
|
37
|
+
```py
|
|
31
38
|
repo.branch_count
|
|
32
39
|
```
|
|
33
40
|
|
|
34
41
|
#### get a list of pull requests
|
|
42
|
+
|
|
35
43
|
Provide a `page` argument to get more pages than just the first one (30 result are returned per page).
|
|
36
|
-
|
|
44
|
+
|
|
45
|
+
```py
|
|
37
46
|
repo.get_pull_requests(page=1)
|
|
38
47
|
```
|
|
39
48
|
|
|
40
|
-
By default, this fetches open PRs only.
|
|
49
|
+
By default, this fetches open PRs only. To retrieve other states, pass `state="closed"` or `state="open"`
|
|
41
50
|
|
|
42
51
|
Pull request counts:
|
|
43
|
-
|
|
52
|
+
|
|
53
|
+
```py
|
|
44
54
|
repo.pull_request_count
|
|
45
55
|
repo.open_pull_request_count
|
|
46
56
|
```
|
|
47
57
|
|
|
48
58
|
#### get the contents of the `osgithub` directory on branch `main`
|
|
49
|
-
|
|
59
|
+
|
|
60
|
+
```py
|
|
50
61
|
# returns a list of GithubContentFile objects
|
|
51
62
|
repo.get_contents("osgithub", "main")
|
|
52
63
|
```
|
|
53
64
|
|
|
54
65
|
#### get a single file; returns a GithubContentFile
|
|
55
|
-
|
|
66
|
+
|
|
67
|
+
```py
|
|
56
68
|
repo.get_contents("osgithub/__init__.py", "main")
|
|
57
69
|
```
|
|
58
70
|
|
|
59
71
|
#### get_commits_for_file(self, path, ref, number_of_commits=1):
|
|
72
|
+
|
|
60
73
|
Returns a list of commits, just the last one by default
|
|
61
|
-
|
|
74
|
+
|
|
75
|
+
```py
|
|
62
76
|
repo.get_commits_for_file("osgithub", "main")
|
|
63
77
|
```
|
|
78
|
+
|
|
64
79
|
To return more commits:
|
|
65
|
-
|
|
80
|
+
|
|
81
|
+
```py
|
|
66
82
|
repo.get_commits_for_file("osgithub", "main", number_of_commits=10)
|
|
67
83
|
```
|
|
68
84
|
|
|
69
85
|
Get details for a single commit by sha - returns author and date of the commit
|
|
70
|
-
|
|
86
|
+
|
|
87
|
+
```py
|
|
71
88
|
repo.get_commit("7a6f60e8e74b9c93a9c6322b3151ee437fa4be61")
|
|
72
89
|
```
|
|
73
90
|
|
|
74
|
-
|
|
75
91
|
#### Repo information
|
|
76
92
|
|
|
77
93
|
Fetch the HTML from the README.md of repo:
|
|
78
|
-
|
|
94
|
+
|
|
95
|
+
```py
|
|
79
96
|
repo.get_readme(tag="main")
|
|
80
97
|
```
|
|
81
98
|
|
|
82
99
|
Fetch the About and Name of the repo:
|
|
83
|
-
|
|
100
|
+
|
|
101
|
+
```py
|
|
84
102
|
repo.get_details()
|
|
85
103
|
```
|
|
86
104
|
|
|
87
105
|
Fetch tags with name and sha:
|
|
88
|
-
|
|
106
|
+
|
|
107
|
+
```py
|
|
89
108
|
repo.get_tags()
|
|
90
109
|
```
|
|
91
110
|
|
|
92
|
-
|
|
93
111
|
### Caching
|
|
112
|
+
|
|
94
113
|
Requests can optionally be cached, using a sqlite backend.
|
|
95
114
|
|
|
96
115
|
To use caching:
|
|
97
|
-
|
|
116
|
+
|
|
117
|
+
```py
|
|
98
118
|
client = GithubClient(use_cache=True)
|
|
99
119
|
```
|
|
100
120
|
|
|
101
121
|
Set a global expiry for the session (never expires by default):
|
|
102
|
-
|
|
122
|
+
|
|
123
|
+
```py
|
|
103
124
|
# expire all cached requests after 300s
|
|
104
125
|
client = GithubClient(expire_after=300)
|
|
105
126
|
```
|
|
106
127
|
|
|
107
128
|
Set expiry on specific url patterns (falls back to `expire_after` if no match found)
|
|
108
|
-
|
|
129
|
+
|
|
130
|
+
```py
|
|
109
131
|
urls_expire_after = {
|
|
110
132
|
'*/pulls': 60, # expire requests to get pull requests after 60 secs
|
|
111
133
|
'*/branches': 60 * 5, # expire requests to get branches after 5 mins
|
|
@@ -115,7 +137,8 @@ client = GithubClient(urls_expire_after=urls_expire_after)
|
|
|
115
137
|
```
|
|
116
138
|
|
|
117
139
|
#### Clear the cache for this repo
|
|
118
|
-
|
|
140
|
+
|
|
141
|
+
```py
|
|
119
142
|
repo.clear_cache()
|
|
120
143
|
```
|
|
121
144
|
|
|
@@ -446,6 +446,20 @@ class GithubRepo:
|
|
|
446
446
|
contrib_list = [contrib["login"] for contrib in content]
|
|
447
447
|
return contrib_list
|
|
448
448
|
|
|
449
|
+
def get_topics(self):
|
|
450
|
+
"""
|
|
451
|
+
Gets the repo's topics.
|
|
452
|
+
|
|
453
|
+
If the repo's name and about are also being fetched from GitHub,
|
|
454
|
+
consider setting `use_cache` to True in the GithubClient to avoid
|
|
455
|
+
duplicate calls to the repo endpoint.
|
|
456
|
+
|
|
457
|
+
Returns:
|
|
458
|
+
list[str]: list of topics
|
|
459
|
+
"""
|
|
460
|
+
content = self.client.get_json(self.repo_path_segments)
|
|
461
|
+
return content["topics"]
|
|
462
|
+
|
|
449
463
|
def clear_cache(self):
|
|
450
464
|
"""Clears all request cache urls for this repo"""
|
|
451
465
|
cached_urls = self.client.session.cache.urls()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: osgithub
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Home-page: https://github.com/opensafely-core/osgithub
|
|
5
|
+
Author: OpenSAFELY
|
|
6
|
+
Author-email: tech@opensafely.org
|
|
7
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: requests
|
|
11
|
+
Requires-Dist: requests-cache
|
|
12
|
+
Requires-Dist: furl
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
Dynamic: requires-dist
|
|
19
|
+
Dynamic: requires-python
|
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from base64 import b64encode
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from os import environ
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from furl import furl
|
|
8
|
+
from requests.exceptions import HTTPError
|
|
9
|
+
|
|
10
|
+
from osgithub import GithubAPIException, GithubClient, GithubRepo
|
|
11
|
+
|
|
12
|
+
from .conftest import remove_cache_file_if_exists
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register_uri(httpretty, path, queryparams=None, status=200, body=None):
|
|
16
|
+
url = furl("https://api.github.com")
|
|
17
|
+
url.path.segments += [*path.split("/")]
|
|
18
|
+
if queryparams:
|
|
19
|
+
url.add(queryparams)
|
|
20
|
+
httpretty.register_uri(
|
|
21
|
+
httpretty.GET,
|
|
22
|
+
url.url,
|
|
23
|
+
status=status,
|
|
24
|
+
body=json.dumps(body or ""),
|
|
25
|
+
match_querystring=True,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_github_client_get_repo(httpretty):
|
|
30
|
+
# Mock the github request
|
|
31
|
+
register_uri(httpretty, "repos/test/foo", body={"name": "foo", "description": ""})
|
|
32
|
+
client = GithubClient()
|
|
33
|
+
repo = client.get_repo("test", "foo")
|
|
34
|
+
assert repo.repo_path_segments == ["repos", "test", "foo"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_github_client_token(reset_environment_after_test):
|
|
38
|
+
"""Authorization headers is set based on environment variable"""
|
|
39
|
+
environ["GITHUB_TOKEN"] = "test"
|
|
40
|
+
client = GithubClient()
|
|
41
|
+
assert client.headers["Authorization"] == "token test"
|
|
42
|
+
|
|
43
|
+
del environ["GITHUB_TOKEN"]
|
|
44
|
+
client = GithubClient()
|
|
45
|
+
assert "Authorization" not in client.headers
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_github_repo_get_url():
|
|
49
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
50
|
+
assert repo._url is None
|
|
51
|
+
assert repo.url == "https://github.com/test/foo"
|
|
52
|
+
assert repo._url == repo.url
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_github_client_get_repo_not_found(httpretty):
|
|
56
|
+
# Mock the github request
|
|
57
|
+
register_uri(httpretty, "repos/test/bar", status=404, body={"message": "Not found"})
|
|
58
|
+
client = GithubClient()
|
|
59
|
+
with pytest.raises(GithubAPIException, match="Not found"):
|
|
60
|
+
client.get_repo("test", "bar")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.mark.parametrize("use_cache", [True, False])
|
|
64
|
+
def test_github_client_get_repo_with_cache(httpretty, use_cache):
|
|
65
|
+
client = GithubClient(use_cache=use_cache)
|
|
66
|
+
|
|
67
|
+
# set up mock request with valid response and call it
|
|
68
|
+
register_uri(
|
|
69
|
+
httpretty, "repos/test/test-cache", body={"name": "foo", "description": ""}
|
|
70
|
+
)
|
|
71
|
+
client.get_repo("test", "test-cache")
|
|
72
|
+
|
|
73
|
+
# re-mock the repos request to a 404, should raise an exception if called directly
|
|
74
|
+
register_uri(
|
|
75
|
+
httpretty, "repos/test/test-cache", status=404, body={"message": "Not found"}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if use_cache:
|
|
79
|
+
# No exception raised because the first response was cached
|
|
80
|
+
client.get_repo("test", "test-cache")
|
|
81
|
+
else:
|
|
82
|
+
# Exception raised because the repos endpoint was fetched again
|
|
83
|
+
with pytest.raises(GithubAPIException, match="Not found"):
|
|
84
|
+
client.get_repo("test", "test-cache")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@pytest.mark.parametrize("state", ["open", "closed"])
|
|
88
|
+
def test_github_repo_get_pull_requests(httpretty, state):
|
|
89
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
90
|
+
pull_requests = {
|
|
91
|
+
"open": [
|
|
92
|
+
{
|
|
93
|
+
"url": "https://api.github.com/repos/test/foo/pulls/1",
|
|
94
|
+
"id": 1,
|
|
95
|
+
"state": "open",
|
|
96
|
+
"title": "Open PR",
|
|
97
|
+
"user": {"login": "testuser", "id": 123},
|
|
98
|
+
"body": "",
|
|
99
|
+
"created_at": "2021-08-16T04:11:31Z",
|
|
100
|
+
"updated_at": None,
|
|
101
|
+
"closed_at": None,
|
|
102
|
+
"merged_at": None,
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"closed": [
|
|
106
|
+
{
|
|
107
|
+
"url": "https://api.github.com/repos/test/foo/pulls/2",
|
|
108
|
+
"id": 2,
|
|
109
|
+
"state": "closed",
|
|
110
|
+
"title": "Closed PR",
|
|
111
|
+
"user": {"login": "testuser", "id": 123},
|
|
112
|
+
"body": "",
|
|
113
|
+
"created_at": "2021-08-01T10:00:00Z",
|
|
114
|
+
"updated_at": None,
|
|
115
|
+
"closed_at": "2021-08-12T09:11:00Z",
|
|
116
|
+
"merged_at": None,
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
}
|
|
120
|
+
# Mock the github requests
|
|
121
|
+
register_uri(
|
|
122
|
+
httpretty,
|
|
123
|
+
"repos/test/foo/pulls",
|
|
124
|
+
queryparams=dict(state=state, page=1, per_page=30),
|
|
125
|
+
body=pull_requests[state],
|
|
126
|
+
)
|
|
127
|
+
pulls = repo.get_pull_requests(state)
|
|
128
|
+
assert pulls == pull_requests[state]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_github_repo_get_open_pull_request_count(httpretty):
|
|
132
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
133
|
+
pull_requests = [
|
|
134
|
+
{
|
|
135
|
+
"url": "https://api.github.com/repos/test/foo/pulls/1",
|
|
136
|
+
"id": 1,
|
|
137
|
+
"state": "open",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"url": "https://api.github.com/repos/test/foo/pulls/2",
|
|
141
|
+
"id": 2,
|
|
142
|
+
"state": "open",
|
|
143
|
+
},
|
|
144
|
+
]
|
|
145
|
+
# Mock the github requests
|
|
146
|
+
register_uri(
|
|
147
|
+
httpretty,
|
|
148
|
+
"repos/test/foo/pulls",
|
|
149
|
+
queryparams=dict(state="open", page=1, per_page=30),
|
|
150
|
+
body=pull_requests,
|
|
151
|
+
)
|
|
152
|
+
assert repo.open_pull_request_count == 2
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_github_repo_get_branches(httpretty):
|
|
156
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
157
|
+
sha1 = "1" * 40
|
|
158
|
+
sha2 = "2" * 40
|
|
159
|
+
branches = [
|
|
160
|
+
{
|
|
161
|
+
"name": "test_branch",
|
|
162
|
+
"commit": {
|
|
163
|
+
"sha": sha1,
|
|
164
|
+
"url": f"https://api.github.com/repos/test/foo/commits/{sha1}",
|
|
165
|
+
},
|
|
166
|
+
"protected": False,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"name": "test_branch1",
|
|
170
|
+
"commit": {
|
|
171
|
+
"sha": sha2,
|
|
172
|
+
"url": f"https://api.github.com/repos/test/foo/commits/{sha2}",
|
|
173
|
+
},
|
|
174
|
+
"protected": False,
|
|
175
|
+
},
|
|
176
|
+
]
|
|
177
|
+
# Mock the github requests
|
|
178
|
+
register_uri(httpretty, "repos/test/foo/branches", body=branches)
|
|
179
|
+
assert repo.get_branches() == branches
|
|
180
|
+
assert repo.branch_count == 2
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_github_repo_get_multipage_pull_request_count(httpretty):
|
|
184
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
185
|
+
for i in range(1, 3):
|
|
186
|
+
pull_requests = [
|
|
187
|
+
{
|
|
188
|
+
"url": f"https://api.github.com/repos/test/foo/pulls/{pr_num * i}",
|
|
189
|
+
"id": pr_num * i,
|
|
190
|
+
"state": "open",
|
|
191
|
+
}
|
|
192
|
+
for pr_num in range(1, 31)
|
|
193
|
+
]
|
|
194
|
+
# Mock the github request
|
|
195
|
+
register_uri(
|
|
196
|
+
httpretty,
|
|
197
|
+
"repos/test/foo/pulls",
|
|
198
|
+
queryparams=dict(state="open", page=i, per_page=30),
|
|
199
|
+
body=pull_requests,
|
|
200
|
+
)
|
|
201
|
+
last_page_pull_requests = [
|
|
202
|
+
{
|
|
203
|
+
"url": f"https://api.github.com/repos/test/foo/pulls/{pr_num * 3}",
|
|
204
|
+
"id": pr_num * 3,
|
|
205
|
+
"state": "open",
|
|
206
|
+
}
|
|
207
|
+
for pr_num in range(1, 11)
|
|
208
|
+
]
|
|
209
|
+
register_uri(
|
|
210
|
+
httpretty,
|
|
211
|
+
"repos/test/foo/pulls",
|
|
212
|
+
queryparams=dict(state="open", page=3, per_page=30),
|
|
213
|
+
body=last_page_pull_requests,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
assert repo.open_pull_request_count == 70
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_github_repo_get_contents_single_file(httpretty):
|
|
220
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
221
|
+
str_content = """
|
|
222
|
+
<html>
|
|
223
|
+
<head>
|
|
224
|
+
<style type="text/css">body {margin: 0;}</style>
|
|
225
|
+
<style type="text/css">a {background-color: red;}</style>
|
|
226
|
+
<script src="https://a-js-package.js"></script>
|
|
227
|
+
</head>
|
|
228
|
+
<body><p>foo</p></body>
|
|
229
|
+
</html>
|
|
230
|
+
"""
|
|
231
|
+
# Content retrieved from GitHub is base64-encoded, decoded to str for json
|
|
232
|
+
b64_content = b64encode(bytes(str_content, encoding="utf-8")).decode()
|
|
233
|
+
# Mock the github request
|
|
234
|
+
reponse_json = {
|
|
235
|
+
"name": "test-file.html",
|
|
236
|
+
"path": "test-folder/test-file.html",
|
|
237
|
+
"sha": "abcd1234",
|
|
238
|
+
"size": 1234,
|
|
239
|
+
"encoding": "base64",
|
|
240
|
+
"content": b64_content,
|
|
241
|
+
}
|
|
242
|
+
register_uri(
|
|
243
|
+
httpretty,
|
|
244
|
+
"repos/test/foo/contents/test-folder/test-file.html",
|
|
245
|
+
queryparams=dict(ref="main"),
|
|
246
|
+
body=reponse_json,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
commits_response = [{"commit": {"committer": {"date": "2021-03-01T10:00:00Z"}}}]
|
|
250
|
+
register_uri(
|
|
251
|
+
httpretty,
|
|
252
|
+
"repos/test/foo/commits",
|
|
253
|
+
queryparams=dict(sha="main", path="test-folder/test-file.html", per_page=1),
|
|
254
|
+
body=commits_response,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
content_file = repo.get_contents("test-folder/test-file.html", ref="main")
|
|
258
|
+
assert content_file.name == "test-file.html"
|
|
259
|
+
# decoded content retrieves the original str contents
|
|
260
|
+
assert content_file.decoded_content == str_content
|
|
261
|
+
|
|
262
|
+
# get contents with content fetch type
|
|
263
|
+
content_file, fetch_type = repo.get_contents(
|
|
264
|
+
"test-folder/test-file.html", ref="main", return_fetch_type=True
|
|
265
|
+
)
|
|
266
|
+
assert content_file.name == "test-file.html"
|
|
267
|
+
assert fetch_type == "contents"
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def test_github_repo_get_last_updated(httpretty):
|
|
271
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
272
|
+
commit_dates = [
|
|
273
|
+
"2021-03-01T10:00:00Z",
|
|
274
|
+
"2021-02-14T10:00:00Z",
|
|
275
|
+
"2021-02-01T10:00:00Z",
|
|
276
|
+
]
|
|
277
|
+
commits_response = [
|
|
278
|
+
{"commit": {"committer": {"date": commit_date}}} for commit_date in commit_dates
|
|
279
|
+
]
|
|
280
|
+
register_uri(
|
|
281
|
+
httpretty,
|
|
282
|
+
"repos/test/foo/commits",
|
|
283
|
+
queryparams=dict(sha="main", path="test-folder/test-file.html", per_page=1),
|
|
284
|
+
body=commits_response,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
last_updated = repo.get_last_updated(path="test-folder/test-file.html", ref="main")
|
|
288
|
+
assert last_updated == datetime(2021, 3, 1, 10, 0, 0, tzinfo=timezone.utc)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@pytest.mark.parametrize(
|
|
292
|
+
"status_code,body,expected_exception,expected_match",
|
|
293
|
+
[
|
|
294
|
+
(404, {"message": "Not found"}, GithubAPIException, "Not found"),
|
|
295
|
+
(
|
|
296
|
+
403,
|
|
297
|
+
{"errors": [{"code": "other_code", "message": "An unexpected 403"}]},
|
|
298
|
+
HTTPError,
|
|
299
|
+
"Forbidden for url",
|
|
300
|
+
),
|
|
301
|
+
(
|
|
302
|
+
401,
|
|
303
|
+
{"errors": [{"code": "other_code", "message": "An unexpected 403"}]},
|
|
304
|
+
HTTPError,
|
|
305
|
+
"Unauthorized for url",
|
|
306
|
+
),
|
|
307
|
+
(
|
|
308
|
+
403,
|
|
309
|
+
{
|
|
310
|
+
"unknown": [
|
|
311
|
+
{"code": "other_code", "message": "A 403 without an 'errors' key"}
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
GithubAPIException,
|
|
315
|
+
"A 403 without an 'errors' key",
|
|
316
|
+
),
|
|
317
|
+
],
|
|
318
|
+
)
|
|
319
|
+
def test_github_repo_get_contents_exceptions(
|
|
320
|
+
httpretty, status_code, body, expected_exception, expected_match
|
|
321
|
+
):
|
|
322
|
+
"""
|
|
323
|
+
Test expected and unexpected exceptions from get_contents
|
|
324
|
+
"""
|
|
325
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
326
|
+
# Mock the github request
|
|
327
|
+
register_uri(
|
|
328
|
+
httpretty,
|
|
329
|
+
"repos/test/foo/contents/test-folder/test-file.html",
|
|
330
|
+
queryparams=dict(ref="main"),
|
|
331
|
+
status=status_code,
|
|
332
|
+
body=body,
|
|
333
|
+
)
|
|
334
|
+
with pytest.raises(expected_exception, match=expected_match):
|
|
335
|
+
repo.get_contents("test-folder/test-file.html", ref="main")
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@pytest.mark.parametrize(
|
|
339
|
+
"filepath,expected_filename",
|
|
340
|
+
[
|
|
341
|
+
("test-folder/test-file.html", "test-file.html"),
|
|
342
|
+
("test-folder/test-file1.html", "test-file1.html"),
|
|
343
|
+
("test-folder/test-file2.html", None),
|
|
344
|
+
],
|
|
345
|
+
)
|
|
346
|
+
def test_github_repo_matching_file_from_parent_contents(
|
|
347
|
+
httpretty, filepath, expected_filename
|
|
348
|
+
):
|
|
349
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
350
|
+
# Mock the github requests
|
|
351
|
+
# get the parent folder contents
|
|
352
|
+
response_json = [
|
|
353
|
+
{
|
|
354
|
+
"name": "test-file.html",
|
|
355
|
+
"path": "test-folder/test-file.html",
|
|
356
|
+
"sha": "abcd1234",
|
|
357
|
+
"size": 1234,
|
|
358
|
+
"encoding": "base64",
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"name": "test-file1.html",
|
|
362
|
+
"path": "test-folder/test-file1.html",
|
|
363
|
+
"sha": "abcd2345",
|
|
364
|
+
"size": 1234,
|
|
365
|
+
"encoding": "base64",
|
|
366
|
+
},
|
|
367
|
+
]
|
|
368
|
+
register_uri(
|
|
369
|
+
httpretty,
|
|
370
|
+
"repos/test/foo/contents/test-folder",
|
|
371
|
+
queryparams=dict(ref="main"),
|
|
372
|
+
body=response_json,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
matching_file = repo.get_matching_file_from_parent_contents(filepath, "main")
|
|
376
|
+
if expected_filename is None:
|
|
377
|
+
assert matching_file is None
|
|
378
|
+
else:
|
|
379
|
+
assert matching_file.name == expected_filename
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def test_github_repo_get_contents_folder(httpretty):
|
|
383
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
384
|
+
# Mock the github request
|
|
385
|
+
response_json = [
|
|
386
|
+
{
|
|
387
|
+
"name": "test-file1.html",
|
|
388
|
+
"path": "test-folder/test-file1.html",
|
|
389
|
+
"sha": "abcd1234",
|
|
390
|
+
"size": 1234,
|
|
391
|
+
"encoding": "base64",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
"name": "test-file2.html",
|
|
395
|
+
"path": "test-folder/test-file2.html",
|
|
396
|
+
"sha": "abcd5678",
|
|
397
|
+
"size": 1234,
|
|
398
|
+
"encoding": "base64",
|
|
399
|
+
},
|
|
400
|
+
]
|
|
401
|
+
register_uri(
|
|
402
|
+
httpretty,
|
|
403
|
+
"repos/test/foo/contents/test-folder",
|
|
404
|
+
queryparams=dict(ref="main"),
|
|
405
|
+
body=response_json,
|
|
406
|
+
)
|
|
407
|
+
contents = repo.get_contents("test-folder", ref="main")
|
|
408
|
+
assert isinstance(contents, list)
|
|
409
|
+
assert len(contents) == 2
|
|
410
|
+
assert contents[0].name == "test-file1.html"
|
|
411
|
+
assert contents[1].name == "test-file2.html"
|
|
412
|
+
# decoded content returns None when the ContntFile was generated from a list of files
|
|
413
|
+
# returned from github
|
|
414
|
+
assert contents[0].decoded_content is None
|
|
415
|
+
assert contents[1].decoded_content is None
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def test_github_repo_get_contents_from_git_blob(httpretty):
|
|
419
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
420
|
+
str_content = """
|
|
421
|
+
<html>
|
|
422
|
+
<head>
|
|
423
|
+
<style type="text/css">body {margin: 0;}</style>
|
|
424
|
+
<style type="text/css">a {background-color: red;}</style>
|
|
425
|
+
<script src="https://a-js-package.js"></script>
|
|
426
|
+
</head>
|
|
427
|
+
<body><p>foo</p></body>
|
|
428
|
+
</html>
|
|
429
|
+
"""
|
|
430
|
+
# Content retrieved from GitHub is base64-encoded, decoded to str for json
|
|
431
|
+
b64_content = b64encode(bytes(str_content, encoding="utf-8")).decode()
|
|
432
|
+
# Mock the github requests
|
|
433
|
+
# get the parent folder contents
|
|
434
|
+
response_json = [
|
|
435
|
+
{
|
|
436
|
+
"name": "test-file.html",
|
|
437
|
+
"path": "test-folder/test-file.html",
|
|
438
|
+
"sha": "abcd1234",
|
|
439
|
+
"size": 1234,
|
|
440
|
+
"encoding": "base64",
|
|
441
|
+
},
|
|
442
|
+
]
|
|
443
|
+
register_uri(
|
|
444
|
+
httpretty,
|
|
445
|
+
"repos/test/foo/contents/test-folder",
|
|
446
|
+
queryparams=dict(ref="main"),
|
|
447
|
+
body=response_json,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
# get the git blob
|
|
451
|
+
response_json = {
|
|
452
|
+
"name": "test-file.html",
|
|
453
|
+
"path": "test-folder/test-file.html",
|
|
454
|
+
"sha": "abcd1234",
|
|
455
|
+
"size": 1234,
|
|
456
|
+
"encoding": "base64",
|
|
457
|
+
"content": b64_content,
|
|
458
|
+
}
|
|
459
|
+
register_uri(httpretty, "repos/test/foo/git/blobs/abcd1234", body=response_json)
|
|
460
|
+
|
|
461
|
+
# get the commits for last updated
|
|
462
|
+
commits_response = [{"commit": {"committer": {"date": "2021-03-01T10:00:00Z"}}}]
|
|
463
|
+
register_uri(
|
|
464
|
+
httpretty,
|
|
465
|
+
"repos/test/foo/commits",
|
|
466
|
+
queryparams=dict(sha="main", path="test-folder/test-file.html", per_page=1),
|
|
467
|
+
body=commits_response,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
content_file = repo.get_contents(
|
|
471
|
+
"test-folder/test-file.html", "main", from_git_blob=True
|
|
472
|
+
)
|
|
473
|
+
assert content_file.name == "test-file.html"
|
|
474
|
+
# decoded content retrieves the original str contents
|
|
475
|
+
assert content_file.decoded_content == str_content
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def test_github_repo_get_contents_too_large_file(httpretty):
|
|
479
|
+
"""
|
|
480
|
+
Test get_contents with a too-large file resorts to fetching content from the git blob
|
|
481
|
+
"""
|
|
482
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
483
|
+
str_content = """
|
|
484
|
+
<html>
|
|
485
|
+
<head>
|
|
486
|
+
<style type="text/css">body {margin: 0;}</style>
|
|
487
|
+
<style type="text/css">a {background-color: red;}</style>
|
|
488
|
+
<script src="https://a-js-package.js"></script>
|
|
489
|
+
</head>
|
|
490
|
+
<body><p>foo</p></body>
|
|
491
|
+
</html>
|
|
492
|
+
"""
|
|
493
|
+
# Content retrieved from GitHub is base64-encoded, decoded to str for json
|
|
494
|
+
b64_content = b64encode(bytes(str_content, encoding="utf-8")).decode()
|
|
495
|
+
|
|
496
|
+
# Mock the github requests
|
|
497
|
+
# First tries the contents endpoint and gets a 403
|
|
498
|
+
register_uri(
|
|
499
|
+
httpretty,
|
|
500
|
+
"repos/test/foo/contents/test-folder/test-file.html",
|
|
501
|
+
status=200,
|
|
502
|
+
queryparams=dict(ref="main"),
|
|
503
|
+
body={
|
|
504
|
+
"name": "test-file.html",
|
|
505
|
+
"path": "test-folder/test-file.html",
|
|
506
|
+
"sha": "abcd1234",
|
|
507
|
+
"size": 1234,
|
|
508
|
+
"encoding": "base64",
|
|
509
|
+
"content": "",
|
|
510
|
+
},
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
# gets the parent folder contents
|
|
514
|
+
register_uri(
|
|
515
|
+
httpretty,
|
|
516
|
+
"repos/test/foo/contents/test-folder",
|
|
517
|
+
status=200,
|
|
518
|
+
queryparams=dict(ref="main"),
|
|
519
|
+
body=[
|
|
520
|
+
{
|
|
521
|
+
"name": "test-file.html",
|
|
522
|
+
"path": "test-folder/test-file.html",
|
|
523
|
+
"sha": "abcd1234",
|
|
524
|
+
"size": 1234,
|
|
525
|
+
"encoding": "base64",
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
# then gets the git blob
|
|
531
|
+
register_uri(
|
|
532
|
+
httpretty,
|
|
533
|
+
"repos/test/foo/git/blobs/abcd1234",
|
|
534
|
+
status=200,
|
|
535
|
+
body={
|
|
536
|
+
"name": "test-file.html",
|
|
537
|
+
"path": "test-folder/test-file.html",
|
|
538
|
+
"sha": "abcd1234",
|
|
539
|
+
"size": 1234,
|
|
540
|
+
"encoding": "base64",
|
|
541
|
+
"content": b64_content,
|
|
542
|
+
},
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
# get the commits for last updated
|
|
546
|
+
commits_response = [{"commit": {"committer": {"date": "2021-03-01T10:00:00Z"}}}]
|
|
547
|
+
register_uri(
|
|
548
|
+
httpretty,
|
|
549
|
+
"repos/test/foo/commits",
|
|
550
|
+
queryparams=dict(sha="main", path="test-folder/test-file.html", per_page=1),
|
|
551
|
+
body=commits_response,
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
content_file = repo.get_contents("test-folder/test-file.html", ref="main")
|
|
555
|
+
assert content_file.decoded_content == str_content
|
|
556
|
+
assert content_file.last_updated == datetime(
|
|
557
|
+
2021, 3, 1, 10, 0, 0, tzinfo=timezone.utc
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def test_github_repo_get_readme(httpretty):
|
|
562
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
563
|
+
readme_content = b"<div id='readme'><h1>Foo</h1><p>A README.</p></div>"
|
|
564
|
+
httpretty.register_uri(
|
|
565
|
+
httpretty.GET,
|
|
566
|
+
"https://api.github.com/repos/test/foo/readme?ref=main",
|
|
567
|
+
status=200,
|
|
568
|
+
body=readme_content,
|
|
569
|
+
match_querystring=True,
|
|
570
|
+
)
|
|
571
|
+
readme_content = repo.get_readme(tag="main")
|
|
572
|
+
assert readme_content == "<div id='readme'><h1>Foo</h1><p>A README.</p></div>"
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def test_github_repo_details(httpretty):
|
|
576
|
+
register_uri(
|
|
577
|
+
httpretty,
|
|
578
|
+
"repos/test/foo",
|
|
579
|
+
status=200,
|
|
580
|
+
body={"name": "foo", "description": "A test repo"},
|
|
581
|
+
)
|
|
582
|
+
# If the repo is instantiated with an "about", the endpoint isn't called
|
|
583
|
+
repo = GithubRepo(
|
|
584
|
+
client=GithubClient(use_cache=False),
|
|
585
|
+
owner="test",
|
|
586
|
+
name="foo",
|
|
587
|
+
about="A different description",
|
|
588
|
+
)
|
|
589
|
+
details = repo.get_repo_details()
|
|
590
|
+
assert details == {"name": "foo", "about": "A different description"}
|
|
591
|
+
assert httpretty.latest_requests() == []
|
|
592
|
+
|
|
593
|
+
# instantiate with no "about" arg, need to fetch it for the details
|
|
594
|
+
repo1 = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
595
|
+
details = repo1.get_repo_details()
|
|
596
|
+
assert details == {"name": "foo", "about": "A test repo"}
|
|
597
|
+
latest_requests = httpretty.latest_requests()
|
|
598
|
+
assert len(latest_requests) == 1
|
|
599
|
+
assert latest_requests[0].url == "https://api.github.com/repos/test/foo"
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def test_github_repo_get_tags(httpretty):
|
|
603
|
+
sha1 = "1" * 40
|
|
604
|
+
sha2 = "2" * 40
|
|
605
|
+
tags = [
|
|
606
|
+
{"name": "v1.0", "commit": {"sha": sha1}},
|
|
607
|
+
{"name": "v2.0", "commit": {"sha": sha2}},
|
|
608
|
+
]
|
|
609
|
+
register_uri(httpretty, "repos/test/foo/tags", status=200, body=tags)
|
|
610
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
611
|
+
assert repo.get_tags() == [
|
|
612
|
+
{"tag_name": "v1.0", "sha": sha1},
|
|
613
|
+
{"tag_name": "v2.0", "sha": sha2},
|
|
614
|
+
]
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def test_github_repo_get_commit(httpretty):
|
|
618
|
+
sha = "1" * 40
|
|
619
|
+
commit_body = {
|
|
620
|
+
"author": {"name": "Donald Duck"},
|
|
621
|
+
"committer": {"date": "2021-03-01T10:00:00Z"},
|
|
622
|
+
}
|
|
623
|
+
register_uri(
|
|
624
|
+
httpretty, f"repos/test/foo/git/commits/{sha}", status=200, body=commit_body
|
|
625
|
+
)
|
|
626
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
627
|
+
assert repo.get_commit(sha) == {
|
|
628
|
+
"author": "Donald Duck",
|
|
629
|
+
"date": "2021-03-01T10:00:00Z",
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def test_github_repo_get_contributors(httpretty):
|
|
634
|
+
contribs = [{"login": "octocat"}, {"login": "octodog"}]
|
|
635
|
+
register_uri(httpretty, "repos/test/foo/contributors", status=200, body=contribs)
|
|
636
|
+
repo = GithubRepo(client=GithubClient(use_cache=False), owner="test", name="foo")
|
|
637
|
+
assert repo.get_contributors() == ["octocat", "octodog"]
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
@pytest.mark.parametrize(
|
|
641
|
+
"use_cache,num_requests",
|
|
642
|
+
[
|
|
643
|
+
(True, 1),
|
|
644
|
+
(False, 2),
|
|
645
|
+
],
|
|
646
|
+
)
|
|
647
|
+
def test_github_repo_get_topics(httpretty, use_cache, num_requests):
|
|
648
|
+
register_uri(
|
|
649
|
+
httpretty,
|
|
650
|
+
"repos/test/foo",
|
|
651
|
+
status=200,
|
|
652
|
+
body={"name": "foo", "description": "a description", "topics": ["bar", "baz"]},
|
|
653
|
+
)
|
|
654
|
+
repo = GithubClient(use_cache=use_cache).get_repo("test", "foo")
|
|
655
|
+
assert repo.get_topics() == ["bar", "baz"]
|
|
656
|
+
assert len(httpretty.latest_requests()) == num_requests
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def test_clear_cache(httpretty, reset_environment_after_test):
|
|
660
|
+
# mock the requests
|
|
661
|
+
register_uri(httpretty, "repos/test/foo", body={"name": "foo", "description": ""})
|
|
662
|
+
httpretty.register_uri(httpretty.GET, "https://www.test.com/", status=200)
|
|
663
|
+
|
|
664
|
+
# make sure we start with a fresh cache
|
|
665
|
+
remove_cache_file_if_exists()
|
|
666
|
+
client = GithubClient(use_cache=True)
|
|
667
|
+
# no github requests have been made, so cache is currently clear
|
|
668
|
+
assert client.session.cache.urls() == []
|
|
669
|
+
|
|
670
|
+
# A real repo
|
|
671
|
+
repo = client.get_repo("test", "foo")
|
|
672
|
+
|
|
673
|
+
# 1 call made, to get contents
|
|
674
|
+
assert len(client.session.cache.urls()) == 1
|
|
675
|
+
# make another request using this cache session
|
|
676
|
+
client.session.get("https://www.test.com/")
|
|
677
|
+
assert len(client.session.cache.urls()) == 2
|
|
678
|
+
# Clearing the cache only clears urls related to this report
|
|
679
|
+
repo.clear_cache()
|
|
680
|
+
assert client.session.cache.urls() == ["https://www.test.com/"]
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
@pytest.mark.integration
|
|
684
|
+
def test_integration(reset_environment_after_test):
|
|
685
|
+
"""Test repo methods with a real github repo"""
|
|
686
|
+
# make sure we start with a fresh cache
|
|
687
|
+
remove_cache_file_if_exists()
|
|
688
|
+
client = GithubClient(use_cache=True)
|
|
689
|
+
# Set up a real repo
|
|
690
|
+
repo = client.get_repo("opensafely", "output-explorer-test-repo")
|
|
691
|
+
|
|
692
|
+
# Fetch a known folder
|
|
693
|
+
contents = repo.get_contents("test-outputs", ref="master")
|
|
694
|
+
assert len(contents) == 4
|
|
695
|
+
assert sorted([contentfile.name for contentfile in contents]) == [
|
|
696
|
+
"output.html",
|
|
697
|
+
"sro-measures.html",
|
|
698
|
+
"vaccine-coverage-new.html",
|
|
699
|
+
"vaccine-coverage-original.html",
|
|
700
|
+
]
|
|
701
|
+
|
|
702
|
+
# Fetch a file
|
|
703
|
+
contents, fetch_type = repo.get_contents(
|
|
704
|
+
"test-outputs/output.html", ref="master", return_fetch_type=True
|
|
705
|
+
)
|
|
706
|
+
assert contents.name == "output.html"
|
|
707
|
+
assert fetch_type == "contents"
|
|
708
|
+
|
|
709
|
+
# Fetch a non-existent file
|
|
710
|
+
with pytest.raises(GithubAPIException):
|
|
711
|
+
repo.get_contents("test-outputs/output-unknown.html", ref="master")
|
|
712
|
+
|
|
713
|
+
# Fetch a non-existent branch
|
|
714
|
+
with pytest.raises(GithubAPIException):
|
|
715
|
+
repo.get_contents("test-outputs/output.html", ref="foo")
|
|
716
|
+
|
|
717
|
+
# Fetch README
|
|
718
|
+
readme = repo.get_readme(tag="master")
|
|
719
|
+
assert readme.startswith("<div")
|
|
720
|
+
assert "This is a test repo for use by output-explorer's tests." in readme
|
|
721
|
+
|
|
722
|
+
# Fetch details
|
|
723
|
+
details = repo.get_repo_details()
|
|
724
|
+
assert details == {
|
|
725
|
+
"name": "output-explorer-test-repo",
|
|
726
|
+
"about": "A test repo for output-explorer's tests",
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
# Fetch tags
|
|
730
|
+
tagged_sha = "7a6f60e8e74b9c93a9c6322b3151ee437fa4be61"
|
|
731
|
+
tags = repo.get_tags()
|
|
732
|
+
assert len(tags) >= 1
|
|
733
|
+
assert {"tag_name": "test-tag", "sha": tagged_sha} in tags
|
|
734
|
+
|
|
735
|
+
# get commit details
|
|
736
|
+
commit = repo.get_commit(sha=tagged_sha)
|
|
737
|
+
assert commit == {"author": "Ben Butler-Cole", "date": "2021-06-02T10:52:37Z"}
|
osgithub-0.4.0/PKG-INFO
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: osgithub
|
|
3
|
-
Version: 0.4.0
|
|
4
|
-
Summary: UNKNOWN
|
|
5
|
-
Home-page: https://github.com/opensafely-core/osgithub
|
|
6
|
-
Author: OpenSAFELY
|
|
7
|
-
Author-email: tech@opensafely.org
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
10
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
11
|
-
Requires-Python: >=3.9
|
|
12
|
-
License-File: LICENSE
|
|
13
|
-
|
|
14
|
-
UNKNOWN
|
|
15
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: osgithub
|
|
3
|
-
Version: 0.4.0
|
|
4
|
-
Summary: UNKNOWN
|
|
5
|
-
Home-page: https://github.com/opensafely-core/osgithub
|
|
6
|
-
Author: OpenSAFELY
|
|
7
|
-
Author-email: tech@opensafely.org
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
10
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
11
|
-
Requires-Python: >=3.9
|
|
12
|
-
License-File: LICENSE
|
|
13
|
-
|
|
14
|
-
UNKNOWN
|
|
15
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|