tests.yaml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. # Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. # Exceptions. See /LICENSE for license information.
  3. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. name: test
  5. on:
  6. push:
  7. branches: [trunk]
  8. pull_request:
  9. merge_group:
  10. # Cancel previous workflows on the PR when there are multiple fast commits.
  11. # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency
  12. concurrency:
  13. group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
  14. cancel-in-progress: true
  15. jobs:
  16. test:
  17. strategy:
  18. matrix:
  19. # At present, these images are newer than "latest". We use them to test
  20. # against more recent tooling versions.
  21. # https://github.com/actions/runner-images
  22. os: [ubuntu-22.04, macos-12]
  23. build_mode: [fastbuild, opt]
  24. runs-on: ${{ matrix.os }}
  25. steps:
  26. # Ubuntu images start with 23GB available, and this adds 14GB more. For
  27. # comparison, MacOS images have >100GB free.
  28. - name: Free up disk space (Ubuntu)
  29. if: matrix.os == 'ubuntu-22.04'
  30. uses: jlumbroso/free-disk-space@v1.2.0
  31. with:
  32. android: true
  33. dotnet: true
  34. haskell: true
  35. # Although we could delete more, if we run into a limit, it provides a
  36. # little flexibility to get space while trying to shrink the build.
  37. # There's also support for docker images at head (1.2.0 is still
  38. # the latest release).
  39. large-packages: false
  40. swap-storage: false
  41. # Checkout the pull request head or the branch.
  42. - name: Checkout pull request
  43. if: github.event_name == 'pull_request'
  44. uses: actions/checkout@v3
  45. with:
  46. ref: ${{ github.event.pull_request.head.sha }}
  47. - name: Checkout branch
  48. if: github.event_name != 'pull_request'
  49. uses: actions/checkout@v3
  50. # Tests should only run on applicable paths, but we still need to have an
  51. # action run for the merge queue. We filter steps based on the paths here,
  52. # and condition steps on the output.
  53. - id: filter
  54. uses: dorny/paths-filter@v2
  55. with:
  56. filters: |
  57. ignore:
  58. - '**/*.md'
  59. - 'LICENSE'
  60. - 'CODEOWNERS'
  61. - '.git*'
  62. # Setup Python and related tools.
  63. - uses: actions/setup-python@v4
  64. if: steps.filter.outputs.ignore == 'false'
  65. with:
  66. # Match the min version listed in docs/project/contribution_tools.md
  67. python-version: '3.9'
  68. # Use LLVM following:
  69. # https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md
  70. # Both 14 and 15 are candidates because GitHub is testing new images.
  71. - name: Setup LLVM and Clang (macOS)
  72. if: steps.filter.outputs.ignore == 'false' && matrix.os == 'macos-12'
  73. run: |
  74. LLVM_PATH="$(brew --prefix llvm@15)"
  75. if [[ ! -e "${LLVM_PATH}" ]]; then
  76. LLVM_PATH="$(brew --prefix llvm@14)"
  77. fi
  78. echo "Using ${LLVM_PATH}"
  79. echo "${LLVM_PATH}/bin" >> $GITHUB_PATH
  80. echo '*** ls "${LLVM_PATH}"'
  81. ls "${LLVM_PATH}"
  82. echo '*** ls "${LLVM_PATH}/bin"'
  83. ls "${LLVM_PATH}/bin"
  84. # Use LLVM following:
  85. # https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md
  86. # Both 14 and 15 are candidates for forwards compatibility, although 15
  87. # isn't provided.
  88. - name: Setup LLVM and Clang (Ubuntu)
  89. if:
  90. steps.filter.outputs.ignore == 'false' && matrix.os == 'ubuntu-22.04'
  91. run: |
  92. # TODO: Re-enable once llvm-15 is working.
  93. # https://github.com/actions/runner-images/issues/8253
  94. # LLVM_PATH="/usr/lib/llvm-15"
  95. # if [[ ! -e "${LLVM_PATH}" ]]; then
  96. LLVM_PATH="/usr/lib/llvm-14"
  97. # fi
  98. echo "Using ${LLVM_PATH}"
  99. echo "${LLVM_PATH}/bin" >> $GITHUB_PATH
  100. echo '*** ls "${LLVM_PATH}"'
  101. ls "${LLVM_PATH}"
  102. echo '*** ls "${LLVM_PATH}/bin"'
  103. ls "${LLVM_PATH}/bin"
  104. # Print the various tool paths and versions to help in debugging.
  105. - name: Print tool debugging info
  106. if: steps.filter.outputs.ignore == 'false'
  107. run: |
  108. echo '*** PATH'
  109. echo $PATH
  110. echo '*** bazelisk'
  111. which bazelisk
  112. bazelisk --version
  113. echo '*** python'
  114. which python
  115. python --version
  116. echo '*** clang'
  117. which clang
  118. clang --version
  119. echo '*** clang++'
  120. which clang++
  121. clang++ --version
  122. # Disable uploads when the remote cache is read-only.
  123. - name: Set up remote cache access (read-only)
  124. if:
  125. steps.filter.outputs.ignore == 'false' && github.event_name ==
  126. 'pull_request'
  127. run: |
  128. echo "remote_cache_upload=--remote_upload_local_results=false" \
  129. >> $GITHUB_ENV
  130. # Provide a cache key when the remote cache is read-write.
  131. - name: Set up remote cache access (read-write)
  132. if:
  133. steps.filter.outputs.ignore == 'false' && github.event_name !=
  134. 'pull_request'
  135. env:
  136. REMOTE_CACHE_KEY: ${{ secrets.CARBON_BUILDS_GITHUB }}
  137. run: |
  138. echo "$REMOTE_CACHE_KEY" | base64 -d > $HOME/remote_cache_key.json
  139. echo "remote_cache_upload=--google_credentials=$HOME/remote_cache_key.json" \
  140. >> $GITHUB_ENV
  141. # We need to replace the `.` with a `_` for the build cache.
  142. - name: Setup LLVM and Clang (macOS)
  143. if: steps.filter.outputs.ignore == 'false' && matrix.os == 'macos-12'
  144. run: |
  145. echo "os_for_cache=macos-12" >> $GITHUB_ENV
  146. - name: Setup LLVM and Clang (Ubuntu)
  147. if:
  148. steps.filter.outputs.ignore == 'false' && matrix.os == 'ubuntu-22.04'
  149. run: |
  150. echo "os_for_cache=ubuntu-22_04" >> $GITHUB_ENV
  151. # Add our bazel configuration and print basic info to ease debugging.
  152. - name: Configure Bazel and print info
  153. if: steps.filter.outputs.ignore == 'false'
  154. env:
  155. # Add a cache version for changes that bazel won't otherwise detect,
  156. # like llvm version changes.
  157. CACHE_VERSION: 1
  158. run: |
  159. cat >user.bazelrc <<EOF
  160. # Enable remote cache for our CI but minimize downloads.
  161. build --remote_cache=https://storage.googleapis.com/carbon-builds-github-v${CACHE_VERSION}-${{ env.os_for_cache }}
  162. build --remote_download_minimal
  163. build ${{ env.remote_cache_upload }}
  164. # Set an artificially high jobs count. This flag controls the number
  165. # of concurrency Bazel itself uses, which is essential for actions
  166. # that are internally blocked on for example downloading results form
  167. # the cache above. Without setting this high, Bazel will pick a small
  168. # number based on the available host CPUs and the reality will be a
  169. # long chain of largely serialized download events with little or no
  170. # usage of the host machine. Fortunately, local actions are
  171. # *separately* gated on '--local_*_resources' that will avoid a large
  172. # jobs value overwhelming the host. There is a bug to make downloads
  173. # behave completely asynchronously and remove the need for this filed
  174. # back in 2018 but work seemed to not finish:
  175. # https://github.com/bazelbuild/bazel/issues/6394
  176. #
  177. # There is a new effort (yay!) but until then it seems worth using the
  178. # workaround of a high jobs value. The biggest downside (increased
  179. # heap usage) seems like it isn't currently a big loss for our builds.
  180. #
  181. # Higher values like 50 have led to CI failures with network errors
  182. # and IOExceptions, see
  183. # https://discord.com/channels/655572317891461132/707150492370862090/1151605725576056934
  184. build --jobs=32
  185. # General build options.
  186. build --verbose_failures
  187. test --test_output=errors
  188. EOF
  189. bazelisk info
  190. # Just for visibility, print space before and after the build.
  191. - name: Disk space before build
  192. if: steps.filter.outputs.ignore == 'false'
  193. run: df -h
  194. # Build and run all targets on branch pushes to ensure we always have a
  195. # clean tree. We don't expect this to be an interactive path and so don't
  196. # optimize the latency of this step.
  197. - name: Compute impacted pull request targets (for push)
  198. if:
  199. steps.filter.outputs.ignore == 'false' && github.event_name == 'push'
  200. env:
  201. TARGETS_FILE: ${{ runner.temp }}/targets
  202. run: |
  203. echo "//..." >$TARGETS_FILE
  204. # Compute the set of possible rules impacted by this change using
  205. # Bazel-based diffing. This lets PRs and the merge queue have a much more
  206. # efficient test CI action by avoiding even enumerating (and downloading)
  207. # all of the unaffected Bazel targets.
  208. - name: Compute impacted pull request targets
  209. if:
  210. steps.filter.outputs.ignore == 'false' && github.event_name != 'push'
  211. env:
  212. # Compute the base SHA from the different event structures.
  213. GIT_BASE_SHA:
  214. ${{ github.event_name == 'pull_request' &&
  215. github.event.pull_request.base.sha ||
  216. github.event.merge_group.base_sha }}
  217. TARGETS_FILE: ${{ runner.temp }}/targets
  218. run: |
  219. # First fetch the relevant base into the git repository.
  220. git fetch --depth=1 origin $GIT_BASE_SHA
  221. # Then use `target-determinator` as wrapped by our script.
  222. ./scripts/target_determinator.py $GIT_BASE_SHA >$TARGETS_FILE
  223. # Build and run just the tests impacted by the PR or merge group.
  224. - name: Test (${{ matrix.build_mode }})
  225. if: steps.filter.outputs.ignore == 'false'
  226. env:
  227. # 'libtool_check_unique failed to generate' workaround.
  228. # https://github.com/bazelbuild/bazel/issues/14113#issuecomment-999794586
  229. BAZEL_USE_CPP_ONLY_TOOLCHAIN: 1
  230. TARGETS_FILE: ${{ runner.temp }}/targets
  231. run: |
  232. # Bazel requires a test target to run the test command. There may be
  233. # no targets or there may only be non-test targets that we want to
  234. # build, so simply inject an explicit no-op test target.
  235. echo "//scripts:no_op_test" >> $TARGETS_FILE
  236. for i in {1..5}; do
  237. if (( $i == 4 )); then
  238. # Decrease the jobs sharply if we see repeated failures to try to
  239. # work around transient network errors even if it makes things
  240. # slower.
  241. echo "build --jobs=4" >>user.bazelrc
  242. fi
  243. bazel_exit=0
  244. bazelisk test -c ${{ matrix.build_mode }} \
  245. --target_pattern_file=$TARGETS_FILE || bazel_exit=$?
  246. # If we succeed, we're done.
  247. if (( $bazel_exit == 0 )); then
  248. break
  249. fi
  250. # Several error codes are reliably permanent, break immediately.
  251. # `1` -- The build failed.
  252. # `2` -- Command line or environment problem.
  253. # `3` -- Tests failed or timed out, we don't retry at this layer
  254. # on execution timeout.
  255. # `4` -- No tests found, which should be impossible here.
  256. # `8` -- Explicitly interrupted build.
  257. #
  258. # Note that `36` is documented as "likely permanent", but we retry
  259. # it as most of our transient failures actually produce that error
  260. # code.
  261. if (( $bazel_exit == 1 || $bazel_exit == 2 || $bazel_exit == 3 || \
  262. $bazel_exit == 4 || $bazel_exit == 8 || $bazel_exit == 8 ))
  263. then
  264. break
  265. fi
  266. echo "Retrying a failed build as it may be transient..."
  267. # Also sleep a bit to try to skip over transient machine load.
  268. sleep $i
  269. done
  270. # Propagate the Bazel exit code.
  271. exit $bazel_exit
  272. # See "Disk space before build".
  273. - name: Disk space after build
  274. if: steps.filter.outputs.ignore == 'false'
  275. run: df -h