travis_diff.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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
  5. # look through the commit history and determine whether or not the current
  6. # framework test directory needs to be run.
  7. import subprocess
  8. import os
  9. import re
  10. from sets import Set
  11. # Returns a unique list of dockerfile changes
  12. def get_docker_changes(changes_output):
  13. return list(
  14. Set(
  15. re.findall(r"toolset/setup/docker/.+/(.+)\.dockerfile", changes_output,
  16. re.M)))
  17. def fw_found_in_changes(test, changes_output):
  18. return re.search(
  19. r"frameworks/" + re.escape(test) + "/",
  20. changes_output, re.M)
  21. # Cleans up diffing and grep output and into an array of strings
  22. def clean_output(output):
  23. return os.linesep.join([s for s in output.splitlines() if s])
  24. def quit_diffing():
  25. if len(run_tests):
  26. print("travis-run-tests {!s}".format(" ".join(set(run_tests))))
  27. else:
  28. print("No tests to run.")
  29. exit(0)
  30. print("TRAVIS_COMMIT_RANGE: {!s}".format(os.getenv("TRAVIS_COMMIT_RANGE")))
  31. print("TRAVIS_COMMIT : {!s}".format(os.getenv("TRAVIS_COMMIT")))
  32. is_PR = (os.getenv("TRAVIS_PULL_REQUEST") != "false")
  33. commit_range = ""
  34. first_commit = ""
  35. last_commit = ""
  36. if is_PR:
  37. print('I am testing a pull request')
  38. first_commit = os.getenv("TRAVIS_COMMIT_RANGE").split('...')[0]
  39. last_commit = subprocess.check_output(
  40. "git rev-list -n 1 FETCH_HEAD^2", shell=True).rstrip('\n')
  41. print("Guessing that first commit in PR is : {!s}".format(first_commit))
  42. print("Guessing that final commit in PR is : {!s}".format(last_commit))
  43. if first_commit == "":
  44. print("No first commit, using Github's automerge commit")
  45. commit_range = "--first-parent -1 -m FETCH_HEAD"
  46. elif first_commit == last_commit:
  47. print("Only one commit in range, examining {!s}".format(last_commit))
  48. commit_range = "-m --first-parent -1 {!s}".format(last_commit)
  49. else:
  50. commit_range = "--first-parent {!s}...{!s}".format(
  51. first_commit, last_commit)
  52. if not is_PR:
  53. print('I am not testing a pull request')
  54. commit_range = "--first-parent -m {!s}".format(
  55. os.getenv("TRAVIS_COMMIT_RANGE"))
  56. # Handle 1
  57. if commit_range == "":
  58. commit_range = "--first-parent -m -1 {!s}".format(
  59. os.getenv("TRAVIS_COMMIT"))
  60. print("Using commit range `{!s}`".format(commit_range))
  61. print("Running `git log --name-only --pretty=\"format:\" {!s}`".format(
  62. commit_range))
  63. changes = clean_output(
  64. subprocess.check_output([
  65. 'bash', '-c',
  66. 'git log --name-only --pretty="format:" {!s}'.format(commit_range)
  67. ]))
  68. print("Determining what to run based on the following file changes: \n{!s}"
  69. .format(changes))
  70. # COMMIT MESSAGES:
  71. # Before any complicated diffing, check for forced runs from the commit message
  72. # Use -2 because travis now inserts a merge commit as the last commit
  73. last_commit_msg = subprocess.check_output(
  74. ["bash", "-c", "git log --format=%B -n 1 {!s}".format(last_commit)])
  75. print("Parsing commit message for travis commands: {!s}"
  76. .format(last_commit_msg))
  77. test_dirs = []
  78. run_tests = []
  79. # Break the test env variable down into test directories
  80. if os.getenv("TESTLANG"):
  81. dir = "frameworks/" + os.getenv("TESTLANG") + "/"
  82. test_dirs = map(lambda x: os.getenv("TESTLANG") + "/" + x,
  83. filter(lambda x: os.path.isdir(dir + x), os.listdir(dir)))
  84. elif os.getenv("TESTDIR"):
  85. test_dirs = os.getenv("TESTDIR").split(' ')
  86. # Forced full run
  87. if re.search(r'\[ci run-all\]', last_commit_msg, re.M):
  88. print("All tests have been forced to run from the commit message.")
  89. run_tests = test_dirs
  90. quit_diffing()
  91. # Forced *fw-only* specific tests
  92. if re.search(r'\[ci fw-only .+\]', last_commit_msg, re.M):
  93. tests = re.findall(r'\[ci fw-only (.+)\]', last_commit_msg, re.M)[0].strip().split(' ')
  94. for test in tests:
  95. if test in test_dirs:
  96. print("{!s} has been forced to run from the commit message.".format(test))
  97. run_tests.append(test)
  98. # quit here because we're using "only"
  99. quit_diffing()
  100. # Forced *lang-only* specific tests
  101. if re.search(r'\[ci lang-only .+\]', last_commit_msg, re.M):
  102. langs = re.findall(r'\[ci lang-only (.+)\]', last_commit_msg, re.M)[0].strip().split(' ')
  103. for test in test_dirs:
  104. for lang in langs:
  105. if test.startswith(lang + "/"):
  106. print("{!s} has been forced to run from the commit message.".format(test))
  107. run_tests.append(test)
  108. # quit here because we're using "only"
  109. quit_diffing()
  110. # Forced framework run in addition to other tests
  111. if re.search(r'\[ci fw .+\]', last_commit_msg, re.M):
  112. tests = re.findall(r'\[ci fw (.+)\]', last_commit_msg, re.M)[0].strip().split(' ')
  113. for test in tests:
  114. if test in test_dirs:
  115. print("{!s} has been forced to run from the commit message.".format(test))
  116. run_tests.append(test)
  117. # Forced lang run in addition to other running tests
  118. if re.search(r'\[ci lang .+\]', last_commit_msg, re.M):
  119. langs = re.findall(r'\[ci lang (.+)\]', last_commit_msg, re.M)[0].strip().split(' ')
  120. for test in test_dirs:
  121. for lang in langs:
  122. if test.startswith(lang + "/"):
  123. print("{!s} has been forced to run from the commit message.".format(test))
  124. run_tests.append(test)
  125. # Ignore travis and docker directory changes
  126. # Also for now, ignore the old linux setup folders, as we don't want to
  127. # trigger a full run as we remove old fw_depends scripts. [ci run-all] will
  128. # still work if it's needed.
  129. if re.search(r'^toolset/(?!(travis/|setup/|continuous/))', changes, re.M) is not None:
  130. print("Found changes to core toolset. Running all tests.")
  131. run_tests = test_dirs
  132. quit_diffing()
  133. for test in test_dirs:
  134. if fw_found_in_changes(test, changes):
  135. print("Found changes that affect {!s}".format(test))
  136. run_tests.append(test)
  137. quit_diffing()