diff --git a/.github/actions/setup-devservices/action.yml b/.github/actions/setup-devservices/action.yml new file mode 100644 index 00000000000000..061ff20d9b72e4 --- /dev/null +++ b/.github/actions/setup-devservices/action.yml @@ -0,0 +1,38 @@ +name: 'Early Devservices' +description: 'Starts devservices in the background so image pulls overlap with venv setup' +inputs: + mode: + description: 'devservices mode (must match the mode passed to setup-sentry)' + required: true + timeout-minutes: + description: 'Maximum minutes for devservices up' + required: false + default: '10' + +runs: + using: 'composite' + steps: + - uses: astral-sh/setup-uv@884ad927a57e558e7a70b92f2bccf9198a4be546 # v6 + with: + version: '0.9.28' + enable-cache: false + + - name: Start devservices in background + shell: bash --noprofile --norc -euo pipefail {0} + run: | + DS_VERSION=$(python3 -c " + import tomllib + with open('uv.lock', 'rb') as f: + lock = tomllib.load(f) + for pkg in lock['package']: + if pkg['name'] == 'devservices': + print(pkg['version']) + break + ") + echo "Installing devservices==${DS_VERSION}" + uv venv /tmp/ds-venv --python python3 -q + uv pip install --python /tmp/ds-venv/bin/python -q \ + --index-url https://pypi.devinfra.sentry.io/simple \ + "devservices==${DS_VERSION}" + (set +e; timeout ${{ inputs.timeout-minutes }}m /tmp/ds-venv/bin/devservices up --mode ${{ inputs.mode }}; echo $? > /tmp/ds-exit) \ + > /tmp/ds.log 2>&1 & diff --git a/.github/actions/setup-devservices/bootstrap-snuba.py b/.github/actions/setup-devservices/bootstrap-snuba.py new file mode 100755 index 00000000000000..c0f4ea8a7e7bc2 --- /dev/null +++ b/.github/actions/setup-devservices/bootstrap-snuba.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +"""Bootstrap per-worker Snuba instances for CI. + +Overlaps the expensive ClickHouse table setup with the devservices +health-check wait. + +Phase 1 (early): As soon as ClickHouse is accepting queries, create + per-worker databases and run ``snuba bootstrap --force``. +Phase 2 (after devservices): Stop snuba-snuba-1 and start per-worker + API containers. We must wait for devservices to finish first — + stopping the container while devservices is health-checking it would + cause a timeout. + +Requires: XDIST_WORKERS env var +Reads: /tmp/ds-exit (written by setup-devservices/wait.sh) +Writes: /tmp/snuba-bootstrap-exit +""" + +from __future__ import annotations + +import os +import subprocess +import sys +import time +from concurrent.futures import ThreadPoolExecutor, as_completed +from functools import partial +from pathlib import Path +from typing import Any, Callable +from urllib.error import URLError +from urllib.request import urlopen + +DS_EXIT = Path("/tmp/ds-exit") +SNUBA_EXIT = Path("/tmp/snuba-bootstrap-exit") + +SNUBA_ENV = { + "CLICKHOUSE_HOST": "clickhouse", + "CLICKHOUSE_PORT": "9000", + "CLICKHOUSE_HTTP_PORT": "8123", + "DEFAULT_BROKERS": "kafka:9093", + "REDIS_HOST": "redis", + "REDIS_PORT": "6379", + "REDIS_DB": "1", + "SNUBA_SETTINGS": "docker", +} + +ENV_ARGS = [flag for k, v in SNUBA_ENV.items() for flag in ("-e", f"{k}={v}")] + + +def retry( + fn: Callable[[], Any], *, attempts: int = 3, delay: int = 5, label: str = "operation" +) -> Any: + for i in range(attempts): + try: + return fn() + except Exception: + if i == attempts - 1: + raise + log(f"{label} failed (attempt {i + 1}/{attempts}), retrying in {delay}s...") + time.sleep(delay) + + +def log(msg: str) -> None: + print(msg, flush=True) + + +def fail(msg: str) -> None: + log(f"::error::{msg}") + SNUBA_EXIT.write_text("1") + sys.exit(1) + + +def http_ok(url: str) -> bool: + try: + with urlopen(url, timeout=3): + return True + except (URLError, OSError): + return False + + +def docker( + *args: str, check: bool = False, timeout: int | None = None +) -> subprocess.CompletedProcess[str]: + return subprocess.run( + ["docker", *args], capture_output=True, text=True, check=check, timeout=timeout + ) + + +def docker_inspect(container: str, fmt: str) -> str: + r = docker("inspect", container, "--format", fmt) + return r.stdout.strip() if r.returncode == 0 else "" + + +def inspect_snuba_container() -> tuple[str, str]: + image = docker_inspect("snuba-snuba-1", "{{.Config.Image}}") + network = docker_inspect( + "snuba-snuba-1", + "{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}", + ) + if not image or not network: + fail("Could not inspect snuba-snuba-1 container") + return image, network + + +def run_parallel(fn: Callable[[int], Any], workers: range, *, fail_fast: bool = True) -> int: + """Run fn(i) in parallel for each i in workers. Returns 0 on full success.""" + rc = 0 + with ThreadPoolExecutor(max_workers=len(workers)) as pool: + futs = {pool.submit(fn, i): i for i in workers} + for fut in as_completed(futs): + try: + fut.result() + except Exception as e: + if fail_fast: + fail(str(e)) + log(f"ERROR: {e}") + rc = 1 + return rc + + +def wait_for_prerequisites(timeout: int = 300) -> None: + log("Waiting for ClickHouse and Snuba container...") + start = time.monotonic() + while True: + if time.monotonic() - start > timeout: + fail("Timed out waiting for Snuba bootstrap prerequisites") + if http_ok("http://localhost:8123/") and docker_inspect("snuba-snuba-1", "{{.Id}}"): + break + time.sleep(2) + log(f"Prerequisites ready ({time.monotonic() - start:.0f}s)") + + +def wait_for_devservices(timeout: int = 300) -> None: + start = time.monotonic() + while not DS_EXIT.exists(): + if time.monotonic() - start > timeout: + fail("Timed out waiting for devservices to finish") + time.sleep(1) + rc = int(DS_EXIT.read_text().strip()) + if rc != 0: + fail(f"devservices failed (exit {rc}), skipping Phase 2") + + +def bootstrap_worker(worker_id: int, *, image: str, network: str) -> None: + """Create a ClickHouse database and run snuba bootstrap.""" + db = f"default_gw{worker_id}" + + def create_db() -> None: + with urlopen( + "http://localhost:8123/", f"CREATE DATABASE IF NOT EXISTS {db}".encode(), timeout=30 + ): + pass + + retry(create_db, label=f"CREATE DATABASE {db}") + + def run_bootstrap() -> None: + r = docker( + "run", + "--rm", + "--network", + network, + "-e", + f"CLICKHOUSE_DATABASE={db}", + *ENV_ARGS, + image, + "bootstrap", + "--force", + ) + for line in (r.stdout + r.stderr).strip().splitlines()[-3:]: + log(line) + if r.returncode != 0: + raise RuntimeError(f"snuba bootstrap failed for worker {worker_id}") + + retry(run_bootstrap, label=f"snuba bootstrap gw{worker_id}") + + +def start_worker_container(worker_id: int, *, image: str, network: str) -> None: + """Start a per-worker Snuba API container and wait for health.""" + db = f"default_gw{worker_id}" + port = 1230 + worker_id + name = f"snuba-gw{worker_id}" + + docker("rm", "-f", name) + + r = docker( + "run", + "-d", + "--name", + name, + "--network", + network, + "-p", + f"{port}:1218", + "-e", + f"CLICKHOUSE_DATABASE={db}", + *ENV_ARGS, + "-e", + "DEBUG=1", + image, + "api", + ) + if r.returncode != 0: + raise RuntimeError(f"docker run {name} failed: {r.stderr.strip()}") + + for attempt in range(1, 31): + if http_ok(f"http://127.0.0.1:{port}/health"): + log(f"{name} healthy on port {port}") + return + if attempt == 30: + r = docker("logs", name) + for line in (r.stdout + r.stderr).strip().splitlines()[-20:]: + log(line) + raise RuntimeError(f"{name} failed health check after 30 attempts") + time.sleep(2) + + +def main() -> None: + workers_str = os.environ.get("XDIST_WORKERS") + if not workers_str: + fail("XDIST_WORKERS must be set") + workers = range(int(workers_str)) + start = time.monotonic() + + wait_for_prerequisites() + image, network = inspect_snuba_container() + + log("Phase 1: bootstrapping ClickHouse databases") + run_parallel(partial(bootstrap_worker, image=image, network=network), workers) + log(f"Phase 1 done ({time.monotonic() - start:.0f}s)") + + wait_for_devservices() + try: + docker("stop", "snuba-snuba-1", timeout=30) + except subprocess.TimeoutExpired: + log("WARNING: docker stop snuba-snuba-1 timed out, killing") + docker("kill", "snuba-snuba-1") + + log("Phase 2: starting per-worker Snuba API containers") + rc = run_parallel( + partial(start_worker_container, image=image, network=network), + workers, + fail_fast=False, + ) + + log(f"Snuba bootstrap complete ({time.monotonic() - start:.0f}s total)") + SNUBA_EXIT.write_text(str(rc)) + sys.exit(rc) + + +if __name__ == "__main__": + main() diff --git a/.github/actions/setup-devservices/wait.sh b/.github/actions/setup-devservices/wait.sh new file mode 100755 index 00000000000000..00c319469d7347 --- /dev/null +++ b/.github/actions/setup-devservices/wait.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -euo pipefail + +# Wait for the background devservices process started by the setup-devservices action. +# Usage: wait.sh [timeout_seconds] +TIMEOUT=${1:-600} + +SECONDS=0 +while [ ! -f /tmp/ds-exit ]; do + if [ $SECONDS -gt "$TIMEOUT" ]; then + echo "::error::Timed out waiting for devservices after ${TIMEOUT}s" + cat /tmp/ds.log + exit 1 + fi + sleep 2 +done + +DS_RC=$(< /tmp/ds-exit) +if [ "$DS_RC" -ne 0 ]; then + echo "::error::devservices up failed (exit $DS_RC)" + cat /tmp/ds.log + exit 1 +fi + +echo "DJANGO_LIVE_TEST_SERVER_ADDRESS=$(docker network inspect bridge --format='{{(index .IPAM.Config 0).Gateway}}')" >> "$GITHUB_ENV" +docker ps -a diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 8bad8cfafc0683..652d2ca72b9392 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -68,6 +68,12 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 name: Checkout sentry + - run: mkdir -p config/chartcuterie + + - uses: ./.github/actions/setup-devservices + with: + mode: acceptance-ci + - name: Step configurations id: config run: | @@ -98,6 +104,12 @@ jobs: id: setup with: mode: acceptance-ci + skip-devservices: 'true' + + - name: Wait for devservices + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh - name: Run acceptance tests (#${{ steps.setup.outputs.matrix-instance-number }} of ${{ steps.setup.outputs.matrix-instance-total }}) run: make run-acceptance @@ -105,6 +117,11 @@ jobs: - name: Inspect failure if: failure() run: | + if [ -f /tmp/ds.log ]; then + echo "--- devservices startup log ---" + cat /tmp/ds.log + fi + if command -v devservices; then devservices logs fi diff --git a/.github/workflows/backend-with-coverage.yml b/.github/workflows/backend-with-coverage.yml deleted file mode 100644 index 65f1496ce76571..00000000000000 --- a/.github/workflows/backend-with-coverage.yml +++ /dev/null @@ -1,199 +0,0 @@ -name: backend - with test coverage - -on: - workflow_dispatch: - workflow_call: - # Every 30 minutes - schedule: - - cron: '0,30 * * * *' - -env: - SENTRY_SKIP_SELENIUM_PLUGIN: '1' - -jobs: - # Skip generating coverage if already exists in GCS - check-existing-coverage: - name: check for existing coverage - runs-on: ubuntu-24.04 - timeout-minutes: 5 - permissions: - contents: read - id-token: write - outputs: - has-coverage: ${{ steps.check-coverage.outputs.exists }} - commit-sha: ${{ steps.get-sha.outputs.sha }} - steps: - - name: Determine commit SHA - id: get-sha - run: | - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - COMMIT_SHA="${{ github.event.pull_request.head.sha }}" - else - COMMIT_SHA="${{ github.sha }}" - fi - echo "sha=${COMMIT_SHA}" >> "$GITHUB_OUTPUT" - - - 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: Check if coverage exists - id: check-coverage - env: - COMMIT_SHA: ${{ steps.get-sha.outputs.sha }} - run: | - if gcloud storage ls "gs://sentry-coverage-data/${COMMIT_SHA}/.coverage.combined" &>/dev/null; then - echo "Coverage already exists for commit ${COMMIT_SHA}, skipping test run" - echo "exists=true" >> "$GITHUB_OUTPUT" - else - echo "No coverage found for commit ${COMMIT_SHA}, will run tests" - echo "exists=false" >> "$GITHUB_OUTPUT" - fi - - calculate-shards: - name: calculate test shards - if: needs.check-existing-coverage.outputs.has-coverage != 'true' - needs: [check-existing-coverage] - runs-on: ubuntu-24.04 - timeout-minutes: 5 - outputs: - shard-count: ${{ steps.calculate-shards.outputs.shard-count }} - shard-indices: ${{ steps.calculate-shards.outputs.shard-indices }} - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Setup sentry env - uses: ./.github/actions/setup-sentry - id: setup - with: - mode: backend-ci - skip-devservices: true - - - name: Calculate test shards - id: calculate-shards - run: python3 .github/workflows/scripts/calculate-backend-test-shards.py - - backend-test-with-cov-context: - name: backend test - if: needs.check-existing-coverage.outputs.has-coverage != 'true' - runs-on: ubuntu-24.04 - needs: [check-existing-coverage, calculate-shards] - timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - instance: ${{ fromJSON(needs.calculate-shards.outputs.shard-indices) }} - env: - MATRIX_INSTANCE_TOTAL: ${{ needs.calculate-shards.outputs.shard-count }} - TEST_GROUP_STRATEGY: roundrobin - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Setup sentry env - uses: ./.github/actions/setup-sentry - id: setup - with: - mode: backend-ci - - - name: Download odiff binary - run: | - curl -sL https://registry.npmjs.org/odiff-bin/-/odiff-bin-4.3.2.tgz \ - | tar -xz --strip-components=2 package/raw_binaries/odiff-linux-x64 - sudo install -m 755 odiff-linux-x64 /usr/local/bin/odiff - rm odiff-linux-x64 - - - name: Run backend test with coverage (${{ steps.setup.outputs.matrix-instance-number }} of ${{ steps.setup.outputs.matrix-instance-total }}) - run: make test-backend-ci-with-coverage - - - name: Validate coverage database - if: always() - run: | - set -euxo pipefail - if [[ ! -f .coverage ]]; then - echo "Error: No .coverage file found after tests" - exit 1 - fi - python -c "import sqlite3; sqlite3.connect('.coverage').execute('SELECT COUNT(*) FROM file')" - - - name: Upload raw coverage sqlite as artifact - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: pycoverage-sqlite-${{ github.run_id }}-${{ steps.setup.outputs.matrix-instance-number }} - path: .coverage - if-no-files-found: error - retention-days: 7 - include-hidden-files: true - - combine-coverage: - name: combine coverage - # Only upload coverage if all test shards pass - incomplete coverage could cause selective testing to skip tests incorrectly - if: needs.check-existing-coverage.outputs.has-coverage != 'true' - runs-on: ubuntu-24.04 - permissions: - contents: read - id-token: write - actions: read # used for DIM metadata - needs: [check-existing-coverage, backend-test-with-cov-context, calculate-shards] - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - uses: astral-sh/setup-uv@884ad927a57e558e7a70b92f2bccf9198a4be546 # v6 - with: - version: '0.9.28' - enable-cache: false - - - name: Download all coverage artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - pattern: pycoverage-sqlite-${{ github.run_id }}-* - path: .artifacts/all-coverage - - - name: Verify all shards produced coverage - env: - EXPECTED_SHARD_COUNT: ${{ needs.calculate-shards.outputs.shard-count }} - run: | - set -euxo pipefail - - echo "Downloaded coverage artifacts:" - ls -la .artifacts/all-coverage || true - - COVERAGE_FILE_COUNT=$(find .artifacts/all-coverage -name ".coverage" -type f | wc -l) - echo "Found ${COVERAGE_FILE_COUNT} coverage files, expected ${EXPECTED_SHARD_COUNT}" - - if [[ "$COVERAGE_FILE_COUNT" -ne "$EXPECTED_SHARD_COUNT" ]]; then - echo "Error: Missing coverage files. Expected ${EXPECTED_SHARD_COUNT}, found ${COVERAGE_FILE_COUNT}" - echo "This indicates some test shards failed to produce coverage." - find .artifacts/all-coverage -name ".coverage" -type f - exit 1 - fi - - - name: Combine all coverage databases - run: | - set -euxo pipefail - uvx --with covdefaults --with sentry-covdefaults-disable-branch-coverage \ - coverage combine $(find .artifacts/all-coverage -name ".coverage" -type f) - - if [[ ! -f .coverage ]]; then - echo "Error: Combined coverage file was not created" - exit 1 - fi - - mv .coverage ".coverage.combined" - - - name: Authenticate to Google Cloud - id: gcloud-auth - 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: Upload coverage to GCS - uses: google-github-actions/upload-cloud-storage@c0f6160ff80057923ff50e5e567695cea181ec23 # v2.2.4 - with: - path: .coverage.combined - destination: sentry-coverage-data/${{ needs.check-existing-coverage.outputs.commit-sha }} diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index 23b62cfad4243c..fbcde1793f949e 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -59,11 +59,23 @@ jobs: node-version-file: '.node-version' - uses: pnpm/action-setup@9b5745cdf0a2e8c2620f0746130f809adb911c19 # v4 + + - name: Start devservices early + uses: ./.github/actions/setup-devservices + with: + mode: default + - name: Setup sentry python env uses: ./.github/actions/setup-sentry id: setup with: mode: default + skip-devservices: 'true' + + - name: Wait for devservices + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh - name: Run API docs tests run: | @@ -234,11 +246,28 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Start devservices early + uses: ./.github/actions/setup-devservices + with: + mode: backend-ci + - name: Setup sentry env uses: ./.github/actions/setup-sentry id: setup with: mode: backend-ci + skip-devservices: 'true' + + - name: Wait for devservices + run: | + sentry init + # Start Snuba per-worker bootstrap in background — it polls for + # ClickHouse readiness itself, so it can begin before all containers + # are healthy, overlapping with the remaining devservices health checks. + if [ "${XDIST_PER_WORKER_SNUBA}" = "1" ]; then + python3 ./.github/actions/setup-devservices/bootstrap-snuba.py & + fi + ./.github/actions/setup-devservices/wait.sh - name: Download odiff binary run: | @@ -247,59 +276,23 @@ jobs: sudo install -m 755 odiff-linux-x64 /usr/local/bin/odiff rm odiff-linux-x64 - - name: Bootstrap per-worker Snuba instances + - name: Wait for Snuba bootstrap if: env.XDIST_PER_WORKER_SNUBA == '1' run: | - set -eo pipefail - SNUBA_IMAGE=$(docker inspect snuba-snuba-1 --format '{{.Config.Image}}') - SNUBA_NETWORK=$(docker inspect snuba-snuba-1 --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}') - if [ -z "$SNUBA_IMAGE" ] || [ -z "$SNUBA_NETWORK" ]; then - echo "ERROR: Could not inspect snuba-snuba-1 container. Is devservices running?" + SECONDS=0 + while [ ! -f /tmp/snuba-bootstrap-exit ]; do + if [ $SECONDS -gt 600 ]; then + echo "::error::Timed out waiting for Snuba bootstrap after 600s" + exit 1 + fi + sleep 2 + done + SNUBA_RC=$(cat /tmp/snuba-bootstrap-exit) + if [ "$SNUBA_RC" -ne 0 ]; then + echo "::error::Snuba per-worker bootstrap failed" exit 1 fi - docker stop snuba-snuba-1 || true - - PIDS=() - for i in $(seq 0 $(( ${XDIST_WORKERS} - 1 ))); do - ( - WORKER_DB="default_gw${i}" - WORKER_PORT=$((1230 + i)) - curl -sf 'http://localhost:8123/' --data-binary "CREATE DATABASE IF NOT EXISTS ${WORKER_DB}" - docker run --rm --network "$SNUBA_NETWORK" \ - -e "CLICKHOUSE_DATABASE=${WORKER_DB}" -e "CLICKHOUSE_HOST=clickhouse" \ - -e "CLICKHOUSE_PORT=9000" -e "CLICKHOUSE_HTTP_PORT=8123" \ - -e "DEFAULT_BROKERS=kafka:9093" -e "REDIS_HOST=redis" \ - -e "REDIS_PORT=6379" -e "REDIS_DB=1" -e "SNUBA_SETTINGS=docker" \ - "$SNUBA_IMAGE" bootstrap --force 2>&1 | tail -3 - docker run -d --name "snuba-gw${i}" --network "$SNUBA_NETWORK" \ - -p "${WORKER_PORT}:1218" \ - -e "CLICKHOUSE_DATABASE=${WORKER_DB}" -e "CLICKHOUSE_HOST=clickhouse" \ - -e "CLICKHOUSE_PORT=9000" -e "CLICKHOUSE_HTTP_PORT=8123" \ - -e "DEFAULT_BROKERS=kafka:9093" -e "REDIS_HOST=redis" \ - -e "REDIS_PORT=6379" -e "REDIS_DB=1" -e "SNUBA_SETTINGS=docker" \ - -e "DEBUG=1" "$SNUBA_IMAGE" api - - for attempt in $(seq 1 30); do - if curl -sf "http://127.0.0.1:${WORKER_PORT}/health" > /dev/null 2>&1; then - echo "snuba-gw${i} healthy on port ${WORKER_PORT}" - break - fi - if [ "$attempt" -eq 30 ]; then - echo "ERROR: snuba-gw${i} failed health check after 30 attempts" - docker logs "snuba-gw${i}" 2>&1 | tail -20 || true - exit 1 - fi - sleep 2 - done - ) & - PIDS+=($!) - done - - for pid in "${PIDS[@]}"; do - wait "$pid" || { echo "ERROR: Snuba bootstrap subshell (PID $pid) failed"; exit 1; } - done - - name: Download selected tests artifact if: needs.select-tests.outputs.has-selected-tests == 'true' uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 @@ -339,6 +332,11 @@ jobs: - name: Inspect failure if: failure() run: | + if [ -f /tmp/ds.log ]; then + echo "--- devservices startup log ---" + cat /tmp/ds.log + fi + if command -v devservices; then devservices logs fi @@ -375,11 +373,22 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Start devservices early + uses: ./.github/actions/setup-devservices + with: + mode: default + - name: Setup sentry env uses: ./.github/actions/setup-sentry id: setup with: mode: default + skip-devservices: 'true' + + - name: Wait for devservices + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh - name: run tests run: | @@ -413,11 +422,22 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Start devservices early + uses: ./.github/actions/setup-devservices + with: + mode: migrations + - name: Setup sentry env uses: ./.github/actions/setup-sentry id: setup with: mode: migrations + skip-devservices: 'true' + + - name: Wait for devservices + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh - name: Run test env: @@ -489,10 +509,21 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Start devservices early + uses: ./.github/actions/setup-devservices + with: + mode: backend-ci + - name: Setup sentry env uses: ./.github/actions/setup-sentry with: mode: backend-ci + skip-devservices: 'true' + + - name: Wait for devservices + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh - name: Sync API Urls to TypeScript run: | @@ -515,11 +546,22 @@ jobs: - name: Checkout sentry uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Start devservices early + uses: ./.github/actions/setup-devservices + with: + mode: migrations + - name: Setup sentry env uses: ./.github/actions/setup-sentry id: setup with: mode: migrations + skip-devservices: 'true' + + - name: Wait for devservices + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh - name: Migration & lockfile checks env: @@ -549,11 +591,22 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Start devservices early + uses: ./.github/actions/setup-devservices + with: + mode: migrations + - name: Setup sentry env uses: ./.github/actions/setup-sentry id: setup with: mode: migrations + skip-devservices: 'true' + + - name: Wait for devservices + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh - name: Run test run: | diff --git a/.github/workflows/openapi-diff.yml b/.github/workflows/openapi-diff.yml index 9c510beafba3d9..556d036b53b456 100644 --- a/.github/workflows/openapi-diff.yml +++ b/.github/workflows/openapi-diff.yml @@ -27,12 +27,25 @@ jobs: token: ${{ github.token }} filters: .github/file-filters.yml + - name: Start devservices early + uses: ./.github/actions/setup-devservices + if: steps.changes.outputs.api_docs == 'true' + with: + mode: migrations + - name: Setup sentry env uses: ./.github/actions/setup-sentry with: mode: migrations + skip-devservices: 'true' if: steps.changes.outputs.api_docs == 'true' + - name: Wait for devservices + if: steps.changes.outputs.api_docs == 'true' + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh + - name: Checkout getsentry/sentry-api-schema uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 if: steps.changes.outputs.api_docs == 'true' diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml index 35a7bad47fdef4..1ed5a0720e9af9 100644 --- a/.github/workflows/openapi.yml +++ b/.github/workflows/openapi.yml @@ -32,12 +32,25 @@ jobs: token: ${{ github.token }} filters: .github/file-filters.yml + - name: Start devservices early + uses: ./.github/actions/setup-devservices + if: steps.changes.outputs.api_docs == 'true' + with: + mode: migrations + - name: Setup sentry env uses: ./.github/actions/setup-sentry with: mode: migrations + skip-devservices: 'true' if: steps.changes.outputs.api_docs == 'true' + - name: Wait for devservices + if: steps.changes.outputs.api_docs == 'true' + run: | + sentry init + ./.github/actions/setup-devservices/wait.sh + - name: Checkout getsentry/sentry-api-schema if: steps.changes.outputs.api_docs == 'true' uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7