Skip to content

Commit d326ff5

Browse files
authored
pkg_in_pipe: log the progress, the cache usage and the source problems during the generation (#849)
* Fix plane connection error blocking the script execution It's meant to generate a message in the report, not block the report generation Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech> * pkg_in_pipe: log the progress, the cache usage and the source problems during the generation Add a --debug option and log the koji tag and the build being processed, whether values come from the cache or are fetched again, and warn when the plane tickets or github can't be reached, using the same logger setup as gen-dnf-proxy.py. Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech> --------- Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech>
1 parent bc4028b commit d326ff5

2 files changed

Lines changed: 71 additions & 12 deletions

File tree

‎scripts/pkg_in_pipe/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ environment variable or the `--plane-token` command line option.
1616

1717
An extra `--generated-info` command line option may be used to add some info about the report generation process.
1818

19+
The `--debug` option logs more details about the generation on stderr.
20+
1921
A machine readable version of the report can also be generated with the `--json-output` option:
2022

2123
```sh

‎scripts/pkg_in_pipe/pkg_in_pipe.py‎

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
import argparse
33
import io
44
import json
5+
import logging
56
import os
67
import re
8+
import sys
79
import tomllib
810
from collections import defaultdict
911
from datetime import datetime
@@ -19,6 +21,31 @@
1921
from github.GithubException import BadCredentialsException
2022
from github.PullRequest import PullRequest
2123

24+
log = logging.getLogger(__name__)
25+
26+
27+
class ColorFormatter(logging.Formatter):
28+
colors = {
29+
logging.CRITICAL: '\033[31m',
30+
logging.ERROR: '\033[31m',
31+
logging.WARNING: '\033[33m',
32+
logging.DEBUG: '\033[34m',
33+
}
34+
35+
def __init__(self, use_color: bool) -> None:
36+
super().__init__(fmt='{message}', style='{')
37+
self.use_color = use_color
38+
39+
def format(self, record: logging.LogRecord) -> str:
40+
message = super().format(record)
41+
if record.levelno == logging.INFO:
42+
return message
43+
44+
prefix = f'{record.levelname.lower()}: '
45+
if color := self.colors.get(record.levelno) if self.use_color else None:
46+
prefix = f'{color}{prefix}\033[0m'
47+
return '\n'.join(f'{prefix}{line}' for line in message.splitlines() or [''])
48+
2249

2350
with open(os.path.join(os.path.dirname(__file__), 'missing_sources.toml'), 'rb') as missing_sources_file:
2451
MISSING_SOURCES = tomllib.load(missing_sources_file)
@@ -254,6 +281,21 @@ def find_released_build(build_tag, package_name):
254281
return get_koji_build(max(tagged, key=lambda t: t['build_id'])['build_id'])
255282
return None
256283

284+
def cached(key):
285+
"""Return the value cached for that key, or None when it must be fetched again."""
286+
if args.re_cache:
287+
log.debug("cache ignored because of --re-cache: %s", key)
288+
return None
289+
if key in CACHE:
290+
log.debug("cache hit: %s", key)
291+
return CACHE[key]
292+
log.debug("cache miss: %s", key)
293+
return None
294+
295+
def cache(key, value):
296+
log.debug("caching: %s", key)
297+
CACHE.set(key, value, expire=RETENTION_TIME)
298+
257299
def find_commits(gh, repo, start_sha, end_sha) -> list[Commit]:
258300
"""
259301
List the commits in the range [start_sha,end_sha[.
@@ -262,24 +304,24 @@ def find_commits(gh, repo, start_sha, end_sha) -> list[Commit]:
262304
A commit older that the end_sha commit and added by a merge commit won't appear in this list.
263305
"""
264306
cache_key = f'commits-2-{start_sha}-{end_sha}'
265-
if not args.re_cache and cache_key in CACHE:
266-
return cast(list[Commit], CACHE[cache_key])
307+
if (commits := cast(list[Commit], cached(cache_key))) is not None:
308+
return commits
267309
commits = []
268310
if gh:
269311
for commit in gh.get_repo(repo).get_commits(start_sha):
270312
if commit.sha == end_sha:
271313
break
272314
commits.append(commit)
273-
CACHE.set(cache_key, commits, expire=RETENTION_TIME)
315+
cache(cache_key, commits)
274316
return commits
275317

276318
def find_pull_requests(repo, start_sha, end_sha):
277319
"""Find the pull requests for the commits in the [start_sha,end_sha[ range."""
278320
prs = set()
279321
for commit in find_commits(GITHUB, repo, start_sha, end_sha):
280322
cache_key = f'commit-prs-6-{commit.sha}'
281-
if not args.re_cache and cache_key in CACHE:
282-
prs.update(cast(list[PullRequest], CACHE[cache_key]))
323+
if (commit_prs := cast(list[PullRequest], cached(cache_key))) is not None:
324+
prs.update(commit_prs)
283325
elif GITHUB:
284326
commit_prs = list(commit.get_pulls())
285327
if not commit_prs:
@@ -295,17 +337,17 @@ def find_pull_requests(repo, start_sha, end_sha):
295337
# commit. The latter is needed for the PRs merged by rebase or squash, whose merge
296338
# commit on the base branch is not one of the PR commits.
297339
commit_prs = [pr for pr in commit_prs if commit in pr.get_commits() or commit.sha == pr.merge_commit_sha]
298-
CACHE.set(cache_key, commit_prs, expire=RETENTION_TIME)
340+
cache(cache_key, commit_prs)
299341
prs.update(commit_prs)
300342
return sorted(prs, key=lambda p: p.number, reverse=True)
301343

302344
def get_koji_build(build_id) -> dict:
303345
cache_key = f'koji-build-{build_id}'
304-
if not args.re_cache and cache_key in CACHE:
305-
return cast(dict, CACHE[cache_key])
346+
if (build := cast(dict, cached(cache_key))) is not None:
347+
return build
306348
else:
307349
build = KOJI.getBuild(build_id)
308-
CACHE.set(cache_key, build, expire=RETENTION_TIME)
350+
cache(cache_key, build)
309351
return build
310352

311353
def get_plane_issues(plane_token):
@@ -409,8 +451,15 @@ def get_plane_issues_with_milestones(plane_token):
409451
)
410452
parser.add_argument('--re-cache', help="Refresh the cache", action='store_true')
411453
parser.add_argument('--json-output', help="Also write a machine readable report in json format to this path")
454+
parser.add_argument('--debug', help="Log more details on stderr", action='store_true')
412455
args = parser.parse_args()
413456

457+
handler = logging.StreamHandler()
458+
handler.setFormatter(ColorFormatter(
459+
not os.environ.get('NO_COLOR') and (bool(os.environ.get('FORCE_COLOR')) or sys.stderr.isatty()),
460+
))
461+
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO, handlers=[handler])
462+
414463
CACHE = diskcache.Cache(args.cache)
415464
RETENTION_TIME = 24 * 60 * 60 # 24 hours
416465

@@ -426,9 +475,9 @@ def get_plane_issues_with_milestones(plane_token):
426475
# load the issues from plane, so we can search for the plane card related to a build
427476
try:
428477
issues = get_plane_issues_with_milestones(args.plane_token)
429-
except Exception:
478+
except Exception as e:
430479
issues = []
431-
raise
480+
log.warning("failed to load the tickets from plane: %s", e)
432481

433482
# connect to github
434483
GITHUB = None
@@ -438,6 +487,12 @@ def get_plane_issues_with_milestones(plane_token):
438487
GITHUB.get_repo('xcp-ng/xcp') # check that the token is valid
439488
except BadCredentialsException:
440489
GITHUB = None
490+
log.warning("the github token is invalid")
491+
except github.GithubException as e:
492+
GITHUB = None
493+
log.warning("failed to connect to github: %s", e)
494+
else:
495+
log.warning("no github token, the pull requests come from the cache")
441496

442497
# load the packages maintainers
443498
with urlopen('https://github.com/xcp-ng/xcp/raw/refs/heads/master/scripts/rpm_owners/packages.json') as f:
@@ -464,6 +519,7 @@ def get_plane_issues_with_milestones(plane_token):
464519
KOJI = koji.ClientSession('https://kojihub.xcp-ng.org', config)
465520
KOJI.ssl_login(config['cert'], None, config['serverca'])
466521
for tag in tags:
522+
log.info('processing tag %s', tag)
467523
tag_data = {'tag': tag, 'builds': []}
468524
report_data['tags'].append(tag_data)
469525
tag_history = dict(
@@ -475,6 +531,8 @@ def get_plane_issues_with_milestones(plane_token):
475531
taggeds = (t for t in taggeds if t['package_name'] in args.packages or args.packages == [])
476532
taggeds = sorted(taggeds, key=lambda t: (tag_history[t['build_id']], t['build_id']), reverse=True)
477533
for tagged in taggeds:
534+
build_url = f'https://koji.xcp-ng.org/buildinfo?buildID={tagged["build_id"]}'
535+
log.info(' processing build %s (%s)', tagged['nvr'], build_url)
478536
build = get_koji_build(tagged['build_id'])
479537
prs: list[PullRequest] = []
480538
maintained_by = None
@@ -484,7 +542,6 @@ def get_plane_issues_with_milestones(plane_token):
484542
(repo, sha) = parse_source(build['source'])
485543
prs = find_pull_requests(repo, sha, previous_build_sha)
486544
maintained_by = PACKAGES.get(tagged['package_name'], {}).get('maintainer')
487-
build_url = f'https://koji.xcp-ng.org/buildinfo?buildID={tagged["build_id"]}'
488545
build_issues = filter_issues(issues, [build_url] + [pr.html_url for pr in prs])
489546
print_table_line(
490547
temp_out, tagged['nvr'], build_url, build_issues, tagged['owner_name'], prs, maintained_by

0 commit comments

Comments
 (0)