Skip to content

feat: repository profiles page#562

Merged
marqode merged 22 commits intodeb-archivefrom
feature/repository-profiles
Apr 27, 2026
Merged

feat: repository profiles page#562
marqode merged 22 commits intodeb-archivefrom
feature/repository-profiles

Conversation

@marqode
Copy link
Copy Markdown
Contributor

@marqode marqode commented Apr 20, 2026

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:

  • Target Branch: deb-archive
  • Version Impact:
    • Patch (Fix)
    • Minor (Feature)
    • Major (Breaking)

Checklist

  • Changeset Added: I have run pnpm changeset and committed the resulting .md file.
  • UI Verified: I have verified the changes locally.
  • Linting: No linting errors are present (especially in 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.md upon merging to main.

@marqode marqode changed the title Feature/repository profiles feat: repository profiles page Apr 22, 2026
@marqode marqode force-pushed the feature/repository-profiles branch from 8e08b7f to 909a080 Compare April 22, 2026 16:58
@marqode marqode requested review from a team and Copilot April 23, 2026 22:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 profiles feature (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_sources is part of EditRepositoryProfileParams and is populated by RepositoryProfileForm, but editRepositoryProfileQuery drops 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. Include remove_apt_sources in the request body (and consider passing through unchanged fields consistently).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +74 to +75
{isSecurityProfile(profile) && profile.mode == "audit-fix-restart" && (
<InfoGrid.Item
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

Use strict equality here (===) instead of == to avoid unexpected type coercion when checking the security profile mode.

Copilot uses AI. Check for mistakes.
Comment thread src/context/profiles.tsx
Comment on lines +70 to +73
if (path !== resetKey) {
setIsProfileLimitReached(false);
setResetKey(path);
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

address in Standardize Profiles PR

Comment on lines +16 to +22
queryFn: async () =>
authFetch.get("computers", {
params: {
query: `profile:repository:${profileId}`,
root_only: false,
},
}),
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +13
const { data: response, isLoading } = useQuery<
AxiosResponse<ApiPaginatedResponse<Instance>>,
AxiosError<ApiError>
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +30
.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;
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The .content rule is duplicated with identical declarations. Remove the duplicate block to avoid confusion and keep the stylesheet maintainable.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +18
apt_sources: [
aptSources[0],
aptSources[1],
aptSources[2],
aptSources[3],
aptSources[4],
aptSources[5]],
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +73
{
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[],
},

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment on lines 15 to 16
const unfilteredRepositoryProfilesResult = getRepositoryProfilesQuery();

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +10
if (!path) {
throw new Error();
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +33
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);
};
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
@marqode marqode force-pushed the feature/repository-profiles branch from 6ca42ee to b795053 Compare April 24, 2026 19:39
marqode and others added 5 commits April 27, 2026 09:37
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
@ethanashaw ethanashaw requested review from ethanashaw and removed request for a team April 27, 2026 20:36
Copy link
Copy Markdown
Contributor

@ethanashaw ethanashaw left a comment

Choose a reason for hiding this comment

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

Looks good!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it would be good to include a "Back" button here to be consistent with other side panels, if we're still doing that:

Image

@marqode marqode merged commit 4dab9da into deb-archive Apr 27, 2026
@marqode marqode deleted the feature/repository-profiles branch April 27, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants