Skip to content

feat(data-warehouse): implement aviator import source - #70022

Merged
talyn-app[bot] merged 6 commits into
masterfrom
posthog-code/implement-aviator-source
Jul 13, 2026
Merged

feat(data-warehouse): implement aviator import source#70022
talyn-app[bot] merged 6 commits into
masterfrom
posthog-code/implement-aviator-source

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

Aviator (YC S21) is a developer-productivity suite built around a GitHub merge queue. Its public JSON API is self-serve, so any customer can mint a user access token and pull their merge-queue data. We had a scaffolded stub for it but no working sync, so the connector could not import anything. This wires it up so teams can join Aviator merge-queue data with their product and engineering analytics in the Data warehouse.

Changes

Filled in the scaffolded aviator source end to end. It follows the standard source.py / settings.py / aviator.py split and inherits ResumableSource.

Tables:

Table Endpoint Sync
repositories GET /repo Full refresh (page pagination)
merge_queue_analytics GET /analytics Incremental (date window), per repo
queued_pull_requests GET /pull_request/queued Full refresh snapshot, per repo
queue_stats GET /queue/stats Full refresh snapshot, per repo
config_history GET /config/history Full refresh, per repo

Key decisions:

  • Only merge_queue_analytics is incremental. Its start/end UTC date window is the endpoint's fundamental interface (it returns per-day aggregate rows bounded by that window), so it is a genuine server-side filter. Each incremental run advances from the stored watermark minus a 7-day trailing window, because recent daily aggregates get revised upstream; merge dedupes the re-pulled days on the [repo, date] key. The endpoint returns five separate daily series (time_in_queue, wait_times_to_queue, mergequeue_usage, blocked_reason, sync_frequency); I merge them into one row per date with per-series-prefixed columns so their shared min/avg/pXX names do not collide.
  • Everything else is full refresh. The queue/PR endpoints are current-state snapshots with no timestamp filter. GET /config/history documents optional start/end params, but I could not curl-verify they actually filter server-side (no test credentials), so it stays full refresh conservatively - config changes are low volume, so re-reading them each sync is cheap.
  • Fan-out over repositories. The four repo-scoped endpoints enumerate repos via GET /repo and call once per repo, injecting org/repo into each row so composite primary keys stay unique table-wide. Resume is bookmarked at repo granularity (a stable org/name key), and fan-out endpoints report sort_mode="desc" so the incremental watermark only persists at successful job end.
  • All outbound HTTP goes through make_tracked_session(); retries use tenacity on 429/5xx and transient network errors.

Note

The source stays behind unreleasedSource=True with releaseStatus=ALPHA as requested, so it is not yet visible to users.

Two follow-ups needed outside this repo:

  • Icon: no frontend/public/services/aviator.svg exists yet and I had no Logo.dev key to fetch one legitimately, so I did not commit a placeholder. iconPath points at the expected path; the SVG needs to be added.
  • Docs: the user-facing doc belongs in the posthog.com repo (contents/docs/cdp/sources/aviator.md). No posthog.com checkout was available, so the finished doc is committed here at aviator.posthog-doc.md and needs to be copied across, after which audit_source_docs should be run.

How did you test this code?

Automated only (I am an agent and did not run a live sync - I have no Aviator credentials, so endpoint response shapes were taken from the public API docs and confirmed against the live host only for auth behavior and the base URL). Added two test modules run with hogli test:

  • tests/test_aviator.py (50 assertions across transport): the five-series analytics flattening, the incremental date-window computation (first sync, watermark rewind, future clamp, value-type coercion), repo-list pagination termination, per-endpoint fan-out row shapes, resume bookmarking (save-after-each-repo, resume-from-saved, deleted-repo restart), credential-status mapping, retry behavior, and per-endpoint sort/partition/primary-key wiring.
  • tests/test_aviator_source.py (source class): source type, config fields/flags, per-endpoint incremental support, credential validation, resumable-manager binding, source_for_pipeline plumbing, non-retryable errors, and canonical-description coverage.

All 50 aviator tests pass, plus the existing test_source_categories suite (1506) stays green. ruff check and ruff format are clean on the changed files.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

🤖 Agent context

Autonomy: Fully autonomous

Implemented by Claude (Claude Code). Invoked the /implementing-warehouse-sources, /documenting-warehouse-sources, and /writing-tests skills.

I verified the base URL and Bearer auth against the live API (api.aviator.co/api/v1 returns 401 with www-authenticate: Bearer) but could not exercise authenticated endpoints without a token, so response shapes come from the current public JSON API docs. I chose a custom transport over the declarative rest_source.RESTClient because the responses are heterogeneous (a bare array, wrapped lists, and the five-series analytics object that needs merging). I considered making config_history incremental via its documented start/end params but shipped it full refresh since I could not confirm server-side filtering - noted inline and above. The generated config (AviatorSourceConfig) was updated by hand to add the single api_token field because no database was reachable to run generate:source-configs; the output is identical to what the generator emits for a single required password field (matches the existing AwinSourceConfig).


Created with PostHog Code

Fill in the scaffolded Aviator data warehouse source end to end: repositories, daily merge-queue analytics, queued pull requests, live queue stats, and config-change history. Analytics syncs incrementally via the API's start/end date window; the snapshot/list endpoints ship as full refresh.

Generated-By: PostHog Code
Task-Id: 3e4316ad-d1e6-4f96-a230-9843de197bdf
@github-actions

Copy link
Copy Markdown
Contributor

Hey @Gilbert09! 👋

It looks like your git author email on this PR isn't your @posthog.com address (owerstom@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(data-warehouse): implement aviator ..." | Re-trigger Greptile

@trunk-io

trunk-io Bot commented Jul 10, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

- Register the API token for value-based redaction on both the credential-validation
  and sync sessions so it can't leak into logged URLs or captured HTTP samples.
- Stop logging Aviator error response bodies; log only status and URL to avoid spilling
  request-specific data (config-history diffs, token-like values) into application logs.
- Skip queued-PR rows missing `number` and config-history rows missing `applied_at`
  rather than emitting null primary keys that silently collapse distinct rows on merge.
- Resume fan-out syncs by tracking the set of completed repo keys instead of a positional
  bookmark, so a repository added between a crash and the retry is processed rather than
  skipped (which would strand its older analytics outside the trailing lookback window).
- Add the missing aviator.svg source icon.
- Rename the placeholder source doc to .mdx and apply formatting.

Generated-By: PostHog Code
Task-Id: 8b4d7ee8-6606-465b-94eb-eed204b4d177
@github-actions
github-actions Bot requested a deployment to preview-pr-70022 July 10, 2026 14:38 In progress
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🦔 Hogbox preview · ❌ build failed

The preview didn't come up for commit 340b53c. See the build log for the failing step. It'll retry on the next push.

Generated-By: PostHog Code
Task-Id: 8b4d7ee8-6606-465b-94eb-eed204b4d177
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

Bundle size — no change

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 64.35 MiB · no change

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.21 MiB · 22 files no change ███░░░░░░░ 28.1% of 4.29 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.11 MiB · 2,973 files no change █████████░ 87.6% of 9.25 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
668 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
278.6 KiB ../node_modules/.pnpm/posthog-js@1.399.4/node_modules/posthog-js/dist/rrweb.js
267.7 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
234.9 KiB src/taxonomy/core-filter-definitions-by-group.json
221.8 KiB ../node_modules/.pnpm/posthog-js@1.399.4/node_modules/posthog-js/dist/module.js
164.0 KiB src/queries/validators.js
154.3 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
105.9 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

⚠️ Dist folder size — 🔺 +318 B (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1278.81 MiB · 🔺 +318 B (+0.0%)

Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 97.0% of changed backend lines covered — 10 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ███████████████████░ 97.0% (466 / 476)

File Patch Uncovered changed lines
products/warehouse_sources/backend/temporal/data_imports/sources/aviator/aviator.py 93.6% 158, 240, 243, 252–255, 283, 302, 342

🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 29288010754 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
warehouse_sources ███████████████████░ 96.1% 210,870 / 219,482

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

Copy link
Copy Markdown
Member Author

All CI checks pass except deploy preview, which fails on preview-environment infrastructure unrelated to this change. I re-ran it once and it failed again with a different infra error, confirming it's environmental rather than caused by this PR:

  • First run: hogland API error (HTTP 500): placement failed: … chunkfs mount: chunkfsd ready: not ready within 5s (preview object-storage provisioning timeout).
  • Re-run: failed to resolve reference "docker.io/clickhouse/clickhouse-server:…": tls: failed to verify certificate: x509: certificate has expired or is not yet valid (runner clock/registry-cert issue while pulling ClickHouse).

Neither touches the Aviator source code. The 166 required/product checks (including the warehouse-sources product tests, Jest, and Python quality) are green. Remaining merge block is the required human review.

@estefaniarabadan estefaniarabadan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isolated new data-warehouse source scaffold — self-contained under its own source directory with only the expected shared-registry additions (SOURCES.md, generated_configs.py, icon), tests included, CI green. Reviewed as part of a batch. LGTM 👍

@veria-ai

veria-ai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

Aviator responses carry arbitrary repository data (config-history diffs,
PR titles, branch names) that the name-based sample scrubber cannot
sanitise. Pass capture=False on both the sync session and the credential
probe so response bodies never reach the HTTP sample bucket, while
requests stay metered and logged.

Generated-By: PostHog Code
Task-Id: dbb3d70d-6421-4ead-96e0-a4c1110de0b6
Generated-By: PostHog Code
Task-Id: dbb3d70d-6421-4ead-96e0-a4c1110de0b6

Copy link
Copy Markdown
Member Author

Status

  • The unresolved review thread (HTTP sample capture retaining repository data) is fixed in 869caa4 and resolved — both the sync session and the credential probe now pass capture=False, so Aviator response bodies are excluded from HTTP sample capture while requests stay metered and logged.
  • All required checks are green; the PR is MERGEABLE / approved.

Note on the red deploy preview check (not caused by this PR)

deploy preview fails at migrate/startup with:

ModuleNotFoundError: No module named 'common.alerting'

Root cause is on master, not in this branch. .dockerignore is an allowlist (* followed by !common/<pkg> re-includes), and f34776e4 ("extract shared alert lifecycle state machine (1/6)", #68936) added the common/alerting package and an import from it in products/logs, but did not add !common/alerting to .dockerignore. So the package is excluded from the Docker build context and the image can't import it. This affects every PR's deploy preview (and the production image build) that includes that commit — it is unrelated to the Aviator data-warehouse source.

deploy preview is a non-required check, so the PR remains mergeable. The fix (!common/alerting in .dockerignore) belongs with the alerting state-machine series rather than in this feature PR, so I've left it out of scope here.

🦉 via talyn.dev

@talyn-app
talyn-app Bot merged commit 4e0e254 into master Jul 13, 2026
237 of 239 checks passed
@talyn-app
talyn-app Bot deleted the posthog-code/implement-aviator-source branch July 13, 2026 22:40
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 14, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-14 02:24 UTC Run
prod-us ✅ Deployed 2026-07-14 02:36 UTC Run
prod-eu ✅ Deployed 2026-07-14 02:36 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants