Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add pagination for artifacts retrieval #453

Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.coverage
dev-env-vars
.coverage*
__pycache__
12 changes: 12 additions & 0 deletions Makefile
Copy link
Member

Choose a reason for hiding this comment

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

I'm not a huge fan of Makefiles, mainly because they tend to look like a mess (not commenting your Makefile in particular, but it's an inherent property of the format). Also, they lost me many years ago when I discovered you have to mix tabs and space on some lines.

My philosophy is more to try and use standard tools and configure them so they're ready to use.

In the case of your Makefile: if you think the project is missing some directions on how to launch some actions, you're right of course, but I'd like to direct you to changing the CONTRIBUTING.md doc first. That said, I'm totally ok to keep the Makefile as a documentation TL;DR, but let's make it so (remove the help target, fix the few comments I left which should show that targets are synonym to a 1 (or few) words command.

If you're curious, my go-to approach these days when I need to make complex commands accessible in a project is a scripts folder, which offers the advantage of letting you write some scripts with Python when they become too complex to be readable in bash. But I don't think we need this here. A Makefile will be very OK.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.PHONY: install
install: ## install dependencies
poetry install --with dev
pre-commit install

.PHONY: lint
lint: ## lint code
pre-commit

.PHONY: test
test: ## run all tests
poetry run pytest tests
28 changes: 23 additions & 5 deletions coverage_comment/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ def download_artifact(
filename: pathlib.Path,
) -> str:
repo_path = github.repos(repository)
artifacts = repo_path.actions.runs(run_id).artifacts.get().artifacts

try:
artifact = next(
iter(artifact for artifact in artifacts if artifact.name == artifact_name),
artifact
for artifact in _fetch_artifacts(repo_path, run_id)
if artifact.name == artifact_name
)
except StopIteration:
raise NoArtifact(
f"Not artifact found with name {artifact_name} in run {run_id}"
)
raise NoArtifact(f"No artifact found with name {artifact_name} in run {run_id}")

zip_bytes = io.BytesIO(repo_path.actions.artifacts(artifact.id).zip.get(bytes=True))
zipf = zipfile.ZipFile(zip_bytes)
Expand All @@ -73,6 +73,24 @@ def download_artifact(
raise NoArtifact(f"File named {filename} not found in artifact {artifact_name}")


def _fetch_artifacts(repo_path, run_id):
page = 1
total_fetched = 0

while True:
result = repo_path.actions.runs(run_id).artifacts.get(page=str(page))
if not result or not result.artifacts:
break

yield from result.artifacts

total_fetched += len(result.artifacts)
if total_fetched >= result.total_count:
break

page += 1


def get_branch_from_workflow_run(
github: github_client.GitHub, repository: str, run_id: int
) -> tuple[str, str]:
Expand Down
98 changes: 93 additions & 5 deletions tests/integration/test_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_download_artifact(gh, session, zip_bytes):
{"name": "foo", "id": 789},
]
session.register("GET", "/repos/foo/bar/actions/runs/123/artifacts")(
json={"artifacts": artifacts}
json={"artifacts": artifacts, "total_count": 2}
)

session.register("GET", "/repos/foo/bar/actions/artifacts/789/zip")(
Expand All @@ -70,12 +70,44 @@ def test_download_artifact(gh, session, zip_bytes):
assert result == "bar"


def test_download_artifact_from_page_2(gh, session, zip_bytes):
artifacts_page_1 = [
{"name": "test", "id": 000},
]
artifacts_page_2 = [
{"name": "bar", "id": 456},
{"name": "foo", "id": 789},
]
session.register("GET", "/repos/foo/bar/actions/runs/123/artifacts")(
json={"artifacts": artifacts_page_1, "total_count": 3}
)
session.register(
"GET",
"/repos/foo/bar/actions/runs/123/artifacts",
params={"page": "2"},
)(json={"artifacts": artifacts_page_2, "total_count": 3})

session.register("GET", "/repos/foo/bar/actions/artifacts/789/zip")(
content=zip_bytes(filename="foo.txt", content="bar")
)

result = github.download_artifact(
github=gh,
repository="foo/bar",
artifact_name="foo",
run_id=123,
filename=pathlib.Path("foo.txt"),
)

assert result == "bar"


def test_download_artifact__no_artifact(gh, session):
artifacts = [
{"name": "bar", "id": 456},
]
session.register("GET", "/repos/foo/bar/actions/runs/123/artifacts")(
json={"artifacts": artifacts}
json={"artifacts": artifacts, "total_count": 1}
)

with pytest.raises(github.NoArtifact):
Expand All @@ -89,12 +121,15 @@ def test_download_artifact__no_artifact(gh, session):


def test_download_artifact__no_file(gh, session, zip_bytes):
artifacts = [
{"name": "foo", "id": 789},
]
artifacts = [{"name": "foo", "id": 789}]
session.register("GET", "/repos/foo/bar/actions/runs/123/artifacts")(
json={"artifacts": artifacts}
)
session.register(
"GET",
"/repos/foo/bar/actions/runs/123/artifacts",
params={"page": "2"},
)(json={})

session.register("GET", "/repos/foo/bar/actions/artifacts/789/zip")(
content=zip_bytes(filename="foo.txt", content="bar")
Expand All @@ -109,6 +144,59 @@ def test_download_artifact__no_file(gh, session, zip_bytes):
)


def test_fetch_artifacts_empty_response(gh, session):
session.register("GET", "/repos/foo/bar/actions/runs/123/artifacts")(
json={"artifacts": [], "total_count": 0}
)

repo_path = gh.repos("foo/bar")

result = github._fetch_artifacts(
repo_path=repo_path,
run_id=123,
)

assert not list(result)


def test_fetch_artifacts_single_page(gh, session):
artifacts = [{"name": "bar", "id": 456}]

session.register("GET", "/repos/foo/bar/actions/runs/123/artifacts")(
json={"artifacts": artifacts, "total_count": 1}
)

repo_path = gh.repos("foo/bar")

result = github._fetch_artifacts(
repo_path=repo_path,
run_id=123,
)

assert list(result) == artifacts


def test_fetch_artifacts_multiple_pages(gh, session):
artifacts_page_1 = [{"name": "bar", "id": 456}]
artifacts_page_2 = [{"name": "bar", "id": 789}]

session.register("GET", "/repos/foo/bar/actions/runs/123/artifacts")(
json={"artifacts": artifacts_page_1, "total_count": 2}
)
session.register(
"GET", "/repos/foo/bar/actions/runs/123/artifacts", params={"page": "2"}
)(json={"artifacts": artifacts_page_2, "total_count": 2})

repo_path = gh.repos("foo/bar")

result = github._fetch_artifacts(
repo_path=repo_path,
run_id=123,
)

assert list(result) == artifacts_page_1 + artifacts_page_2


def test_get_branch_from_workflow_run(gh, session):
json = {
"head_branch": "other",
Expand Down
9 changes: 7 additions & 2 deletions tests/integration/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ def test_action__workflow_run__no_artifact(
session.register(
"GET",
"/repos/py-cov-action/foobar/actions/runs/123/artifacts",
)(json={"artifacts": [{"name": "wrong_name"}]})
)(json={"artifacts": [{"name": "wrong_name"}], "total_count": 1})

result = main.action(
config=workflow_run_config(),
Expand Down Expand Up @@ -852,7 +852,12 @@ def test_action__workflow_run__post_comment(
session.register(
"GET",
"/repos/py-cov-action/foobar/actions/runs/123/artifacts",
)(json={"artifacts": [{"name": "python-coverage-comment-action", "id": 789}]})
)(
json={
"artifacts": [{"name": "python-coverage-comment-action", "id": 789}],
"total_count": 1,
}
)

session.register(
"GET",
Expand Down
Loading