Skip to content

Commit 4e4102d

Browse files
Merge pull request #5 from codecov/joseph/diff-in-pr-comment
PR comment mention diff between PR base and head
2 parents 47bea70 + 4bc1bf3 commit 4e4102d

File tree

12 files changed

+1067
-363
lines changed

12 files changed

+1067
-363
lines changed

database/models/core.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ def __repr__(self):
8181

8282

8383
class Repository(CodecovBaseModel):
84-
8584
__tablename__ = "repos"
8685

8786
repoid = Column(types.Integer, primary_key=True)
@@ -127,7 +126,6 @@ def __repr__(self):
127126

128127

129128
class Commit(CodecovBaseModel):
130-
131129
__tablename__ = "commits"
132130

133131
id_ = Column("id", types.BigInteger, primary_key=True)
@@ -185,7 +183,6 @@ def report(self):
185183

186184

187185
class Branch(CodecovBaseModel):
188-
189186
__tablename__ = "branches"
190187

191188
repoid = Column(types.Integer, ForeignKey("repos.repoid"), primary_key=True)
@@ -204,7 +201,6 @@ def __repr__(self):
204201

205202

206203
class LoginSession(CodecovBaseModel):
207-
208204
__tablename__ = "sessions"
209205

210206
sessionid = Column(types.Integer, primary_key=True)
@@ -218,7 +214,6 @@ class LoginSession(CodecovBaseModel):
218214

219215

220216
class Pull(CodecovBaseModel):
221-
222217
__tablename__ = "pulls"
223218

224219
id_ = Column("id", types.BigInteger, primary_key=True)
@@ -238,6 +233,8 @@ class Pull(CodecovBaseModel):
238233
diff = Column(postgresql.JSON)
239234
flare = Column(postgresql.JSON)
240235
author_id = Column("author", types.Integer, ForeignKey("owners.ownerid"))
236+
behind_by = Column(types.Integer)
237+
behind_by_commit = Column(types.Text)
241238

242239
author = relationship(Owner)
243240
repository = relationship(Repository, backref=backref("pulls", cascade="delete"))
@@ -276,7 +273,6 @@ def get_head_commit_notifications(self):
276273

277274

278275
class CommitNotification(CodecovBaseModel):
279-
280276
__tablename__ = "commit_notifications"
281277

282278
id_ = Column("id", types.BigInteger, primary_key=True)

services/comparison/__init__.py

+47
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@ def __init__(self, comparison: Comparison):
4242
self._diff = None
4343
self._changes = None
4444
self._existing_statuses = None
45+
self._behind_by = None
46+
self._branch = None
4547
self._diff_lock = asyncio.Lock()
4648
self._changes_lock = asyncio.Lock()
4749
self._existing_statuses_lock = asyncio.Lock()
50+
self._behind_by_lock = asyncio.Lock()
4851
self._archive_service = None
4952
self._overlays = {}
5053

@@ -144,6 +147,50 @@ async def get_changes(self) -> Optional[List[Change]]:
144147
)
145148
return self._changes
146149

150+
async def get_behind_by(self):
151+
async with self._behind_by_lock:
152+
if self._behind_by is None:
153+
if not getattr(self.comparison.base.commit, "commitid", None):
154+
log.info(
155+
"Comparison base commit does not have commitid, unable to get behind_by"
156+
)
157+
return None
158+
159+
provider_pull = self.comparison.enriched_pull.provider_pull
160+
if provider_pull is None:
161+
log.info(
162+
"Comparison does not have provider pull request information, unable to get behind_by"
163+
)
164+
return None
165+
if self._branch is None:
166+
branches = await self.repository_service.get_branches()
167+
branch = [
168+
b for b in branches if b[0] == provider_pull["base"]["branch"]
169+
]
170+
171+
if len(branch) == 0:
172+
log.warning(
173+
"Unable to find branch in list of branches on repo",
174+
extra=dict(
175+
branch=provider_pull["base"]["branch"],
176+
),
177+
)
178+
return None
179+
180+
self._branch = branch
181+
182+
distance = await self.repository_service.get_distance_in_commits(
183+
branch[0][1],
184+
self.comparison.base.commit.commitid,
185+
with_commits=False,
186+
)
187+
self._behind_by = distance["behind_by"]
188+
self.enriched_pull.database_pull.behind_by = distance["behind_by"]
189+
self.enriched_pull.database_pull.behind_by_commit = distance[
190+
"behind_by_commit"
191+
]
192+
return self._behind_by
193+
147194
async def get_existing_statuses(self):
148195
async with self._existing_statuses_lock:
149196
if self._existing_statuses is None:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import pytest
2+
3+
from services.comparison import ComparisonProxy
4+
5+
6+
class TestGetBehindBy(object):
7+
@pytest.mark.asyncio
8+
async def test_get_behind_by(self, mocker, mock_repo_provider):
9+
comparison = ComparisonProxy(mocker.MagicMock())
10+
comparison.comparison.enriched_pull.provider_pull = {"base": {"branch": "a"}}
11+
mock_repo_provider.get_branches.return_value = [("a", "1")]
12+
mock_repo_provider.get_distance_in_commits.return_value = {
13+
"behind_by": 3,
14+
"behind_by_commit": 123456,
15+
}
16+
mocker.patch(
17+
"services.comparison.get_repo_provider_service",
18+
return_value=mock_repo_provider,
19+
)
20+
res = await comparison.get_behind_by()
21+
assert res == 3
22+
23+
@pytest.mark.asyncio
24+
async def test_get_behind_by_no_base_commit(self, mocker):
25+
comparison = ComparisonProxy(mocker.MagicMock())
26+
del comparison.comparison.base.commit.commitid
27+
res = await comparison.get_behind_by()
28+
assert res is None
29+
30+
@pytest.mark.asyncio
31+
async def test_get_behind_by_no_provider_pull(self, mocker):
32+
comparison = ComparisonProxy(mocker.MagicMock())
33+
comparison.comparison.enriched_pull.provider_pull = None
34+
res = await comparison.get_behind_by()
35+
assert res is None
36+
37+
@pytest.mark.asyncio
38+
async def test_get_behind_by_no_matching_branches(self, mocker, mock_repo_provider):
39+
mock_repo_provider.get_branches.return_value = []
40+
mocker.patch(
41+
"services.comparison.get_repo_provider_service",
42+
return_value=mock_repo_provider,
43+
)
44+
comparison = ComparisonProxy(mocker.MagicMock())
45+
res = await comparison.get_behind_by()
46+
assert res is None

services/notification/notifiers/mixins/message/__init__.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ async def create_message(
3939
"""
4040
changes = await comparison.get_changes()
4141
diff = await comparison.get_diff()
42+
behind_by = await comparison.get_behind_by()
4243
base_report = comparison.base.report
4344
head_report = comparison.head.report
4445
pull = comparison.pull
@@ -80,8 +81,9 @@ async def create_message(
8081
settings,
8182
current_yaml,
8283
)
84+
8385
await self.write_section_to_msg(
84-
comparison, changes, diff, links, write, section_writer
86+
comparison, changes, diff, links, write, section_writer, behind_by
8587
)
8688

8789
is_compact_message = should_message_be_compact(comparison, settings)
@@ -121,7 +123,12 @@ async def create_message(
121123
)
122124

123125
await self.write_section_to_msg(
124-
comparison, changes, diff, links, write, section_writer
126+
comparison,
127+
changes,
128+
diff,
129+
links,
130+
write,
131+
section_writer,
125132
)
126133

127134
if is_compact_message:
@@ -141,7 +148,12 @@ async def create_message(
141148
current_yaml,
142149
)
143150
await self.write_section_to_msg(
144-
comparison, changes, diff, links, write, section_writer
151+
comparison,
152+
changes,
153+
diff,
154+
links,
155+
write,
156+
section_writer,
145157
)
146158

147159
return [m for m in message if m is not None]
@@ -161,15 +173,16 @@ async def _possibly_write_gh_app_login_announcement(
161173
write("")
162174

163175
async def write_section_to_msg(
164-
self, comparison, changes, diff, links, write, section_writer
176+
self, comparison, changes, diff, links, write, section_writer, behind_by=None
165177
):
166178
with metrics.timer(
167179
f"worker.services.notifications.notifiers.comment.section.{section_writer.name}"
168180
):
169181
for line in await section_writer.write_section(
170-
comparison, diff, changes, links
182+
comparison, diff, changes, links, behind_by=behind_by
171183
):
172184
write(line)
185+
173186
write("")
174187

175188
def get_middle_layout_section_names(self, settings):

services/notification/notifiers/mixins/message/sections.py

+24-10
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ async def write_section(*args, **kwargs):
7676

7777

7878
class NewFooterSectionWriter(BaseSectionWriter):
79-
async def do_write_section(self, comparison, diff, changes, links):
79+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
8080
repo_service = comparison.repository_service.service
8181
yield ("")
8282
yield (
@@ -94,7 +94,7 @@ async def do_write_section(self, comparison, diff, changes, links):
9494

9595

9696
class NewHeaderSectionWriter(BaseSectionWriter):
97-
async def do_write_section(self, comparison, diff, changes, links):
97+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
9898
yaml = self.current_yaml
9999
base_report = comparison.base.report
100100
head_report = comparison.head.report
@@ -148,6 +148,7 @@ async def do_write_section(self, comparison, diff, changes, links):
148148
head_cov=round_number(yaml, Decimal(head_report.totals.coverage)),
149149
)
150150
)
151+
151152
else:
152153
yield (
153154
"> :exclamation: No coverage uploaded for {request_type} {what} (`{branch}@{commit}`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-{what}-commit).".format(
@@ -175,6 +176,11 @@ async def do_write_section(self, comparison, diff, changes, links):
175176
else:
176177
yield "> Patch has no changes to coverable lines."
177178

179+
if behind_by:
180+
yield (
181+
f"> Report is {behind_by} commits behind head on {pull_dict['base']['branch']}."
182+
)
183+
178184
if (
179185
comparison.enriched_pull.provider_pull is not None
180186
and comparison.head.commit.commitid
@@ -226,7 +232,7 @@ async def do_write_section(self, comparison, diff, changes, links):
226232

227233

228234
class HeaderSectionWriter(BaseSectionWriter):
229-
async def do_write_section(self, comparison, diff, changes, links):
235+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
230236
yaml = self.current_yaml
231237
base_report = comparison.base.report
232238
head_report = comparison.head.report
@@ -261,6 +267,7 @@ async def do_write_section(self, comparison, diff, changes, links):
261267
links=links,
262268
)
263269
)
270+
264271
else:
265272
yield (
266273
"> :exclamation: No coverage uploaded for pull request {what} (`{branch}@{commit}`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-{what}-commit).".format(
@@ -272,6 +279,11 @@ async def do_write_section(self, comparison, diff, changes, links):
272279
)
273280
)
274281

282+
if isinstance(behind_by, int) and behind_by > 0:
283+
yield (
284+
f"> Report is {behind_by} commits behind head on {pull_dict['base']['branch']}."
285+
)
286+
275287
diff_totals = head_report.apply_diff(diff)
276288
if diff_totals and diff_totals.coverage is not None:
277289
yield (
@@ -347,7 +359,7 @@ async def do_write_section(*args, **kwargs):
347359

348360

349361
class ImpactedEntrypointsSectionWriter(BaseSectionWriter):
350-
async def do_write_section(self, comparison, diff, changes, links):
362+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
351363
overlay = comparison.get_overlay(OverlayType.line_execution_count)
352364
impacted_endpoints = await overlay.find_impacted_endpoints()
353365
if impacted_endpoints:
@@ -367,7 +379,7 @@ async def do_write_section(self, comparison, diff, changes, links):
367379

368380

369381
class FooterSectionWriter(BaseSectionWriter):
370-
async def do_write_section(self, comparison, diff, changes, links):
382+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
371383
pull_dict = comparison.enriched_pull.provider_pull
372384
yield ("------")
373385
yield ("")
@@ -393,7 +405,7 @@ async def do_write_section(self, comparison, diff, changes, links):
393405

394406

395407
class ReachSectionWriter(BaseSectionWriter):
396-
async def do_write_section(self, comparison, diff, changes, links):
408+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
397409
pull = comparison.enriched_pull.database_pull
398410
yield (
399411
"[![Impacted file tree graph]({})]({}?src=pr&el=tree)".format(
@@ -411,7 +423,7 @@ async def do_write_section(self, comparison, diff, changes, links):
411423

412424

413425
class DiffSectionWriter(BaseSectionWriter):
414-
async def do_write_section(self, comparison, diff, changes, links):
426+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
415427
base_report = comparison.base.report
416428
head_report = comparison.head.report
417429
if base_report is None:
@@ -432,7 +444,7 @@ async def do_write_section(self, comparison, diff, changes, links):
432444

433445

434446
class FileSectionWriter(BaseSectionWriter):
435-
async def do_write_section(self, comparison, diff, changes, links):
447+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
436448
# create list of files changed in diff
437449
base_report = comparison.base.report
438450
head_report = comparison.head.report
@@ -535,7 +547,7 @@ def tree_cell(typ, path, metrics, _=None):
535547

536548

537549
class FlagSectionWriter(BaseSectionWriter):
538-
async def do_write_section(self, comparison, diff, changes, links):
550+
async def do_write_section(self, comparison, diff, changes, links, behind_by=None):
539551
# flags
540552
base_report = comparison.base.report
541553
head_report = comparison.head.report
@@ -683,7 +695,9 @@ async def _get_table_data_for_components(
683695
)
684696
return component_data
685697

686-
async def do_write_section(self, comparison: ComparisonProxy, diff, changes, links):
698+
async def do_write_section(
699+
self, comparison: ComparisonProxy, diff, changes, links, behind_by=None
700+
):
687701
all_components = get_components_from_yaml(self.current_yaml)
688702
if all_components == []:
689703
return # fast return if there's noting to process

0 commit comments

Comments
 (0)