patchtest-send-results 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #!/usr/bin/env python3
  2. # ex:ts=4:sw=4:sts=4:et
  3. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  4. #
  5. # patchtest: execute all unittest test cases discovered for a single patch
  6. # Note that this script is currently under development and has been
  7. # hard-coded with default values for testing purposes. This script
  8. # should not be used without changing the default recipient, at minimum.
  9. #
  10. # Copyright (C) 2023 BayLibre Inc.
  11. #
  12. # SPDX-License-Identifier: GPL-2.0-only
  13. #
  14. import argparse
  15. import boto3
  16. import configparser
  17. import mailbox
  18. import os
  19. import re
  20. import sys
  21. greeting = """Thank you for your submission. Patchtest identified one
  22. or more issues with the patch. Please see the log below for
  23. more information:\n\n---\n"""
  24. suggestions = """\n---\n\nPlease address the issues identified and
  25. submit a new revision of the patch, or alternatively, reply to this
  26. email with an explanation of why the patch should be accepted. If you
  27. believe these results are due to an error in patchtest, please submit a
  28. bug at https://bugzilla.yoctoproject.org/ (use the 'Patchtest' category
  29. under 'Yocto Project Subprojects'). For more information on specific
  30. failures, see: https://wiki.yoctoproject.org/wiki/Patchtest. Thank
  31. you!"""
  32. def has_a_failed_test(raw_results):
  33. return any(raw_result.split(':')[0] == "FAIL" for raw_result in raw_results.splitlines())
  34. parser = argparse.ArgumentParser(description="Send patchtest results to a submitter for a given patch")
  35. parser.add_argument("-p", "--patch", dest="patch", required=True, help="The patch file to summarize")
  36. parser.add_argument("-d", "--debug", dest="debug", required=False, action='store_true', help="Print raw email headers and content, but don't actually send it")
  37. args = parser.parse_args()
  38. if not os.path.exists(args.patch):
  39. print(f"Patch '{args.patch}' not found - did you provide the right path?")
  40. sys.exit(1)
  41. elif not os.path.exists(args.patch + ".testresult"):
  42. print(f"Found patch '{args.patch}' but '{args.patch}.testresult' was not present. Have you run patchtest on the patch?")
  43. sys.exit(1)
  44. result_file = args.patch + ".testresult"
  45. testresult = None
  46. with open(result_file, "r") as f:
  47. testresult = f.read()
  48. # we know these patch files will only contain a single patch, so only
  49. # worry about the first element for getting the subject
  50. mbox = mailbox.mbox(args.patch)
  51. mbox_subject = mbox[0]['subject']
  52. subject_line = f"Patchtest results for {mbox_subject}"
  53. # extract the submitter email address and use it as the reply address
  54. # for the results
  55. reply_address = mbox[0]['from']
  56. # extract the message ID and use that as the in-reply-to address
  57. # TODO: This will need to change again when patchtest can handle a whole
  58. # series at once
  59. in_reply_to = mbox[0]['Message-ID']
  60. # the address the results email is sent from
  61. from_address = "patchtest@automation.yoctoproject.org"
  62. # mailing list to CC
  63. cc_address = "openembedded-core@lists.openembedded.org"
  64. if has_a_failed_test(testresult):
  65. reply_contents = None
  66. if len(max(open(result_file, 'r'), key=len)) > 220:
  67. warning = "Tests failed for the patch, but the results log could not be processed due to excessive result line length."
  68. reply_contents = greeting + warning + suggestions
  69. else:
  70. reply_contents = greeting + testresult + suggestions
  71. ses_client = boto3.client('ses', region_name='us-west-2')
  72. # Construct the headers for the email. We only want to reply
  73. # directly to the tested patch, so make In-Reply-To and References
  74. # the same value.
  75. raw_data = 'From: ' + from_address + '\nTo: ' + reply_address + \
  76. '\nCC: ' + cc_address + '\nSubject:' + subject_line + \
  77. '\nIn-Reply-To:' + in_reply_to + \
  78. '\nReferences:' + in_reply_to + \
  79. '\nMIME-Version: 1.0" + \
  80. "\nContent-type: Multipart/Mixed;boundary="NextPart"\n\n--NextPart\nContent-Type: text/plain\n\n' + \
  81. reply_contents + '\n\n--NextPart'
  82. if args.debug:
  83. print(f"RawMessage: \n\n{raw_data}")
  84. else:
  85. response = ses_client.send_raw_email(
  86. Source="patchtest@automation.yoctoproject.org",
  87. RawMessage={
  88. "Data": raw_data,
  89. },
  90. )
  91. else:
  92. print(f"No failures identified for {args.patch}.")