Skip to content

feat(api,diff,output): diff NDES CA and EULA; flag global-only controls in fleet files - #71

Merged
robbiet480 merged 3 commits into
mainfrom
fix/not-diffed-ca-eula-global-only
Oct 10, 2026
Merged

robbiet480 merged 3 commits into
mainfrom
fix/not-diffed-ca-eula-global-only

Conversation

@robbiet480

@robbiet480 robbiet480 commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Summary

The "Not diffed" note on CampusTech/fleet-gitops#177 said "Fleet does not report these, or the token cannot read them" for 7 keys. None of the 7 was really a token problem. Two settings moved out of /config, and three are global-only controls set in fleet files, where Fleet ignores them. This PR diffs the moved settings and gives the global-only controls their own warning.

Changes

  • internal/api
    • New AddMovedConfig adds settings Fleet serves outside /config, at the paths default.yml uses. FetchAll calls it when fetching global config.
    • certificate_authorities.ndes_scep_proxy: url, admin_url and username come from GET /certificate_authorities and /certificate_authorities/{id}. The password is write-only.
    • mdm.end_user_license_agreement: the file name comes from GET /setup_experience/eula/metadata. A 404 means none is uploaded and maps to "".
    • A 402 (Premium-only on Fleet Free), 403 or 404 leaves a setting unreported instead of aborting the fetch.
  • internal/diff
    • diffEULA compares the EULA by file name against the basename of the configured path. When default.yml sets no EULA, an uploaded one shows as a removal, because fleetctl gitops deletes it (doGitOpsEULA).
    • diffNDESAdded: if the CA list was read and has no NDES proxy, the configured leaves show as additions. Secret-looking keys are redacted.
    • globalOnlyControls covers controls that exist only on AppConfig.MDM. diffTeamConfig takes them out of a team's controls and returns them as a new DiffResult.GlobalOnlyControls field. The no-team flow still diffs them against global config.
  • internal/output
    • Markdown gets a ⚠️ note listing each global-only key with the fleets that set it, in both the changes and no-changes paths. HasChanges is unchanged, so the note never changes the exit code.
  • docs/: added the new endpoints to API-Endpoints.md and described the behavior in Architecture.md.

Test plan

  • go build ./...
  • go vet ./...
  • go test -race ./...
  • golangci-lint run reports no issues
  • Coverage did not regress (92.5% total, every package above the floor)
  • Validated against a live Fleet instance:
    • Fleet 4.93.0 with fleet-gitops#177's branch: the same 2 profile rows and no ℹ️ note. The NDES and EULA settings show no changes. The ⚠️ note lists 4 global-only keys across Workstations, Zoom Rooms and Personal mobile devices.
    • With CampusTech/fleet-gitops fix/remove-global-only-controls-from-fleets: No changes detected.

Invariants

  • API client still issues GET only — no Fleet state is mutated
  • HTTPS still enforced (FLEET_PLAN_INSECURE=1 remains the only escape hatch)
  • No secrets in code, logs, output, fixtures, or CI comments. The NDES password is never fetched, and secret-looking keys in NDES additions are redacted.
  • No platform-specific code

Follow-up

  • Terminal and JSON renderers show neither the ⚠️ nor the ℹ️ note. The ℹ️ note was already markdown-only.
  • After merge, bump FLEET_PLAN_SHA in fleet-gitops .github/workflows/workflow.yml.

References (Fleet v4.93.0, tag fleet-v4.93.0 / 92732182)

  • Docs, docs/Configuration/yaml-files.md: windows_enabled_and_configured, windows_entra_tenant_ids, windows_entra_client_ids, enable_turn_on_windows_mdm_manually, windows_migration_enabled, apple_require_hardware_attestation and android_enabled_and_configured (lines 415–423), plus apple_account_provisioning (line 595). Each entry says "Can only be configured for "All fleets" (default.yml)."
  • Source, server/fleet/app.go:285-333: the fields exist only on global AppConfig.MDM; team MDM has none of them.
  • Source, server/service/client.go DoGitOps: these keys are written only in the global branch (incoming.TeamName == nil, lines 2282–2581). The team branch never reads them.
  • Source, pkg/spec/gitops.go:1324: only apple_account_provisioning is rejected in a fleet file. The rest are accepted and ignored.
  • Live 4.93.0 API: GET /teams mdm leaves out all of them; GET /config mdm reports them.

🤖 Generated with Claude Code

https://claude.ai/code/session_018oc93zfUqr8neLyxFh1Br2

Summary by CodeRabbit

  • New Features
    • Configuration comparisons now include NDES SCEP proxy settings and end-user license agreement filenames.
    • Reports identify global-only controls set in team configuration files and explain that Fleet ignores them there.
  • Bug Fixes
    • Improved reporting for settings Fleet does not return, including unavailable certificate authority or license agreement details.
    • Sensitive NDES values are redacted, and environment-variable passwords are omitted from reported changes.

robbiet480 and others added 3 commits October 9, 2026 21:49
…ls in fleet files

The "Not diffed" note blamed Fleet or the token for keys that were really
something else:

- certificate_authorities.ndes_scep_proxy.*: CAs moved out of /config.
  Fetch them from /certificate_authorities and the NDES detail endpoint and
  merge url, admin_url, username in at the default.yml path.
- mdm.end_user_license_agreement: the EULA lives at
  /setup_experience/eula/metadata. Compare its file name with the basename
  of the configured path; 404 means none uploaded and diffs as "" -> name.
- windows_migration_enabled, enable_turn_on_windows_mdm_manually,
  apple_require_hardware_attestation, etc.: global-only (AppConfig.MDM).
  Fleet accepts them in a fleet file but ignores them. They now get their
  own warning naming the fleets that set them, instead of "Not diffed".

Against live Fleet 4.93.0 the NDES and EULA settings now diff clean and the
note is gone; the warning lists the four global-only keys set in fleet files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oc93zfUqr8neLyxFh1Br2
From diff-reviewer on the previous commit:

- The CA and EULA endpoints are Premium-only and answer 402 on Fleet Free.
  That aborted the whole fetch; 402 now leaves the settings unreported,
  like 403/404.
- A configured NDES proxy that Fleet does not have yet was listed as "not
  diffed". When the CA list was read without one, its leaves are additions.
- fleetctl gitops deletes the uploaded EULA when default.yml sets none
  (doGitOpsEULA -> DeleteEULAIfNeeded); that removal is now shown.
- The global-only note no longer names only the fleets/ no-team file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oc93zfUqr8neLyxFh1Br2
diffNDESAdded emitted configured NDES leaves as-is, so a literal password
(not an env var) would be posted in the MR comment. Redact secret-looking
keys the same way diffFlat does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oc93zfUqr8neLyxFh1Br2
@codecov

codecov Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.70968% with 14 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/api/client.go 77.55% 9 Missing and 2 partials ⚠️
internal/output/markdown.go 87.50% 1 Missing and 1 partial ⚠️
internal/diff/differ.go 98.30% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Review in Change Stack →Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 47c38df4-2e52-4b67-bcf5-f456c54849c9




📥 Commits

Reviewing files that changed from the base of the PR and between e4e4df1 and 51daa5c.





📒 Files selected for processing (8)
  • docs/API-Endpoints.md
  • docs/Architecture.md
  • internal/api/client.go
  • internal/api/client_test.go
  • internal/diff/differ.go
  • internal/diff/differ_test.go
  • internal/output/markdown.go
  • internal/output/markdown_test.go




Included review availability: This review used your included allowance. 1 included review remains after this review. Your included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.






Walkthrough

The client adds selected certificate-authority and EULA settings to fetched configuration. The diff engine compares those settings and identifies global-only controls in team files. Markdown output reports those controls, and documentation describes the endpoints and comparison behavior.

Changes

Configuration Fetch and Diff Reporting

Layer / File(s) Summary
Fetch moved configuration settings
docs/API-Endpoints.md, internal/api/client.go, internal/api/client_test.go
FetchAll adds selected NDES SCEP proxy fields and EULA metadata from separate endpoints. Tests cover successful responses and inaccessible endpoints.
Compare moved settings and team controls
docs/Architecture.md, internal/diff/differ.go, internal/diff/differ_test.go
The diff engine compares EULA filenames and NDES proxy additions. It also returns global-only team controls separately from config changes. Tests cover these comparisons and skipped values.
Render global-only control warnings
internal/output/markdown.go, internal/output/markdown_test.go
Markdown output groups global-only controls by key and lists the teams that set them. The warning appears with or without detected changes.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DiffEngine
  participant MarkdownRenderer
  Client->>DiffEngine: Provide fetched configuration and Fleet settings
  DiffEngine->>DiffEngine: Compare moved settings and collect global-only controls
  DiffEngine-->>MarkdownRenderer: Return changes and global-only controls
  MarkdownRenderer-->>Client: Render diff and control warnings
Loading

Suggested reviewers: ads-0x1

Merge Risk: ⚪ Minimal · up to 51daa

The investigated EULA and NDES paths do not show a merge-blocking issue. The change is mergeable after normal checks.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 43.48% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 23 functions across 6 files. (2 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly identifies the three main changes: NDES CA diffing, EULA diffing, and warnings for global-only controls in fleet files.
Description check Passed The description follows the repository template. It includes a detailed summary, per-package changes, completed test and validation results, checked invariants, follow-up work, and references.

Full details: Docstring Coverage

Explanation

Docstring coverage is 43.48% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 23 functions across 6 files. (2 skipped: 2 unsupported.)



  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR




🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR








  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

A rabbit checks the settings flow,
NDES fields and names now show.
EULA files meet their match,
Team controls get a warning patch.
Sorted keys hop into view,
The rabbit thumps: “The diff came through!”

Comment @coderabbitai help to get the list of available commands.

@robbiet480
robbiet480 force-pushed the fix/not-diffed-ca-eula-global-only branch from 6727166 to 51daa5c Compare October 10, 2026 02:23
@robbiet480
robbiet480 merged commit fc00aec into main Oct 10, 2026
11 checks passed
robbiet480 added a commit that referenced this pull request Oct 10, 2026
#71 added unreadable() as isPermissionError plus 402. This branch makes
isPermissionError match 402 itself, so AddMovedConfig uses it directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oc93zfUqr8neLyxFh1Br2
robbiet480 added a commit that referenced this pull request Oct 10, 2026
* fix(api,diff,output): only blame the token when Fleet returns 403

Several messages said "API token lacks permission" without knowing it:

- Team software/profiles/scripts and no-team policies/profiles/scripts
  were marked unavailable on 403 or 404 and always reported as a token
  problem. The flags now carry the refusing HTTP status. A 403 keeps the
  token message; anything else says "Fleet returned HTTP <n> for ...",
  and only 403s feed the "Token lacks read access" warning.
- Every label at 0 hosts was added to that warning as "label host counts",
  though it may just mean no matching hosts. It is now its own neutral
  note: "Label host counts hidden: Fleet reported 0 hosts for every label."
- The not-diffed note no longer suggests the token: "Not diffed (Fleet did
  not report these)".

Fleet-maintained app categories already blamed the token only on 403.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oc93zfUqr8neLyxFh1Br2

* fix(api): skip resources Fleet refuses with 402 instead of failing

A Premium-only endpoint answers 402 on Fleet Free. isPermissionError only
matched 403/404, so a 402 from a team or no-team resource aborted FetchAll
instead of recording the status for the skipped-diff path. It now matches
402 too; transport errors and other statuses stay fatal. The FetchAll
no-team test returns 402 for policies.

Addresses CodeRabbit review on #72.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oc93zfUqr8neLyxFh1Br2

* refactor(api): drop unreadable now that isPermissionError covers 402

#71 added unreadable() as isPermissionError plus 402. This branch makes
isPermissionError match 402 itself, so AddMovedConfig uses it directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oc93zfUqr8neLyxFh1Br2

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant