Skip to content

Add activities when toggling GitOps exception settings - #44094

Merged
sgress454 merged 8 commits into
mainfrom
sgress454/add-activity-for-exceptions
Apr 24, 2026
Merged

Add activities when toggling GitOps exception settings#44094
sgress454 merged 8 commits into
mainfrom
sgress454/add-activity-for-exceptions

Conversation

@sgress454

@sgress454 sgress454 commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Related issue: For #40171

Details

Adds audit activity when enabling or disabling GitOps exceptions.

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.
    n/a, unreleased

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually
image

For unreleased bug fixes in a release candidate, one of:

  • Confirmed that the fix is not expected to adversely impact load test results

Summary by CodeRabbit

  • New Features
    • Activity feed now records when GitOps exceptions (labels, software, secrets) are enabled or disabled.
  • UI
    • Activity messages show which specific exception was enabled or disabled.
  • Tests
    • Added unit and integration tests verifying generation and rendering of enable/disable exception activities, including single and multiple flips and no-op updates.

Copilot AI review requested due to automatic review settings April 23, 2026 22:30
@sgress454
sgress454 requested review from a team and rachaelshaw as code owners April 23, 2026 22:30

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.33333% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.77%. Comparing base (493de18) to head (9e2ae08).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
server/service/appconfig.go 90.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #44094   +/-   ##
=======================================
  Coverage   66.77%   66.77%           
=======================================
  Files        2629     2629           
  Lines      211234   211264   +30     
  Branches     9535     9502   -33     
=======================================
+ Hits       141041   141075   +34     
+ Misses      57365    57362    -3     
+ Partials    12828    12827    -1     
Flag Coverage Δ
backend 68.55% <90.00%> (+<0.01%) ⬆️
frontend 54.80% <100.00%> (+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.

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds two GitOps-exception activity types ("enabled_gitops_exception" and "disabled_gitops_exception") end-to-end. Frontend: extends ActivityType enum, makes IActivityDetails.exception optional, maps new types to filter labels, and renders/tests messages including details.exception. Backend: registers two ActivityDetails structs with an exception JSON field and ActivityName values; ModifyAppConfig emits one enabled/disabled activity per exception boolean flip. Test helpers and integration tests updated to assert these emitted activities and payloads.

Possibly related PRs

  • fleetdm/fleet PR 40752: Overlaps edits to server/fleet/activities.go and activity type/registration code touching the same activity-type definitions.
  • fleetdm/fleet PR 38453: Also adds activity types and extends ModifyAppConfig-side activity emission logic for config changes.
  • fleetdm/fleet PR 42013: Introduces the UIGitOpsMode.Exceptions config/schema that these activity events record and verify.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description provides a brief summary and includes completed testing items and QA confirmation, but is missing key checklist items like changes file documentation and detailed technical context.
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 directly and accurately summarizes the main change: adding audit activities when GitOps exception settings are toggled, which aligns with all file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sgress454/add-activity-for-exceptions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx`:
- Around line 961-965: The editedGitOpsException formatter produces awkward text
when activity.details or its fields are missing; update the
editedGitOpsException function (signature editedGitOpsException: (activity:
IActivity) => { ... }) to first guard that activity.details and
activity.details.exception and activity.details.enabled are present, and if
either is missing return a safe fallback (e.g., the default template or a
generic message like "updated GitOps exceptions") instead of interpolating empty
values; if both fields exist, produce the current "{enabled/disabled} the
{exception} exception for GitOps." string.

In `@server/service/appconfig.go`:
- Around line 861-883: The code emits ActivityTypeEditedGitOpsException
activities (via svc.NewActivity) while comparing
oldAppConfig.GitOpsConfig.Exceptions to appConfig.GitOpsConfig.Exceptions before
persisting changes; move the loop that builds/sends these activities to after
the call to SaveAppConfig so activities are only created if SaveAppConfig
succeeds, i.e. compute exceptionChanges using oldAppConfig and appConfig as now
but defer invoking svc.NewActivity (and constructing
ActivityTypeEditedGitOpsException) until after the SaveAppConfig call returns
without error.
🪄 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: 5bc135e0-31da-42d0-952c-92000fb784a4

📥 Commits

Reviewing files that changed from the base of the PR and between fa97579 and 95d030c2fd0acf2b28cedaf79203e82a21b980c1.

⛔ Files ignored due to path filters (1)
  • docs/Contributing/reference/audit-logs.md is excluded by !**/*.md
📒 Files selected for processing (7)
  • frontend/interfaces/activity.ts
  • frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsx
  • frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx
  • server/fleet/activities.go
  • server/service/appconfig.go
  • server/service/appconfig_test.go
  • server/service/integration_enterprise_test.go

Comment on lines +961 to +965
editedGitOpsException: (activity: IActivity) => {
const verb = activity.details?.enabled ? "enabled" : "disabled";
const exception = activity.details?.exception ?? "";
return `${verb} the ${exception} exception for GitOps.`;
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Minor: degenerate output when exception is missing.

If activity.details?.exception is absent, the rendered string is "enabled the exception for GitOps." (double space, ungrammatical). Similarly, an undefined enabled silently renders as "disabled", which could misrepresent history if the backend ever omits the flag. Consider falling back to the default template (or skipping the verb/exception entirely) when the expected fields are missing.

🛠️ Suggested guard
   editedGitOpsException: (activity: IActivity) => {
-    const verb = activity.details?.enabled ? "enabled" : "disabled";
-    const exception = activity.details?.exception ?? "";
-    return `${verb} the ${exception} exception for GitOps.`;
+    const { enabled, exception } = activity.details ?? {};
+    if (enabled === undefined || !exception) {
+      return TAGGED_TEMPLATES.defaultActivityTemplate(activity);
+    }
+    const verb = enabled ? "enabled" : "disabled";
+    return `${verb} the ${exception} exception for GitOps.`;
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
editedGitOpsException: (activity: IActivity) => {
const verb = activity.details?.enabled ? "enabled" : "disabled";
const exception = activity.details?.exception ?? "";
return `${verb} the ${exception} exception for GitOps.`;
},
editedGitOpsException: (activity: IActivity) => {
const { enabled, exception } = activity.details ?? {};
if (enabled === undefined || !exception) {
return TAGGED_TEMPLATES.defaultActivityTemplate(activity);
}
const verb = enabled ? "enabled" : "disabled";
return `${verb} the ${exception} exception for GitOps.`;
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx`
around lines 961 - 965, The editedGitOpsException formatter produces awkward
text when activity.details or its fields are missing; update the
editedGitOpsException function (signature editedGitOpsException: (activity:
IActivity) => { ... }) to first guard that activity.details and
activity.details.exception and activity.details.enabled are present, and if
either is missing return a safe fallback (e.g., the default template or a
generic message like "updated GitOps exceptions") instead of interpolating empty
values; if both fields exist, produce the current "{enabled/disabled} the
{exception} exception for GitOps." string.

Comment thread server/service/appconfig.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new audit-log activity for GitOps exception toggles (labels/software/secrets), wiring it end-to-end from backend activity generation through UI rendering and documentation.

Changes:

  • Introduces edited_gitops_exception activity type with exception + enabled details.
  • Emits an activity per exception flag change when modifying app config.
  • Updates UI activity feed rendering/filter labels and adds backend/frontend tests + audit-log docs.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
server/service/integration_enterprise_test.go Extends GitOps exceptions integration test to assert activities are generated.
server/service/appconfig.go Emits edited_gitops_exception activities when exception flags change.
server/service/appconfig_test.go Adds unit test coverage for exception activity emission behavior.
server/fleet/activities.go Registers new ActivityTypeEditedGitOpsException activity detail type.
frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx Renders new activity type in the global activity feed.
frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsx Adds UI tests for enabled/disabled exception messages.
frontend/interfaces/activity.ts Adds enum value + detail fields + filter label for the new activity type.
docs/Contributing/reference/audit-logs.md Documents edited_gitops_exception payload shape and example.

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

Comment thread server/service/integration_enterprise_test.go Outdated
Comment thread server/service/appconfig.go
rachaelshaw
rachaelshaw previously approved these changes Apr 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
server/service/testing_client.go (1)

687-710: Optional: parse wantDetails once and compare with reflect.DeepEqual.

json.Unmarshal([]byte(details), &wantDetails) is re-run for every activity even though details is invariant across the loop, and the marshal-then-string-compare round trip is only needed because we're comparing structurally. Parsing once outside the loop and using reflect.DeepEqual on the decoded interface{} values is equivalent (numbers round-trip to float64, strings to string, etc.), cheaper, and drops the ignored marshal errors.

♻️ Proposed refactor
 func (ts *withServer) lastActivityOfTypeMatches(name, details string, id uint) uint {
 	t := ts.s.T()
 
 	var listActivities listActivitiesResponse
 	ts.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK,
 		&listActivities, "order_key", "a.id", "order_direction", "desc", "per_page", "10")
 	require.True(t, len(listActivities.Activities) > 0)
 
+	var wantDetails interface{}
+	if details != "" {
+		require.NoError(t, json.Unmarshal([]byte(details), &wantDetails))
+	}
+
 	for _, act := range listActivities.Activities {
 		if act.Type == name {
 			if details != "" {
 				if act.Details == nil {
 					continue
 				}
-				// Use details as a filter: skip activities whose details don't match.
-				var wantDetails, gotDetails interface{}
-				require.NoError(t, json.Unmarshal([]byte(details), &wantDetails))
+				// Use details as a filter: skip activities whose details don't match.
+				var gotDetails interface{}
 				if err := json.Unmarshal([]byte(*act.Details), &gotDetails); err != nil {
 					continue
 				}
-				wantJSON, _ := json.Marshal(wantDetails)
-				gotJSON, _ := json.Marshal(gotDetails)
-				if string(wantJSON) != string(gotJSON) {
+				if !reflect.DeepEqual(wantDetails, gotDetails) {
 					continue
 				}
 			}
 			if id > 0 {
 				assert.Equal(t, id, act.ID)
 			}
 			return act.ID
 		}
 	}

(Requires adding "reflect" to the imports.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/service/testing_client.go` around lines 687 - 710, Move the one-time
parsing of the test `details` string out of the loop in the function that
iterates over `listActivities.Activities` (the loop shown) by calling
`json.Unmarshal([]byte(details), &wantDetails)` once before the for-loop (and
fail the test if that unmarshal errors, e.g., with `require.NoError`), add the
"reflect" import, and inside the loop unmarshal each activity's `act.Details`
into `gotDetails` and compare with `reflect.DeepEqual(wantDetails, gotDetails)`
instead of re-parsing `details` repeatedly and using the marshal/string
comparison; keep the existing ID assertion (`assert.Equal(t, id, act.ID)`) and
same continue/return control flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@server/service/testing_client.go`:
- Around line 687-710: Move the one-time parsing of the test `details` string
out of the loop in the function that iterates over `listActivities.Activities`
(the loop shown) by calling `json.Unmarshal([]byte(details), &wantDetails)` once
before the for-loop (and fail the test if that unmarshal errors, e.g., with
`require.NoError`), add the "reflect" import, and inside the loop unmarshal each
activity's `act.Details` into `gotDetails` and compare with
`reflect.DeepEqual(wantDetails, gotDetails)` instead of re-parsing `details`
repeatedly and using the marshal/string comparison; keep the existing ID
assertion (`assert.Equal(t, id, act.ID)`) and same continue/return control flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8e2c270f-8e2a-481a-b688-14b374db3cfa

📥 Commits

Reviewing files that changed from the base of the PR and between 37d9e943d7c69f5f328b0d4abb6516ac3f66e130 and fff22e5d0a396c4d2443ca22eefb16575192863f.

📒 Files selected for processing (1)
  • server/service/testing_client.go

@sgress454 sgress454 changed the title exception activities Add activities when toggling GitOps exception settings Apr 24, 2026

@lukeheath lukeheath 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! Reviewed the Go, as well.

@sgress454
sgress454 merged commit 6c28db8 into main Apr 24, 2026
55 checks passed
@sgress454
sgress454 deleted the sgress454/add-activity-for-exceptions branch April 24, 2026 18:09
AndreyKizimenko pushed a commit that referenced this pull request Apr 24, 2026
…tings (#44145)

Cherry-pick of #44094 into the RC branch.

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sgress454 <553428+sgress454@users.noreply.github.com>
Co-authored-by: Luke Heath <luke@fleetdm.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.

5 participants