backend.yml 14 KB

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