Skip to content

Commit 42b2024

Browse files
authored
Merge branch 'MeltanoLabs:main' into diffs-stream-fix
2 parents 56f64ed + f03bcbb commit 42b2024

File tree

16 files changed

+579
-601
lines changed

16 files changed

+579
-601
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Global owner
2+
* @MeltanoLabs/tap-github

.github/dependabot.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ updates:
1010
schedule:
1111
interval: weekly
1212
day: monday
13-
reviewers:
14-
- "edgarrmondragon"
1513
versioning-strategy: increase-if-necessary
1614
groups:
1715
development-dependencies:
@@ -25,8 +23,6 @@ updates:
2523
schedule:
2624
interval: weekly
2725
day: monday
28-
reviewers:
29-
- "edgarrmondragon"
3026
groups:
3127
actions:
3228
patterns:

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
if: startsWith(github.ref, 'refs/tags/')
3131

3232
steps:
33-
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
33+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
3434
with:
3535
name: Packages
3636
path: dist

.github/workflows/test_tap.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ on:
1616
- poetry.lock
1717
- pyproject.toml
1818
- 'tap_github/**'
19+
workflow_dispatch:
20+
schedule:
21+
# Every 6 hours
22+
- cron: "0 */6 * * *"
1923

2024
concurrency:
2125
group: ${{ github.workflow }}-${{ github.ref }}
@@ -68,14 +72,19 @@ jobs:
6872
virtualenvs-create: true
6973
virtualenvs-in-project: true
7074
- name: Set up Python ${{ matrix.python-version }}
71-
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
75+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
7276
with:
7377
python-version: ${{ matrix.python-version }}
7478
cache: poetry
7579
- name: Install dependencies
7680
run: |
7781
poetry env use ${{ matrix.python-version }}
7882
poetry install
83+
- name: Type check with mypy
84+
id: type_check
85+
continue-on-error: true
86+
run: |
87+
poetry run mypy tap_github
7988
- name: Test with pytest
8089
id: test_pytest
8190
continue-on-error: true

.pre-commit-config.yaml

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,8 @@ repos:
1616

1717

1818
- repo: https://github.com/astral-sh/ruff-pre-commit
19-
rev: v0.9.9
19+
rev: v0.11.8
2020
hooks:
2121
- id: ruff
2222
args: [ --fix ]
2323
- id: ruff-format
24-
25-
- repo: https://github.com/pre-commit/mirrors-mypy
26-
rev: v1.15.0
27-
hooks:
28-
- id: mypy
29-
pass_filenames: true
30-
additional_dependencies:
31-
- types-requests
32-
- types-simplejson
33-
- types-python-dateutil

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22

33
`tap-github` is a Singer tap for GitHub.
44

5-
Build with the [Singer SDK](https://gitlab.com/meltano/singer-sdk).
5+
Built with the [Singer SDK](https://gitlab.com/meltano/singer-sdk).
66

77
## Installation
88

99
```bash
10-
pipx install git+https://github.com/MeltanoLabs/tap-github.git
11-
```
10+
# use uv (https://docs.astral.sh/uv/)
11+
uv tool install meltanolabs-tap-github
1212

13-
Or better yet, please pin to a [release version](https://github.com/MeltanoLabs/tap-github/releases) for a stable experience:
13+
# or pipx (https://pipx.pypa.io/stable/)
14+
pipx install meltanolabs-tap-github
1415

15-
```bash
16-
pipx install git+https://github.com/MeltanoLabs/tap-github.git@vX.Y.Z
16+
# or Meltano
17+
meltano add extractor tap-github
1718
```
1819

1920
A list of release versions is available at https://github.com/MeltanoLabs/tap-github/releases

poetry.lock

Lines changed: 437 additions & 404 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,18 @@ packages = [
3333

3434
[tool.poetry.dependencies]
3535
beautifulsoup4 = "~=4.13.3"
36-
cryptography = { version = "~=44.0.0", python = ">3.9.0,<3.9.1 || >3.9.1" }
36+
cryptography = { version = "~=45.0.2", python = ">3.9.0,<3.9.1 || >3.9.1" }
3737
nested-lookup = "~=0.2.25"
3838
PyJWT = "2.10.1"
3939
python = ">=3.9"
4040
python-dateutil = "~=2.9"
4141
requests = "~=2.32.3"
4242
# For local SDK dev:
4343
# singer-sdk = {path = "../singer-sdk", develop = true}
44-
singer-sdk = "~=0.44.0"
44+
singer-sdk = "~=0.46.0"
4545

4646
[tool.poetry.group.dev.dependencies]
47+
mypy = ">=1.15.0"
4748
pytest = ">=7.3.1"
4849
requests-cache = ">=1.0.1"
4950
types-beautifulsoup4 = ">=4.12.0"
@@ -55,13 +56,16 @@ types-simplejson = "~=3.20.0"
5556
enable = true
5657

5758
[[tool.mypy.overrides]]
58-
module = ["backoff"]
59+
module = [
60+
"backoff",
61+
"nested_lookup",
62+
]
5963
ignore_missing_imports = true
6064

6165
[build-system]
6266
requires = [
6367
"poetry-core==2.1.1",
64-
"poetry-dynamic-versioning==1.7.1",
68+
"poetry-dynamic-versioning==1.8.2",
6569

6670
]
6771
build-backend = "poetry_dynamic_versioning.backend"

tap_github/client.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import inspect
77
import random
88
import time
9-
from types import FrameType
109
from typing import TYPE_CHECKING, Any, ClassVar, cast
1110
from urllib.parse import parse_qs, urlparse
1211

@@ -20,9 +19,11 @@
2019

2120
if TYPE_CHECKING:
2221
from collections.abc import Iterable
22+
from types import FrameType
2323

2424
import requests
2525
from backoff.types import Details
26+
from singer_sdk.helpers.types import Context
2627

2728
EMPTY_REPO_ERROR_STATUS = 409
2829

@@ -61,7 +62,7 @@ def url_base(self) -> str:
6162
def http_headers(self) -> dict[str, str]:
6263
"""Return the http headers needed."""
6364
headers = {"Accept": "application/vnd.github.v3+json"}
64-
headers["User-Agent"] = cast(str, self.config.get("user_agent", "tap-github"))
65+
headers["User-Agent"] = cast("str", self.config.get("user_agent", "tap-github"))
6566
return headers
6667

6768
def get_next_page_token(
@@ -74,7 +75,8 @@ def get_next_page_token(
7475
previous_token
7576
and self.MAX_RESULTS_LIMIT
7677
and (
77-
cast(int, previous_token) * self.MAX_PER_PAGE >= self.MAX_RESULTS_LIMIT
78+
cast("int", previous_token) * self.MAX_PER_PAGE
79+
>= self.MAX_RESULTS_LIMIT
7880
)
7981
):
8082
return None
@@ -139,7 +141,7 @@ def get_next_page_token(
139141

140142
def get_url_params(
141143
self,
142-
context: dict | None,
144+
context: Context | None,
143145
next_page_token: Any | None, # noqa: ANN401
144146
) -> dict[str, Any]:
145147
"""Return a dictionary of values to be used in URL parameterization."""
@@ -172,7 +174,7 @@ def get_url_params(
172174
params[since_key] = since.isoformat(sep="T")
173175
# Leverage conditional requests to save API quotas
174176
# https://github.community/t/how-does-if-modified-since-work/139627
175-
self._http_headers["If-modified-since"] = email.utils.format_datetime(since)
177+
self.http_headers["If-modified-since"] = email.utils.format_datetime(since)
176178
return params
177179

178180
def validate_response(self, response: requests.Response) -> None:
@@ -270,7 +272,7 @@ def parse_response(self, response: requests.Response) -> Iterable[dict]:
270272

271273
yield from results
272274

273-
def post_process(self, row: dict, context: dict[str, str] | None = None) -> dict:
275+
def post_process(self, row: dict, context: Context | None = None) -> dict:
274276
"""Add `repo_id` by default to all streams."""
275277
if context is not None and "repo_id" in context:
276278
row["repo_id"] = context["repo_id"]
@@ -283,8 +285,8 @@ def backoff_handler(self, details: Details) -> None:
283285
# FIXME: replace this once https://github.com/litl/backoff/issues/158
284286
# is fixed
285287
exc = cast(
286-
FrameType,
287-
cast(FrameType, cast(FrameType, inspect.currentframe()).f_back).f_back,
288+
"FrameType",
289+
cast("FrameType", cast("FrameType", inspect.currentframe()).f_back).f_back,
288290
).f_locals["e"]
289291
if (
290292
exc.response is not None
@@ -300,7 +302,7 @@ def calculate_sync_cost(
300302
self,
301303
request: requests.PreparedRequest,
302304
response: requests.Response,
303-
context: dict | None,
305+
context: Context | None,
304306
) -> dict[str, int]:
305307
"""Return the cost of the last REST API call."""
306308
return {"rest": 1, "graphql": 0, "search": 0}
@@ -448,11 +450,11 @@ def get_next_page_token(
448450

449451
def get_url_params(
450452
self,
451-
context: dict | None,
453+
context: Context | None,
452454
next_page_token: Any | None, # noqa: ANN401
453455
) -> dict[str, Any]:
454456
"""Return a dictionary of values to be used in URL parameterization."""
455-
params = context.copy() if context else {}
457+
params = dict(context) if context else {}
456458
params["per_page"] = self.MAX_PER_PAGE
457459
if next_page_token:
458460
params.update(next_page_token)
@@ -467,7 +469,7 @@ def calculate_sync_cost(
467469
self,
468470
request: requests.PreparedRequest,
469471
response: requests.Response,
470-
context: dict | None,
472+
context: Context | None,
471473
) -> dict[str, int]:
472474
"""Return the cost of the last graphql API call."""
473475
costgen = extract_jsonpath("$.data.rateLimit.cost", input=response.json())

tap_github/organization_streams.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
if TYPE_CHECKING:
1212
from collections.abc import Iterable
1313

14+
from singer_sdk.helpers.types import Context
15+
1416

1517
class OrganizationStream(GitHubRestStream):
1618
"""Defines a GitHub Organization Stream.
@@ -24,12 +26,12 @@ class OrganizationStream(GitHubRestStream):
2426
def partitions(self) -> list[dict] | None:
2527
return [{"org": org} for org in self.config["organizations"]]
2628

27-
def get_child_context(self, record: dict, context: dict | None) -> dict:
29+
def get_child_context(self, record: dict, context: Context | None) -> dict:
2830
return {
2931
"org": record["login"],
3032
}
3133

32-
def get_records(self, context: dict | None) -> Iterable[dict[str, Any]]:
34+
def get_records(self, context: Context | None) -> Iterable[dict[str, Any]]:
3335
"""
3436
Override the parent method to allow skipping API calls
3537
if the stream is deselected and skip_parent_streams is True in config.
@@ -106,7 +108,7 @@ class TeamsStream(GitHubRestStream):
106108
parent_stream_type = OrganizationStream
107109
state_partitioning_keys: ClassVar[list[str]] = ["org"]
108110

109-
def get_child_context(self, record: dict, context: dict | None) -> dict:
111+
def get_child_context(self, record: dict, context: Context | None) -> dict:
110112
new_context = {"team_slug": record["slug"]}
111113
if context:
112114
return {
@@ -161,7 +163,7 @@ class TeamMembersStream(GitHubRestStream):
161163
parent_stream_type = TeamsStream
162164
state_partitioning_keys: ClassVar[list[str]] = ["team_slug", "org"]
163165

164-
def get_child_context(self, record: dict, context: dict | None) -> dict:
166+
def get_child_context(self, record: dict, context: Context | None) -> dict:
165167
new_context = {"username": record["login"]}
166168
if context:
167169
return {

0 commit comments

Comments
 (0)