backend.yml 14 KB

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