feat(cohorts): add used-in references endpoint and UI #84772
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: Migration & Service Separation Check | |
| on: | |
| pull_request: | |
| merge_group: | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| check-migration-service-separation: | |
| name: Check migrations and service changes are not in same PR | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| # Author-attested escape hatch for DB-noop migrations (e.g. SeparateDatabaseAndState | |
| # state-only renames) that ship safely alongside service code. The check is path-only | |
| # and can't tell a CREATE TABLE from an app-label rename, so the label opt-in matches | |
| # the precedent set by skip-inkeep-docs. | |
| - name: Check for skip label | |
| id: skip-label | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| PR_LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }} | |
| GITHUB_REF: ${{ github.ref }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| run: | | |
| if [ "$EVENT_NAME" = "pull_request" ]; then | |
| labels="$PR_LABELS_JSON" | |
| elif [ "$EVENT_NAME" = "merge_group" ]; then | |
| # GITHUB_REF looks like refs/heads/gh-readonly-queue/<base>/pr-<num>-<sha> | |
| pr_num=$(echo "$GITHUB_REF" | grep -oE 'pr-[0-9]+' | head -1 | sed 's/pr-//') | |
| if [ -n "$pr_num" ]; then | |
| labels=$(gh api "repos/$GITHUB_REPOSITORY/issues/$pr_num/labels" --jq '[.[].name]') | |
| else | |
| labels='[]' | |
| fi | |
| else | |
| labels='[]' | |
| fi | |
| echo "Labels: $labels" | |
| if echo "$labels" | grep -q '"skip-migration-service-check"'; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Skipping check — 'skip-migration-service-check' label is set." | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 | |
| id: app-token | |
| if: steps.skip-label.outputs.skip != 'true' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository | |
| with: | |
| client-id: ${{ secrets.GH_APP_POSTHOG_PATHS_FILTER_APP_ID }} | |
| private-key: ${{ secrets.GH_APP_POSTHOG_PATHS_FILTER_PRIVATE_KEY }} | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: filter | |
| if: steps.skip-label.outputs.skip != 'true' | |
| with: | |
| token: ${{ steps.app-token.outputs.token || github.token }} | |
| filters: | | |
| migrations: | |
| - 'posthog/migrations/*.py' | |
| - 'posthog/clickhouse/migrations/*.py' | |
| - 'products/*/backend/migrations/*.py' | |
| - 'ee/migrations/*.py' | |
| sqlx_migrations: | |
| - 'rust/persons_migrations/*.sql' | |
| - 'rust/behavioral_cohorts_migrations/*.sql' | |
| - 'rust/cyclotron-core/migrations/*.sql' | |
| - 'rust/cyclotron-node-migrations/*.sql' | |
| nodejs: | |
| - 'nodejs/**' | |
| # Separate step with predicate-quantifier: 'every' so that the | |
| # negative patterns actually exclude migration directories. | |
| # The default quantifier is 'some' (OR logic), which means a file | |
| # matching ANY pattern (including the positive rust/**) passes the | |
| # filter — the ! patterns are silently ignored. | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: rust-filter | |
| if: steps.skip-label.outputs.skip != 'true' | |
| with: | |
| token: ${{ steps.app-token.outputs.token || github.token }} | |
| predicate-quantifier: 'every' | |
| filters: | | |
| rust_services: | |
| - 'rust/**' | |
| - '!rust/persons_migrations/**' | |
| - '!rust/behavioral_cohorts_migrations/**' | |
| - '!rust/cyclotron-core/migrations/**' | |
| - '!rust/cyclotron-node-migrations/**' | |
| - name: Check for conflicting changes | |
| id: check | |
| if: steps.skip-label.outputs.skip != 'true' | |
| env: | |
| MIGRATIONS_CHANGED: ${{ steps.filter.outputs.migrations }} | |
| NODEJS_CHANGED: ${{ steps.filter.outputs.nodejs }} | |
| SQLX_MIGRATIONS_CHANGED: ${{ steps.filter.outputs.sqlx_migrations }} | |
| RUST_SERVICES_CHANGED: ${{ steps.rust-filter.outputs.rust_services }} | |
| run: | | |
| has_error=false | |
| error_messages="" | |
| # Check nodejs + Django migrations | |
| if [ "$MIGRATIONS_CHANGED" == "true" ] && [ "$NODEJS_CHANGED" == "true" ]; then | |
| error_messages="${error_messages}❌ Found both Django migration and nodejs changes\n" | |
| has_error=true | |
| fi | |
| # Check nodejs + sqlx migrations | |
| if [ "$SQLX_MIGRATIONS_CHANGED" == "true" ] && [ "$NODEJS_CHANGED" == "true" ]; then | |
| error_messages="${error_messages}❌ Found both sqlx migration and nodejs changes\n" | |
| has_error=true | |
| fi | |
| # Check rust services + sqlx migrations | |
| if [ "$SQLX_MIGRATIONS_CHANGED" == "true" ] && [ "$RUST_SERVICES_CHANGED" == "true" ]; then | |
| error_messages="${error_messages}❌ Found both sqlx migration and rust service changes\n" | |
| has_error=true | |
| fi | |
| if [ "$has_error" == "true" ]; then | |
| echo "error=true" >> $GITHUB_OUTPUT | |
| echo -e "$error_messages" | |
| else | |
| echo "error=false" >> $GITHUB_OUTPUT | |
| echo "✅ No conflicting changes detected" | |
| fi | |
| - name: Fail if conflicting changes detected | |
| if: steps.check.outputs.error == 'true' | |
| run: | | |
| echo "::error::This PR contains both migration files and service changes. These must be separated into different PRs." | |
| echo "" | |
| echo "Why? Migration jobs are not orchestrated with service deployments. If you merge" | |
| echo "service code that depends on new database schema alongside the migration, your" | |
| echo "service may deploy before the migration runs, causing failures." | |
| echo "" | |
| echo "Solution: Split into two PRs:" | |
| echo " 1. First PR: Migration changes only (merge and wait for it to deploy)" | |
| echo " 2. Second PR: Service code changes (merge after migration is deployed)" | |
| echo "" | |
| echo "If every migration in this PR is DB-noop (e.g. SeparateDatabaseAndState" | |
| echo "with empty database_operations, or RunPython data-only steps), apply the" | |
| echo "'skip-migration-service-check' label and the check will skip." | |
| exit 1 |