Repository navigation
ref(typing): Type request.user with service accounts everywhere in sentry #322263
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: backend | |
| on: | |
| push: | |
| branches: | |
| - master | |
| pull_request: | |
| types: [opened, synchronize, reopened, labeled] | |
| workflow_dispatch: | |
| # Cancel in progress workflows on pull_requests. | |
| # https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} | |
| cancel-in-progress: true | |
| # hack for https://github.com/actions/cache/issues/810#issuecomment-1222550359 | |
| env: | |
| SEGMENT_DOWNLOAD_TIMEOUT_MINS: 3 | |
| SNUBA_NO_WORKERS: 1 | |
| SENTRY_SKIP_SELENIUM_PLUGIN: '1' | |
| jobs: | |
| files-changed: | |
| name: detect what files changed | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 3 | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| # Map a step output to a job output | |
| outputs: | |
| api_docs: ${{ steps.changes.outputs.api_docs }} | |
| backend: ${{ steps.changes.outputs.backend_all_without_acceptance }} | |
| backend_dependencies: ${{ steps.changes.outputs.backend_dependencies }} | |
| backend_api_urls: ${{ steps.changes.outputs.backend_api_urls }} | |
| backend_any_type: ${{ steps.changes.outputs.backend_any_type }} | |
| migration_lockfile: ${{ steps.changes.outputs.migration_lockfile }} | |
| migration_tests: ${{ steps.changes.outputs.migration_tests }} | |
| # When true, skip selective testing and run the full backend suite | |
| skip_selective_testing: "${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Trigger: Override Selective Testing') }}" | |
| steps: | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Check for backend file changes | |
| uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2 | |
| id: changes | |
| with: | |
| token: ${{ github.token }} | |
| filters: .github/file-filters.yml | |
| api-docs: | |
| if: needs.files-changed.outputs.api_docs == 'true' | |
| needs: files-changed | |
| name: api docs test | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| env: | |
| DEVSERVICES_TIMEOUT: '10' | |
| steps: | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Setup sentry python env | |
| uses: ./.github/actions/setup-sentry | |
| id: setup | |
| with: | |
| skip-devservices: 'true' | |
| # Only the pytest step needs devservices (~60s to pull and get healthy in | |
| # default mode), so bring them up while node is set up and the specs build. | |
| - name: Start devservices in the background | |
| env: | |
| ENABLE_AUTORUN_MIGRATION_SEARCH_ISSUES: '1' | |
| DEVSERVICES_WORKDIR: . | |
| DEVSERVICES_LOCKFILE: uv.lock | |
| DEVSERVICES_MODE: default | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| sentry init | |
| python3 .github/actions/setup-sentry/devservices-bg.py start | |
| - uses: ./.github/actions/setup-node-pnpm | |
| - name: Build and validate API docs | |
| run: make validate-api-docs | |
| - name: Wait for devservices | |
| run: python3 .github/actions/setup-sentry/devservices-bg.py wait | |
| - name: Run API docs tests | |
| run: python3 -b -m pytest tests/apidocs --reuse-db | |
| - name: Inspect failure | |
| if: failure() | |
| run: | | |
| if command -v devservices; then | |
| devservices logs | |
| fi | |
| # Selective testing - only on PRs, determine which tests to run based on coverage data. | |
| # This job is skipped on push-to-master where the full suite runs instead. | |
| select-tests: | |
| if: >- | |
| needs.files-changed.outputs.backend == 'true' && | |
| needs.files-changed.outputs.skip_selective_testing != 'true' && | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.head.repo.full_name == github.repository | |
| needs: files-changed | |
| name: select tests | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| id-token: write | |
| outputs: | |
| has-selected-tests: ${{ steps.compute-tests.outputs.has-selected-tests }} | |
| test-count: ${{ steps.compute-tests.outputs.test-count }} | |
| test-item-count: ${{ steps.compute-tests.outputs.test-item-count }} | |
| steps: | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Get changed files | |
| id: changed | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files \ | |
| --paginate | python3 .github/workflows/scripts/parse-pr-files.py >> "$GITHUB_OUTPUT" | |
| - name: Setup Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 | |
| with: | |
| python-version-file: '.python-version' | |
| - name: Authenticate to Google Cloud | |
| uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.3 | |
| with: | |
| project_id: sentry-dev-tooling | |
| workload_identity_provider: ${{ secrets.SENTRY_GCP_DEV_WORKLOAD_IDENTITY_POOL }} | |
| service_account: ${{ secrets.COLLECT_TEST_DATA_SERVICE_ACCOUNT_EMAIL }} | |
| - name: Download coverage database | |
| id: download-coverage | |
| run: | | |
| mkdir -p .artifacts/coverage | |
| GCS_PATH="gs://getsentry-coverage-data/latest/.coverage.combined" | |
| echo "Fetching coverage DB from: $GCS_PATH" | |
| gcloud storage ls -l "$GCS_PATH" 2>/dev/null || true | |
| if ! gcloud storage cp "$GCS_PATH" \ | |
| .artifacts/coverage/.coverage.combined 2>/dev/null; then | |
| echo "Warning: Failed to download coverage from GCS, will run full test suite" | |
| echo "coverage-file=" >> "$GITHUB_OUTPUT" | |
| else | |
| ls -lh .artifacts/coverage/.coverage.combined | |
| echo "coverage-file=.artifacts/coverage/.coverage.combined" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Compute selected tests | |
| id: compute-tests | |
| if: steps.download-coverage.outputs.coverage-file != '' | |
| env: | |
| COVERAGE_DB: ${{ steps.download-coverage.outputs.coverage-file }} | |
| CHANGED_FILES: ${{ steps.changed.outputs.files }} | |
| PREVIOUS_FILENAMES: ${{ steps.changed.outputs.previous-filenames }} | |
| run: | | |
| python3 .github/workflows/scripts/compute-sentry-selected-tests.py \ | |
| --coverage-db "$COVERAGE_DB" \ | |
| --changed-files "$CHANGED_FILES" \ | |
| --previous-filenames "$PREVIOUS_FILENAMES" \ | |
| --output .artifacts/selected-tests.txt \ | |
| --github-output | |
| - name: Upload selected tests artifact | |
| if: steps.compute-tests.outputs.has-selected-tests == 'true' | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: selected-tests-${{ github.run_id }} | |
| path: | | |
| .artifacts/selected-tests.txt | |
| .artifacts/selected-tests.counts.json | |
| retention-days: 1 | |
| calculate-shards: | |
| # Use always() so this job runs even when select-tests is skipped (master) | |
| if: >- | |
| always() && | |
| !cancelled() && | |
| needs.files-changed.outputs.backend == 'true' | |
| needs: [files-changed, select-tests] | |
| name: calculate test shards | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 5 | |
| outputs: | |
| shard-count: ${{ steps.static-shards.outputs.shard-count || steps.calculate-shards.outputs.shard-count }} | |
| shard-indices: ${{ steps.static-shards.outputs.shard-indices || steps.calculate-shards.outputs.shard-indices }} | |
| steps: | |
| - name: Use default shards (no selective testing) | |
| id: static-shards | |
| if: needs.select-tests.outputs.has-selected-tests != 'true' | |
| # Keep in sync with MAX_SHARDS in .github/workflows/scripts/calculate-backend-test-shards.py | |
| run: | | |
| echo "shard-count=30" >> "$GITHUB_OUTPUT" | |
| echo "shard-indices=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29]" >> "$GITHUB_OUTPUT" | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| if: needs.select-tests.outputs.has-selected-tests == 'true' | |
| - name: Download selected tests artifact | |
| if: needs.select-tests.outputs.has-selected-tests == 'true' | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: selected-tests-${{ github.run_id }} | |
| path: .artifacts/ | |
| - name: Calculate test shards | |
| id: calculate-shards | |
| if: needs.select-tests.outputs.has-selected-tests == 'true' | |
| env: | |
| SELECTED_TESTS_FILE: .artifacts/selected-tests.txt | |
| SELECTED_TEST_ITEM_COUNT: ${{ needs.select-tests.outputs.test-item-count }} | |
| run: | | |
| python3 .github/workflows/scripts/calculate-backend-test-shards.py | |
| backend-test: | |
| # Use always() so this job runs even when select-tests is skipped (master) | |
| if: >- | |
| always() && | |
| !cancelled() && | |
| needs.files-changed.outputs.backend == 'true' && needs.calculate-shards.outputs.shard-count != '0' | |
| needs: [files-changed, select-tests, calculate-shards] | |
| name: backend test | |
| # Arm runners on pull requests only; master stays on amd64 for now. | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} | |
| timeout-minutes: 60 | |
| permissions: | |
| contents: read | |
| id-token: write | |
| actions: read # used for DIM metadata and job URL lookup | |
| pull-requests: write # used to post failure comments | |
| strategy: | |
| # This helps not having to run multiple jobs because one fails, thus, reducing resource usage | |
| # and reducing the risk that one of many runs would turn red again (read: intermittent tests) | |
| fail-fast: false | |
| matrix: | |
| # Dynamic matrix from calculate-shards | |
| instance: ${{ fromJSON(needs.calculate-shards.outputs.shard-indices) }} | |
| env: | |
| # Dynamic total from calculate-shards | |
| MATRIX_INSTANCE_TOTAL: ${{ needs.calculate-shards.outputs.shard-count }} | |
| TEST_GROUP_STRATEGY: roundrobin | |
| PYTHONHASHSEED: '0' | |
| XDIST_PER_WORKER_SNUBA: '1' | |
| XDIST_WORKERS: '4' | |
| steps: | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Start resource monitor | |
| run: python3 .github/workflows/scripts/monitor-resources.py --start | |
| # Overlaps the per-worker ClickHouse bootstrap with devservices up. | |
| - name: Start per-worker Snuba bootstrap in the background | |
| if: env.XDIST_PER_WORKER_SNUBA == '1' | |
| env: | |
| DEVSERVICES_STATUS_FILE: ${{ runner.temp }}/devservices-ci/up.exit | |
| run: python3 .github/workflows/scripts/bootstrap-snuba.py --start | |
| - name: Setup sentry env | |
| uses: ./.github/actions/setup-sentry | |
| id: setup | |
| with: | |
| mode: backend-ci | |
| early-devservices: 'true' | |
| # Restart postgres with a Unix socket mount. Django's postgres driver uses TCP by | |
| # default, and each ORM call is multiple round-trips — switching to a Unix socket | |
| # eliminates TCP loopback overhead and saves ~36s per shard. | |
| - name: Reconnect postgres via Unix socket | |
| run: | | |
| mkdir -p /tmp/pg-sock | |
| PG_IMAGE=$(docker inspect postgres-postgres-1 --format '{{.Config.Image}}') | |
| docker stop postgres-postgres-1 && docker rm postgres-postgres-1 | |
| docker run -d --name postgres-postgres-1 \ | |
| --network devservices --label orchestrator=devservices \ | |
| -p 127.0.0.1:5432:5432 \ | |
| -v postgres-data:/var/lib/postgresql/data \ | |
| -v /tmp/pg-sock:/var/run/postgresql \ | |
| -e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_DB=sentry \ | |
| "$PG_IMAGE" postgres \ | |
| -c wal_level=logical -c max_replication_slots=1 -c max_wal_senders=1 \ | |
| -c fsync=off -c synchronous_commit=off -c full_page_writes=off | |
| # docker run -d returns 0 even if postgres dies right after — bound the | |
| # wait so a crashing postgres surfaces fast instead of hitting the job | |
| # timeout silently. | |
| for _ in $(seq 1 120); do | |
| [ -S "/tmp/pg-sock/.s.PGSQL.5432" ] && break | |
| sleep 0.5 | |
| done | |
| if [ ! -S "/tmp/pg-sock/.s.PGSQL.5432" ]; then | |
| echo "::error::postgres unix socket did not appear after 60s" | |
| docker logs postgres-postgres-1 2>&1 | tail -30 || true | |
| exit 1 | |
| fi | |
| echo "SENTRY_DB_SOCKET=/tmp/pg-sock" >> "$GITHUB_ENV" | |
| - name: Download odiff binary | |
| run: | | |
| case "$(uname -m)" in | |
| aarch64) odiff=odiff-linux-arm64 ;; | |
| *) odiff=odiff-linux-x64 ;; | |
| esac | |
| curl -sL https://registry.npmjs.org/odiff-bin/-/odiff-bin-4.3.8.tgz \ | |
| | tar -xz --strip-components=2 "package/raw_binaries/$odiff" | |
| sudo install -m 755 "$odiff" /usr/local/bin/odiff | |
| rm "$odiff" | |
| - name: Bootstrap per-worker Snuba instances | |
| if: env.XDIST_PER_WORKER_SNUBA == '1' | |
| run: python3 .github/workflows/scripts/bootstrap-snuba.py --wait | |
| # sentry-shared-kafka's healthcheck runs `kafka-topics --list` every 5s, which starts | |
| # a JVM each time: about 0.25 of the runner's 4 cores during tests. Everything that | |
| # waits on Kafka's health is done by now, so answer that exact command with a TCP | |
| # probe instead (kafka-topics-healthcheck.sh). | |
| - name: Make the Kafka healthcheck cheap | |
| continue-on-error: true | |
| run: | | |
| real=$(docker exec kafka-kafka-1 sh -c 'command -v kafka-topics') | |
| docker exec -u 0 kafka-kafka-1 mv "$real" "$real.real" | |
| docker cp .github/workflows/scripts/kafka-topics-healthcheck.sh "kafka-kafka-1:$real" | |
| time docker exec kafka-kafka-1 kafka-topics --bootstrap-server 127.0.0.1:9092 --list | |
| # To profile pytest and its xdist workers (adds ~0.4 cores of overhead), uncomment | |
| # this step, run the test command through | |
| # .github/workflows/scripts/profile-backend-tests.sh, and add .artifacts/py-spy.txt | |
| # to the resource usage upload. | |
| # - name: Install py-spy | |
| # run: | | |
| # uv tool install py-spy==0.4.1 | |
| # echo "PY_SPY=$(uv tool dir --bin)/py-spy" >> "$GITHUB_ENV" | |
| # echo "PY_SPY_OUTPUT=$PWD/.artifacts/py-spy.txt" >> "$GITHUB_ENV" | |
| # # py-spy attaches to the running pytest controller, which isn't its child. | |
| # sudo sysctl -w kernel.yama.ptrace_scope=0 | |
| - name: Download selected tests artifact | |
| if: needs.select-tests.outputs.has-selected-tests == 'true' | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: selected-tests-${{ github.run_id }} | |
| path: .artifacts/ | |
| - name: Run backend test (${{ steps.setup.outputs.matrix-instance-number }} of ${{ steps.setup.outputs.matrix-instance-total }}) | |
| env: | |
| SELECTED_TESTS_FILE: ${{ needs.select-tests.outputs.has-selected-tests == 'true' && '.artifacts/selected-tests.txt' || '' }} | |
| run: | | |
| if [ -n "${XDIST_WORKERS}" ]; then | |
| export PYTEST_ADDOPTS="$PYTEST_ADDOPTS -n ${XDIST_WORKERS} --dist=loadfile" | |
| timeout -k 30 1200 make test-python-ci || { | |
| rc=$? | |
| if [ "$rc" -eq 124 ]; then | |
| echo "::error::Test run timed out after 20 minutes (possible xdist hang)" | |
| fi | |
| exit "$rc" | |
| } | |
| else | |
| make test-python-ci | |
| fi | |
| - name: Stop resource monitor | |
| if: always() | |
| continue-on-error: true | |
| run: python3 .github/workflows/scripts/monitor-resources.py --stop | |
| # pytest-selected.py balances full-run shards with these. See | |
| # .github/workflows/scripts/backend-test-durations.py to refresh them. | |
| - name: Extract test durations | |
| id: durations | |
| if: ${{ !cancelled() && needs.select-tests.outputs.has-selected-tests != 'true' && hashFiles('.artifacts/pytest.json') != '' }} | |
| continue-on-error: true | |
| run: | | |
| python3 .github/workflows/scripts/backend-test-durations.py extract \ | |
| .artifacts/pytest.json .artifacts/test-durations.json | |
| - name: Upload test durations | |
| if: ${{ !cancelled() && steps.durations.outcome == 'success' }} | |
| continue-on-error: true | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: backend-test-durations-${{ github.run_id }}-${{ matrix.instance }} | |
| path: .artifacts/test-durations.json | |
| retention-days: 30 | |
| - name: Upload resource usage | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: backend-resource-usage-${{ github.run_id }}-${{ matrix.instance }} | |
| path: | | |
| .artifacts/resource-usage.jsonl | |
| .artifacts/resource-usage-summary.json | |
| retention-days: 30 | |
| - name: Report failures | |
| if: ${{ !cancelled() && github.event_name == 'pull_request' }} | |
| continue-on-error: true | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| PYTEST_JSON_PATH: ${{ github.workspace }}/.artifacts/pytest.json | |
| PYTEST_ARTIFACT_DIR: pytest-results-backend-${{ github.run_id }}-${{ matrix.instance }} | |
| with: | |
| script: | | |
| const { reportShard } = await import(`${process.env.GITHUB_WORKSPACE}/.github/workflows/scripts/report-backend-test-failures.js`); | |
| await reportShard({ github, context, core }); | |
| - name: Inspect failure | |
| if: failure() | |
| run: | | |
| if command -v devservices; then | |
| devservices logs | |
| fi | |
| if [ "${XDIST_PER_WORKER_SNUBA}" = "1" ]; then | |
| for i in $(seq 0 $(( ${XDIST_WORKERS} - 1 ))); do | |
| echo "--- snuba-gw${i} logs ---" | |
| docker logs "snuba-gw${i}" 2>&1 | tail -30 || true | |
| done | |
| fi | |
| backend-migration-tests: | |
| if: >- | |
| needs.files-changed.outputs.backend == 'true' && | |
| ( | |
| github.event_name != 'pull_request' || | |
| needs.files-changed.outputs.migration_tests == 'true' || | |
| needs.files-changed.outputs.skip_selective_testing == 'true' | |
| ) | |
| needs: files-changed | |
| name: backend migration tests | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| actions: read | |
| pull-requests: write | |
| env: | |
| MIGRATION_DB_DIR: .artifacts/migration-test-db | |
| SAVE_MIGRATION_DB: ${{ github.ref == 'refs/heads/master' }} | |
| steps: | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Setup sentry env | |
| uses: ./.github/actions/setup-sentry | |
| id: setup | |
| with: | |
| mode: migrations | |
| early-devservices: 'true' | |
| # Only master saves, so PRs restore the newest master dump. | |
| - name: Restore migrated test databases | |
| id: db-cache | |
| uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 | |
| with: | |
| path: ${{ env.MIGRATION_DB_DIR }} | |
| key: migration-test-db-v1-${{ hashFiles('src/**/migrations/*.py', 'src/sentry/new_migrations/**/*.py') }} | |
| restore-keys: migration-test-db-v1- | |
| - name: Load migrated test databases | |
| run: .github/workflows/scripts/migration-test-db.py restore "$MIGRATION_DB_DIR" | |
| - name: run tests | |
| run: | | |
| make test-migrations-ci | |
| - name: Dump migrated test databases | |
| if: env.SAVE_MIGRATION_DB == 'true' && steps.db-cache.outputs.cache-hit != 'true' | |
| run: .github/workflows/scripts/migration-test-db.py save "$MIGRATION_DB_DIR" | |
| - name: Save migrated test databases | |
| if: env.SAVE_MIGRATION_DB == 'true' && steps.db-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 | |
| with: | |
| path: ${{ env.MIGRATION_DB_DIR }} | |
| key: ${{ steps.db-cache.outputs.cache-primary-key }} | |
| - name: Report failures | |
| if: ${{ !cancelled() && github.event_name == 'pull_request' }} | |
| continue-on-error: true | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| PYTEST_JSON_PATH: ${{ github.workspace }}/.artifacts/pytest.json | |
| PYTEST_ARTIFACT_DIR: pytest-results-migration-${{ github.run_id }} | |
| with: | |
| script: | | |
| const { reportShard } = await import(`${process.env.GITHUB_WORKSPACE}/.github/workflows/scripts/report-backend-test-failures.js`); | |
| await reportShard({ github, context, core }); | |
| - name: Inspect failure | |
| if: failure() | |
| run: | | |
| if command -v devservices; then | |
| devservices logs | |
| fi | |
| cli: | |
| if: needs.files-changed.outputs.backend == 'true' | |
| needs: files-changed | |
| name: cli test | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Setup sentry env | |
| uses: ./.github/actions/setup-sentry | |
| id: setup | |
| with: | |
| mode: migrations | |
| - name: Run test | |
| env: | |
| # This short circuits the create_default_projects post_upgrade hook | |
| # which spawns taskworkers which will spin for 5 minutes on kafka | |
| # not being up. We don't need the default project here as this is not local dev. | |
| SENTRY_NO_CREATE_DEFAULT_PROJECT: 1 | |
| run: make test-cli | |
| - name: Inspect failure | |
| if: failure() | |
| run: | | |
| if command -v devservices; then | |
| devservices logs | |
| fi | |
| requirements: | |
| if: needs.files-changed.outputs.backend_dependencies == 'true' | |
| needs: files-changed | |
| name: requirements check | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 3 | |
| steps: | |
| - uses: getsentry/action-github-app-token@5c1e90706fe007857338ac1bfbd7a4177db2f789 # v4.0.0 | |
| id: token | |
| continue-on-error: true | |
| with: | |
| app_id: ${{ vars.SENTRY_INTERNAL_APP_ID }} | |
| private_key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }} | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - uses: astral-sh/setup-uv@884ad927a57e558e7a70b92f2bccf9198a4be546 # v6 | |
| with: | |
| version: '0.13.5' | |
| - uses: getsentry/action-setup-venv@5a80476d175edf56cb205b08bc58986fa99d1725 # v3.2.0 | |
| with: | |
| cache-dependency-path: uv.lock | |
| install-cmd: echo | |
| - name: check requirements | |
| run: | | |
| make freeze-requirements | |
| if ! git diff --exit-code; then | |
| echo $'\n\nrun `make freeze-requirements` locally to update requirements' | |
| exit 1 | |
| fi | |
| - name: apply any requirements changes | |
| if: steps.token.outcome == 'success' && github.ref != 'refs/heads/master' && always() | |
| uses: getsentry/action-github-commit@31f6706ca1a7b9ad6d22c1b07bf3a92eabb05632 # v2.0.0 | |
| with: | |
| github-token: ${{ steps.token.outputs.token }} | |
| message: ':snowflake: re-freeze requirements' | |
| api-url-typescript: | |
| if: needs.files-changed.outputs.backend_api_urls == 'true' | |
| needs: files-changed | |
| name: api url typescript generation | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: getsentry/action-github-app-token@5c1e90706fe007857338ac1bfbd7a4177db2f789 # v4.0.0 | |
| id: token | |
| with: | |
| app_id: ${{ vars.SENTRY_INTERNAL_APP_ID }} | |
| private_key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }} | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Setup sentry env | |
| uses: ./.github/actions/setup-sentry | |
| with: | |
| mode: backend-ci | |
| - name: Sync API Urls to TypeScript | |
| run: | | |
| python3 -m tools.api_urls_to_typescript | |
| - name: Apply any file changes | |
| if: github.ref != 'refs/heads/master' && always() | |
| uses: getsentry/action-github-commit@31f6706ca1a7b9ad6d22c1b07bf3a92eabb05632 # v2.0.0 | |
| with: | |
| github-token: ${{ steps.token.outputs.token }} | |
| message: ':hammer_and_wrench: Sync API Urls to TypeScript' | |
| migration: | |
| if: needs.files-changed.outputs.migration_lockfile == 'true' | |
| needs: files-changed | |
| name: check migration | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| steps: | |
| - name: Checkout sentry | |
| uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Setup sentry env | |
| uses: ./.github/actions/setup-sentry | |
| id: setup | |
| with: | |
| mode: migrations | |
| - name: Migration & lockfile checks | |
| env: | |
| SENTRY_LOG_LEVEL: ERROR | |
| PGPASSWORD: postgres | |
| run: | | |
| ./.github/workflows/scripts/migration-check.sh | |
| - name: Inspect failure | |
| if: failure() | |
| run: | | |
| if command -v devservices; then | |
| devservices logs | |
| fi | |
| monolith-dbs: | |
| if: needs.files-changed.outputs.backend == 'true' | |
| needs: files-changed | |
| name: monolith-dbs test | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 20 | |
| permissions: | |
| contents: read | |
| id-token: write | |
| actions: read | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - name: Setup sentry env | |
| uses: ./.github/actions/setup-sentry | |
| id: setup | |
| with: | |
| mode: migrations | |
| early-devservices: 'true' | |
| # The backup tests are mostly waiting on postgres, so 4 workers cut ~4m to ~1.5m. | |
| - name: Run test | |
| run: | | |
| export PYTEST_ADDOPTS="$PYTEST_ADDOPTS -n 4 --dist=load" | |
| make test-monolith-dbs | |
| - name: Report failures | |
| if: ${{ !cancelled() && github.event_name == 'pull_request' }} | |
| continue-on-error: true | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| PYTEST_JSON_PATH: ${{ github.workspace }}/.artifacts/pytest.monolith-dbs.json | |
| PYTEST_ARTIFACT_DIR: pytest-results-monolith-dbs-${{ github.run_id }} | |
| with: | |
| script: | | |
| const { reportShard } = await import(`${process.env.GITHUB_WORKSPACE}/.github/workflows/scripts/report-backend-test-failures.js`); | |
| await reportShard({ github, context, core }); | |
| - name: Inspect failure | |
| if: failure() | |
| run: | | |
| if command -v devservices; then | |
| devservices logs | |
| fi | |
| typing: | |
| if: needs.files-changed.outputs.backend == 'true' | |
| needs: files-changed | |
| name: backend typing | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | |
| - uses: astral-sh/setup-uv@884ad927a57e558e7a70b92f2bccf9198a4be546 # v6 | |
| with: | |
| version: '0.13.5' | |
| # we just cache the venv-dir directly in action-setup-venv | |
| enable-cache: false | |
| - uses: getsentry/action-setup-venv@5a80476d175edf56cb205b08bc58986fa99d1725 # v3.2.0 | |
| with: | |
| cache-dependency-path: uv.lock | |
| install-cmd: uv sync --frozen --active | |
| - name: setup sentry (lite) | |
| run: | | |
| python3 -m tools.fast_editable --path . | |
| sentry init | |
| - run: PYTHONWARNINGS=error::RuntimeWarning mypy | |
| id: run | |
| - uses: getsentry/action-github-app-token@5c1e90706fe007857338ac1bfbd7a4177db2f789 # v4.0.0 | |
| id: token | |
| continue-on-error: true | |
| with: | |
| app_id: ${{ vars.SENTRY_INTERNAL_APP_ID }} | |
| private_key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }} | |
| # only if `mypy` succeeds should we try and trim the blocklist | |
| - run: python3 -m tools.mypy_helpers.make_module_ignores | |
| id: regen-blocklist | |
| - run: git diff --exit-code | |
| - run: | | |
| # mypy does not have granular codes so don't allow specific messages to regress | |
| set -euo pipefail | |
| ! grep "'Settings' object has no attribute" .artifacts/mypy-all | |
| ! grep 'Argument .* of "dispatch" is incompatible with' .artifacts/mypy-all | |
| ! grep 'Cannot override class variable' .artifacts/mypy-all | |
| ! grep 'Exception type must be derived from BaseException' .artifacts/mypy-all | |
| ! grep 'Incompatible default for argument' .artifacts/mypy-all | |
| ! grep 'Incompatible return value type (got "HttpResponseBase"' .artifacts/mypy-all | |
| ! grep 'Incompatible types in "yield"' .artifacts/mypy-all | |
| ! grep 'Module "sentry.*has no attribute' .artifacts/mypy-all | |
| ! grep 'No return value expected' .artifacts/mypy-all | |
| ! grep 'Return value expected' .artifacts/mypy-all | |
| ! grep 'Unpacking a string is disallowed' .artifacts/mypy-all | |
| ! grep 'base class .* defined the type as.*Permission' .artifacts/mypy-all | |
| ! grep 'does not explicitly export attribute' .artifacts/mypy-all | |
| ! grep 'gets multiple values for' .artifacts/mypy-all | |
| - name: apply blocklist changes | |
| if: | | |
| steps.token.outcome == 'success' && | |
| steps.run.outcome == 'success' && | |
| steps.regen-blocklist.outcome == 'success' && | |
| github.ref != 'refs/heads/master' && | |
| always() | |
| uses: getsentry/action-github-commit@31f6706ca1a7b9ad6d22c1b07bf3a92eabb05632 # v2.0.0 | |
| with: | |
| github-token: ${{ steps.token.outputs.token }} | |
| message: ':knife: regenerate mypy module blocklist' | |
| # This check runs once all dependent jobs have passed | |
| # It symbolizes that all required Backend checks have succesfully passed (Or skipped) | |
| # This step is the only required backend check | |
| backend-required-check: | |
| needs: | |
| [ | |
| api-docs, | |
| backend-test, | |
| backend-migration-tests, | |
| calculate-shards, | |
| cli, | |
| files-changed, | |
| requirements, | |
| migration, | |
| monolith-dbs, | |
| typing, | |
| ] | |
| name: Backend | |
| # This is necessary since a failed/skipped dependent job would cause this job to be skipped | |
| if: always() | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-26.04-arm' || 'ubuntu-24.04' }} | |
| steps: | |
| # If any jobs we depend on fail, we will fail since this is a required check | |
| # NOTE: A timeout is considered a failure | |
| - name: Check for failures | |
| if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') | |
| run: | | |
| echo "One of the dependent jobs have failed. You may need to re-run it." && exit 1 |