feat(frontend): add Check remote now to the Commits tab [IFC-3101]

Merged
PA
1mo ago
+1018-47

Why

An operator who looks at a read-only repository's Commits tab today cannot ask Infrahub to look at the remote. They have to wait up to 15 minutes for the scheduled check (the default of INFRAHUB_GIT_READ_ONLY_REFS_CHECK_INTERVAL_MINS), or call the InfrahubReadOnlyRepositoryCheckRefs mutation through the API. FR-015 in the spec requires the check to be available on demand, and the plan puts that entry point on the Commits tab.

This PR adds a "Check remote now" button to the Commits tab of read-only repositories. It covers tasks T072, T073, T074 and T075 of tasks.md, and adds the e2e test for T095.

Not in this PR:

  • The design decisions in T094 (button placement on the design canvas). The button's position after the freshness line is provisional.
  • Showing a check that is already running when the user presses the button. See "Known gap" below.

Builds on the Commits tab (#10805, merged into the epic branch). The reader that makes the Commits tab show real commits came in with #10836.

What changed

What users see:

  • On a read-only repository's Commits tab, a user with update permission on the repository can press "Check remote now". A user without that permission sees the button disabled, with the permission message as a tooltip. Read-write repositories have no button.
  • After the user presses the button, it stays disabled and a "View task" link beside it opens the started task in the repository's Tasks tab. When that task ends, the commit log reloads and shows the new check time, and any commit the remote gained since the last import, even if the remote has not moved.
  • The started check is followed on the branch it was started on, including after the commit view reloads. A check started from another browser tab or by another user is not shown.

How it is built:

  • The layers follow the entity pattern: api/check-remote-refs-from-api.ts (GraphQL mutation) โ†’ domain/use-cases/check-remote-refs.ts โ†’ ui/queries/check-remote-refs.mutation.ts โ†’ ui/repository-check-remote-button.tsx, rendered by the commits header. The page decides whether the repository is read-only and whether the user may update it, from the object-details outlet context, and passes both down.

  • ui/queries/get-remote-check-task.query.ts reads the started task back from the mutation cache with useMutationState, as entities/diff/ui/diff-refresh-button.tsx does, matched on repository and branch, and polls its state by id every 2 seconds until it leaves the ongoing states. The run is created before the mutation returns its id, so a task that is not listed was deleted and ends the check. Reading the state rather than a count also avoids the task manager's count cache. A failed poll keeps the last answer instead of raising a toast. When the task is first seen ended, the poll invalidates the repository's commit log; its query key is outside the repository key prefix, so that invalidation does not refetch the poll.

  • It uses the tasks entity only through its domain/model (TASK_ONGOING_STATES), never its api/.

  • When the commit log fails to load, the toolbar with the refresh and "Check remote now" buttons still shows.

Fixes to the Commits tab from the review of this PR (separate commit):

  • A tracked ref that no longer exists on the remote (REF_MISSING) now shows a warning.
  • Copying a commit hash reports a failure instead of throwing in a browser without document.execCommand.
  • The behind fixture's pending_count counts the head, as the backend does (backend/infrahub/git/state/log_reader.py).
  • The pending-count tests cover REF_MISSING.

What stayed the same: no schema change, and no change to the read-write repository view. graphql-cache.d.ts also gains the REPOSITORY_COMMITS entry, which #10805 did not regenerate.

How to review

  1. ui/queries/get-remote-check-task.query.ts: the one design choice without precedent in the codebase. The commit-log invalidation runs inside the poll's queryFn, guarded so it runs once. The rules on invalidation cover mutations only. The alternative is an effect that runs when the task changes to ended.
  2. ui/repository-check-remote-button.tsx and ui/queries/check-remote-refs.mutation.ts.
  3. The prop changes in repository-commits-header.tsx, repository-commits-manager.tsx and pages/objects/object-details/repository-commits.tsx.
  4. Tests: repository-check-remote-button.test.tsx and tests/e2e/repository/test_repository_check_remote.py.
  5. Spec documents: the T072 to T075 and T095 notes in tasks.md, the "Check remote now" and "Departure from FR-025" bullets in plan.md, and steps 2 and 3 of quickstart.md Phase C.

Known gap (FR-025)

The Commits tab follows only the task it started, so it cannot see a scheduled check. If one holds the repository when the user presses the button, the new run exits at once as SKIPPED_CLAIMED and records no check time (checker.py). The new check time then appears only after the scheduled check ends and the user refreshes. FR-025 allows the duplicate run when it reports the claim it found. The run records claimed_by, but the interface does not show it. This is recorded in plan.md and not fixed here.

Review findings declined

  • The mutation key ["repository", โ€ฆ] differs from the ["repositories"] query-key root. The entity's other mutation keys use the same form.
  • The e2e test has no branch fixture. The sibling test also runs on main, and the repository is registered there on purpose.
  • The changelog fragment is longer than one sentence. Its length predates this change.
  • "HISTORY rows render no state label": the blank cell is deliberate in the Commits tab (repository-commits.view.ts, asserted in repository-commits.view.test.ts), and it uses no colour, so FR-028 holds. Whether to add a "History" label is a product decision.
  • The e2e helper functions have no one-line description. They are test-module helpers.

How to test

cd frontend/app && pnpm exec vitest run src/entities/repository src/pages/objects
cd frontend && pnpm exec biome ci .
cd frontend/app && pnpm knip && pnpm exec betterer ci
INFRAHUB_TESTING_IMAGE_VER=local INFRAHUB_TESTING_DOCKER_PULL=false uv run pytest -c tests/e2e/pytest.ini tests/e2e/repository/test_repository_check_remote.py

Results on this branch:

  • Vitest: the full suite passes, 1,829 tests in 231 files. biome ci, knip and betterer ci pass.
  • The two tests for reloading the commit log fail when the reload in the poll is disabled.
  • e2e: not confirmed locally. Two runs failed during setup because the database container restarted ("Unable to connect to the database", HTTP 503) while another Infrahub stack was running on the same machine. No test code ran. T095 stays open until the test passes in CI.
  • Manual: on a local stack built from this branch, with a read-only repository on a local remote, pressing the button after pushing a commit showed the new commit as "Remote head", "1 commit pending import", and a check time 2 minutes later than before.

Impact & rollout

  • Frontend only. The InfrahubReadOnlyRepositoryCheckRefs mutation already exists and requires update permission on the repository.
  • The task manager is polled only while a check started from the Commits tab runs.
  • The changelog fragment changelog/+ifc-3101-readonly-refs-check.added.md now describes the button.

๐Ÿค– Generated with Claude Code