You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hydra-eval-failures.py 2.7KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/usr/bin/env nix-shell
  2. #!nix-shell -i python -p pythonFull pythonPackages.requests pythonPackages.pyquery pythonPackages.click
  3. # To use, just execute this script with --help to display help.
  4. import subprocess
  5. import json
  6. import sys
  7. import click
  8. import requests
  9. from pyquery import PyQuery as pq
  10. maintainers_json = subprocess.check_output([
  11. 'nix-instantiate', '-E', 'import ./maintainers/maintainer-list.nix {}', '--eval', '--json'
  12. ])
  13. maintainers = json.loads(maintainers_json)
  14. MAINTAINERS = {v: k for k, v in maintainers.iteritems()}
  15. def get_response_text(url):
  16. return pq(requests.get(url).text) # IO
  17. EVAL_FILE = {
  18. 'nixos': 'nixos/release.nix',
  19. 'nixpkgs': 'pkgs/top-level/release.nix',
  20. }
  21. def get_maintainers(attr_name):
  22. try:
  23. nixname = attr_name.split('.')
  24. meta_json = subprocess.check_output([
  25. 'nix-instantiate',
  26. '--eval',
  27. '--strict',
  28. '-A',
  29. '.'.join(nixname[1:]) + '.meta',
  30. EVAL_FILE[nixname[0]],
  31. '--json'])
  32. meta = json.loads(meta_json)
  33. if meta.get('maintainers'):
  34. return [MAINTAINERS[name] for name in meta['maintainers'] if MAINTAINERS.get(name)]
  35. except:
  36. return []
  37. @click.command()
  38. @click.option(
  39. '--jobset',
  40. default="nixos/release-17.09",
  41. help='Hydra project like nixos/release-17.09')
  42. def cli(jobset):
  43. """
  44. Given a Hydra project, inspect latest evaluation
  45. and print a summary of failed builds
  46. """
  47. url = "http://hydra.nixos.org/jobset/{}".format(jobset)
  48. # get the last evaluation
  49. click.echo(click.style(
  50. 'Getting latest evaluation for {}'.format(url), fg='green'))
  51. d = get_response_text(url)
  52. evaluations = d('#tabs-evaluations').find('a[class="row-link"]')
  53. latest_eval_url = evaluations[0].get('href')
  54. # parse last evaluation page
  55. click.echo(click.style(
  56. 'Parsing evaluation {}'.format(latest_eval_url), fg='green'))
  57. d = get_response_text(latest_eval_url + '?full=1')
  58. # TODO: aborted evaluations
  59. # TODO: dependency failed without propagated builds
  60. for tr in d('img[alt="Failed"]').parents('tr'):
  61. a = pq(tr)('a')[1]
  62. print("- [ ] [{}]({})".format(a.text, a.get('href')))
  63. sys.stdout.flush()
  64. maintainers = get_maintainers(a.text)
  65. if maintainers:
  66. print(" - maintainers: {}".format(", ".join(map(lambda u: '@' + u, maintainers))))
  67. # TODO: print last three persons that touched this file
  68. # TODO: pinpoint the diff that broke this build, or maybe it's transient or maybe it never worked?
  69. sys.stdout.flush()
  70. if __name__ == "__main__":
  71. try:
  72. cli()
  73. except:
  74. import pdb;pdb.post_mortem()