tests.yaml 13 KB

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