Auto-update, pin, and rollback Fleet-maintained apps via UI and GitOps - #48293
Conversation
…47808) <!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #46726 Updates the `PATCH /api/v1/fleet/software/titles/:id/package` endpoint to be able to set the pinned version in the `software_title_team_pins` table and set the active installer if it changed. `GET /software/titles/:id` returns the pinned version for a title from that table now. A few small fixes and updates that are related to this feature but not strictly in the scope of the subtask are also included (see individual comments). # Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [ ] Timeouts are implemented and retries are limited to avoid infinite loops - [ ] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [x] Added/updated automated tests - [ ] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually ## New Fleet configuration settings This setting is already handled by gitops, this PR adds control of it with the update software installer api - [ ] Setting(s) is/are explicitly excluded from GitOps If you didn't check the box above, follow this checklist for GitOps-enabled settings: - [ ] Verified that the setting is exported via `fleetctl generate-gitops` - TODO in another PR - [ ] Verified the setting is documented in a separate PR to [the GitOps documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485) - [ ] Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional) - [ ] Verified that any relevant UI is disabled when GitOps mode is enabled <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added version pinning for Fleet-maintained app installers, allowing teams to lock to specific versions or clear pins to use "Latest" * Policies automatically repoint to the newly active installer when a version pin is changed * Support for semantic version syntax including caret-style version constraints * **Tests** * Added comprehensive integration test for Fleet-maintained app version pinning workflows and validation rules <!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #47679 Adds a few things: - exports pinned version in `generate-gitops` enclosed in double quotes - adds `pinned_version` to the edited software activity. When set to a full or major version it shows up in details, when set to latest or unchanged it shows up as `pinned_version: null` (some other fields like display_name also dont show up when unchanged) - fixes a bug where some FMA's like google chrome couldn't be pinned to major version because they couldn't be converted to semver (by just splitting the version on periods instead of converting to semver) # Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [ ] Timeouts are implemented and retries are limited to avoid infinite loops - [ ] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [x] Added/updated automated tests - [ ] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually ## New Fleet configuration settings - [ ] Setting(s) is/are explicitly excluded from GitOps If you didn't check the box above, follow this checklist for GitOps-enabled settings: - [x] Verified that the setting is exported via `fleetctl generate-gitops` - [ ] Verified the setting is documented in a separate PR to [the GitOps documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485) - [ ] Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional) - [ ] Verified that any relevant UI is disabled when GitOps mode is enabled <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Software titles now support version pinning in GitOps exports for fleet-managed applications, with pinned version values properly formatted as quoted strings in the exported YAML configuration * Activity logs now record when pinned version information is modified during software editing operations <!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** Resolves #47215 ## Summary Pinning an FMA to a version via GitOps only resolved against versions already cached on the instance. Bumping a pin to a freshly released version failed with `specified version is not available` because Fleet never downloaded a pinned version it hadn't cached. `Hydrate` now falls back to the published app manifest on a cache miss. When the requested version is currently published, Fleet downloads and caches it, so an admin can bump a pin to a new version in a single apply. A version that's neither cached nor published still returns the same error. # Checklist for submitter - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually
**Related issue:** Resolves #47623 ## Summary Wires up the Fleet-maintained app version-pinning UI on the software title details page: - **Versions modal** — opened from Actions ▾ → Versions or the active row's version badge. Choose *Automatically update to latest*, pin to an exact version, or pin to a major version; pre-selected from the current pin, and Save is disabled with a tooltip in GitOps mode. - **Multi-row Library** — one accordion row per cached version; the active/pinned row is badged (Latest / Pinned / Major version) and older versions are dimmed. - Adds `pinned_version` to the software-package interface and a `version` field to the installer `PATCH` request. Frontend only; targets the `feat/38504-auto-update-pin-rollback-fma` feature branch (backend, layout, and accordion already merged there). # Checklist for submitter ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually https://github.com/user-attachments/assets/f9822cfa-57d2-4f0c-9918-c5ee914f0431
**Related issue:** Resolves #47681 Adds an hourly, Premium-only cron (maintained_apps_auto_update) that keeps Fleet-maintained apps current. For each FMA-backed active installer (per team), it fetches the latest manifest, downloads and caches a newly-published version when the pin allows, and advances the team's active installer based on the pin state: - Unpinned (Latest): download/cache the newest published version and advance the active installer to it. - Caret pin (^N): advance to the newest version within major N (downloading it if newly published). Never cross into another major. - Literal pin: never advance and never download. New versions are cached as additional software_installers rows (no schema change; reuses the existing (global_or_team_id, title_id, version) index), capped at the two most recent per team for rollback. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #48293 +/- ##
==========================================
+ Coverage 67.31% 67.47% +0.16%
==========================================
Files 3661 3672 +11
Lines 231523 232687 +1164
Branches 12301 12256 -45
==========================================
+ Hits 155849 157006 +1157
+ Misses 61725 61592 -133
- Partials 13949 14089 +140
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
There was a problem hiding this comment.
Warning
- Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.
Pull request overview
This PR extends Fleet-maintained app management to support auto-updating, version pinning, and rollback via the UI and GitOps. It wires new backend pin state into GET /software/titles/:id, extends the installer update flow to accept a version pin, adds an hourly premium-only auto-update cron, and introduces/updates multiple frontend components (Library accordion, Versions modal, policies modal, and supporting UI primitives).
Changes:
- Backend: add pinned-version retrieval to software title details, extend installer update payloads to support version pinning, and add FMA auto-update cron plumbing.
- Maintained apps: update Hydrate behavior to fall back to remote manifests on cache misses; add test coverage for new hydration and auto-update behavior.
- Frontend: add Versions modal + supporting helpers, refactor title details layout (summary/header pills, policies modal), and introduce new UI primitives (Chip, TruncatedTextList) plus Storybook updates.
Reviewed changes
Copilot reviewed 99 out of 104 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| changes/38504-auto-update-pin-rollback-fma | Release note entry (content excluded by policy). |
| server/service/software_titles.go | Populate pinned_version on software title detail responses. |
| server/service/software_installers.go | Accept multipart version field and pass through as PinnedVersion. |
| server/service/integration_vpp_install_test.go | Update edited_software activity assertions to include pinned_version. |
| server/service/integration_software_titles_test.go | Update edited_software activity assertions to include pinned_version. |
| server/mock/datastore_mock.go | Extend mock datastore interface with FMA auto-update + pin methods. |
| server/mdm/maintainedapps/sync.go | Allow Hydrate to fall back to manifest on cache miss and select requested version. |
| server/mdm/maintainedapps/sync_test.go | Add unit tests for Hydrate cache-hit/miss behavior and version availability. |
| server/fleet/software.go | Add UploadedAt to cached versions and define FMAAutoUpdateCandidate. |
| server/fleet/software_installer.go | Add PinnedVersion to installer structs/payloads and include in noop detection. |
| server/fleet/datastore.go | Add datastore interface methods for pin management and auto-update cron support. |
| server/fleet/cron_schedules.go | Add cron schedule name constant for maintained apps auto-update. |
| server/fleet/activities.go | Add pinned_version to edited_software activity payload. |
| server/datastore/mysql/teams_test.go | Ensure team deletion tests cover cleanup of software title pins. |
| server/datastore/mysql/software_titles.go | Include uploaded_at in cached version rows; add semver-aware sorting when requested. |
| frontend/utilities/permissions/permissions.ts | Add canWriteSoftware helper for admin/maintainer gating. |
| frontend/utilities/permissions/permissions.tests.ts | Add unit tests for canWriteSoftware. |
| frontend/test/storybook-utils.tsx | Add withFrame decorator helper for Storybook layout. |
| frontend/services/entities/software.ts | Allow editSoftwarePackage to handle Versions modal pin payload (version). |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal.tsx | New Versions modal UI and save flow for version pins. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal.tests.tsx | Tests for Versions modal defaulting, selection, and save behavior. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/index.ts | Export VersionsModal. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/helpers.ts | Build version pin radio options and preselection helper. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/helpers.tests.ts | Unit tests for Versions modal helpers. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/_styles.scss | Styles for Versions modal layout. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/TitleVersionsTableConfig.tsx | Fix VulnerabilitiesCell import path after table relocation. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/TitleVersionsTable.tsx | New Versions section table component. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/TitleVersionsTable.tests.tsx | Tests for Versions section table rendering and empty state. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/index.ts | Export TitleVersionsTable. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/_styles.scss | Styles for Versions section table. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareSummaryCard/SoftwareSummaryCard.tsx | Refactor summary card: header pills, policies modal trigger, Versions action gating. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/SoftwareInstallerCard.tsx | Remove old installer card implementation (replaced by new Library surfaces). |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTableConfig.tsx | Remove old status table config (superseded by accordion/status UI). |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTable.tsx | Remove old status table component. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTable.tests.tsx | Remove old status table tests. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/index.ts | Remove index export for deleted status table. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/_styles.scss | Remove styles for deleted status table. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerPoliciesTable/InstallerPoliciesTable.tsx | Add option to hide count (used by Policies modal). |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerPoliciesTable/InstallerPoliciesTable.tests.tsx | Adjust tooltip hover tests after table changes. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/InstallerDetailsWidget.tsx | Add tooltip suppression and installer-type hiding for embedding in accordion rows. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/InstallerDetailsWidget.tests.tsx | Remove SHA-256 rendering assertions (hash moved to accordion). |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/_styles.scss | Update truncation widths and remove sha256 styles. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/index.ts | Remove index export for deleted installer card. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/helpers.ts | Remove helpers for deleted installer card actions. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/_styles.scss | Remove styles for deleted installer card. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/PoliciesModal/PoliciesModal.tsx | New modal listing policies linked to software (from Auto install pill). |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/PoliciesModal/PoliciesModal.tests.tsx | Tests for Policies modal rendering and exit behavior. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/PoliciesModal/index.ts | Export PoliciesModal. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/PoliciesModal/_styles.scss | Styles for Policies modal empty state. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/LibraryItemAccordionList.tsx | New wrapper for composing accordion rows as a list. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/LibraryItemAccordion.stories.tsx | Storybook stories for accordion row states and permissions. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/index.ts | Export LibraryItemAccordion. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/helpers.ts | Derive row badge/active state from active + pinned versions. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/helpers.tests.ts | Unit tests for row state derivation. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/_styles.scss | Accordion/list styling (active/inactive rows, panels, badges). |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/helpers.ts | Add buildLibraryVersionRows helper for Library accordion rows. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/helpers.tests.ts | Tests for buildLibraryVersionRows. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditIconModal/EditIconModal.tsx | Update import for TitleVersionsLastUpdatedInfo after table relocation. |
| frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/_styles.scss | Adjust section styling structure. |
| frontend/pages/SoftwarePage/helpers.tsx | Make mergePolicies immutable (avoid mutating shared Sets). |
| frontend/pages/SoftwarePage/components/cards/SoftwareDetailsSummary/SoftwareDetailsSummary.tsx | Add Versions action, header pills slot, and Apple VPP gitops gating refactor. |
| frontend/pages/SoftwarePage/components/cards/SoftwareDetailsSummary/SoftwareDetailsSummary.tests.tsx | Extend tests for Versions option and headerPills rendering. |
| frontend/pages/SoftwarePage/components/cards/SoftwareDetailsSummary/_styles.scss | Switch layout to grid and add pills slot styling. |
| frontend/interfaces/software.ts | Export IFleetMaintainedVersion, add uploaded_at + pinned_version to package interface. |
| frontend/components/TruncatedTextList/TruncatedTextList.tsx | New component for truncating a long list into a “+N more” tooltip UI. |
| frontend/components/TruncatedTextList/TruncatedTextList.stories.tsx | Storybook stories for TruncatedTextList. |
| frontend/components/TruncatedTextList/index.ts | Export TruncatedTextList. |
| frontend/components/TruncatedTextList/_styles.scss | Styles for TruncatedTextList. |
| frontend/components/TooltipTruncatedText/TooltipTruncatedText.tsx | Add disableTooltip prop to suppress nested tooltips when parent owns hover. |
| frontend/components/Tag/Tag.tsx | Remove deprecated Tag component (replaced by Chip). |
| frontend/components/Tag/index.ts | Remove Tag export. |
| frontend/components/Tag/_styles.scss | Remove Tag styles. |
| frontend/components/icons/Tag.tsx | Add Tag icon asset. |
| frontend/components/icons/Pin.tsx | Add Pin icon asset. |
| frontend/components/icons/index.ts | Register new Pin/Tag icons. |
| frontend/components/Icon/Icon.stories.tsx | Improve Icon story controls to select from the full icon map. |
| frontend/components/Chip/index.ts | Export Chip component. |
| frontend/components/Chip/Chip.tsx | New Chip component (icons, tooltips, clickable variant). |
| frontend/components/Chip/Chip.tests.tsx | Unit tests for Chip. |
| frontend/components/Chip/Chip.stories.tsx | Storybook stories for Chip. |
| frontend/components/Chip/_styles.scss | Styles for Chip and focus outline behavior. |
| frontend/components/buttons/Button/Button.tsx | Clarify/deprecate text-icon variant and prefer inverse. |
| ee/server/service/software_installers.go | Add pin-only update path, caret parsing helpers, and activity logging for pins. |
| ee/server/service/software_installers_test.go | Update tests for caret parsing and add tests for major matching + pin parsing. |
| ee/server/service/maintained_apps_auto_update.go | Add premium auto-update cron implementation for FMAs (download + promote). |
| ee/server/service/maintained_apps_auto_update_test.go | Tests for auto-update promote logic and error isolation. |
| cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/fleets/team-a-thumbsup.yml | Update GitOps expected output to include pinned version values. |
| cmd/fleetctl/fleetctl/testdata/generateGitops/expectedTeamSoftware.yaml | Update expected generateSoftware structure to include pinned version. |
| cmd/fleetctl/fleetctl/generate_gitops.go | Ensure pinned versions are exported and quote version: in generated YAML. |
| cmd/fleetctl/fleetctl/generate_gitops_test.go | Update mock software titles to include pinned versions. |
| cmd/fleet/cron.go | Register and define maintained apps auto-update cron schedule. |
| cmd/fleet/cron_registration.go | Register premium cron schedule for maintained apps auto-update. |
| .storybook/preview.scss | Adjust Storybook canvas padding for more realistic layout. |
| .storybook/preview.js | Import Storybook preview stylesheet. |
| .storybook/main.ts | Include SoftwareTitleDetailsPage stories in Storybook build. |
Files excluded by content exclusion policy (1)
- changes/38504-auto-update-pin-rollback-fma
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughAdds Fleet-maintained app auto-update, pinning, rollback, and GitOps version export support across backend and frontend code. The software page now includes a Versions action, versions and policies modals, updated summary/header pills, and a new library accordion flow for version badges and install-state display. Storybook coverage and reusable UI primitives were expanded, while installer-card/status-table paths were removed or replaced. Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.44.0)server/service/integration_enterprise_test.goThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ee/server/service/software_installers.go (1)
2539-2547: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick winSelect a cached version within the requested major before indexing.
When the remote latest does not match
^major, this path fetchesversions[0]without checking that the slice is non-empty or that the first version matches the requested major. That can panic or hydrate the wrong major.Proposed fix
versions, err := svc.ds.GetFleetMaintainedVersionsByTitleID(ctx, teamID, *app.TitleID, true) if err != nil { return fleet.NewUserMessageError(errMajorVersionNotFound, http.StatusNotFound) } + var cachedVersion string + for _, v := range versions { + if versionMatchesMajor(v.Version, majorVersionString) { + cachedVersion = v.Version + break + } + } + if cachedVersion == "" { + return fleet.NewUserMessageError(errMajorVersionNotFound, http.StatusNotFound) + } // This is a bit inefficient as we are duplicating strings for categories and install/uninstall scripts, // but it can be optimized in softwareBatchUpload if it accepted only passing category and script content IDs. - installer, err := svc.ds.GetCachedFMAInstallerMetadata(ctx, teamID, app.ID, versions[0].Version) + installer, err := svc.ds.GetCachedFMAInstallerMetadata(ctx, teamID, app.ID, cachedVersion)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ee/server/service/software_installers.go` around lines 2539 - 2547, The cached installer lookup in software_installers.go can panic or use the wrong major because it indexes versions[0] without verifying the slice is non-empty and that the selected version matches the requested major. Update the flow around GetFleetMaintainedVersionsByTitleID and GetCachedFMAInstallerMetadata to first find a version within the requested major (using the version list already returned) and return the existing not-found error if none match, rather than assuming the first entry is valid.
🧹 Nitpick comments (7)
.storybook/preview.scss (1)
6-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winScope this padding override to the affected stories.
.sb-main-padded.sb-show-mainis Storybook-global, so this adds 90px padding to every padded story, not just the new Software page previews. A per-story decorator/parameter would avoid shifting unrelated stories and visual baselines.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.storybook/preview.scss around lines 6 - 8, The padding override in the Storybook stylesheet is applied globally through the `.sb-main-padded.sb-show-main` selector, so it affects every padded story instead of only the new Software page previews. Move this spacing logic out of the shared `preview.scss` and scope it to the specific stories using a per-story decorator or parameters in the relevant Storybook story definition so only those previews get the 90px padding.frontend/components/Chip/Chip.tests.tsx (1)
11-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winScope the SVG assertions to the rendered chip.
These checks read
document.querySelector(All)("svg"), so an unrelated SVG from a portal/decorator/tooltip can fail the test even whenChipstill renders correctly. Query within the render container instead.Example
- render(<Chip text="Fleet-maintained" />); + const { container } = render(<Chip text="Fleet-maintained" />); expect(screen.getByText("Fleet-maintained")).toBeInTheDocument(); - expect(document.querySelector("svg")).toBeNull(); + expect(container.querySelector("svg")).toBeNull();Also applies to: 19-20, 29-30
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/components/Chip/Chip.tests.tsx` around lines 11 - 13, The Chip tests are using document-wide SVG queries, which can pick up unrelated icons outside the component under test. Update the assertions in Chip.tests.tsx to scope SVG lookups to the rendered Chip container returned by the test render helper, using the existing Chip render/query setup rather than document.querySelector(All). Apply the same change in the affected test cases so the SVG checks only validate what Chip renders.frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal.tests.tsx (1)
111-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a save-path test for clearing the pin back to Latest.
The suite only verifies PATCH payloads for exact-version pins. The inverse path is the brittle one here: switching from a pinned version to “Automatically update to latest” must send the empty-string sentinel, and a regression there would currently pass this suite.
Suggested test shape
it("PATCHes an empty string when switching back to Latest", async () => { const editSpy = jest .spyOn(softwareAPI, "editSoftwarePackage") .mockResolvedValue({} as never); const { user } = renderModal({ pinned_version: "148.0.1" }); await user.click( screen.getByRole("radio", { name: "Automatically update to latest" }) ); await user.click(screen.getByRole("button", { name: "Save" })); await waitFor(() => { expect(editSpy).toHaveBeenCalledWith({ data: { pinnedVersion: "" }, softwareId: 1, teamId: 1, }); }); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal.tests.tsx` around lines 111 - 137, The VersionsModal test coverage is missing the reverse save path where a pinned version is cleared back to “Automatically update to latest.” Add a test in VersionsModal.tests.tsx alongside the existing save-path case that renders VersionsModal with an initial pinned_version, selects the radio for “Automatically update to latest,” clicks Save, and asserts softwareAPI.editSoftwarePackage is called with pinnedVersion set to the empty-string sentinel, using renderModal, editSpy, and the existing radio/button labels to locate the flow.frontend/services/entities/software.ts (1)
41-41: 🩺 Stability & Availability | 🔵 TrivialResolve the circular dependency between
services/entities/software.tsandVersionsModal.tsx.The
softwareservice file at line 41 importsIVersionPinFormDatafrompages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal, but that modal file importssoftwareAPIfrom this service. This creates a runtime circular dependency and tightly couples the service layer to a specific page.Convert this to a type-only import to prevent runtime resolution issues, and consider moving
IVersionPinFormDatato a shared type definition module (e.g.,interfaces) to remove the dependency on thepagesdirectory entirely.```Suggested change
- import { IVersionPinFormData } from "pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal"; + import type { IVersionPinFormData } from "pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal";Note: If possible, move
IVersionPinFormDatatointerfaces/software.tsfor cleaner layering.Also applies to: 606-610
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/services/entities/software.ts` at line 41, The import of IVersionPinFormData in software service layer creates a runtime circular dependency with VersionsModal because that page also imports softwareAPI from this module. Update the import in software.ts to be type-only so it is erased at runtime, and if possible move IVersionPinFormData into a shared interfaces module such as interfaces/software so the service no longer depends on a pages component. Use the software.ts and VersionsModal.tsx symbols to verify the dependency is broken without changing runtime behavior.ee/server/service/maintained_apps_auto_update.go (1)
324-342: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueConsider not memoizing fetch errors across teams.
manifestEntryis cached by slug and storeserras well asapp. The first team encountered for a slug runsds.GetMaintainedAppByID(ctx, c.FleetMaintainedAppID, c.TeamID), which is team-scoped. If that call fails transiently for that one team, the cached error short-circuits every other team sharing the slug for the rest of the run, skipping otherwise-healthy teams. The manifest itself is team-independent, so caching success is correct; caching the error is the part with collateral impact.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ee/server/service/maintained_apps_auto_update.go` around lines 324 - 342, The slug-level cache in hydrateLatestManifest is memoizing team-scoped fetch failures, causing one transient ds.GetMaintainedAppByID error to poison all other teams sharing that slug. Keep caching successful hydrated manifests, but do not store or replay fetch errors in manifestEntry for this path. Update hydrateLatestManifest so only successful results are cached by slug, and let failed lookups retry per team instead of short-circuiting on a cached err.server/datastore/mysql/software_installers_test.go (1)
6202-6203: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse a real team ID for the second pin scope.
otherTeam := new(uint(42))only proves the row is keyed by an integer. It doesn't exercise the real team-backed path, so FK/cascade/lookup regressions tied to actual teams would still pass here.♻️ Suggested fix
- noTeam := new(uint(0)) - otherTeam := new(uint(42)) + noTeam := new(uint(0)) + otherTeamRow, err := ds.NewTeam(ctx, &fleet.Team{Name: "pins-other-team"}) + require.NoError(t, err) + otherTeam := &otherTeamRow.ID🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/datastore/mysql/software_installers_test.go` around lines 6202 - 6203, The second pin-scope setup in the software installers test is using a fake team identifier, which bypasses the real team-backed behavior. Update the test data near noTeam and otherTeam so the second scope uses an actual team record/ID created through the test fixtures or helpers, and then bind the pin scope to that real team instead of hardcoding 42. Keep the existing assertions but ensure the path exercises the same team lookup/foreign key behavior used by the production team model.cmd/fleetctl/fleetctl/generate_gitops.go (1)
650-676: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNormalize pinned-version quoting in both export paths.
This post-processing only runs in the file-writing branch. The
--keybranch returns at Line 646 before it executes, so partial and full GitOps exports already serialize the same pinned-version field differently. Please move this into a helper and run it on both paths.♻️ Suggested direction
- // Keep software versions quoted so YAML treats them as strings (e.g. "10.0" must not become a float). - b = softwareVersion.ReplaceAll(b, []byte(`${1}"${2}"`)) + b = normalizeSoftwareVersionYAML(b)// Also apply normalizeSoftwareVersionYAML to the bytes emitted by the --key path // before writing them to stdout.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/fleetctl/fleetctl/generate_gitops.go` around lines 650 - 676, The pinned-version quoting logic is only applied in the file-writing path, so the `--key` export path in `generateGitOps` can emit a differently formatted software version. Extract the post-processing in `generateGitOps` into a helper (for example, around the existing `softwareVersion` replacement and related YAML cleanup) and call it from both the stdout `--key` branch and the `cmd.FilesToWrite` branch so both export modes normalize the version field the same way.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ee/server/service/software_installers.go`:
- Around line 676-685: The caret-pin handling in software_installers.go
currently falls back to versions[0] when no cached version matches the requested
major, which can activate an unrelated major. In the usesCaret branch of the
installer selection logic, remove that fallback and return
errMajorVersionNotFound when versionMatchesMajor does not find a match; keep the
activeInstallerID assignment only for matching majors.
- Around line 821-823: The `edited_software` activity in
`software_installers.go` is skipping `pinned_version` when the user clears a pin
to Latest because `payload.PinnedVersion` is non-nil but empty. Update the
`activity.PinnedVersion` assignment logic in this software installer edit flow
so it records the field whenever the request explicitly includes it, even when
the value is an empty string, and keep the change localized to the relevant
activity-building code path that uses `payload.PinnedVersion` and
`activity.PinnedVersion`.
In `@frontend/components/TruncatedTextList/TruncatedTextList.tsx`:
- Around line 62-84: The truncated-list rendering in TruncatedTextList should
not show the separator or the “+0 more” badge when there is only one item.
Update the logic around truncatedFirstContent in TruncatedTextList.tsx so the
TooltipWrapper for the truncated first item is shown by itself for single-item
lists, and only render the separator plus the second TooltipWrapper when
items.length > 1. Use the existing items, truncatedFirst, and separator handling
to keep the multi-item behavior unchanged.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/helpers.ts`:
- Around line 36-40: The helper that determines row badge state is marking every
non-empty pinnedVersion as active, which makes unsatisfied pins look like the
current active row. Update the logic in the version-badge helper used by
buildLibraryVersionRows so it compares pinnedVersion against activeVersion and
only returns isActive true when the active version actually satisfies the pin;
otherwise keep the row labeled as Pinned or Major version until the promoted
version catches up.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/LibraryItemAccordion.tsx`:
- Around line 487-492: The LibraryItemAccordion header key handler is also
responding to keyboard activation from the nested badge buttons, which causes
unwanted row toggling. Update handleHeaderKeyDown in LibraryItemAccordion so it
ignores Enter/space events originating from the Latest, Pinned, and label-count
button elements, using the event target/currentTarget relationship or a similar
guard before calling toggleExpanded(). Keep the header toggle behavior unchanged
for genuine header keyboard interactions.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/_styles.scss`:
- Around line 8-13: The `&__title` SCSS block has a Stylelint spacing violation
because `max-width` immediately follows `@include ellipse-text` in
`InstallerDetailsWidget/_styles.scss`; add the required blank line in that rule
so the mixin and property are separated and the lint check passes.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/InstallerDetailsWidget.tsx`:
- Around line 171-179: The tooltip in InstallerDetailsWidget is pointing users
to the old Actions > Edit software flow, but pin/rollback now lives under
Actions > Versions. Update the tooltip copy in the isFma branch of
InstallerDetailsWidget so it matches the new Versions flow and uses the same
destination wording already used elsewhere in the UI.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareSummaryCard/SoftwareSummaryCard.tsx`:
- Around line 237-242: The Versions action is still exposed without a concrete
team context because canManageVersions does not account for hasValidTeamId, even
though VersionsModal only renders when teamIdForApi is numeric. Update the
gating in SoftwareSummaryCard so the Versions dropdown item is only shown when
both canManageVersions and hasValidTeamId are true, using the existing
canManageVersions, hasValidTeamId, and VersionsModal/ teamIdForApi checks to
keep the menu actionable in the All teams view.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareTitleDetailsPage.tsx`:
- Around line 225-345: The Library section in renderLibrarySection should not
expose edit/download/version/delete actions when viewing All teams, because
those handlers no-op and the edit modal gets an invalid team id. Gate the action
props and modal rendering on typeof teamIdForApi === "number", and pass
teamIdForApi through to EditSoftwareModal instead of currentTeamId; use the same
guard for the LibraryItemAccordion callbacks in both the package and appStore
paths.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/helpers.ts`:
- Around line 39-45: The major-pin option in helpers.ts only gets added for the
newest cached major, so older cached majors like 148 are missing a selectable
track. Update the logic around sorted and majorOf in the helper that builds the
versions modal options so it adds a pin-to-major entry for every distinct cached
major version, not just sorted[0], while keeping the label and value pattern
consistent for each major track.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal.tsx`:
- Around line 96-111: Add a programmatic label to the radio group rendered in
VersionsModal by updating the fieldset around the versionPin options to include
a legend or an aria-labelledby association. Keep the existing Radio mapping
intact, but ensure the group itself has a clear accessible name so screen
readers announce the set of options properly.
In `@server/service/integration_enterprise_test.go`:
- Around line 33318-33321: The multipart PATCH requests in the integration tests
are using team_id, but updateSoftwareInstallerRequest.DecodeRequest only reads
fleet_id, so the team-scoped path is not actually exercised. Update the
multipart body construction in the affected test cases to send fleet_id instead
of team_id, using the existing generateMultipartRequest helper and the same
request flow, so the SoftwareInstallerRequest.TeamID is populated through the
real decode path.
---
Outside diff comments:
In `@ee/server/service/software_installers.go`:
- Around line 2539-2547: The cached installer lookup in software_installers.go
can panic or use the wrong major because it indexes versions[0] without
verifying the slice is non-empty and that the selected version matches the
requested major. Update the flow around GetFleetMaintainedVersionsByTitleID and
GetCachedFMAInstallerMetadata to first find a version within the requested major
(using the version list already returned) and return the existing not-found
error if none match, rather than assuming the first entry is valid.
---
Nitpick comments:
In @.storybook/preview.scss:
- Around line 6-8: The padding override in the Storybook stylesheet is applied
globally through the `.sb-main-padded.sb-show-main` selector, so it affects
every padded story instead of only the new Software page previews. Move this
spacing logic out of the shared `preview.scss` and scope it to the specific
stories using a per-story decorator or parameters in the relevant Storybook
story definition so only those previews get the 90px padding.
In `@cmd/fleetctl/fleetctl/generate_gitops.go`:
- Around line 650-676: The pinned-version quoting logic is only applied in the
file-writing path, so the `--key` export path in `generateGitOps` can emit a
differently formatted software version. Extract the post-processing in
`generateGitOps` into a helper (for example, around the existing
`softwareVersion` replacement and related YAML cleanup) and call it from both
the stdout `--key` branch and the `cmd.FilesToWrite` branch so both export modes
normalize the version field the same way.
In `@ee/server/service/maintained_apps_auto_update.go`:
- Around line 324-342: The slug-level cache in hydrateLatestManifest is
memoizing team-scoped fetch failures, causing one transient
ds.GetMaintainedAppByID error to poison all other teams sharing that slug. Keep
caching successful hydrated manifests, but do not store or replay fetch errors
in manifestEntry for this path. Update hydrateLatestManifest so only successful
results are cached by slug, and let failed lookups retry per team instead of
short-circuiting on a cached err.
In `@frontend/components/Chip/Chip.tests.tsx`:
- Around line 11-13: The Chip tests are using document-wide SVG queries, which
can pick up unrelated icons outside the component under test. Update the
assertions in Chip.tests.tsx to scope SVG lookups to the rendered Chip container
returned by the test render helper, using the existing Chip render/query setup
rather than document.querySelector(All). Apply the same change in the affected
test cases so the SVG checks only validate what Chip renders.
In
`@frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal.tests.tsx`:
- Around line 111-137: The VersionsModal test coverage is missing the reverse
save path where a pinned version is cleared back to “Automatically update to
latest.” Add a test in VersionsModal.tests.tsx alongside the existing save-path
case that renders VersionsModal with an initial pinned_version, selects the
radio for “Automatically update to latest,” clicks Save, and asserts
softwareAPI.editSoftwarePackage is called with pinnedVersion set to the
empty-string sentinel, using renderModal, editSpy, and the existing radio/button
labels to locate the flow.
In `@frontend/services/entities/software.ts`:
- Line 41: The import of IVersionPinFormData in software service layer creates a
runtime circular dependency with VersionsModal because that page also imports
softwareAPI from this module. Update the import in software.ts to be type-only
so it is erased at runtime, and if possible move IVersionPinFormData into a
shared interfaces module such as interfaces/software so the service no longer
depends on a pages component. Use the software.ts and VersionsModal.tsx symbols
to verify the dependency is broken without changing runtime behavior.
In `@server/datastore/mysql/software_installers_test.go`:
- Around line 6202-6203: The second pin-scope setup in the software installers
test is using a fake team identifier, which bypasses the real team-backed
behavior. Update the test data near noTeam and otherTeam so the second scope
uses an actual team record/ID created through the test fixtures or helpers, and
then bind the pin scope to that real team instead of hardcoding 42. Keep the
existing assertions but ensure the path exercises the same team lookup/foreign
key behavior used by the production team model.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7072d9d9-2a2b-4211-a882-0ff31bb354c9
📒 Files selected for processing (104)
.storybook/main.ts.storybook/preview.js.storybook/preview.scsschanges/38504-auto-update-pin-rollback-fmacmd/fleet/cron.gocmd/fleet/cron_registration.gocmd/fleetctl/fleetctl/generate_gitops.gocmd/fleetctl/fleetctl/generate_gitops_test.gocmd/fleetctl/fleetctl/testdata/generateGitops/expectedTeamSoftware.yamlcmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/fleets/team-a-thumbsup.ymlee/server/service/maintained_apps_auto_update.goee/server/service/maintained_apps_auto_update_download_test.goee/server/service/maintained_apps_auto_update_test.goee/server/service/software_installers.goee/server/service/software_installers_test.gofrontend/components/Chip/Chip.stories.tsxfrontend/components/Chip/Chip.tests.tsxfrontend/components/Chip/Chip.tsxfrontend/components/Chip/_styles.scssfrontend/components/Chip/index.tsfrontend/components/Icon/Icon.stories.tsxfrontend/components/Tag/Tag.tsxfrontend/components/Tag/_styles.scssfrontend/components/Tag/index.tsfrontend/components/TooltipTruncatedText/TooltipTruncatedText.tsxfrontend/components/TruncatedTextList/TruncatedTextList.stories.tsxfrontend/components/TruncatedTextList/TruncatedTextList.tsxfrontend/components/TruncatedTextList/_styles.scssfrontend/components/TruncatedTextList/index.tsfrontend/components/buttons/Button/Button.tsxfrontend/components/icons/Pin.tsxfrontend/components/icons/Tag.tsxfrontend/components/icons/index.tsfrontend/interfaces/software.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditIconModal/EditIconModal.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/LibraryItemAccordion.stories.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/LibraryItemAccordion.tests.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/LibraryItemAccordion.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/LibraryItemAccordionList.stories.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/LibraryItemAccordionList.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/_styles.scssfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/helpers.tests.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/helpers.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/LibraryItemAccordion/index.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/PoliciesModal/PoliciesModal.tests.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/PoliciesModal/PoliciesModal.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/PoliciesModal/_styles.scssfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/PoliciesModal/index.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/InstallerDetailsWidget.tests.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/InstallerDetailsWidget.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/_styles.scssfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerPoliciesTable/InstallerPoliciesTable.tests.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerPoliciesTable/InstallerPoliciesTable.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTable.tests.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTable.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTableConfig.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/_styles.scssfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/index.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/SoftwareInstallerCard.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/_styles.scssfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/helpers.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/index.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareSummaryCard/SoftwareSummaryCard.tests.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareSummaryCard/SoftwareSummaryCard.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareTitleDetailsPage.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/TitleVersionsTable.tests.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/TitleVersionsTable.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/TitleVersionsTableConfig.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/_styles.scssfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/TitleVersionsTable/index.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal.tests.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/VersionsModal.tsxfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/_styles.scssfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/helpers.tests.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/helpers.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/VersionsModal/index.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/_styles.scssfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/helpers.tests.tsfrontend/pages/SoftwarePage/SoftwareTitleDetailsPage/helpers.tsfrontend/pages/SoftwarePage/components/cards/SoftwareDetailsSummary/SoftwareDetailsSummary.tests.tsxfrontend/pages/SoftwarePage/components/cards/SoftwareDetailsSummary/SoftwareDetailsSummary.tsxfrontend/pages/SoftwarePage/components/cards/SoftwareDetailsSummary/_styles.scssfrontend/pages/SoftwarePage/helpers.tsxfrontend/services/entities/software.tsfrontend/test/storybook-utils.tsxfrontend/utilities/permissions/permissions.tests.tsfrontend/utilities/permissions/permissions.tsserver/datastore/mysql/software_installers.goserver/datastore/mysql/software_installers_test.goserver/datastore/mysql/software_titles.goserver/datastore/mysql/teams_test.goserver/fleet/activities.goserver/fleet/cron_schedules.goserver/fleet/datastore.goserver/fleet/software.goserver/fleet/software_installer.goserver/mdm/maintainedapps/sync.goserver/mdm/maintainedapps/sync_test.goserver/mock/datastore_mock.goserver/service/integration_enterprise_test.goserver/service/integration_software_titles_test.goserver/service/integration_vpp_install_test.goserver/service/software_installers.goserver/service/software_titles.go
💤 Files with no reviewable changes (13)
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/_styles.scss
- frontend/components/Tag/_styles.scss
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTableConfig.tsx
- frontend/components/Tag/index.ts
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTable.tests.tsx
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/index.ts
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/index.ts
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/InstallerStatusTable.tsx
- frontend/components/Tag/Tag.tsx
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/helpers.ts
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerStatusTable/_styles.scss
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/SoftwareInstallerCard.tsx
- frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareInstallerCard/InstallerDetailsWidget/InstallerDetailsWidget.tests.tsx
**Related issue:** Resolves #48334 The software title response now returns a per-version `filename` in `fleet_maintained_versions`, and the Library version rows render each version's own filename instead of the active installer's. Previously, every cached-version row showed the active installer's filename because the array didn't include one. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually
**Related issue:** Resolves #48309 Deleting a Fleet-maintained app from a team now also deletes its `software_title_team_pins` row. Previously the pin survived the delete (the FK cascades only on title deletion, and the title row outlives the installer rows), so re-adding the app resurfaced a stale pin pointing at a version no longer cached. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually
**Related issue:** Resolves #48301 When the auto-update cron downloads a new Fleet-maintained app version, it now carries forward the previously-active install/uninstall scripts when they were customized (e.g. via GitOps), instead of overwriting them with the manifest defaults. Customization is detected per-script by comparing the active scripts against the manifest (the uninstall script is compared against the manifest template substituted with the active version's package IDs, since it's version-specific). When the active scripts match the manifest, the new version's manifest scripts are used as before. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually
Related issue: Resolves #38504
Constituent PRs (merged into this feature branch):
pinned_versiontoedited_softwareactivityChecklist for submitter
Changes file added for user-visible changes in
changes/,orbit/changes/oree/fleetd-chrome/changes. See Changes files for more information.Input data is properly validated,
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.Timeouts are implemented and retries are limited to avoid infinite loops
If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes
Testing
Added/updated automated tests
Where appropriate, automated tests simulate multiple hosts and test for host isolation (updates
to one hosts's records do not affect another)
QA'd all new/changed functionality manually
Summary by CodeRabbit