Skip to content
This repository was archived by the owner on May 5, 2025. It is now read-only.

Commit eb455b4

Browse files
Merge branch 'master' into dorian/lint-compare
2 parents 9c20a64 + 760d2b4 commit eb455b4

File tree

16 files changed

+319
-15
lines changed

16 files changed

+319
-15
lines changed

Makefile.circle

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ save:
107107
# - spinnaker pipeline: https://spinnaker.codecov.dev/?to=https%3A%2F%2Fspinnaker.codecov.dev%2F%23%2Fsearch#/applications/codecov-staging/executions?pipeline=api
108108
# - staging url: https://stage-api.codecov.dev
109109
staging:
110+
docker load -i app.tar
110111
docker tag "${GCR_REPO}:${VERSION}" "${GCR_REPO}:staging-${VERSION}"
111112
docker push ${GCR_REPO}:staging-${VERSION}
112113

codecov/settings_base.py

+2
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,5 @@
228228

229229
IS_ENTERPRISE = get_settings_module() == SettingsModule.ENTERPRISE.value
230230
IS_DEV = get_settings_module() == SettingsModule.DEV.value
231+
DATA_UPLOAD_MAX_MEMORY_SIZE = get_config("setup", "http", "upload_max_memory_size", default=2621440)
232+
FILE_UPLOAD_MAX_MEMORY_SIZE = get_config("setup", "http", "file_upload_max_memory_size", default=2621440)

codecov/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"<str:service>/<str:owner_username>/<str:repo_name>/",
2929
include("graphs.urls"),
3030
),
31-
path("upload/<str:version>", include("upload.urls")),
31+
path("upload/", include("upload.urls")),
3232
]
3333

3434
if not settings.IS_ENTERPRISE:

codecov_auth/admin.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def impersonate_owner(self, request, queryset):
2121
"staff_user",
2222
owner.username,
2323
domain=settings.COOKIES_DOMAIN,
24-
samesite="Strict",
24+
samesite="Lax",
2525
)
2626
return response
2727

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from asgiref.sync import sync_to_async
2+
from codecov.commands.base import BaseInteractor
3+
from codecov_auth.models import Owner
4+
5+
class FetchOwnerInteractor(BaseInteractor):
6+
@sync_to_async
7+
def execute(self, username):
8+
return Owner.objects.filter(username=username, service=self.service).first()

codecov_auth/commands/owner/owner.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .interactors.set_yaml_on_owner import SetYamlOnOwnerInteractor
66
from .interactors.delete_session import DeleteSessionInteractor
77
from .interactors.update_profile import UpdateProfileInteractor
8+
from .interactors.fetch_owner import FetchOwnerInteractor
89

910

1011
class OwnerCommands(BaseCommand):
@@ -19,3 +20,6 @@ def set_yaml_on_owner(self, username, yaml):
1920

2021
def update_profile(self, **kwargs):
2122
return self.get_interactor(UpdateProfileInteractor).execute(**kwargs)
23+
24+
def fetch_owner(self, username):
25+
return self.get_interactor(FetchOwnerInteractor).execute(username)

codecov_auth/views/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ def _set_proper_cookies_and_session(self, user, request, response):
101101
domain=domain_to_use,
102102
httponly=True,
103103
secure=True,
104-
samesite="Strict",
104+
samesite="Lax",
105105
)
106106
response.set_cookie(
107107
f"{self.service}-username",
108108
user.username,
109109
domain=domain_to_use,
110110
httponly=True,
111111
secure=True,
112-
samesite="Strict",
112+
samesite="Lax",
113113
)
114114

115115
def _check_user_count_limitations(self):

codecov_auth/views/logout.py

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ def logout_view(request, service):
2222
response = redirect("/")
2323
delete_session(request, service_name)
2424
logout(request)
25+
kwargs_cookie = dict(domain=settings.COOKIES_DOMAIN, samesite="Lax")
26+
response.delete_cookie("staff_user", **kwargs_cookie)
27+
response.delete_cookie(f"{service_name}-username", **kwargs_cookie)
28+
response.delete_cookie(f"{service_name}-token", **kwargs_cookie)
29+
# temporary as we use to set cookie to Strict SameSite; but we need Lax
30+
# So we need delete in both samesite Strict / Lax for a little while
2531
kwargs_cookie = dict(domain=settings.COOKIES_DOMAIN, samesite="Strict")
2632
response.delete_cookie("staff_user", **kwargs_cookie)
2733
response.delete_cookie(f"{service_name}-username", **kwargs_cookie)

enterprise/generate_pyinstaller_args.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ def main():
8282
"whitenoise.middleware",
8383
"graphql_api",
8484
"legacy_migrations",
85-
"shared.celery_config"
85+
"legacy_migrations.migrations",
86+
"shared.celery_config",
87+
"kombu.transport.pyamqp"
8688
]
8789
)
8890

enterprise/hooks/hook-core.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from PyInstaller.utils.hooks import collect_submodules, collect_data_files
22

3-
datas = collect_data_files("core", include_py_files=True, subdir="migrations")
3+
datas = collect_data_files("core", include_py_files=True, subdir="migrations") + collect_data_files("legacy_migrations", include_py_files=True, subdir="migrations")

reports/models.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import uuid
22

3+
from django.urls import reverse
4+
35
from django.db import models
46
from django.contrib.postgres.fields import ArrayField
57

@@ -98,12 +100,17 @@ class Meta:
98100
@property
99101
def download_url(self):
100102
repository = self.report.commit.repository
101-
owner = repository.author
102-
short_service = get_short_service_name(owner.service)
103-
path_download = (
104-
f"/api/{short_service}/{owner.username}/{repository.name}/download/build"
103+
return (
104+
reverse(
105+
"upload-download",
106+
kwargs={
107+
"service": get_short_service_name(repository.author.service),
108+
"owner_username": repository.author.username,
109+
"repo_name": repository.name,
110+
},
111+
)
112+
+ f"?path={self.storage_path}"
105113
)
106-
return f"{path_download}?path={self.storage_path}"
107114

108115
@property
109116
def ci_url(self):

reports/tests/test_models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def test_get_download_url(self):
1414
repository = session.report.commit.repository
1515
assert (
1616
session.download_url
17-
== f"/api/gh/{repository.author.username}/{repository.name}/download/build?path={storage_path}"
17+
== f"/upload/gh/{repository.author.username}/{repository.name}/download?path={storage_path}"
1818
)
1919

2020
def test_ci_url_when_no_provider(self):

upload/tests/test_upload.py

+92
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,19 @@ def _post(
944944
url = reverse("upload-handler", kwargs=kwargs) + query_string
945945
return self.client.post(url, data=data, content_type=content_type, **headers)
946946

947+
def _post_slash(
948+
self,
949+
kwargs=None,
950+
data=None,
951+
query=None,
952+
content_type="application/json",
953+
headers=None,
954+
):
955+
headers = headers or {}
956+
query_string = f"?{urlencode(query)}" if query else ""
957+
url = "/upload/v2/" + query_string
958+
return self.client.post(url, data=data, content_type=content_type, **headers)
959+
947960
def setUp(self):
948961
self.org = G(Owner, username="codecovtest", service="github")
949962
self.repo = G(
@@ -1067,6 +1080,85 @@ async def get_commit(self, commit, token):
10671080
== "https://codecov.io/github/codecovtest/upload-test-repo/commit/b521e55aef79b101f48e2544837ca99a7fa3bf6b"
10681081
)
10691082

1083+
@patch("upload.views.get_redis_connection")
1084+
@patch("upload.views.uuid4")
1085+
@patch("upload.views.dispatch_upload_task")
1086+
@patch("services.repo_providers.RepoProviderService.get_adapter")
1087+
def test_successful_upload_v2_slash(
1088+
self,
1089+
mock_repo_provider_service,
1090+
mock_dispatch_upload,
1091+
mock_uuid4,
1092+
mock_get_redis,
1093+
):
1094+
class MockRepoProviderAdapter:
1095+
async def get_commit(self, commit, token):
1096+
return {"message": "This is not a merge commit"}
1097+
1098+
mock_get_redis.return_value = MockRedis()
1099+
mock_repo_provider_service.return_value = MockRepoProviderAdapter()
1100+
mock_uuid4.return_value = (
1101+
"dec1f00b-1883-40d0-afd6-6dcb876510be" # this will be the reportid
1102+
)
1103+
1104+
query_params = {
1105+
"commit": "b521e55aef79b101f48e2544837ca99a7fa3bf6b",
1106+
"token": "test27s4f3uz3ha9pi0foipg5bqojtrmbt67",
1107+
"pr": "456",
1108+
"branch": "",
1109+
"flags": "",
1110+
"build_url": "",
1111+
}
1112+
1113+
response = self._post_slash(
1114+
kwargs={"version": "v2"}, query=query_params, data="coverage report"
1115+
)
1116+
1117+
assert response.status_code == 200
1118+
1119+
headers = response._headers
1120+
1121+
assert headers["access-control-allow-origin"] == (
1122+
"Access-Control-Allow-Origin",
1123+
"*",
1124+
)
1125+
assert headers["access-control-allow-headers"] == (
1126+
"Access-Control-Allow-Headers",
1127+
"Origin, Content-Type, Accept, X-User-Agent",
1128+
)
1129+
assert headers["content-type"] != (
1130+
"Content-Type",
1131+
"text/plain",
1132+
)
1133+
1134+
assert mock_dispatch_upload.call_args[0][0] == {
1135+
"commit": "b521e55aef79b101f48e2544837ca99a7fa3bf6b",
1136+
"token": "test27s4f3uz3ha9pi0foipg5bqojtrmbt67",
1137+
"pr": "456",
1138+
"version": "v2",
1139+
"service": None,
1140+
"owner": None,
1141+
"repo": None,
1142+
"using_global_token": False,
1143+
"build_url": None,
1144+
"branch": None,
1145+
"reportid": "dec1f00b-1883-40d0-afd6-6dcb876510be",
1146+
"redis_key": "upload/b521e55/dec1f00b-1883-40d0-afd6-6dcb876510be/plain",
1147+
"url": None,
1148+
"branch": None,
1149+
"job": None,
1150+
}
1151+
1152+
result = loads(response.content)
1153+
assert result["message"] == "Coverage reports upload successfully"
1154+
assert result["uploaded"] == True
1155+
assert result["queued"] == True
1156+
assert result["id"] == "dec1f00b-1883-40d0-afd6-6dcb876510be"
1157+
assert (
1158+
result["url"]
1159+
== "https://codecov.io/github/codecovtest/upload-test-repo/commit/b521e55aef79b101f48e2544837ca99a7fa3bf6b"
1160+
)
1161+
10701162
@patch("services.storage.MINIO_CLIENT.presigned_put_object")
10711163
@patch("services.archive.ArchiveService.get_archive_hash")
10721164
@patch("upload.views.get_redis_connection")

upload/tests/test_upload_download.py

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import minio
2+
from ddf import G
3+
from unittest.mock import patch
4+
from core.models import Repository
5+
from codecov_auth.models import Owner
6+
from rest_framework.reverse import reverse
7+
from rest_framework.test import APITestCase
8+
9+
10+
class UploadDownloadHelperTest(APITestCase):
11+
def _get(self, kwargs={}, data={}):
12+
path = f"/upload/{kwargs.get('service')}/{kwargs.get('owner_username')}/{kwargs.get('repo_name')}/download"
13+
return self.client.get(path, data=data)
14+
15+
def setUp(self):
16+
self.org = G(Owner, username="codecovtest", service="github")
17+
self.repo = G(
18+
Repository,
19+
author=self.org,
20+
name="upload-test-repo",
21+
upload_token="test27s4f3uz3ha9pi0foipg5bqojtrmbt67",
22+
)
23+
self.repo = G(
24+
Repository, author=self.org, name="private-upload-test-repo", private=True
25+
)
26+
27+
def test_no_path_param(self):
28+
response = self._get()
29+
assert response.status_code == 404
30+
31+
def test_invalid_path_param(self):
32+
response = self._get(data={"path": "v2"})
33+
assert response.status_code == 404
34+
35+
def test_invalid_owner(self):
36+
response = self._get(
37+
kwargs={
38+
"service": "gh",
39+
"owner_username": "invalid",
40+
"repo_name": "invalid",
41+
},
42+
data={"path": "v4/raw"},
43+
)
44+
assert response.status_code == 404
45+
46+
def test_invalid_repo(self):
47+
response = self._get(
48+
kwargs={
49+
"service": "gh",
50+
"owner_username": "codecovtest",
51+
"repo_name": "invalid",
52+
},
53+
data={"path": "v4/raw"},
54+
)
55+
assert response.status_code == 404
56+
57+
@patch("services.archive.ArchiveService.get_archive_hash")
58+
@patch("services.archive.ArchiveService.read_file")
59+
def test_invalid_archive_path(self, read_file, get_archive_hash):
60+
read_file.side_effect = [minio.error.NoSuchKey]
61+
get_archive_hash.return_value = "path"
62+
response = self._get(
63+
kwargs={
64+
"service": "gh",
65+
"owner_username": "codecovtest",
66+
"repo_name": "upload-test-repo",
67+
},
68+
data={"path": "v4/raw/path"},
69+
)
70+
assert response.status_code == 404
71+
72+
@patch("services.archive.ArchiveService.get_archive_hash")
73+
@patch("services.archive.ArchiveService.read_file")
74+
def test_valid_repo_archive_path(self, mock_read_file, get_archive_hash):
75+
mock_read_file.return_value = "Report!"
76+
get_archive_hash.return_value = "hasssshhh"
77+
response = self._get(
78+
kwargs={
79+
"service": "gh",
80+
"owner_username": "codecovtest",
81+
"repo_name": "upload-test-repo",
82+
},
83+
data={"path": "v4/raw/hasssshhh"},
84+
)
85+
assert response.status_code == 200
86+
headers = response._headers
87+
assert headers["content-type"] == ("Content-Type", "text/plain")
88+
89+
@patch("services.archive.ArchiveService.read_file")
90+
def test_invalid_repo_archive_path(self, mock_read_file):
91+
mock_read_file.return_value = "Report!"
92+
response = self._get(
93+
kwargs={
94+
"service": "gh",
95+
"owner_username": "codecovtest",
96+
"repo_name": "upload-test-repo",
97+
},
98+
data={"path": "v4/raw"},
99+
)
100+
assert response.status_code == 404
101+
102+
def test_private_valid_archive_path(self):
103+
response = self._get(
104+
kwargs={
105+
"service": "gh",
106+
"owner_username": "codecovtest",
107+
"repo_name": "private-upload-test-repo",
108+
},
109+
data={"path": "v4/raw"},
110+
)
111+
assert response.status_code == 404

upload/urls.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
from django.urls import re_path
2-
from .views import UploadHandler
1+
from django.urls import path, re_path
2+
from .views import UploadHandler, UploadDownloadHandler
33

44

55
urlpatterns = [
66
# use regex to make trailing slash optional
7-
re_path("^/?", UploadHandler.as_view(), name="upload-handler"),
7+
path(
8+
"<str:service>/<str:owner_username>/<str:repo_name>/download",
9+
UploadDownloadHandler.as_view(),
10+
name="upload-download",
11+
),
12+
re_path("(?P<version>\w+)/?", UploadHandler.as_view(), name="upload-handler"),
813
]

0 commit comments

Comments
 (0)