feat: repository profiles page#562
Conversation
8e08b7f to
909a080
Compare
There was a problem hiding this comment.
Pull request overview
Adds the Repository Profiles page and introduces shared “Profiles” UI primitives (list/header/side panel/actions/removal modal) used across multiple profile types, with corresponding MSW handlers and Vitest coverage.
Changes:
- Added Repository Profiles page support, including repository profile source editing and updated MSW handlers/mocks.
- Introduced a new shared
profilesfeature (list, empty state, view side panel, actions, removal modal) and adopted it in several profile pages. - Added/updated tests across repository/security/script/package/WSL profiles and adjusted navigation styling.
Reviewed changes
Copilot reviewed 209 out of 210 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
src/tests/server/handlers/repositoryProfiles.ts |
Extends MSW coverage for repository profile CRUD and legacy removal action |
src/tests/server/handlers/instance.ts |
Adds MSW behavior for repository-profile instance queries |
src/tests/mocks/repositoryProfiles.ts |
Updates mock shape for repository profiles (APT sources objects, applied_count) |
src/tests/mocks/apt-sources.ts |
Extends APT sources mocks used by repository profiles |
src/pages/dashboard/repositories/repository-profiles/RepositoryProfilesPage.tsx |
Adjusts repository profiles query usage on page load |
src/features/repository-profiles/api/useRepositoryProfiles.ts |
Updates repository profile mutations to send APT source payloads |
src/features/repository-profiles/api/useGetProfileInstancesCount.ts |
New hook to compute associated instance count for repository profiles |
src/context/profiles.tsx |
New Profiles context/provider to share remove/limit state across profile pages |
src/features/security-profiles/hooks/useSecurityProfileDownloadAudit.tsx |
Adds client-side CSV download logic |
src/features/profiles/... |
New shared profile components (list/header/empty state/view side panel/removal modal, etc.) |
src/templates/dashboard/SecondaryNavigation/... |
Fixes active className logic and sticky nav styling |
.changeset/*.md |
Adds changesets for repository profiles feature and nav fix |
Comments suppressed due to low confidence (1)
src/features/repository-profiles/api/useRepositoryProfiles.ts:100
remove_apt_sourcesis part ofEditRepositoryProfileParamsand is populated byRepositoryProfileForm, buteditRepositoryProfileQuerydrops it when building the PUT payload. As a result, removed APT sources won’t be sent to the API and won’t be removed server-side. Includeremove_apt_sourcesin the request body (and consider passing through unchanged fields consistently).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| {isSecurityProfile(profile) && profile.mode == "audit-fix-restart" && ( | ||
| <InfoGrid.Item |
There was a problem hiding this comment.
Use strict equality here (===) instead of == to avoid unexpected type coercion when checking the security profile mode.
| if (path !== resetKey) { | ||
| setIsProfileLimitReached(false); | ||
| setResetKey(path); | ||
| } |
There was a problem hiding this comment.
State updates are being triggered during render when path !== resetKey (calling setIsProfileLimitReached / setResetKey). This can cause React warnings and render loops. Move this reset logic into a useEffect that runs when path changes (or derive state from path), rather than calling setters during render.
There was a problem hiding this comment.
address in Standardize Profiles PR
| queryFn: async () => | ||
| authFetch.get("computers", { | ||
| params: { | ||
| query: `profile:repository:${profileId}`, | ||
| root_only: false, | ||
| }, | ||
| }), |
There was a problem hiding this comment.
This query only uses response.data.count, but it doesn’t pass a limit param. That likely downloads an entire page of instances unnecessarily. Set limit: 1 (and optionally offset: 0) in the request params to reduce payload while still getting an accurate count from the paginated response.
| const { data: response, isLoading } = useQuery< | ||
| AxiosResponse<ApiPaginatedResponse<Instance>>, | ||
| AxiosError<ApiError> |
There was a problem hiding this comment.
This hook uses isLoading, but other new/updated hooks in this PR use React Query v5’s isPending. To keep consistency (and avoid isLoading being undefined depending on version), switch to isPending here as well.
| .content { | ||
| flex-grow: 1; | ||
| overflow-y: auto; | ||
| padding: 1rem 1.5rem; | ||
| } | ||
|
|
||
| .title { | ||
| flex-grow: 1; | ||
| font-size: 1rem; | ||
| font-weight: 400; | ||
| margin: 0; | ||
| } | ||
|
|
||
| .content { | ||
| flex-grow: 1; | ||
| overflow-y: auto; | ||
| padding: 1rem 1.5rem; | ||
| } |
There was a problem hiding this comment.
The .content rule is duplicated with identical declarations. Remove the duplicate block to avoid confusion and keep the stylesheet maintainable.
| apt_sources: [ | ||
| aptSources[0], | ||
| aptSources[1], | ||
| aptSources[2], | ||
| aptSources[3], | ||
| aptSources[4], | ||
| aptSources[5]], |
There was a problem hiding this comment.
There is trailing whitespace after the commas in this array (e.g., aptSources[0], ). Many linters flag trailing spaces and it also creates noisy diffs. Please run the formatter / remove trailing whitespace and format the array consistently (each item on its own line, closing bracket aligned).
| { | ||
| id: 6, | ||
| access_group: "group1", | ||
| gpg_key: "key1", | ||
| line: "deb http://example.com/ubuntu focal main", | ||
| name: "source6", | ||
| profiles: ["repo-profile-1",] as string[], | ||
| }, | ||
|
|
||
| { | ||
| id: 7, | ||
| access_group: "group1", | ||
| gpg_key: "key1", | ||
| line: "deb http://example.com/ubuntu focal main", | ||
| name: "source7", | ||
| profiles: ["repo-profile-1",] as string[], | ||
| }, | ||
|
|
There was a problem hiding this comment.
These new mock entries use profiles: ["repo-profile-1",] (dangling comma after the last element) and add extra blank lines between objects. This is likely to be reformatted by Prettier and can cause lint churn; please format consistently with the rest of the file (no trailing element comma in single-line arrays, no extra blank lines).
| const unfilteredRepositoryProfilesResult = getRepositoryProfilesQuery(); | ||
|
|
There was a problem hiding this comment.
getRepositoryProfilesQuery() is now called without limit: 0, but RepositoryProfileList does client-side pagination and therefore needs the full unpaginated dataset. With the current MSW/default backend behavior, omitting limit defaults to 20, so users with >20 profiles won’t be able to reach the remaining profiles. Either restore limit: 0 here, or reintroduce server-side pagination in the container/list.
| if (!path) { | ||
| throw new Error(); | ||
| } |
There was a problem hiding this comment.
When path is null/empty, this throws new Error() without a message. That makes debugging and user-facing error reporting harder. Throw an error with a clear message (or handle null by returning early) so failures are actionable.
| const url = URL.createObjectURL(blob); | ||
|
|
||
| const link = document.createElement("a"); | ||
|
|
||
| link.href = url; | ||
|
|
||
| link.download = path.slice(path.lastIndexOf("/") + 1); | ||
|
|
||
| document.body.appendChild(link); | ||
|
|
||
| link.click(); | ||
|
|
||
| document.body.removeChild(link); | ||
| }; |
There was a problem hiding this comment.
URL.createObjectURL is used but the created object URL is never revoked. This can leak memory if users download multiple audits. Call URL.revokeObjectURL(url) after triggering the download (ideally in a finally) once the link click has occurred.
… doesn't scroll vertically
…to remove compliance data since it isn't supported by the API
…n of profiles and sources
6ca42ee to
b795053
Compare
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>

Summary
Add the Repository Profiles page to the deb-archive branch. Resolves this ticket. Incorporates components from feat/standardize-profiles but will also cause conflicts with that branch since both modify /features/repository-profiles.
Release Impact
According to the Landscape Server Release Cycle, this change will target the following release cycle:
deb-archiveChecklist
pnpm changesetand committed the resulting.mdfile.scripts/).Versioning Reminder
Important
This repository now uses CalVer ($YY.0M.Point.Patch$ ).
Please ensure your changeset description is clear, as it will be automatically added to the
CHANGELOG.mdupon merging tomain.