Skip to content

Merge stable into release-1.10#9529

Draft
infrahub-github-bot-app[bot] wants to merge 11 commits into
release-1.10from
stable
Draft

Merge stable into release-1.10#9529
infrahub-github-bot-app[bot] wants to merge 11 commits into
release-1.10from
stable

Conversation

@infrahub-github-bot-app

@infrahub-github-bot-app infrahub-github-bot-app Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Merging stable into release-1.10 after merging pull request #9506.


Summary by cubic

Reduces Git repository lock time by guarding only clone/fetch/worktree mutations and running imports after the lock, cutting contention during syncs and repo creation. Adds an Artifact detail Refresh button, fixes batching and utilization issues, and corrects repository diff file actions.

  • Bug Fixes

    • Repository lock now covers only on-disk Git changes; object import runs outside the lock.
    • create_commit_worktree raises CommitNotFoundError; InfrahubRepository.init fetches a missing commit under the repo lock, then retries.
    • Sync pins the commit to import while locked and isolates per-branch failures (missing commits and other errors are logged and skipped, not aborting the run).
    • Repository diff now reports added/removed correctly in GET /api/diff/files and the Proposed Change Files tab.
    • Computed-attribute workflow submission chunk size is floored at 1 to avoid errors when the Prefect limit is 1.
    • IPAM utilization deduplicates repeated prefixes when collecting children to avoid inflated counts.
    • Artifact detail now has a Refresh button to reload updated content without a full page refresh.
  • Refactors

    • Added infrahub/git/sync.py with RepositorySyncer, RepositoryAdder, RepositoryImporter, and RepositoryFileImporter; introduced PendingObjectImport.
    • Split InfrahubRepository.sync into collect_pending_imports (locked) and a separate import phase; tasks use these components.
    • Introduced a lock test adapter (RecordingLockRegistry, LockTimeline, RecordingImporter) and tests to assert lock scope; updated testing docs.
    • Dependency bumps: react-router to 7.17.0, qs to 6.15.2, shell-quote to 1.8.4, plus existing docs/frontend updates (e.g., vite, postcss, ws).

Written for commit a6eb1a7. Summary will update on new commits.

Review in cubic

…9506)

* test: reusable lock recording adapter (#9439)

* adding new lock test adapter

* make the lock installation a fixture

* use constants

* typing issues

* docs(test): trim LockTimeline docstring to one line

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(test): give RecordingLock an explicit typed constructor

ty (the CI type checker) does not honor the mypy-style `# type: ignore[arg-type]`, so
forwarding `*args: object` into InfrahubLock.__init__ failed ty check. Mirror the base
constructor signature and forward named arguments instead, dropping the ignore.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: accept a collection of lock names in assert_never_overlap

Generalize assert_never_overlap to take a Collection[str] and verify that
no two of the given locks are ever held simultaneously, instead of being
limited to exactly two lock names.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: split checkpoint assertion and require injected timeline

- Replace the confusing `expected` flag on assert_held_at_checkpoint with
  two intent-revealing methods (assert_held_at_checkpoint /
  assert_not_held_at_checkpoint) delegating to a shared private helper.
- Make `timeline` a required argument of RecordingLockRegistry instead of
  constructing one internally; install_recording_lock_registry now owns
  the default construction and injects it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): run object import outside the repository lock during sync (#9440)

* create a seam to inject the new test adapter

* refactor(git): split repo sync into git-state collection and import

Extract collect_pending_imports() which performs the on-disk git mutations
(fetch, branch creation, pull, commit-worktree pinning) and returns one entry
per branch to import, instead of importing inline. sync() now runs the returned
imports. Behavior is preserved; this is the seam that lets the import run outside
the repository lock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): run object import outside the repository lock during sync

The repository lock is held to serialize git working-copy mutations, but it was
also kept reserved during the object import, which is the slow phase when a branch
carries conflicting data. Narrow the lock so it covers only the git-state work:

- sync_repository now locks collect_pending_imports() and runs the imports after
  releasing the lock; the import target is injectable for testing.
- sync_remote_repositories no longer wraps the origin sync and the reinit-fallback
  import in its outer repository lock; only the local init/reinit stays locked.

Imports read from immutable per-commit worktrees pinned during the locked phase, so
they no longer need the lock. Resolves the long lock reservation reported in IFC-1566.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(git): assert object import runs outside the repository lock

Drives sync_repository with a recording lock registry and an injected recording
importer, asserting the repository lock is not held when the import runs. Replaces
the earlier pydantic-recast/flow-override scaffold with the injected-importer seam.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(git): create RepositorySyncer and RepositoryImporter components

Replace the injected callable with a RepositoryImporter implementation that checkpoints
into the lock timeline, asserting the repository lock is not held when the import runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(git): drop redundant lock-scope comment in sync flow

The narrow async-with scope is self-evident from the structure, and the
intent is pinned by test_repository_lock_released_before_import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(git): rename BranchImport to PendingObjectImport

The dataclass models a pending import of repository objects (the target
Infrahub branch and the source commit), not a branch. Rename it and the
corresponding parameter so they read as queued import work, matching
collect_pending_imports / pending_imports.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(git): call assert_not_held_at_checkpoint for the released-lock check

The test invoked assert_held_at_checkpoint with an unsupported expected=
kwarg. Use the adapter's dedicated assert_not_held_at_checkpoint, which
expresses the intent that the repository lock is released before import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): run object import outside the repository lock when adding a repository (#9441)

* create a seam to inject the new test adapter

* fix(git): run object import outside the repository lock during sync

The repository lock is held to serialize git working-copy mutations, but it was
also kept reserved during the object import, which is the slow phase when a branch
carries conflicting data. Narrow the lock so it covers only the git-state work:

- sync_repository now locks collect_pending_imports() and runs the imports after
  releasing the lock; the import target is injectable for testing.
- sync_remote_repositories no longer wraps the origin sync and the reinit-fallback
  import in its outer repository lock; only the local init/reinit stays locked.

Imports read from immutable per-commit worktrees pinned during the locked phase, so
they no longer need the lock. Resolves the long lock reservation reported in IFC-1566.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(git): create RepositorySyncer and RepositoryImporter components

Replace the injected callable with a RepositoryImporter implementation that checkpoints
into the lock timeline, asserting the repository lock is not held when the import runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): run object import outside the repository lock when adding a repository

add_git_repository held the repository lock for the whole flow (local clone, default-branch
object import, and origin sync). Narrow it to cover only the local clone and pinning its
commit worktree; the import (via the injected RepositoryImporter) and the origin sync (via
RepositorySyncer) now run after the lock is released. Imports read from the immutable
per-commit worktree, so they no longer need the lock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(git): assert add_git_repository imports outside the repository lock

Add a no-mock test driving add_git_repository with a recording lock registry and a recording
RepositoryImporter, asserting the repository lock is not held when the import runs. Update the
existing mock-based create test for the new structure (import via the importer, sync via
RepositorySyncer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(git): drop redundant add_git_repository lock comment

The narrow async-with scope is self-evident, and the import-outside-lock invariant is pinned by test_add_git_repository_releases_lock_before_import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(git): restore create_commit_worktree as a local-only primitive

Revert the on-demand remote fetch that was placed inside the worktree
primitive. The fetch mutates the shared clone and belongs at the
orchestration layer under the repository lock, not in a synchronous local
working-copy primitive. The primitive again raises CommitNotFoundError when
the commit is absent from the local clone.

The fetch-on-miss safety net is re-homed under the repository lock in a
follow-up commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): fetch a missing commit under the repository lock during init

When a task references a commit that this worker's clone has not fetched
yet, init() now fetches once under the repository lock and retries the
worktree creation before surfacing CommitNotFoundError. The lock serializes
the shared-clone fetch against concurrent resets and merges.

This re-homes, at the orchestration layer, the recovery that previously
lived inside the create_commit_worktree primitive, and keeps the safety net
for the window the eventually-consistent sync fan-out leaves open: a
dispatched task can outrun the fetch broadcast, or run on a restarted or
newly added worker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor: recording adapter test package + common class RecordingImporter

* refactor: renamed locally overridden object

* test(git): assert CommitNotFoundError message in worktree and init tests

Add match= to the CommitNotFoundError assertions so they verify the commit
SHA and repository identifier carried in the message, not just the exception
type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(git): tighten init fetch-retry comment

Keep only the non-obvious rationale (the commit may be remote-only; the lock
serializes shared-clone mutations); drop the unknowable database provenance
and the enumerated operation list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(git): use the recording lock adapter in the add-repo create test

Replace the patched lock mock and the weak assert_any_call with
install_recording_lock_registry and a timeline assertion that the repository
lock was acquired, removing unittest.mock from the lock check in this test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* tests: use proper fixtures

* refactor(git): extract RepositoryAdder and drop the add_git_repository importer param

Move the locked clone/worktree and the outside-lock default-branch import into
a RepositoryAdder component (sibling to RepositorySyncer). The task no longer
needs an injectable importer parameter and always uses RepositoryFileImporter;
the lock-release test injects the recording importer through the component
instead. Invert the active-status check to return early.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): pin the sync import commit while the repository lock is held

sync_remote_repositories resolved the import commit from the live branch head
after releasing the lock, so a concurrent operation could advance the branch
between lock release and import. Capture the commit while the lock is held and
pass it to import_objects_from_files, matching the add and sync-via-syncer flows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): isolate per-branch failures while collecting pending imports

collect_pending_imports performed the git working-copy side of a sync for every
branch and only then returned the imports for the caller to apply. A failure on
a later branch aborted the whole collection, dropping the imports for branches
already processed in the same run. Wrap each branch in its own error boundary so
a single failing branch is logged and skipped while the rest are still imported.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(git): mark git_repos_dir as a usefixtures dependency

git_repos_dir is needed only for its side effect (pointing the repositories
directory at a temp path); it was declared as an unused parameter. Request it
via usefixtures so the dependency is explicit and not mistaken for dead code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor: split the method, too many statements

* fix docstring collect_pending_imports

* docs: clarify locked-phase scope in import docstring

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): log traceback when skipping a branch during import collection

The per-branch error boundaries swallow the exception, so without exc_info the
traceback was lost. Pass exc_info=exc to capture the skipped branch's call
stack; the exception message is already part of the rendered traceback, so no
separate error field is added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(git): catch CommitNotFoundError in repository failure handlers

create_commit_worktree now raises CommitNotFoundError, a sibling of
RepositoryError rather than a subclass, so the existing per-branch
isolation and failure-tagging handlers no longer caught a missing-commit
failure they previously did. Add CommitNotFoundError explicitly to the
relevant except clauses so a missing commit skips the branch / tags and
skips the repo instead of aborting the whole sync.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: add changelog fragment for repository lock narrowing [#6639]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: update testing documentation for lock test adapter

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The artifact detail page offered no way to reload the artifact view
after regenerating it, so the only way to see updated content was a
full browser refresh.

This adds the shared refresh button to the artifact header, next to
the regenerate action. Clicking it refetches the artifact object and
its rendered content in place, matching the refresh behavior already
used on object detail pages.

Closes #9335
@bilalabbad bilalabbad requested a review from a team as a code owner June 10, 2026 15:41
… updates (#9298)

Bumps the npm_and_yarn group with 5 updates in the /docs directory:

| Package | From | To |
| --- | --- | --- |
| [@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs) | `7.29.0` | `7.29.4` |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.3.3` | `3.4.5` |
| [fast-uri](https://github.com/fastify/fast-uri) | `3.1.0` | `3.1.2` |
| [follow-redirects](https://github.com/follow-redirects/follow-redirects) | `1.15.11` | `1.16.0` |
| [lodash](https://github.com/lodash/lodash) | `4.17.23` | `4.18.1` |

Bumps the npm_and_yarn group with 1 update in the /frontend/app directory: [ws](https://github.com/websockets/ws).
Bumps the npm_and_yarn group with 2 updates in the /frontend/packages/plugins/template directory: [postcss](https://github.com/postcss/postcss) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `@babel/plugin-transform-modules-systemjs` from 7.29.0 to 7.29.4
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.4/packages/babel-plugin-transform-modules-systemjs)

Updates `dompurify` from 3.3.3 to 3.4.5
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](cure53/DOMPurify@3.3.3...3.4.5)

Updates `fast-uri` from 3.1.0 to 3.1.2
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](fastify/fast-uri@v3.1.0...v3.1.2)

Updates `follow-redirects` from 1.15.11 to 1.16.0
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](follow-redirects/follow-redirects@v1.15.11...v1.16.0)

Updates `lodash` from 4.17.23 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](lodash/lodash@4.17.23...4.18.1)

Updates `mermaid` from 11.12.3 to 11.15.0
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.12.3...mermaid@11.15.0)

Updates `postcss` from 8.5.9 to 8.5.15
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.5.3...8.5.15)

Updates `webpack-dev-server` from 5.2.2 to 5.2.4
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/main/CHANGELOG.md)
- [Commits](webpack/webpack-dev-server@v5.2.2...v5.2.4)

Updates `ws` from 8.20.0 to 8.20.1
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](websockets/ws@8.20.0...8.20.1)

Updates `postcss` from 8.5.3 to 8.5.15
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.5.3...8.5.15)

Updates `vite` from 6.4.1 to 6.4.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.4.2/packages/vite)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-modules-systemjs"
  dependency-version: 7.29.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: dompurify
  dependency-version: 3.4.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: fast-uri
  dependency-version: 3.1.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: mermaid
  dependency-version: 11.15.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: postcss
  dependency-version: 8.5.15
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: webpack-dev-server
  dependency-version: 5.2.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: ws
  dependency-version: 8.20.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: postcss
  dependency-version: 8.5.15
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 6.4.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@bilalabbad bilalabbad requested review from a team as code owners June 10, 2026 16:24
polmichel and others added 8 commits June 10, 2026 18:51
When PREFECT_SERVER_EVENTS_MAXIMUM_RELATED_RESOURCES is 1, the chunk size
derived from integer-dividing it by 2 became 0, raising ValueError from
range() during batch submission. Floor the chunk size at 1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
get_children iterated the input prefix list directly, so passing the same
prefix more than once double-counted its children and inflated utilization
counts and percentages. Collect the prefix ids into a set before lookup,
restoring the prior dedup semantics. No current caller passes duplicates;
this is defensive hardening.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…updates (#9537)

Bumps the npm_and_yarn group with 1 update in the /docs directory: [qs](https://github.com/ljharb/qs).
Bumps the npm_and_yarn group with 1 update in the /frontend/app directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).


Updates `qs` from 6.14.2 to 6.15.2
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](ljharb/qs@v6.14.2...v6.15.2)

Updates `react-router` from 7.14.2 to 7.15.0
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.15.0/packages/react-router)

Updates `react-router` from 7.14.2 to 7.15.0
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.15.0/packages/react-router)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.15.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: react-router
  dependency-version: 7.15.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: react-router
  dependency-version: 7.15.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…date (#9539)

Bumps the npm_and_yarn group with 1 update in the /docs directory: [shell-quote](https://github.com/ljharb/shell-quote).


Updates `shell-quote` from 1.8.3 to 1.8.4
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](ljharb/shell-quote@v1.8.3...v1.8.4)

Updates `shell-quote` from 1.8.3 to 1.8.4
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](ljharb/shell-quote@v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.8.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: shell-quote
  dependency-version: 1.8.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
calculate_diff_between_commits labelled a file present only in the new
commit as "removed" and one present only in the old commit as "added",
inverting the action reported by GET /api/diff/files and the Proposed
Change Files tab. The mapping now follows git diff first->second
(first=old, second=new). The proposed-change repo-diff caller, which
relied on the inverted behaviour via reversed arguments, is corrected to
match, and the component test now asserts in the API consumption
direction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: floor computed-attribute chunk size at 1; dedup ipam utilization children
fix(git): correct added/removed file action in repository diffs
@polmichel polmichel marked this pull request as draft June 11, 2026 14:49
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.

3 participants