travis_diff.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/env python
  2. # @file: toolset/travis/travis_diff.py
  3. # @author: Nate Brady
  4. # @description: This script is only for use within Travis-CI. It is meant to look through the commit history
  5. # and determine whether or not the current framework test directory needs to be run.
  6. #
  7. # Notes: This script will run in python 2 and 3. print is being used instead of the logging import because
  8. # travis does not echo python logging during the before_script lifecycle.
  9. import subprocess
  10. import os
  11. import re
  12. from sets import Set
  13. # Returns a unique list of fw_depends changes
  14. def get_fw_depends_changes(changes_output):
  15. return list(Set(re.findall(r"toolset/setup/linux/.+/(.+)\.sh", changes_output, re.M)))
  16. # Returns a unique list of frameworks that have been changed
  17. def fw_found_in_changes(changes_output):
  18. return re.search(r"" + re.escape(os.environ['TESTDIR']), changes_output, re.M)
  19. # Cleans up diffing and grep output and into an array of strings
  20. def clean_output(output):
  21. return os.linesep.join([s for s in output.splitlines() if s])
  22. def quit_diffing(should_test_run):
  23. if should_test_run:
  24. print("travis-diff-continue")
  25. exit(0)
  26. # COMMIT MESSAGES:
  27. # Before any complicated diffing, check for forced runs from the commit message
  28. last_commit_msg = subprocess.check_output(['bash', '-c', 'git log -1 --pretty=%B'])
  29. # Forced *fw-only* specific tests
  30. if re.search(r'\[ci fw-only.+\]', last_commit_msg, re.M):
  31. if re.search(r'\[ci fw-only(.?)+ ' + re.escape(os.environ['TESTDIR']) + '( .+\]|])', last_commit_msg, re.M):
  32. print("This test has been forced to run from the commit message.")
  33. quit_diffing(True)
  34. else:
  35. print("Skipping this test from the commit message.")
  36. quit_diffing(False)
  37. # Forced full run
  38. if re.search(r'\[ci run-all\]', last_commit_msg, re.M):
  39. print("All tests have been forced to run from the commit message.")
  40. quit_diffing(True)
  41. # Forced framework run
  42. if re.search(r'\[ci fw(.?)+ ' + re.escape(os.environ['TESTDIR']) + '( .+\]|\])', last_commit_msg, re.M):
  43. print('This test has been forced to run from the commit message.')
  44. quit_diffing(True)
  45. print("TRAVIS_COMMIT_RANGE: {!s}".format(os.environ['TRAVIS_COMMIT_RANGE']))
  46. print("TRAVIS_COMMIT : {!s}".format(os.environ['TRAVIS_COMMIT']))
  47. is_PR = (os.environ['TRAVIS_PULL_REQUEST'] != "false")
  48. commit_range = ""
  49. if is_PR:
  50. print('I am testing a pull request')
  51. first_commit = os.environ['TRAVIS_COMMIT_RANGE'].split('...')[0]
  52. last_commit = subprocess.check_output("git rev-list -n 1 FETCH_HEAD^2", shell=True).rstrip('\n')
  53. print("Guessing that first commit in PR is : {!s}".format(first_commit))
  54. print("Guessing that final commit in PR is : {!s}".format(last_commit))
  55. if first_commit == "":
  56. print("No first commit, using Github's automerge commit")
  57. commit_range = "--first-parent -1 -m FETCH_HEAD"
  58. elif first_commit == last_commit:
  59. print("Only one commit in range, examining {!s}".format(last_commit))
  60. commit_range = "-m --first-parent -1 {!s}".format(last_commit)
  61. else:
  62. commit_range = "--first-parent {!s}...{!s}".format(first_commit, last_commit)
  63. if not is_PR:
  64. print('I am not testing a pull request')
  65. commit_range = "--first-parent -m {!s}".format(os.environ['TRAVIS_COMMIT_RANGE'])
  66. # Handle 1
  67. if commit_range == "":
  68. commit_range = "--first-parent -m -1 {!s}".format(os.environ['TRAVIS_COMMIT'])
  69. print("Using commit range `{!s}`".format(commit_range))
  70. print("Running `git log --name-only --pretty=\"format:\" {!s}`".format(commit_range))
  71. changes = clean_output(subprocess.check_output(['bash', '-c', 'git log --name-only --pretty="format:" {!s}'.format(commit_range)]))
  72. # Satisfies this requirement:
  73. # Anything in the toolset/ that isn't in the setup/linux/*/ subdirectory
  74. # Any commit message that contains [ci run]
  75. if re.search(r'^toolset/(?!setup/linux/.+/)', changes, re.M) is not None:
  76. print("Found changes to core toolset. Running all tests.")
  77. quit_diffing(True)
  78. if fw_found_in_changes(changes):
  79. print("Found changes that affect this framework. Running test.")
  80. quit_diffing(True)
  81. # Satisfies this requirement:
  82. # ^toolset/setup/linux/.+/(.+)\.sh
  83. # Determine what has been changed based on initial diffing output
  84. fw_depends_changes = get_fw_depends_changes(changes)
  85. # For each of these, find the files that depend on them, if we find more fw_depends stuff,
  86. # add it to the bottom of the list, if it isn't already there.
  87. i = 0
  88. while i <= len(fw_depends_changes) - 1:
  89. # Generates output of files that contain the fw_depends for this dependency
  90. more_changes = subprocess.check_output(['bash', '-c', 'grep -RP "fw_depends(.?)+ ' + re.escape(fw_depends_changes[i]) + '( |$)" . || echo ""'])
  91. print("more_changes: {!s}".format(more_changes))
  92. if fw_found_in_changes(more_changes):
  93. print("Found changes that affect this framework. Running test.")
  94. quit_diffing(True)
  95. # Preserves the order of the list, so we can continue with this loop
  96. fw_depends_changes.extend(Set(get_fw_depends_changes(more_changes)) - Set(fw_depends_changes))
  97. i += 1
  98. # If we get here, there was nothing found
  99. print("Did not find any changes that affect this framework.")
  100. quit_diffing(False)