chore(clickhouse): split the query tracing decorator into helpers #333677
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
| # This workflow runs generic Python tests global to Dagster and PostHog. | |
| name: Python CI | |
| on: | |
| push: | |
| branches: | |
| - master | |
| pull_request: | |
| schedule: | |
| # Where master's coverage for these checks comes from. The merge queue's | |
| # trunk-merge/** run gates every commit before it lands, so the push lane does | |
| # not repeat them. | |
| # Offset from the other hourly CI crons so the runs do not all fire at once. | |
| - cron: '43 * * * *' | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| # The hourly lane gets its own group, so it queues behind itself rather than | |
| # behind master pushes. Sharing the ref group would let a slow hourly run hold | |
| # the group while pushes pile up pending, and GitHub keeps only the newest | |
| # pending run per group — so the older push, and its per-commit checks, would | |
| # be dropped. | |
| group: ${{ github.workflow }}-${{ github.event_name == 'schedule' && 'scheduled' || github.head_ref || github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| # Learning mode: nothing reads these outputs, so a recommendation cannot change what runs. | |
| # Fork and Dependabot runs get no secret; the queue always runs everything anyway. | |
| # The step fails open, because this telemetry must not turn a passing workflow red. | |
| dynamic-ci-filter: | |
| name: Trunk Dynamic CI (Learning Mode) | |
| if: >- | |
| github.event_name == 'pull_request' && | |
| github.repository == 'PostHog/posthog' && | |
| github.event.pull_request.head.repo.full_name == github.repository && | |
| github.actor != 'dependabot[bot]' && | |
| !startsWith(github.head_ref, 'trunk-merge/') | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| permissions: {} | |
| steps: | |
| - name: Ask Trunk which jobs this diff needs | |
| continue-on-error: true | |
| uses: trunk-io/dynamic-ci@7e3af9331e8ebdfe0c71ba7d0ff6b7424dde8c57 # v1 | |
| with: | |
| token: ${{ secrets.TRUNK_API_TOKEN }} | |
| # Job to decide if we should run python ci | |
| # See .github/actions/paths-filter/README.md for filter semantics | |
| changes: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| name: Determine need to run python checks | |
| # Set job outputs to values from filter step | |
| outputs: | |
| python: ${{ steps.filter.outputs.python || 'true' }} | |
| complexity_files: ${{ steps.filter.outputs.complexity_files }} | |
| # Per-tool outputs gating the self-contained pytest steps in code-quality. | |
| # Each defaults to true, so master push and the hourly cron — where the filter | |
| # is skipped — still run every suite. | |
| hogli: ${{ steps.filter.outputs.hogli || 'true' }} | |
| owners: ${{ steps.filter.outputs.owners || 'true' }} | |
| query_perf: ${{ steps.filter.outputs.query_perf || 'true' }} | |
| stamphog: ${{ steps.filter.outputs.stamphog || 'true' }} | |
| bin_scripts: ${{ steps.filter.outputs.bin_scripts || 'true' }} | |
| api_ratchet: ${{ steps.filter.outputs.api_ratchet || 'true' }} | |
| steps: | |
| # For pull requests it's not necessary to checkout the code, but we | |
| # also want this to run on master so we need to checkout | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| sparse-checkout: .github/actions/paths-filter | |
| sparse-checkout-cone-mode: false | |
| - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 | |
| id: app-token | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository | |
| with: | |
| client-id: ${{ vars.GH_APP_POSTHOG_PATHS_FILTER_APP_ID }} | |
| private-key: ${{ secrets.GH_APP_POSTHOG_PATHS_FILTER_PRIVATE_KEY }} | |
| - uses: ./.github/actions/paths-filter | |
| id: filter | |
| env: | |
| # This workflow opts in for the whole repository. It runs on every pull | |
| # request, so one wiring covers every shape the comparison needs to see. | |
| PATHS_FILTER_SHADOW_POSTHOG_TOKEN: ${{ secrets.POSTHOG_DEVEX_PROJECT_API_TOKEN }} | |
| # Skipped on master push and on the hourly schedule: every output then | |
| # defaults to true. See "The hourly master lane" in | |
| # .agents/skills/authoring-ci-workflows/SKILL.md for why a cron cannot | |
| # diff, and what breaks if this guard is dropped. | |
| if: github.event_name != 'push' && github.event_name != 'schedule' | |
| with: | |
| token: ${{ steps.app-token.outputs.token || github.token }} | |
| list-files: 'escape' | |
| filters: | | |
| # Consumed as a file list (complexity_files) by the changed-file | |
| # complexity check — the PR files API, not a hand-rolled git diff. | |
| complexity: | |
| - 'posthog/**/*.py' | |
| - 'ee/**/*.py' | |
| - 'products/**/*.py' | |
| python: | |
| # ruff/mypy/ty run repo-wide (`.`) in code-quality, so any Python | |
| # file anywhere must trigger this workflow — otherwise an unformatted | |
| # file lands undetected and trips the next unrelated PR that does run. | |
| - '**/*.py' | |
| - '*.py' # root-level scripts (e.g. manage.py); '**/' alone may not match root | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| - 'posthog/**/*' | |
| # Make sure we run if someone is explicitly change the workflows | |
| - .github/workflows/ci-python.yml | |
| - .github/workflows/ci-dagster.yml | |
| - .github/workflows/ci-backend.yml | |
| - .github/actions/setup-uv/** | |
| - .depot/actions/setup-uv/** | |
| - '.depot/workflows/*.yml' | |
| - .flox/env/manifest.toml | |
| - .flox/env/manifest.lock | |
| # Schema changes need to trigger Python CI to validate schema.py is regenerated | |
| - 'frontend/src/queries/schema.json' | |
| # Products changes need to trigger Python CI to validate the json usage is still valid | |
| - 'frontend/src/products.json' | |
| # A hand-edit to a projection output must reach the drift check. Lists | |
| # every output in hogli_commands/projections.py, not a *.generated.* glob, | |
| # so unrelated generated files skip mypy and ty. | |
| # test_generated_files_are_registered.py keeps the two in step. | |
| - 'frontend/src/taxonomy/core-filter-definitions-by-group.json' | |
| - 'services/mcp/src/lib/trace-property-allowlist.generated.ts' | |
| - 'products/desktop/packages/core/src/inbox/objectKinds.generated.ts' | |
| - 'packages/agent/packages/agent-contracts/src/objectTagKinds.generated.ts' | |
| - 'frontend/src/lib/components/AgentObjectTags/objectKinds.generated.ts' | |
| - 'products/tasks/frontend/modelCatalog.generated.ts' | |
| - 'packages/agent/packages/agent-contracts/src/model-catalog.generated.ts' | |
| - 'services/mcp/src/lib/oauth-scopes.generated.ts' | |
| # hogli CLI changes need validation | |
| - 'tools/hogli/**' | |
| - 'tools/hogli-commands/hogli_commands/**' | |
| - 'packages/owners-yaml/**' | |
| # Transitional: branches that predate the move still carry the resolver | |
| # at tools/owners. | |
| - 'tools/owners/**' | |
| - 'hogli.yaml' | |
| - 'bin/**' | |
| # Django templates live outside posthog/ for products, and the | |
| # code-quality job formats them with djlint | |
| - 'products/**/backend/templates/**' | |
| # Dev tools with dedicated pytest steps below | |
| - 'tools/pr-approval-agent/**' | |
| - 'products/stamphog/packages/pr-approval-agent/**' | |
| - 'tools/query-performance-ai/**' | |
| # Stamphog policy files validated by the pr-approval-agent suite | |
| - '.stamphog/**' | |
| - '**/AGENT_APPROVALS.md' | |
| # Per-tool groups below gate the pytest steps in code-quality. Each of | |
| # these trees imports only stdlib and its own package — none of them | |
| # imports posthog — so a change under posthog/ cannot affect them and | |
| # they do not need to run on every Python PR. Verify that before adding | |
| # a tree here: a path filter that skips tests is a claim about the | |
| # import graph (see ci-things-already-tried.md, #50137). | |
| # Dependency and workflow changes run all of them. | |
| hogli: | |
| - 'tools/hogli/**' | |
| - 'tools/hogli-commands/**' | |
| - 'hogli.yaml' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| - .github/workflows/ci-python.yml | |
| owners: | |
| - 'packages/owners-yaml/**' | |
| # Transitional: branches that predate the move still carry the resolver | |
| # at tools/owners. | |
| - 'tools/owners/**' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| - .github/workflows/ci-python.yml | |
| query_perf: | |
| - 'tools/query-performance-ai/**' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| - .github/workflows/ci-python.yml | |
| stamphog: | |
| - 'tools/pr-approval-agent/**' | |
| - 'products/stamphog/packages/pr-approval-agent/**' | |
| - '.stamphog/**' | |
| - '**/AGENT_APPROVALS.md' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| - .github/workflows/ci-python.yml | |
| bin_scripts: | |
| - 'bin/**' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| - .github/workflows/ci-python.yml | |
| # The lib/api.ts ratchet compares the hand-rolled URL builder with | |
| # the generated clients, so either side moving can create new debt. | |
| api_ratchet: | |
| - 'frontend/src/lib/api.ts' | |
| - 'frontend/src/lib/api-ratchet-baseline.txt' | |
| - 'frontend/src/generated/core/api.ts' | |
| - 'products/*/frontend/generated/api.ts' | |
| - 'tools/hogli-commands/hogli_commands/api_ratchet.py' | |
| # The generated rule block is compared against the generator, so | |
| # editing it by hand has to reach the drift check. | |
| - '.semgrep/rules/devex/prefer-codegen-api-namespaced.yaml' | |
| - 'hogli.yaml' | |
| - .github/workflows/ci-python.yml | |
| code-quality: | |
| needs: changes | |
| # Skipped on master push: the merge queue already ran this for every landed | |
| # commit, and the hourly scheduled run keeps master coverage. | |
| if: needs.changes.outputs.python == 'true' && github.event_name != 'push' | |
| timeout-minutes: 30 | |
| name: Python code quality (depot-ubuntu-24.04) | |
| runs-on: depot-ubuntu-24.04 | |
| # Read by shell guards on the pytest steps below. Those steps sit in a | |
| # `parallel:` block, where step-level `if:` is untested in this repo. | |
| env: | |
| RUN_HOGLI: ${{ needs.changes.outputs.hogli }} | |
| RUN_OWNERS: ${{ needs.changes.outputs.owners }} | |
| RUN_QUERY_PERF: ${{ needs.changes.outputs.query_perf }} | |
| RUN_STAMPHOG: ${{ needs.changes.outputs.stamphog }} | |
| RUN_BIN_SCRIPTS: ${{ needs.changes.outputs.bin_scripts }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 1 | |
| - name: Mint setup-action GitHub token | |
| id: setup-gh-token | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | |
| continue-on-error: true | |
| uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 | |
| with: | |
| client-id: ${{ vars.GH_APP_POSTHOG_SETUP_ACTIONS_APP_ID }} | |
| private-key: ${{ secrets.GH_APP_POSTHOG_SETUP_ACTIONS_PRIVATE_KEY }} | |
| skip-token-revoke: true | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: 3.14.7 | |
| token: ${{ steps.setup-gh-token.outputs.token || github.token }} | |
| - name: Install uv | |
| id: setup-uv | |
| uses: ./.github/actions/setup-uv | |
| - name: Check uv and Python version compatibility | |
| shell: bash | |
| run: | | |
| python3 bin/check_uv_python_compatibility.py | |
| - name: Install SAML (python3-saml) dependencies | |
| if: steps.setup-uv.outputs.cache-hit != 'true' | |
| shell: bash | |
| run: | | |
| sudo rm -f /etc/apt/sources.list.d/*twingate* | |
| sudo apt-get update | |
| sudo apt-get install libxml2-dev libxmlsec1 libxmlsec1-dev libxmlsec1-openssl | |
| - name: Install Python dependencies | |
| shell: bash | |
| run: | | |
| UV_PROJECT_ENVIRONMENT=$pythonLocation uv sync --frozen --dev | |
| - name: Download GeoIP database | |
| shell: bash | |
| run: | | |
| ./bin/download-mmdb | |
| - name: Add ty Problem Matcher | |
| shell: bash | |
| run: | | |
| echo "::add-matcher::.github/ty-problem-matcher.json" | |
| - name: Check ty type checking | |
| shell: bash | |
| run: | | |
| echo "📊 Feedback welcome in #team-devex" | |
| uv run ty check | |
| - name: Add mypy Problem Matcher | |
| shell: bash | |
| run: | | |
| echo "::add-matcher::.github/mypy-problem-matcher.json" | |
| - name: Compute mypy cache prefix | |
| id: mypy-cache-key | |
| shell: bash | |
| run: | | |
| python_version=$(python -c 'import platform; print(platform.python_version())') | |
| mypy_version=$(python -c 'from importlib.metadata import version; print(version("mypy"))') | |
| echo "prefix=mypy-v3-${{ runner.os }}-${{ runner.arch }}-${python_version}-${mypy_version}-${{ hashFiles('uv.lock', '**/pyproject.toml', '**/mypy.ini') }}" >> "$GITHUB_OUTPUT" | |
| - name: Restore mypy cache | |
| id: mypy-cache | |
| uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 | |
| with: | |
| path: .mypy_cache | |
| key: ${{ steps.mypy-cache-key.outputs.prefix }}-${{ github.run_id }}-${{ github.run_attempt }} | |
| restore-keys: ${{ steps.mypy-cache-key.outputs.prefix }}- | |
| - name: Check static typing | |
| shell: bash -e {0} | |
| env: | |
| MYPY_NUM_WORKERS: 2 | |
| run: mypy --version && mypy --cache-fine-grained . | |
| - name: Save mypy cache | |
| if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' | |
| uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 | |
| with: | |
| path: .mypy_cache | |
| key: ${{ steps.mypy-cache.outputs.cache-primary-key }} | |
| - name: Check if "schema.py" is up to date | |
| shell: bash | |
| run: | | |
| npm run schema:build:python && git diff --exit-code | |
| # Every projection renderer is Python, so this job needs neither pnpm nor | |
| # node_modules. Keep the step after "Install Python dependencies": the taxonomy | |
| # and object-tag renderers import posthog/__init__.py, which pulls in Django, | |
| # so they need the full synced dependency set, not just stdlib. | |
| # Skip on unrebased branches: the build:projections command may not exist yet. | |
| - name: Check if the generated projections are up to date | |
| shell: bash | |
| run: | | |
| if [ ! -f tools/hogli-commands/hogli_commands/projections.py ]; then | |
| echo "hogli build:projections not present on this branch yet — skipping (rebase to pick it up)" | |
| exit 0 | |
| fi | |
| ./bin/hogli build:projections --check | |
| # Warn-only while the pre-existing violation backlog settles: findings annotate | |
| # the diff and land in the shared CI report comment, never a job failure. | |
| # Scoped to the PR's own diff because C901 stays off repo-wide. Skip on | |
| # unrebased branches: the lint:complexity command may not be registered yet. | |
| - name: Check cyclomatic complexity of changed files | |
| if: github.event_name == 'pull_request' && needs.changes.outputs.complexity_files != '' | |
| shell: bash | |
| # A very large diff overflows the 128 KiB cap on a single environment entry, so the | |
| # runner cannot start this step at all. The findings are warn-only, so losing them | |
| # on such a PR is not worth failing the job for. | |
| continue-on-error: true | |
| env: | |
| # Space-separated, from paths-filter (list-files: escape) — the PR | |
| # files API, so no git-diff plumbing to drift from what the PR changed. | |
| CHANGED_FILES: ${{ needs.changes.outputs.complexity_files }} | |
| run: | | |
| if [ ! -f tools/hogli-commands/hogli_commands/complexity_lint.py ]; then | |
| echo "hogli lint:complexity not present on this branch yet — skipping (rebase to pick it up)" | |
| exit 0 | |
| fi | |
| # xargs re-parses the escape format, keeping escaped paths whole. | |
| printf '%s' "$CHANGED_FILES" | xargs -r ./bin/hogli lint:complexity --report complexity-findings-python.json | |
| - name: Post CI report — complexity | |
| if: always() && github.event_name == 'pull_request' && !startsWith(github.head_ref, 'trunk-merge/') | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| if [ -f .github/scripts/post-complexity-section.mjs ] && [ -f complexity-findings-python.json ]; then | |
| node .github/scripts/post-complexity-section.mjs python complexity-findings-python.json | |
| else | |
| echo "Skipping the complexity report section: script or findings not on this branch" | |
| fi | |
| - parallel: | |
| - name: Check for syntax errors, import sort, and code style violations | |
| shell: bash | |
| run: ruff check . | |
| - name: Check formatting | |
| shell: bash | |
| run: ruff format --check --diff . | |
| - name: Check Django template formatting | |
| shell: bash | |
| run: ./bin/hogli format:html:check | |
| - name: Lint Django templates | |
| shell: bash | |
| run: ./bin/hogli lint:html | |
| - name: Check hogli manifest completeness | |
| shell: bash | |
| run: ./bin/hogli meta:check | |
| - name: Run hogli framework tests | |
| shell: bash | |
| run: | | |
| [ "$RUN_HOGLI" = "true" ] || { echo "hogli unchanged — skipping"; exit 0; } | |
| pytest tools/hogli/tests -v --junitxml=junit-hogli.xml | |
| - name: Run hogli extension tests | |
| shell: bash | |
| run: | | |
| [ "$RUN_HOGLI" = "true" ] || { echo "hogli unchanged — skipping"; exit 0; } | |
| pytest tools/hogli-commands/hogli_commands/tests -v --junitxml=junit-hogli-commands.xml | |
| - name: Run owners resolver tests | |
| shell: bash | |
| run: | | |
| [ "$RUN_OWNERS" = "true" ] || { echo "the owners package is unchanged — skipping"; exit 0; } | |
| pytest packages/owners-yaml/tests -v --junitxml=junit-owners.xml | |
| # A wheel in an empty venv cannot import anything else from the monorepo, so a | |
| # new import of host code fails here instead of for users of the published package. | |
| - name: Check the owners package runs outside the monorepo | |
| shell: bash | |
| run: | | |
| [ "$RUN_OWNERS" = "true" ] || { echo "the owners package is unchanged — skipping"; exit 0; } | |
| scratch=$(mktemp -d) | |
| uv build packages/owners-yaml --wheel --out-dir "$scratch/dist" | |
| uv venv "$scratch/venv" | |
| uv pip install --python "$scratch/venv/bin/python" "$scratch"/dist/*.whl | |
| repo="$scratch/repo" | |
| mkdir -p "$repo/billing" | |
| printf 'version: 1\nowners: team-platform\ngithub_org: example\n' > "$repo/owners.yaml" | |
| printf 'version: 1\nowners: team-billing\n' > "$repo/billing/owners.yaml" | |
| touch "$repo/billing/invoices.py" | |
| "$scratch/venv/bin/owners" lint --repo-root "$repo" | |
| # Capture first. `grep -q` on a pipe exits at the first match, the CLI then | |
| # writes to a closed pipe and exits 1, and pipefail fails the step. | |
| resolved=$("$scratch/venv/bin/owners" who --repo-root "$repo" billing/invoices.py) | |
| grep -q 'team-billing' <<<"$resolved" | |
| - name: Run query-performance-ai tests | |
| shell: bash | |
| run: | | |
| [ "$RUN_QUERY_PERF" = "true" ] || { echo "query-performance-ai unchanged — skipping"; exit 0; } | |
| pytest tools/query-performance-ai/query_performance_ai -v --junitxml=junit-query-performance-ai.xml | |
| - name: Run pr-approval-agent (stamphog) tests | |
| shell: bash | |
| # Two candidate paths. The engine now lives under the stamphog product, but an | |
| # open PR that has not rebased still carries it under tools/, and this workflow | |
| # runs against that branch merged with master. The loop tests both trees when | |
| # both exist, because that merge can add a file at the old path next to the new | |
| # tree. | |
| run: | | |
| [ "$RUN_STAMPHOG" = "true" ] || { echo "pr-approval-agent unchanged — skipping"; exit 0; } | |
| found=0 | |
| for dir in products/stamphog/packages/pr-approval-agent tools/pr-approval-agent; do | |
| [ -d "$dir" ] || continue | |
| found=1 | |
| pytest "$dir" -v --junitxml="junit-pr-approval-agent-$(echo "$dir" | tr / -).xml" | |
| done | |
| if [ "$found" = "0" ]; then | |
| echo "::error::pr-approval-agent engine not found at either path" | |
| exit 1 | |
| fi | |
| - name: Run dependency detection script tests | |
| shell: bash | |
| run: | | |
| [ "$RUN_BIN_SCRIPTS" = "true" ] || { echo "bin/ unchanged — skipping"; exit 0; } | |
| pytest bin/test/test_find_python_dependencies.py -v --junitxml=junit-dependency-detection.xml | |
| - name: Run granian shared socket tests | |
| shell: bash | |
| # The bin_scripts filter includes uv.lock, so every granian upgrade runs this. | |
| run: | | |
| [ "$RUN_BIN_SCRIPTS" = "true" ] || { echo "bin/ unchanged — skipping"; exit 0; } | |
| pytest bin/test/test_granian_shared_socket.py -v --junitxml=junit-granian-shared-socket.xml | |
| - name: Upload test results | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| if: always() | |
| with: | |
| name: junit-results-hogli | |
| # A gated-off suite writes no junit file, which is now the common case. | |
| if-no-files-found: ignore | |
| path: | | |
| junit-hogli.xml | |
| junit-hogli-commands.xml | |
| junit-owners.xml | |
| junit-query-performance-ai.xml | |
| junit-pr-approval-agent-*.xml | |
| - name: Upload test results for dependency detection | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| if: always() | |
| with: | |
| name: junit-results-dependency-detection | |
| if-no-files-found: ignore | |
| path: junit-dependency-detection.xml | |
| - name: Upload test results for granian shared socket | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| if: always() | |
| with: | |
| name: junit-results-granian-shared-socket | |
| if-no-files-found: ignore | |
| path: junit-granian-shared-socket.xml | |
| api-ratchet: | |
| needs: changes | |
| # Its own filter output, so an api.ts-only pull request pays for this job | |
| # instead of the repo-wide code-quality one. | |
| if: needs.changes.outputs.api_ratchet == 'true' && github.event_name != 'push' | |
| name: lib/api.ts codegen ratchet | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| # So `HEAD^1` (the pull request's base commit) resolves below, letting | |
| # the ratchet tell a builder a schema change merely exposed apart from | |
| # one this branch actually added. | |
| fetch-depth: 2 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version-file: 'pyproject.toml' | |
| - name: Install uv | |
| uses: ./.github/actions/setup-uv | |
| - name: Install python dependencies | |
| run: UV_PROJECT_ENVIRONMENT=$pythonLocation uv sync --frozen --dev | |
| # Skip on unrebased branches: the command may not be registered yet. | |
| - name: Ratchet product paths in lib/api.ts | |
| env: | |
| API_RATCHET_BASE: HEAD^1 | |
| run: | | |
| if [ ! -f tools/hogli-commands/hogli_commands/api_ratchet.py ]; then | |
| echo "hogli lint:api-ratchet not present on this branch yet — skipping (rebase to pick it up)" | |
| exit 0 | |
| fi | |
| ./bin/hogli lint:api-ratchet | |
| # Collate job - single required status check for branch protection. | |
| # The code-quality job skips when no Python changes, but this job always | |
| # runs and reports success, allowing PRs to merge. | |
| python_tests: | |
| needs: [changes, code-quality, api-ratchet] | |
| name: Python code quality checks | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| if: ${{ !cancelled() }} | |
| steps: | |
| - name: Check Python CI status | |
| run: | | |
| # Block cancelled too: outputs are empty then, so the exit below would report green. | |
| if [[ "${{ needs.changes.result }}" != "success" && "${{ needs.changes.result }}" != "skipped" ]]; then | |
| echo "Change detection did not succeed (result: ${{ needs.changes.result }})." | |
| exit 1 | |
| fi | |
| # Checked before the no-Python-changes exit below: the ratchet has its | |
| # own filter, so it can run on a pull request that touches no Python. | |
| if [[ "${{ needs.api-ratchet.result }}" != "success" && "${{ needs.api-ratchet.result }}" != "skipped" ]]; then | |
| echo "lib/api.ts codegen ratchet did not succeed (result: ${{ needs.api-ratchet.result }})." | |
| exit 1 | |
| fi | |
| # Pass if no Python changes detected (jobs were skipped) | |
| if [[ "${{ needs.changes.outputs.python }}" != "true" ]]; then | |
| echo "Python checks were skipped (no relevant changes)" | |
| exit 0 | |
| fi | |
| # Check actual job result | |
| if [[ "${{ needs.code-quality.result }}" != "success" && "${{ needs.code-quality.result }}" != "skipped" ]]; then | |
| echo "Python code quality checks failed." | |
| exit 1 | |
| fi | |
| echo "All Python checks passed." |