depot/maintainers/scripts/hydra-eval-failures.py

113 lines
3.1 KiB
Python
Raw Normal View History

#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages(ps: with ps; [ requests pyquery click ])"
# To use, just execute this script with --help to display help.
import subprocess
import json
import sys
import click
import requests
from pyquery import PyQuery as pq
def map_dict (f, d):
for k,v in d.items():
d[k] = f(v)
maintainers_json = subprocess.check_output([
'nix-instantiate', '-A', 'lib.maintainers', '--eval', '--strict', '--json'
])
maintainers = json.loads(maintainers_json)
MAINTAINERS = map_dict(lambda v: v.get('github', None), maintainers)
def get_response_text(url):
return pq(requests.get(url).text) # IO
EVAL_FILE = {
'nixos': 'nixos/release.nix',
'nixpkgs': 'pkgs/top-level/release.nix',
}
def get_maintainers(attr_name):
try:
nixname = attr_name.split('.')
meta_json = subprocess.check_output([
'nix-instantiate',
'--eval',
'--strict',
'-A',
'.'.join(nixname[1:]) + '.meta',
EVAL_FILE[nixname[0]],
'--arg',
'nixpkgs',
'./.',
'--json'])
meta = json.loads(meta_json)
return meta.get('maintainers', [])
except:
return []
def filter_github_users(maintainers):
github_only = []
for i in maintainers:
if i.get('github'):
github_only.append(i)
return github_only
def print_build(table_row):
a = pq(table_row)('a')[1]
print("- [ ] [{}]({})".format(a.text, a.get('href')), flush=True)
job_maintainers = filter_github_users(get_maintainers(a.text))
if job_maintainers:
print(" - maintainers: {}".format(" ".join(map(lambda u: '@' + u.get('github'), job_maintainers))))
# TODO: print last three persons that touched this file
# TODO: pinpoint the diff that broke this build, or maybe it's transient or maybe it never worked?
sys.stdout.flush()
@click.command()
@click.option(
'--jobset',
default="nixos/release-19.09",
help='Hydra project like nixos/release-19.09')
def cli(jobset):
"""
Given a Hydra project, inspect latest evaluation
and print a summary of failed builds
"""
url = "https://hydra.nixos.org/jobset/{}".format(jobset)
# get the last evaluation
click.echo(click.style(
'Getting latest evaluation for {}'.format(url), fg='green'))
d = get_response_text(url)
evaluations = d('#tabs-evaluations').find('a[class="row-link"]')
latest_eval_url = evaluations[0].get('href')
# parse last evaluation page
click.echo(click.style(
'Parsing evaluation {}'.format(latest_eval_url), fg='green'))
d = get_response_text(latest_eval_url + '?full=1')
# TODO: aborted evaluations
# TODO: dependency failed without propagated builds
print('\nFailures:')
for tr in d('img[alt="Failed"]').parents('tr'):
print_build(tr)
print('\nDependency failures:')
for tr in d('img[alt="Dependency failed"]').parents('tr'):
print_build(tr)
if __name__ == "__main__":
try:
cli()
except Exception as e:
import pdb;pdb.post_mortem()