Skip to content

Add GitOps support for macOS script-only packages in setup experience - #49088

Closed
cdcme wants to merge 2 commits into
mainfrom
feat/gitops-scriptonly-macos-setup-experience
Closed

Add GitOps support for macOS script-only packages in setup experience#49088
cdcme wants to merge 2 commits into
mainfrom
feat/gitops-scriptonly-macos-setup-experience

Conversation

@cdcme

@cdcme cdcme commented Jul 9, 2026

Copy link
Copy Markdown
Member

Related issue:

Summary

Adds a setup_experience_platforms field to the GitOps software package spec so .sh script-only installers can be selected for macOS setup experience declaratively. Reconciles the cross-platform selection table on every batch apply.

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

  • Added/updated automated tests

  • QA'd all new/changed functionality manually

New Fleet configuration settings

  • Verified that the setting is exported via fleetctl generate-gitops
  • Verified the setting is documented in a separate PR to the GitOps documentation
  • 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)

Summary by CodeRabbit

  • New Features
    • Added support for choosing setup-experience platforms for software installers, including cross-platform script installers on macOS.
    • GitOps and batch uploads now preserve and apply platform-specific setup-experience selections.
  • Bug Fixes
    • Improved validation so only compatible platform selections are accepted for each installer type.
    • Cleared and re-applied platform selections now behave consistently during updates.
  • Tests
    • Added coverage for platform normalization, validation, and reconciliation behavior.

@cdcme
cdcme marked this pull request as ready for review July 9, 2026 21:45
@cdcme
cdcme requested a review from a team as a code owner July 9, 2026 21:45
Copilot AI review requested due to automatic review settings July 9, 2026 21:45
@cdcme cdcme closed this Jul 9, 2026
@cdcme
cdcme deleted the feat/gitops-scriptonly-macos-setup-experience branch July 9, 2026 21:52

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.

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

Adds GitOps support for selecting macOS setup experience software from script-only (.sh) packages via a new setup_experience_platforms field, and reconciles cross-platform selections declaratively on each /software/batch apply.

Changes:

  • Extend GitOps software package spec and API payloads with setup_experience_platforms (tri-state semantics) and validate/canonicalize platform inputs.
  • Add datastore support for resolving installer IDs and writing cross-platform setup experience selections (setup_experience_software_installers).
  • Update fleetctl generate-gitops to round-trip cross-platform setup experience selections back into YAML.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
changes/47629-gitops-scriptonly-macos-setup-experience Release note entry for the feature (content excluded from review).
server/service/integration_enterprise_test.go Adds integration coverage for cross-platform setup experience selection reconciliation for script packages.
server/service/client.go Includes setup_experience_platforms in the batch software payload built from GitOps specs (tri-state preserved).
server/mock/datastore_mock.go Adds mock hooks for new setup experience cross-installer datastore methods.
server/fleet/software_installer.go Adds spec field, payload field, and helper functions for canonicalizing/validating setup experience platforms.
server/fleet/scripts.go Extends SoftwareInstallerPayload with setup_experience_platforms tri-state field.
server/fleet/datastore.go Extends datastore interface for cross-platform setup experience installer reconciliation.
server/datastore/mysql/software_installers.go Adds query to resolve installer IDs by team and (filename, platform).
server/datastore/mysql/setup_experience.go Adds datastore method to replace cross-platform setup experience installer selections for a team+platform.
pkg/spec/gitops.go Hydrates setup_experience_platforms from GitOps spec into the package-level model.
ee/server/service/software_installers.go Validates/canonicalizes setup_experience_platforms and reconciles cross-platform selections on each batch apply.
ee/server/service/software_installers_test.go Adds unit tests for platform normalization/validation behavior.
cmd/fleetctl/fleetctl/generate_gitops.go Emits setup_experience_platforms so UI selections can round-trip via generate → apply.
Files excluded by content exclusion policy (1)
  • changes/47629-gitops-scriptonly-macos-setup-experience

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

Comment on lines +3247 to +3249
if slices.Contains(normalized, "darwin") && manualAgentInstall {
return errors.New(`Couldn't edit software. "setup_experience_platforms" cannot include "macos" if "macos_manual_agent_install" is enabled.`)
}
Comment on lines +3459 to +3462
}
rows, err := svc.ds.GetSoftwareInstallerIDsByTeamAndFilenamePlatform(ctx, teamID, filenames, platforms)
if err != nil {
return ctxerr.Wrap(ctx, err, "look up installer ids for cross-platform reconcile")
Comment on lines +620 to +626
// SoftwareInstallerLookupRow projects the columns needed to resolve an
// installer's identity from its (filename, platform) natural key.
type SoftwareInstallerLookupRow struct {
ID uint `db:"id"`
Filename string `db:"filename"`
Platform string `db:"platform"`
}
@cdcme
cdcme restored the feat/gitops-scriptonly-macos-setup-experience branch July 9, 2026 21:54
@cdcme cdcme reopened this Jul 9, 2026
@cdcme cdcme closed this Jul 9, 2026
@cdcme
cdcme deleted the feat/gitops-scriptonly-macos-setup-experience branch July 9, 2026 21:54
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9dd75f01-5104-4669-986b-55b84dfc1c9b

📥 Commits

Reviewing files that changed from the base of the PR and between e671a2d and d8e5f40.

📒 Files selected for processing (2)
  • ee/server/service/software_installers.go
  • server/service/integration_enterprise_test.go

Walkthrough

Adds setup_experience_platforms to software package declarations, payloads, and GitOps output. GitOps generation preserves cross-platform setup selections for macOS script-only installers. Batch upload and completion logic normalize, validate, and reconcile platform selections, including clearing and restoring cross-installer rows. New datastore methods, mocks, and tests cover lookup, replacement, normalization, and failure cases.

Possibly related issues

Possibly related PRs

  • fleetdm/fleet#47629: Implements the same cross-platform setup-experience installer mapping flow that this PR builds on.
  • fleetdm/fleet#48710: Modifies pkg/spec/gitops.go’s GitOps package hydration logic in the same area as this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% 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
Title check ✅ Passed The title clearly states the main change: GitOps support for macOS script-only setup-experience packages.
Description check ✅ Passed The description covers the related issue, summary, testing, and checklist items required by the template.
Linked Issues check ✅ Passed The changes address #43667 by making .sh packages selectable for macOS setup experience and exporting/reconciling the new GitOps field.
Out of Scope Changes check ✅ Passed The code changes are centered on the new setup_experience_platforms workflow and supporting validation, tests, and datastore updates.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/gitops-scriptonly-macos-setup-experience

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.1)
server/service/integration_enterprise_test.go

ast-grep timed out on this file


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.

@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: 1

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)

3391-3410: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make the GitOps reconcile part of the same all-or-nothing apply. BatchSetSoftwareInstallers and BatchSetInHouseAppsInstallers commit before reconcileGitOpsSetupExperienceCrossInstallers runs, so a later error leaves the installer rows applied while the cross-platform setup_experience_software_installers state stays stale until another successful apply.

🤖 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 3391 - 3410, The
GitOps reconcile is currently happening after the two batch writes, so a failure
in reconcile can leave the installer state partially applied. Move the reconcile
logic in reconcileGitOpsSetupExperienceCrossInstallers into the same
transactional flow as BatchSetSoftwareInstallers and
BatchSetInHouseAppsInstallers, or otherwise ensure all three operations share
one all-or-nothing unit of work before returning from the apply path.
🤖 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 `@server/datastore/mysql/software_installers.go`:
- Around line 1146-1176: The post-write lookup in
GetSoftwareInstallerIDsByTeamAndFilenamePlatform is using ds.reader(ctx), which
can hit a lagging replica and miss freshly inserted installers. Change this
lookup to use the primary connection path used elsewhere for write-after-read
flows in Datastore, so the reconciliation after BatchSetSoftwareInstallers
always sees the newly created rows. Keep the rest of the query-building logic
intact and update the call site(s) so this method is guaranteed to read from
primary during this reconciliation.

---

Outside diff comments:
In `@ee/server/service/software_installers.go`:
- Around line 3391-3410: The GitOps reconcile is currently happening after the
two batch writes, so a failure in reconcile can leave the installer state
partially applied. Move the reconcile logic in
reconcileGitOpsSetupExperienceCrossInstallers into the same transactional flow
as BatchSetSoftwareInstallers and BatchSetInHouseAppsInstallers, or otherwise
ensure all three operations share one all-or-nothing unit of work before
returning from the apply path.
🪄 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: 9dd75f01-5104-4669-986b-55b84dfc1c9b

📥 Commits

Reviewing files that changed from the base of the PR and between 853bb6a and e671a2d.

📒 Files selected for processing (13)
  • changes/47629-gitops-scriptonly-macos-setup-experience
  • cmd/fleetctl/fleetctl/generate_gitops.go
  • ee/server/service/software_installers.go
  • ee/server/service/software_installers_test.go
  • pkg/spec/gitops.go
  • server/datastore/mysql/setup_experience.go
  • server/datastore/mysql/software_installers.go
  • server/fleet/datastore.go
  • server/fleet/scripts.go
  • server/fleet/software_installer.go
  • server/mock/datastore_mock.go
  • server/service/client.go
  • server/service/integration_enterprise_test.go

Comment on lines +1146 to +1176
func (ds *Datastore) GetSoftwareInstallerIDsByTeamAndFilenamePlatform(
ctx context.Context, teamID uint, filenames []string, platforms []string,
) ([]fleet.SoftwareInstallerLookupRow, error) {
if len(filenames) != len(platforms) {
return nil, ctxerr.New(ctx, "filenames and platforms slices must have the same length")
}
if len(filenames) == 0 {
return nil, nil
}
// sqlx.In can't expand tuple IN, so build the placeholders manually.
rowPlaceholders := strings.Join(slices.Repeat([]string{"(?,?)"}, len(filenames)), ",")
args := make([]any, 0, len(filenames)*2+1)
args = append(args, teamID)
for i := range filenames {
args = append(args, filenames[i], platforms[i])
}
stmt := fmt.Sprintf(`
SELECT
id,
filename,
platform
FROM software_installers
WHERE global_or_team_id = ?
AND is_active = 1
AND (filename, platform) IN (%s)`, rowPlaceholders)
var rows []fleet.SoftwareInstallerLookupRow
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &rows, stmt, args...); err != nil {
return nil, ctxerr.Wrap(ctx, err, "look up installer ids by team and filename+platform")
}
return rows, nil
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the reader() helper to see if it routes to primary after recent writes in the same context.
ast-grep run --pattern 'func ($_ *Datastore) reader($_) $_' --lang go server/datastore/mysql
rg -n -A 15 'func \(ds \*Datastore\) reader\(' server/datastore/mysql

Repository: fleetdm/fleet

Length of output: 1593


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the call path and any recent-write / primary-required context handling.
ast-grep outline ee/server/service/software_installers.go --view expanded
rg -n -A 40 -B 20 'reconcileGitOpsSetupExperienceCrossInstallers|BatchSetSoftwareInstallers|ctxdb\.WithPrimaryRequired|IsPrimaryRequired|primary required|recent write' ee/server/service/software_installers.go server/datastore/mysql server/lib

Repository: fleetdm/fleet

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the narrow call path around the batch-set flow and any primary-required context markers.
rg -n -A 25 -B 15 'reconcileGitOpsSetupExperienceCrossInstallers|BatchSetSoftwareInstallers\(|GetSoftwareInstallerIDsByTeamAndFilenamePlatform\(|WithPrimaryRequired|IsPrimaryRequired' ee/server/service/software_installers.go server/datastore/mysql/mysql.go server/lib server/context

Repository: fleetdm/fleet

Length of output: 17858


Use the primary for this post-write lookup
reader(ctx) only switches to primary when ctxdb.RequirePrimary is set, and this reconciliation runs right after BatchSetSoftwareInstallers without setting that hint. A lagging replica can miss newly inserted installers and leave setup_experience_software_installers incomplete on the first apply.

🤖 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.go` around lines 1146 - 1176, The
post-write lookup in GetSoftwareInstallerIDsByTeamAndFilenamePlatform is using
ds.reader(ctx), which can hit a lagging replica and miss freshly inserted
installers. Change this lookup to use the primary connection path used elsewhere
for write-after-read flows in Datastore, so the reconciliation after
BatchSetSoftwareInstallers always sees the newly created rows. Keep the rest of
the query-building logic intact and update the call site(s) so this method is
guaranteed to read from primary during this reconciliation.

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.43902% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.04%. Comparing base (853bb6a) to head (d8e5f40).

Files with missing lines Patch % Lines
ee/server/service/software_installers.go 88.17% 6 Missing and 5 partials ⚠️
cmd/fleetctl/fleetctl/generate_gitops.go 60.00% 4 Missing and 2 partials ⚠️
server/datastore/mysql/software_installers.go 73.91% 3 Missing and 3 partials ⚠️
server/fleet/software_installer.go 83.33% 6 Missing ⚠️
server/datastore/mysql/setup_experience.go 73.33% 2 Missing and 2 partials ⚠️
server/service/client.go 86.36% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #49088      +/-   ##
==========================================
+ Coverage   68.03%   68.04%   +0.01%     
==========================================
  Files        3743     3743              
  Lines      237236   237392     +156     
  Branches    12380    12380              
==========================================
+ Hits       161393   161532     +139     
- Misses      61269    61280      +11     
- Partials    14574    14580       +6     
Flag Coverage Δ
backend 69.62% <82.43%> (+0.01%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 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.

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.

3 participants