Skip to content

feat(containers): add native sidebar and detail tabs support#304

Open
frruit wants to merge 1 commit into
pulp:mainfrom
frruit:feature/container-sidebar
Open

feat(containers): add native sidebar and detail tabs support#304
frruit wants to merge 1 commit into
pulp:mainfrom
frruit:feature/container-sidebar

Conversation

@frruit
Copy link
Copy Markdown

@frruit frruit commented May 21, 2026

  • add read-only container repository sidebar grouped by local/remote
  • add native container detail tabs: Details, Versions, Distributions
  • show version-level pull references with tag and digest
  • support href-based loading for container-push repositories
  • keep original container files untouched by using native variant files

- add read-only container repository sidebar grouped by local/remote
- add native container detail tabs: Details, Versions, Distributions
- show version-level pull references with tag and digest
- support href-based loading for container-push repositories
- keep original container files untouched by using native variant files
@frruit
Copy link
Copy Markdown
Author

frruit commented May 22, 2026

@sourcery-ai review

@SourceryAI
Copy link
Copy Markdown

Reviewer's Guide

Introduces a native container repository experience with a new list + sidebar layout, a tabbed detail view (Details/Versions/Distributions), and native API clients for container repositories/remotes and repository versions, including version-level pull reference display.

Sequence diagram for loading container detail and version pull references

sequenceDiagram
  actor User
  participant ExecutionEnvironmentDetail
  participant ContainerDistributionAPI
  participant ContainerRepositoryNativeAPI
  participant ContainerRemoteNativeAPI
  participant RepositoryVersionsTab
  participant GenericPulpAPI

  User->>ExecutionEnvironmentDetail: navigate to repository.detail
  ExecutionEnvironmentDetail->>ContainerDistributionAPI: list({ base_path })
  ContainerDistributionAPI-->>ExecutionEnvironmentDetail: distribution
  alt distribution.repository exists
    ExecutionEnvironmentDetail->>ContainerRepositoryNativeAPI: getByHref(distribution.repository)
    ContainerRepositoryNativeAPI-->>ExecutionEnvironmentDetail: ContainerRepositoryNativeType
  end
  alt repository.remote or distribution.remote exists
    ExecutionEnvironmentDetail->>ContainerRemoteNativeAPI: get(id from remote href)
    ContainerRemoteNativeAPI-->>ExecutionEnvironmentDetail: ContainerRemoteNativeType
  end
  ExecutionEnvironmentDetail-->>User: render tabs (Details / Versions / Distributions)

  User->>RepositoryVersionsTab: open Versions tab
  RepositoryVersionsTab->>ContainerRepositoryNativeAPI: listVersionsByHref(repository.pulp_href, { number })
  ContainerRepositoryNativeAPI-->>RepositoryVersionsTab: ContainerRepositoryVersionType
  RepositoryVersionsTab->>GenericPulpAPI: list('content/container/tags/', { repository_version })
  GenericPulpAPI-->>RepositoryVersionsTab: Tag list
  RepositoryVersionsTab->>GenericPulpAPI: list('content/container/manifests/', { repository_version })
  GenericPulpAPI-->>RepositoryVersionsTab: Manifest list
  RepositoryVersionsTab-->>User: show version details + pull references (tag & digest)
Loading

File-Level Changes

Change Details Files
Add native container repository list view with sidebar grouped by local vs remote repositories and CLI help for pushing images.
  • Introduce ExecutionEnvironmentList-native page that lists container distributions with filters, pagination, and registry-type labelling.
  • Embed new ContainerRepositorySidebar component to show all repositories grouped as local or remote with search and sticky layout styling.
  • Replace old list route export with the native implementation and expose sidebar via components barrel export.
  • Add responsive CSS grid layout and sidebar styling for the container list page.
src/containers/execution-environment-list/execution-environment-list-native.tsx
src/components/container-repository-sidebar.tsx
src/containers/execution-environment-list/execution-environment.scss
src/components/index.ts
src/containers/index.ts
Introduce native container detail page with tabs for Details, Versions, and Distributions backed by new API types.
  • Create ExecutionEnvironmentDetail-native container that loads the distribution, related repository, and remote, and wires LinkTabs-based routing for details, repository versions, and distributions.
  • Implement DetailsTab to show repository metadata, labels, and remote link/URL.
  • Implement RepositoryVersionsTab to list repository versions and show a single version’s content summary, base version links, and pull references (tag and digest) using GenericPulpAPI and ContainerRepositoryNativeAPI.
  • Implement DistributionsTab to list distributions related to the container with CLI pull commands, using DetailList with filtering/sorting.
  • Wire routes/exports so the app uses the native detail implementation instead of the legacy one.
src/containers/execution-environment-detail/execution-environment-detail-native.tsx
src/containers/execution-environment-detail/tab-details.tsx
src/containers/execution-environment-detail/tab-repository-versions.tsx
src/containers/execution-environment-detail/tab-distributions.tsx
src/containers/index.ts
src/containers/execution-environment-list/execution-environment.scss
Add native Pulp API clients/types for container repositories, repository versions, and container remotes and switch list/detail views to them where appropriate.
  • Introduce ContainerRepositoryNativeAPI with helpers to get/list repositories and repository versions, by id or by href; define ContainerRepositoryNativeType and ContainerRepositoryVersionType.
  • Introduce ContainerRemoteNativeAPI and ContainerRemoteNativeType for native container remotes, and use it in the new registry list and detail loader.
  • Export new native APIs/types from the central api index.
  • Update ExecutionEnvironmentRegistryList to a native variant that lists remotes via ContainerRemoteNativeAPI while still using existing create/update/delete/sync/index APIs.
src/api/container-repository-native.ts
src/api/container-remote-native.ts
src/api/index.ts
src/containers/execution-environment/registry-list-native.tsx
src/containers/index.ts
Enable native containers and remote registry navigation in the main menu and modernize the remote registries list to the new pattern.
  • Remove BROKEN-based conditions so Containers and Remote registries always appear in the standalone menu.
  • Replace ExecutionEnvironmentRegistryList export with the new native registry-list implementation that uses search, filters, pagination, and AlertList/RemoteForm patterns consistent with other pages.
src/menu.tsx
src/containers/execution-environment/registry-list-native.tsx
src/containers/index.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@SourceryAI SourceryAI left a comment

Choose a reason for hiding this comment

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

Hey - I've found 4 issues, and left some high level feedback:

  • In the new native execution environment list and registry list components, state and helper methods for edit/delete/sync/index (e.g. showDeleteModal, selectedItem, syncRegistry, indexRegistry) exist but ListItemActions is rendered with empty kebabItems/buttons, so these actions are effectively unreachable; either wire up the actions or remove the unused state/methods to avoid dead code.
  • The ContainerRepositorySidebar fetches a single page with page_size: 200 and does not paginate, which will silently drop repositories beyond that limit; if large installations are expected, consider adding pagination or a full list fetch pattern that scales beyond the first 200 items.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the new native execution environment list and registry list components, state and helper methods for edit/delete/sync/index (e.g. `showDeleteModal`, `selectedItem`, `syncRegistry`, `indexRegistry`) exist but `ListItemActions` is rendered with empty `kebabItems`/`buttons`, so these actions are effectively unreachable; either wire up the actions or remove the unused state/methods to avoid dead code.
- The `ContainerRepositorySidebar` fetches a single page with `page_size: 200` and does not paginate, which will silently drop repositories beyond that limit; if large installations are expected, consider adding pagination or a full list fetch pattern that scales beyond the first 200 items.

## Individual Comments

### Comment 1
<location path="src/containers/execution-environment/registry-list-native.tsx" line_range="376" />
<code_context>
+        <Td>
+          <CopyURL url={item.url} />
+        </Td>
+        <ListItemActions kebabItems={[]} buttons={[]} />
+      </Tr>
+    );
</code_context>
<issue_to_address>
**issue (bug_risk):** Table row includes a non-<Td>/<Th> child, which can break table layout/accessibility.

Because PatternFly tables require each direct `<Tr>` child to be a `<Td>` or `<Th>`, rendering `<ListItemActions>` directly under `<Tr>` can break layout and accessibility. Please wrap it in a cell, for example:

```tsx
<Td isActionCell>
  <ListItemActions kebabItems={[]} buttons={[]} />
</Td>
```
</issue_to_address>

### Comment 2
<location path="src/containers/execution-environment-list/execution-environment-list-native.tsx" line_range="353" />
<code_context>
+        <Td>
+          <Label>{item.remote ? t`Remote` : t`Local`}</Label>
+        </Td>
+        <ListItemActions kebabItems={[]} />
+      </Tr>
+    );
</code_context>
<issue_to_address>
**issue (bug_risk):** ListItemActions should be wrapped in a table cell instead of being a direct child of <Tr>.

Rendering `<ListItemActions>` directly under `<Tr>` creates invalid table markup. It should be wrapped in a `<Td>` (usually with `isActionCell`), for example:

```tsx
<Td isActionCell>
  <ListItemActions kebabItems={[]} />
</Td>
```
</issue_to_address>

### Comment 3
<location path="src/containers/execution-environment-list/execution-environment-list-native.tsx" line_range="359-368" />
<code_context>
+  }
+
+  private queryEnvironments() {
+    this.setState({ loading: true }, () =>
+      ContainerDistributionAPI.list(this.state.params)
+        .then((result) => {
+          this.setState({
+            items: result.data.results,
+            itemCount: result.data.count,
+            loading: false,
+          });
+        })
+        .catch((e) =>
+          this.addAlert(t`Error loading environments.`, 'danger', e?.message),
+        ),
</code_context>
<issue_to_address>
**issue (bug_risk):** Loading state is never cleared on error when querying environments.

In `queryEnvironments`, `loading` is set to `true` but never reset in the `catch` block, so a failed `ContainerDistributionAPI.list` call leaves the UI stuck in a loading state. You should also set `loading: false` in the error path, e.g.:

```tsx
.catch((e) => {
  this.setState({ loading: false });
  this.addAlert(t`Error loading environments.`, 'danger', e?.message);
})
```
</issue_to_address>

### Comment 4
<location path="src/containers/execution-environment/registry-list-native.tsx" line_range="406-407" />
<code_context>
+          'success',
+        ),
+      )
+      .catch((err) => {
+        const { status, statusText } = err.response;
+        this.addAlert(
+          t`Remote registry "${name}" could not be deleted.`,
</code_context>
<issue_to_address>
**issue (bug_risk):** Error handling assumes `err.response` exists, which can throw on network/other errors.

In `deleteRegistry` (and similarly `syncRegistry`/`indexRegistry`), `err.response` is destructured without a null check, so network or other non-HTTP errors (where `err.response` is undefined) will throw a new error and hide the original. Please guard access to `status`/`statusText`, e.g. via optional chaining, before calling `addAlert`.
</issue_to_address>

Hi @frruit! 👋

Thanks for trying out Sourcery by commenting with @sourcery-ai review! 🚀

Install the sourcery-ai bot to get automatic code reviews on every pull request ✨

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

<Td>
<CopyURL url={item.url} />
</Td>
<ListItemActions kebabItems={[]} buttons={[]} />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Table row includes a non-/ child, which can break table layout/accessibility.

Because PatternFly tables require each direct <Tr> child to be a <Td> or <Th>, rendering <ListItemActions> directly under <Tr> can break layout and accessibility. Please wrap it in a cell, for example:

<Td isActionCell>
  <ListItemActions kebabItems={[]} buttons={[]} />
</Td>

<Td>
<Label>{item.remote ? t`Remote` : t`Local`}</Label>
</Td>
<ListItemActions kebabItems={[]} />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): ListItemActions should be wrapped in a table cell instead of being a direct child of .

Rendering <ListItemActions> directly under <Tr> creates invalid table markup. It should be wrapped in a <Td> (usually with isActionCell), for example:

<Td isActionCell>
  <ListItemActions kebabItems={[]} />
</Td>

Comment on lines +359 to +368
this.setState({ loading: true }, () =>
ContainerDistributionAPI.list(this.state.params)
.then((result) => {
this.setState({
items: result.data.results,
itemCount: result.data.count,
loading: false,
});
})
.catch((e) =>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Loading state is never cleared on error when querying environments.

In queryEnvironments, loading is set to true but never reset in the catch block, so a failed ContainerDistributionAPI.list call leaves the UI stuck in a loading state. You should also set loading: false in the error path, e.g.:

.catch((e) => {
  this.setState({ loading: false });
  this.addAlert(t`Error loading environments.`, 'danger', e?.message);
})

Comment on lines +406 to +407
.catch((err) => {
const { status, statusText } = err.response;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Error handling assumes err.response exists, which can throw on network/other errors.

In deleteRegistry (and similarly syncRegistry/indexRegistry), err.response is destructured without a null check, so network or other non-HTTP errors (where err.response is undefined) will throw a new error and hide the original. Please guard access to status/statusText, e.g. via optional chaining, before calling addAlert.

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