Skip to content

Show configuration profile name and more fine-grained status - #42126

Merged
iansltx merged 53 commits into
mainfrom
🤖-40177-config-profile-name-status
Apr 9, 2026

Hidden character warning

The head ref may contain hidden characters: "\ud83e\udd16-40177-config-profile-name-status"
Merged

Show configuration profile name and more fine-grained status#42126
iansltx merged 53 commits into
mainfrom
🤖-40177-config-profile-name-status

Conversation

@iansltx

@iansltx iansltx commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Resolves #40177 and subissues.

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

Testing

Database migrations

  • Checked schema for all modified table for columns that will auto-update timestamps during migration.
  • Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects.
  • Ensured the correct collation is explicitly set for character columns (COLLATE utf8mb4_unicode_ci).

Summary by CodeRabbit

  • New Features
    • Profile names are now displayed alongside mobile device management commands for installing or removing profiles. These names are visible in command details modals and within device activity timelines.
    • Added "NotNow" status for deferred profile commands, providing improved transparency into which profiles are being managed and the current status of profile installation or removal operations.

Zed + Opus 4.6; prompt: Implement #40177. Check subtasks in GitHub for
further implementation details, and pull from Figma (linked in the main
ticket) to see what visuals should look like.
@codecov

codecov Bot commented Mar 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.67857% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.87%. Comparing base (51040e5) to head (2a7bf07).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...details/cards/Activity/CommandItem/CommandItem.tsx 62.50% 6 Missing ⚠️
...ons/tables/20260409153713_AddNameToNanoCommands.go 83.78% 4 Missing and 2 partials ⚠️
server/mdm/apple/commander.go 62.50% 4 Missing and 2 partials ⚠️
...onents/CommandDetailsModal/CommandDetailsModal.tsx 0.00% 5 Missing ⚠️
server/service/apple_mdm.go 77.77% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #42126   +/-   ##
=======================================
  Coverage   66.86%   66.87%           
=======================================
  Files        2587     2588    +1     
  Lines      207476   207563   +87     
  Branches     9163     9206   +43     
=======================================
+ Hits       138737   138803   +66     
- Misses      56110    56124   +14     
- Partials    12629    12636    +7     
Flag Coverage Δ
backend 68.65% <84.61%> (+<0.01%) ⬆️
frontend 54.77% <47.61%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@iansltx

iansltx commented Mar 20, 2026

Copy link
Copy Markdown
Contributor Author

@claude[agent] Can you fix the CI issues here? Commit directly to the branch with any fixes.

@Claude

Claude AI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

@iansltx I've opened a new pull request, #42129, to work on those changes. Once the pull request is ready, I'll request review from you.

# Conflicts:
#	server/datastore/mysql/schema.sql
Comment thread server/datastore/mysql/apple_mdm.go
Comment thread server/fleet/apple_mdm.go Outdated
Comment thread server/fleet/mdm.go Outdated
Comment thread server/fleet/mdm.go Outdated
Comment thread server/mdm/apple/profile_processor.go Outdated
if enqueued && target.ProfileName != "" {
if err := ds.SetCommandName(ctx, target.CmdUUID, target.ProfileName); err != nil {
logger.ErrorContext(ctx, "setting command name for profile", "command_uuid", target.CmdUUID, "profile_name", target.ProfileName, "err", err)
}

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.

@JordanMontgomery Is this (plus the associated UPDATE) acceptable perf-wise or do I need to did into nanomdm's call stack and pass the profile name all the way through?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would honestly pass it all the way through to avoid duplicating writes. This can be pretty intensive in certain cases

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.

Got it, thanks. I'll dig in on that refactor. /cc @AndreyKizimenko that we'll absolutely want MDM smoke and macOS load tests for 4.84 as a result of these changes; I don't expect a regression given our test coverage but I'll be editing load-bearing code as a part of this.

logger *slog.Logger,
assocs []fleet.SCEPIdentityAssociation,
profile []byte,
profileName string,

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.

@JordanMontgomery Is this what we want here, or something different?

MagnusHJensen
MagnusHJensen previously approved these changes Apr 9, 2026

@MagnusHJensen MagnusHJensen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good overall, just two smaller comments

# Conflicts:
#	server/datastore/mysql/schema.sql
Comment thread server/mdm/nanomdm/storage/mysql/queue.go
Comment on lines 5825 to +5828
}
if migrationEnrollmentProfile != "" && hasAssocsFromMigration {
profileBytes := []byte(migrationEnrollmentProfile)
if err := renewMDMAppleEnrollmentProfile(ctx, ds, commander, logger, assocsFromMigration, profileBytes); err != nil {
if err := renewMDMAppleEnrollmentProfile(ctx, ds, commander, logger, assocsFromMigration, profileBytes, appConfig.OrgInfo.OrgName+" migration enrollment"); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The PR adds cmd.Name assertions for 4 of the 5 renewMDMAppleEnrollmentProfile call sites in RenewSCEPCertificates, but the 5th call site (line 5828, which passes OrgName + " migration enrollment") is never exercised by any test because the code path is gated on FLEET_SILENT_MIGRATION_ENROLLMENT_PROFILE being non-empty, and no test sets that env var. A typo or regression in the " migration enrollment" suffix string would go undetected. Fix by adding a test case in TestRenewSCEPCertificatesBranches that sets the env var with t.Setenv() and asserts cmd.Name equals the expected value.

Extended reasoning...

What the gap is and how it manifests

The PR deliberately introduces cmd.Name assertions across the call sites to renewMDMAppleEnrollmentProfile in RenewSCEPCertificates. Reviewing the diff against apple_mdm_test.go, assertions exist for:

  • appConfig.OrgInfo.OrgName + " enrollment" (hostsWithoutRefs, hostsWithRefs — tested twice each)
  • appConfig.OrgInfo.OrgName + " account driven enrollment" (userDeviceAssocs — tested twice)
  • appConfig.OrgInfo.OrgName + " ACME enrollment" (tested in TestRenewACMECertificatesBranches)

The 5th call site in apple_mdm.go at lines 5825–5828 — which passes appConfig.OrgInfo.OrgName + " migration enrollment" — is not covered by any test.

The specific code path that is untested

The migration enrollment branch is guarded by:

if migrationEnrollmentProfile != "" && hasAssocsFromMigration {
    ...
    if err := renewMDMAppleEnrollmentProfile(..., appConfig.OrgInfo.OrgName+" migration enrollment"); err != nil {

migrationEnrollmentProfile is populated from os.Getenv("FLEET_SILENT_MIGRATION_ENROLLMENT_PROFILE"). No test case in TestRenewSCEPCertificatesBranches or TestRenewACMECertificatesBranches calls t.Setenv("FLEET_SILENT_MIGRATION_ENROLLMENT_PROFILE", ...), so this branch is permanently skipped in all CI runs. A grep for the env var name in the test file confirms zero matches.

Why existing code does not detect this

Because the path requires a non-empty env var plus hasAssocsFromMigration being true (which requires specific datastore mock setup), it cannot be triggered without an explicit test case. The four other call sites are reachable through the existing test fixture paths, so they have coverage; this one does not. The PR introduced assertions for all the other cases and therefore created a visible asymmetry.

Addressing the refutation

One verifier argued this is "not a real bug" because the production string literal is plainly correct and visible. That is a fair point — the production code is functioning correctly as written. This is a test coverage gap, not a functional defect. However, the PR's explicit intent is to verify that cmd.Name is correct at each call site, and failing to cover one of five call sites is an inconsistency in that stated intent. A future refactor that accidentally corrupts the suffix for this branch would not be caught by the test suite.

How to fix it

Add a test branch in TestRenewSCEPCertificatesBranches that:

  1. Calls t.Setenv("FLEET_SILENT_MIGRATION_ENROLLMENT_PROFILE", someValidProfilePlist)
  2. Configures the datastore mock to return associations tagged as migration associations (hasAssocsFromMigration = true)
  3. Asserts cmd.Name == appConfig.OrgInfo.OrgName + " migration enrollment" in the EnqueueCommandFunc mock

Step-by-step demonstration of the gap

  1. PR reviewer reads the test file and sees cmd.Name assertions for " enrollment", " account driven enrollment", and " ACME enrollment".
  2. Reviewer searches for " migration enrollment" in the test file — no matches.
  3. Developer renames the suffix to " migration_enrollment" (adds underscore) in apple_mdm.go line 5828.
  4. All existing tests still pass, because the migration branch never runs.
  5. At runtime, the wrong command name is stored in nano_commands.name for migration enrollment hosts, causing incorrect display in the Fleet UI for that subset of devices.

MagnusHJensen
MagnusHJensen previously approved these changes Apr 9, 2026

@MagnusHJensen MagnusHJensen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

iansltx added 2 commits April 9, 2026 12:07
… to max-height

Zed + Opus 4.6; prompts:

> On the MDM command details modal, the default height for the text area input is used even when the text being displayed is much longer. What are we missing style-wise to get the text area to expand to fill content, subject to max-height?

...

< Since these fields in the `CommandDetailsModal` are read-only with `enableCopy`, option 1 (switching to the `Textarea` component) would be the most pragmatic fix — it already handles this correctly and is used in a similar context in `SoftwareIpaInstallDetailsModal`. Would you like me to make either change?

> Yes

(it picked the smaller change)
Comment on lines +21 to +22
const isProfileCommand = (requestType: string): boolean =>
requestType === "InstallProfile" || requestType === "RemoveProfile";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The isProfileCommand(requestType) helper defined at lines 21–22 of CommandItem.tsx is never called anywhere in the file and should be removed. The actual rendering logic uses a direct name ? ... : ... ternary for profile name display and getStatusText() reads command_status/status directly, making this function dead code that implies profile-specific guard logic exists when it does not.

Extended reasoning...

What the dead code is

The function isProfileCommand is defined at lines 21–22 of CommandItem.tsx:

const isProfileCommand = (requestType: string): boolean =>
  requestType === "InstallProfile" || requestType === "RemoveProfile";

It is never invoked anywhere in the file or the broader frontend codebase (confirmed by grep).

The specific code paths that ignore it

The PR implements two pieces of logic that might plausibly use isProfileCommand, but neither does:

  1. Profile name display – The activityText JSX uses a plain name ? (...) : (...) ternary (lines 51–59 of the modified file). It does not gate on isProfileCommand(request_type).
  2. Status textgetStatusText() inspects only the command_status and status fields from ICommand. It makes no distinction between profile and non-profile command types.

Why existing code doesn't detect this

TypeScript does not warn about unused function declarations at module scope. The helper is exported-free, so no import check catches it either. It compiles and ships silently.

What the impact would be

No functional impact — the function is genuinely unreachable. The issue is purely readability/maintainability: the presence of isProfileCommand implies that profile-specific branching was intended somewhere in the status or display logic, leaving future maintainers uncertain whether the omission of a call was intentional or a latent bug.

How to fix it

Delete the two-line declaration entirely:

-const isProfileCommand = (requestType: string): boolean =>
-  requestType === "InstallProfile" || requestType === "RemoveProfile";

Step-by-step proof

  1. Search for all usages of isProfileCommand in CommandItem.tsx — only the definition at lines 21–22 appears; no call sites exist.
  2. Grep the broader frontend/ directory for isProfileCommand — zero results outside this file.
  3. Review getStatusText(): it switches on command_status and checks status === "NotNow"; no call to isProfileCommand.
  4. Review activityText: it uses name ? directly; no call to isProfileCommand.
  5. Conclusion: the function is dead code introduced (or left over from an earlier draft) by this PR.

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.

I'll fix this in a follow-up PR

@iansltx
iansltx merged commit da6cfd8 into main Apr 9, 2026
53 of 54 checks passed
@iansltx
iansltx deleted the 🤖-40177-config-profile-name-status branch April 9, 2026 17:46
iansltx added a commit that referenced this pull request Apr 9, 2026
iansltx added a commit that referenced this pull request Apr 9, 2026
Resolves #40177 and subissues.

If some of the following don't apply, delete the relevant line.

- [x] 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.

- [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] Added/updated automated tests
- [x] 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)

- [sorta] QA'd all new/changed functionality manually

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

* **New Features**
* Profile names are now displayed alongside mobile device management
commands for installing or removing profiles. These names are visible in
command details modals and within device activity timelines.
* Added "NotNow" status for deferred profile commands, providing
improved transparency into which profiles are being managed and the
current status of profile installation or removal operations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
iansltx added a commit that referenced this pull request Apr 9, 2026
iansltx added a commit that referenced this pull request Apr 9, 2026
…atus (#43348)

Merged into `main` in #42126 and #43345. Conflict resolution was
required due to DB migration + recovery lock features that are landing
in 4.85 that this work touched, but that resolution was basically "don't
include this function and don't include the corersponding test" so not a
big deal.
@coderabbitai coderabbitai Bot mentioned this pull request May 5, 2026
7 tasks
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.

Host activity: Show configuration profile name and status (success/fail)

7 participants