Hive
fix(server): remote xcresult processing follow-ups (grants + test-run UX)
GitHub issue · Closed
Follow-up fixes surfaced while investigating remote xcresult processing, after #11602 landed the migrate-time processor grants + the webhook_endpoints fix. Four fixes across three commits.
1. Grant tuist_processor read on automation_alerts
#11602 granted webhook_endpoints, which let ingestion run past the test_case.created dispatch and hit the next ungranted table. The flaky-alert enqueue (Tuist.Automations.enqueue_flaky_alert_evaluations -> Repo.all(Alert)) reads automation_alerts, which the least-privilege processor role also can’t read, raising 42501. It’s non-fatal (runs inside Tuist.Tasks.run_async), so the run still ingests fully but flaky-alert scheduling is silently skipped. Enumerated the full Postgres surface of the ingestion path (accounts, projects, webhook_endpoints, automation_alerts, oban_jobs, oban_peers); automation_alerts was the last gap. Added to both do_grant_processor_role and the runbook SQL.
While here, folded in the refactor from the closed #11607 so both land together: the grant set is now driven off a single audited table list (@processor_write_tables / @processor_read_tables) exposed as a DB-free Release.processor_role_grant_statements/3, and do_grant_processor_role/2 applies the REVOKE + re-GRANT sequence inside one repo.transaction/1 so a mid-sequence failure can’t leave the role with a torn privilege set. The runbook SQL was restructured to the same shape (REVOKE-first, ON TABLE on every grant, one combined read grant).
2. Download result for remote tuist inspect test runs
The “Download result” button only rendered when a command_events row was linked to the run, keyed under the command_event id. Remote inspect-test runs create no command_event and store the bundle under the test run id, so the button never appeared even though the bundle is in S3. Resolve the download source run-scoped: prefer the command_event key, else the run-scoped key (get_result_bundle_key/2, exactly what the upload wrote). The route/controller tries the command_event first, then the test run, and 404s cleanly if neither has a bundle.
3. De-duplicate devices on the test-run detail
A run processed across multiple retries could list the same device several times, because create_run_destinations inserts a fresh row per attempt. Collapse run_destinations to distinct (name, platform, os_version) for display.
4. No-test parse shows failed_processing, not “Skipped”
When the parser extracts zero test modules (aborted/empty xcresult), the Swift parser reports “skipped” vacuously from an empty test-case list, which reads on the dashboard as a real skip. Surface a zero-module parse as failed_processing.
Validation
mix formatclean on all touched Elixir files;mix compileclean with no new warnings on the changed files.- New
Tuist.ReleaseTest(DB-free) pins the exactprocessor_role_grant_statements/3output and asserts the runbook SQL keeps the same read/write/REVOKE lines in sync, so the two grant paths can’t silently drift again. - Fixes 2-4 are dashboard/parse behavior with no existing unit tests; verified by compile + review. The download path reuses the existing
get_result_bundle_key/2the upload path already writes to, so keys match.
Notes
automation_alertsis non-urgent (non-fatal). It applies automatically on the next deploy via the migrate-time grant, or can be applied ahead of the deploy with the runbook SQL.- The write-side of duplicate destinations (non-idempotent
create_run_destinations) is intentionally left as display-time dedup, since the grant fix stops the retries that produced duplicates. - The
xctest (N) encountered an errornormalization that was briefly here has been pulled into a dedicated PR that models these as run/target-level errors (matching Xcode’s Errors section) rather than as test cases.
🤖 Generated with Claude Code
Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.
Start a Flight and preserve its objective, outcome, and session here.
No GitHub comments yet.