Skip to content
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
32 changes: 31 additions & 1 deletion tests/project/test_unit_tests_push_reports_2030.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ def nodes(*runs: 'tuple[str, str, list[tuple[str, ...]]]') -> 'list[dict[str, An
('NOTICE', '', 'The ubuntu-latest label will migrate to Ubuntu 26')])

#: A stand-in ``gh``: ``api graphql -f ...``, the check-suite query, answers
#: ``$FAKE_SUITE_ANSWER``; ``run download`` copies ``$FAKE_ARTIFACTS``' folders;
#: ``$FAKE_SUITE_ANSWER``; ``run download`` copies ``$FAKE_ARTIFACTS``' folders,
#: or, given ``say:TEXT``, fails printing ``TEXT``;
#: both log their arguments to ``$FAKE_SUITE_LOG``. ``api graphql --input -``, the
#: posting script's own calls, goes to the stand-in GitHub beside it.
FAKE_GH = textwrap.dedent('''\
Expand All @@ -170,6 +171,9 @@ def nodes(*runs: 'tuple[str, str, list[tuple[str, ...]]]') -> 'list[dict[str, An
if source == 'fail':
print('HTTP 403: Resource not accessible by integration', file=sys.stderr)
sys.exit(1)
if source.startswith('say:'):
print(source[4:], file=sys.stderr)
sys.exit(1)
if not source or not os.listdir(source):
print('no valid artifacts found to download', file=sys.stderr)
sys.exit(1)
Expand Down Expand Up @@ -729,6 +733,32 @@ def test_unreadable_summaries_warn(self) -> None:
report)
self.assertIn('::warning title=CI report not written::The job summaries could not be read', stdout)

def test_each_nothing_to_download_wording_is_no_summary(self) -> None:
# #2074: each way gh's pkg/cmd/run/download/download.go says it found nothing.
for text in ('no valid artifacts found to download',
'no artifact matches any of the names or patterns provided',
'no artifact matches any of the names provided'):
with self.subTest(text=text):
outputs, report, stdout = self.report('success', check_runs(GREEN), FAKE_ARTIFACTS=f'say:{text}')
self.assertEqual(outputs['post'], 'true')
self.assertNotIn('Job summaries', report)
self.assertNotIn('could not be read', report)
self.assertNotIn('::warning', stdout)

def test_a_genuine_download_error_still_warns(self) -> None:
# The last holds a wording inside a longer line, which is not gh saying it.
for text in ('error fetching artifacts: HTTP 401: Bad credentials (https://api.github.com/repos/'
'JarryShaw/PyPCAPKit/actions/runs/1/artifacts?per_page=100)',
'error fetching artifacts: HTTP 404: Not Found (https://api.github.com/repos/'
'JarryShaw/PyPCAPKit/actions/runs/1/artifacts?per_page=100)',
'error fetching artifacts: Get "https://api.github.com/repos/JarryShaw/PyPCAPKit/actions/'
'runs/1/artifacts?per_page=100": dial tcp: lookup api.github.com: no such host',
'error downloading job-summary-test-0: unexpected EOF (not: no valid artifacts found to download)'):
with self.subTest(text=text):
_, report, stdout = self.report('success', check_runs(GREEN), FAKE_ARTIFACTS=f'say:{text}')
self.assertIn(f'The job summaries could not be read: `{text}`.', report)
self.assertIn('::warning title=CI report not written::The job summaries could not be read', stdout)


class TestTheDownload(unittest.TestCase):
""":func:`check_suite_report.download` reads each artifact's folder, and one unpacked in place."""
Expand Down
16 changes: 14 additions & 2 deletions util/check_suite_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,15 @@ class Workflow(NamedTuple):
ARTIFACT = 'job-summary-*'
SHA = re.compile(r'[0-9a-f]{40}')
RUN_PAGE = re.compile(r'https://[\w.-]+/[\w.-]+/[\w.-]+/actions/runs/\d+')
#: What ``gh run download`` says, failing, where it found nothing to download, as
#: :file:`pkg/cmd/run/download/download.go` in cli/cli writes each (#2074): a run
#: with no artifact that has not expired, and one with none :data:`ARTIFACT`
#: matches, worded the latter way since gh 2.11.0 and the former before it.
NOTHING = frozenset({
'no valid artifacts found to download',
'no artifact matches any of the names or patterns provided',
'no artifact matches any of the names provided',
})
#: A mention, which a summary's text must not make.
MENTION = re.compile(r'@(?=[A-Za-z0-9])')
#: What the report joins its job summaries' blocks by.
Expand Down Expand Up @@ -237,8 +246,11 @@ def download(repo: 'str', run_id: 'str', directory: 'pathlib.Path') -> 'tuple[li
return [], f'gh could not run: {error}'
if proc.returncode:
text = proc.stderr.strip() or proc.stdout.strip()
# A run whose jobs wrote no summary uploaded no artifact, which gh reports so.
return [], '' if 'no valid artifacts' in text else (text.splitlines() or ['gh failed'])[-1]
# A run whose jobs wrote no summary uploaded no artifact it matches, which
# gh reports as one of NOTHING's lines; anything else is an error.
if any(line.strip() in NOTHING for line in text.splitlines()):
return [], ''
return [], (text.splitlines() or ['gh failed'])[-1]
found = []
# gh puts each artifact a pattern matches in a folder of its name; a lone
# one unpacked into the directory itself is read there too.
Expand Down
Loading