backend.yml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. name: backend
  2. on:
  3. push:
  4. branches:
  5. - master
  6. pull_request:
  7. # Cancel in progress workflows on pull_requests.
  8. # https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
  9. concurrency:
  10. group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  11. cancel-in-progress: true
  12. # hack for https://github.com/actions/cache/issues/810#issuecomment-1222550359
  13. env:
  14. SEGMENT_DOWNLOAD_TIMEOUT_MINS: 3
  15. jobs:
  16. files-changed:
  17. name: detect what files changed
  18. runs-on: ubuntu-22.04
  19. timeout-minutes: 3
  20. # Map a step output to a job output
  21. outputs:
  22. api_docs: ${{ steps.changes.outputs.api_docs }}
  23. backend: ${{ steps.changes.outputs.backend_all }}
  24. backend_dependencies: ${{ steps.changes.outputs.backend_dependencies }}
  25. backend_any_type: ${{ steps.changes.outputs.backend_any_type }}
  26. migration_lockfile: ${{ steps.changes.outputs.migration_lockfile }}
  27. steps:
  28. - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  29. - name: Check for backend file changes
  30. uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
  31. id: changes
  32. with:
  33. token: ${{ github.token }}
  34. filters: .github/file-filters.yml
  35. api-docs:
  36. if: needs.files-changed.outputs.api_docs == 'true'
  37. needs: files-changed
  38. name: api docs test
  39. runs-on: ubuntu-22.04
  40. steps:
  41. - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  42. - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
  43. id: setup-node
  44. with:
  45. node-version-file: '.volta.json'
  46. - name: Setup sentry python env
  47. uses: ./.github/actions/setup-sentry
  48. id: setup
  49. with:
  50. snuba: true
  51. - name: Run API docs tests
  52. # install ts-node for ts build scripts to execute properly without potentially installing
  53. # conflicting deps when running scripts locally
  54. # see: https://github.com/getsentry/sentry/pull/32328/files
  55. run: |
  56. yarn add ts-node && make test-api-docs
  57. backend-test:
  58. if: needs.files-changed.outputs.backend == 'true'
  59. needs: files-changed
  60. name: backend test
  61. runs-on: ubuntu-22.04
  62. timeout-minutes: 60
  63. permissions:
  64. contents: read
  65. id-token: write
  66. strategy:
  67. # This helps not having to run multiple jobs because one fails, thus, reducing resource usage
  68. # and reducing the risk that one of many runs would turn red again (read: intermittent tests)
  69. fail-fast: false
  70. matrix:
  71. # XXX: When updating this, make sure you also update MATRIX_INSTANCE_TOTAL.
  72. instance: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  73. pg-version: ['14']
  74. env:
  75. # XXX: `MATRIX_INSTANCE_TOTAL` must be hardcoded to the length of `strategy.matrix.instance`.
  76. # If this increases, make sure to also increase `flags.backend.after_n_builds` in `codecov.yml`.
  77. MATRIX_INSTANCE_TOTAL: 11
  78. steps:
  79. - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  80. - name: Setup sentry env
  81. uses: ./.github/actions/setup-sentry
  82. id: setup
  83. with:
  84. redis_cluster: true
  85. kafka: true
  86. snuba: true
  87. symbolicator: true
  88. # Right now, we run so few bigtable related tests that the
  89. # overhead of running bigtable in all backend tests
  90. # is way smaller than the time it would take to run in its own job.
  91. bigtable: true
  92. pg-version: ${{ matrix.pg-version }}
  93. - name: Run backend test (${{ steps.setup.outputs.matrix-instance-number }} of ${{ steps.setup.outputs.matrix-instance-total }})
  94. run: |
  95. make test-python-ci
  96. - name: Collect test data
  97. uses: ./.github/actions/collect-test-data
  98. if: ${{ !cancelled() }}
  99. with:
  100. artifact_path: .artifacts/pytest.json
  101. gcs_bucket: ${{ secrets.COLLECT_TEST_DATA_GCS_BUCKET }}
  102. gcp_project_id: ${{ secrets.COLLECT_TEST_DATA_GCP_PROJECT_ID }}
  103. workload_identity_provider: ${{ secrets.SENTRY_GCP_DEV_WORKLOAD_IDENTITY_POOL }}
  104. service_account_email: ${{ secrets.COLLECT_TEST_DATA_SERVICE_ACCOUNT_EMAIL }}
  105. matrix_instance_number: ${{ steps.setup.outputs.matrix-instance-number }}
  106. # Upload coverage data even if running the tests step fails since
  107. # it reduces large coverage fluctuations
  108. - name: Handle artifacts
  109. if: ${{ always() }}
  110. uses: ./.github/actions/artifacts
  111. with:
  112. token: ${{ secrets.CODECOV_TOKEN }}
  113. commit_sha: ${{ github.event.pull_request.head.sha }}
  114. backend-migration-tests:
  115. if: needs.files-changed.outputs.backend == 'true'
  116. needs: files-changed
  117. name: backend migration tests
  118. runs-on: ubuntu-22.04
  119. timeout-minutes: 30
  120. strategy:
  121. matrix:
  122. pg-version: ['14']
  123. steps:
  124. - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  125. - name: Setup sentry env
  126. uses: ./.github/actions/setup-sentry
  127. id: setup
  128. with:
  129. snuba: true
  130. pg-version: ${{ matrix.pg-version }}
  131. - name: run tests
  132. run: |
  133. PYTEST_ADDOPTS="$PYTEST_ADDOPTS -m migrations --migrations --reruns 0" make test-python-ci
  134. # Upload coverage data even if running the tests step fails since
  135. # it reduces large coverage fluctuations
  136. - name: Handle artifacts
  137. if: ${{ always() }}
  138. uses: ./.github/actions/artifacts
  139. with:
  140. token: ${{ secrets.CODECOV_TOKEN }}
  141. commit_sha: ${{ github.event.pull_request.head.sha }}
  142. cli:
  143. if: needs.files-changed.outputs.backend == 'true'
  144. needs: files-changed
  145. name: cli test
  146. runs-on: ubuntu-22.04
  147. timeout-minutes: 10
  148. strategy:
  149. matrix:
  150. pg-version: ['14']
  151. steps:
  152. - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  153. - name: Setup sentry env
  154. uses: ./.github/actions/setup-sentry
  155. id: setup
  156. with:
  157. pg-version: ${{ matrix.pg-version }}
  158. - name: Run test
  159. run: |
  160. make test-cli
  161. # Upload coverage data even if running the tests step fails since
  162. # it reduces large coverage fluctuations
  163. - name: Handle artifacts
  164. if: ${{ always() }}
  165. uses: ./.github/actions/artifacts
  166. with:
  167. token: ${{ secrets.CODECOV_TOKEN }}
  168. commit_sha: ${{ github.event.pull_request.head.sha }}
  169. requirements:
  170. if: needs.files-changed.outputs.backend_dependencies == 'true'
  171. needs: files-changed
  172. name: requirements check
  173. runs-on: ubuntu-22.04
  174. timeout-minutes: 3
  175. steps:
  176. - uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc # v3.0.0
  177. id: token
  178. continue-on-error: true
  179. with:
  180. app_id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
  181. private_key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
  182. - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  183. - uses: getsentry/action-setup-venv@a133e6fd5fa6abd3f590a1c106abda344f5df69f # v2.1.0
  184. with:
  185. python-version: 3.11.8
  186. cache-dependency-path: requirements-dev-frozen.txt
  187. install-cmd: python3 -m tools.hack_pip && pip install -q --constraint requirements-dev-frozen.txt pip-tools
  188. - name: check requirements
  189. run: |
  190. python -S -m tools.freeze_requirements
  191. if ! git diff --exit-code; then
  192. echo $'\n\nrun `make freeze-requirements` locally to update requirements'
  193. exit 1
  194. fi
  195. - name: apply any requirements changes
  196. if: steps.token.outcome == 'success' && github.ref != 'refs/heads/master' && always()
  197. uses: getsentry/action-github-commit@31f6706ca1a7b9ad6d22c1b07bf3a92eabb05632 # v2.0.0
  198. with:
  199. github-token: ${{ steps.token.outputs.token }}
  200. message: ':snowflake: re-freeze requirements'
  201. migration:
  202. if: needs.files-changed.outputs.migration_lockfile == 'true'
  203. needs: files-changed
  204. name: check migration
  205. runs-on: ubuntu-22.04
  206. strategy:
  207. matrix:
  208. pg-version: ['14']
  209. steps:
  210. - name: Checkout sentry
  211. uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  212. - name: Setup sentry env
  213. uses: ./.github/actions/setup-sentry
  214. id: setup
  215. with:
  216. pg-version: ${{ matrix.pg-version }}
  217. - name: Migration & lockfile checks
  218. env:
  219. SENTRY_LOG_LEVEL: ERROR
  220. PGPASSWORD: postgres
  221. run: |
  222. ./.github/workflows/scripts/migration-check.sh
  223. monolith-dbs:
  224. if: needs.files-changed.outputs.backend == 'true'
  225. needs: files-changed
  226. name: monolith-dbs test
  227. runs-on: ubuntu-22.04
  228. timeout-minutes: 20
  229. permissions:
  230. contents: read
  231. id-token: write
  232. steps:
  233. - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  234. - name: Setup sentry env
  235. uses: ./.github/actions/setup-sentry
  236. id: setup
  237. - name: Run test
  238. run: |
  239. make test-monolith-dbs
  240. - name: Collect test data
  241. uses: ./.github/actions/collect-test-data
  242. if: ${{ !cancelled() }}
  243. with:
  244. artifact_path: .artifacts/pytest.monolith-dbs.json
  245. gcs_bucket: ${{ secrets.COLLECT_TEST_DATA_GCS_BUCKET }}
  246. gcp_project_id: ${{ secrets.COLLECT_TEST_DATA_GCP_PROJECT_ID }}
  247. workload_identity_provider: ${{ secrets.SENTRY_GCP_DEV_WORKLOAD_IDENTITY_POOL }}
  248. service_account_email: ${{ secrets.COLLECT_TEST_DATA_SERVICE_ACCOUNT_EMAIL }}
  249. # Upload coverage data even if running the tests step fails since
  250. # it reduces large coverage fluctuations
  251. - name: Handle artifacts
  252. if: ${{ always() }}
  253. uses: ./.github/actions/artifacts
  254. with:
  255. token: ${{ secrets.CODECOV_TOKEN }}
  256. commit_sha: ${{ github.event.pull_request.head.sha }}
  257. typing:
  258. if: needs.files-changed.outputs.backend == 'true'
  259. needs: files-changed
  260. name: backend typing
  261. runs-on: ubuntu-22.04
  262. timeout-minutes: 20
  263. steps:
  264. - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  265. - uses: getsentry/action-setup-venv@a133e6fd5fa6abd3f590a1c106abda344f5df69f # v2.1.0
  266. with:
  267. python-version: 3.11.8
  268. cache-dependency-path: requirements-dev-frozen.txt
  269. install-cmd: python3 -m tools.hack_pip && pip install -r requirements-dev-frozen.txt
  270. - name: setup sentry (lite)
  271. run: |
  272. python3 -m tools.fast_editable --path .
  273. sentry init
  274. - run: PYTHONWARNINGS=error::RuntimeWarning mypy
  275. id: run
  276. - uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc # v3.0.0
  277. id: token
  278. continue-on-error: true
  279. with:
  280. app_id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
  281. private_key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
  282. # only if `mypy` succeeds should we try and trim the blocklist
  283. - run: python3 -m tools.mypy_helpers.make_module_ignores
  284. id: regen-blocklist
  285. - run: git diff --exit-code
  286. - run: |
  287. # mypy does not have granular codes so don't allow specific messages to regress
  288. set -euo pipefail
  289. ! grep "'Settings' object has no attribute" .artifacts/mypy-all
  290. ! grep 'Cannot override class variable' .artifacts/mypy-all
  291. ! grep 'Exception type must be derived from BaseException' .artifacts/mypy-all
  292. ! grep 'Incompatible default for argument' .artifacts/mypy-all
  293. ! grep 'Incompatible return value type (got "HttpResponseBase"' .artifacts/mypy-all
  294. ! grep 'Incompatible types in "yield"' .artifacts/mypy-all
  295. ! grep 'Module "sentry.*has no attribute' .artifacts/mypy-all
  296. ! grep 'Unpacking a string is disallowed' .artifacts/mypy-all
  297. ! grep 'base class .* defined the type as.*Permission' .artifacts/mypy-all
  298. ! grep 'does not explicitly export attribute' .artifacts/mypy-all
  299. - name: apply blocklist changes
  300. if: |
  301. steps.token.outcome == 'success' &&
  302. steps.run.outcome == 'success' &&
  303. steps.regen-blocklist.outcome == 'success' &&
  304. github.ref != 'refs/heads/master' &&
  305. always()
  306. uses: getsentry/action-github-commit@31f6706ca1a7b9ad6d22c1b07bf3a92eabb05632 # v2.0.0
  307. with:
  308. github-token: ${{ steps.token.outputs.token }}
  309. message: ':knife: regenerate mypy module blocklist'
  310. # This check runs once all dependent jobs have passed
  311. # It symbolizes that all required Backend checks have succesfully passed (Or skipped)
  312. # This step is the only required backend check
  313. backend-required-check:
  314. needs:
  315. [
  316. api-docs,
  317. backend-test,
  318. backend-migration-tests,
  319. cli,
  320. files-changed,
  321. requirements,
  322. migration,
  323. monolith-dbs,
  324. typing,
  325. ]
  326. name: Backend
  327. # This is necessary since a failed/skipped dependent job would cause this job to be skipped
  328. if: always()
  329. runs-on: ubuntu-22.04
  330. steps:
  331. # If any jobs we depend on fail, we will fail since this is a required check
  332. # NOTE: A timeout is considered a failure
  333. - name: Check for failures
  334. if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
  335. run: |
  336. echo "One of the dependent jobs have failed. You may need to re-run it." && exit 1