Skip to content

Show .sh scripts in macOS setup experience - #47629

Merged
cdcme merged 4 commits into
mainfrom
fix-43667-macos-scriptonly-packages
Jun 16, 2026
Merged

Show .sh scripts in macOS setup experience#47629
cdcme merged 4 commits into
mainfrom
fix-43667-macos-scriptonly-packages

Conversation

@cdcme

@cdcme cdcme commented Jun 15, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #43667

Summary

.sh script-only packages weren't appearing in Controls → Setup experience → Software → macOS. The macOS tab filtered titles by exact installer platform (si.platform IN (...)), so .sh rows (stored as platform='linux') only showed up on the Linux tab. A new join table setup_experience_software_installers now tracks linux-.sh-chosen-for-darwin selections independently of the installer's native install_during_setup flag, so the macOS and Linux tabs no longer interfere with each other.

Checklist for submitter

  • Changes file added for user-visible changes in changes/.

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements).

Testing

  • Added/updated automated tests (new CrossPlatformShScripts datastore subtests; new
    TestSetupExperienceMacOSScriptOnlyPackage MDM integration test)
  • QA'd all new/changed functionality manually against the local dev instance — listing, save, independent per-platform toggling, and the delete guard all verified via API

Database migrations

  • Checked schema for all modified tables 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
    • macOS setup experience now surfaces and supports script-only .sh software packages, including cross-platform selections. macOS and Linux selections are tracked independently.
  • Bug Fixes
    • Deleting or batch-updating installers now respects cross-platform setup selections, preventing unintended removals.
  • Tests
    • Added unit/integration coverage for macOS .sh script-only selection behavior and independent macOS vs Linux listings.

@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.11111% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.20%. Comparing base (6f8251c) to head (31bf0d2).

Files with missing lines Patch % Lines
server/datastore/mysql/software_installers.go 65.51% 5 Missing and 5 partials ⚠️
server/datastore/mysql/setup_experience.go 92.10% 3 Missing and 3 partials ⚠️
...0615135619_AddSetupExperienceSoftwareInstallers.go 80.00% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #47629      +/-   ##
==========================================
+ Coverage   67.15%   67.20%   +0.04%     
==========================================
  Files        3616     3617       +1     
  Lines      229035   229172     +137     
  Branches    11937    11791     -146     
==========================================
+ Hits       153813   154004     +191     
+ Misses      61367    61326      -41     
+ Partials    13855    13842      -13     
Flag Coverage Δ
backend 68.84% <86.11%> (+0.05%) ⬆️

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.

@cdcme
cdcme marked this pull request as ready for review June 15, 2026 19:13
@cdcme
cdcme requested a review from a team as a code owner June 15, 2026 19:13
Copilot AI review requested due to automatic review settings June 15, 2026 19:13
@cdcme

cdcme commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

@claude review once

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

This PR fixes macOS Setup experience software listings so .sh script-only packages (stored as platform='linux') appear and can be selected for macOS without interfering with Linux selections, by introducing a dedicated cross-platform selection table.

Changes:

  • Add setup_experience_software_installers table + migration to track cross-platform setup-experience selections (e.g. linux .sh selected for darwin).
  • Update setup experience datastore logic to read/write cross-platform selections and to prevent deleting installers that are selected cross-platform.
  • Add datastore + integration tests covering .sh visibility, selection, and independence between macOS and Linux.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
server/service/integration_mdm_setup_experience_test.go Adds an end-to-end integration test covering macOS listing/saving of script-only .sh packages.
server/datastore/mysql/software_titles.go Adjusts software title listing SQL to include linux .sh packages in darwin setup-experience queries and compute selection state via the new table.
server/datastore/mysql/software_installers.go Extends delete guard to prevent deleting installers selected via the new cross-platform table.
server/datastore/mysql/setup_experience.go Implements cross-platform selection persistence and enqueuing for darwin hosts.
server/datastore/mysql/setup_experience_test.go Adds datastore-level tests for cross-platform .sh behavior and independence.
server/datastore/mysql/schema.sql Updates schema snapshot with the new table.
server/datastore/mysql/migrations/tables/20260615135619_AddSetupExperienceSoftwareInstallers.go Adds migration creating setup_experience_software_installers.
server/datastore/mysql/migrations/tables/20260615135619_AddSetupExperienceSoftwareInstallers_test.go Adds migration test validating table creation and FK cascade behavior.
Files excluded by content exclusion policy (1)
  • changes/43667-macos-script-only-setup-experience

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

Comment thread server/datastore/mysql/software_titles.go
Comment thread server/datastore/mysql/setup_experience_test.go Outdated
Comment thread server/datastore/mysql/software_installers.go Outdated
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 556fc181-3114-46fc-b740-7ea7a01d12a4

📥 Commits

Reviewing files that changed from the base of the PR and between ca0640d and 31bf0d2.

📒 Files selected for processing (4)
  • server/datastore/mysql/setup_experience.go
  • server/datastore/mysql/setup_experience_test.go
  • server/datastore/mysql/software_installers.go
  • server/datastore/mysql/software_titles.go
✅ Files skipped from review due to trivial changes (1)
  • server/datastore/mysql/setup_experience_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/datastore/mysql/software_titles.go
  • server/datastore/mysql/setup_experience.go

Walkthrough

A new MySQL table setup_experience_software_installers is introduced to track cross-platform .sh installer selections for macOS independently from Linux. The setup-experience selection function (SetSetupExperienceSoftwareTitles) now classifies linux .sh installers as cross-platform when requested under macOS, persisting them to the new table. The enqueue function adds a union branch to pull these cross-platform mappings for macOS hosts. The count function sums native and cross-platform rows. The software titles listing is updated to surface linux .sh entries when filtering for darwin in setup-experience context. DeleteSoftwareInstaller gains a guard against deleting cross-selected installers. Migration, datastore, and integration tests cover all new behavior.

Possibly related PRs

  • fleetdm/fleet#47075: Modifies setup_experience.go enqueue logic around the policy_gated field for Windows/Linux installers, overlapping with this PR's addition of policy_gated = FALSE for cross-platform .sh installers in the same function.
  • fleetdm/fleet#46236: Modifies software_installers.go deletion side-effects around setup-experience installs, directly overlapping with this PR's new deletion guard checking setup_experience_software_installers.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% 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 accurately and concisely describes the main change: enabling .sh script visibility in macOS setup experience.
Description check ✅ Passed The PR description clearly explains the problem, solution, and implementation approach; includes the related issue link and documents key checklist items.
Linked Issues check ✅ Passed The PR directly addresses issue #43667 by implementing infrastructure to display .sh script-only packages in the macOS setup experience tab.
Out of Scope Changes check ✅ Passed All changes are directly aligned with enabling script-only packages in macOS setup experience; no extraneous modifications detected.

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

✨ 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 fix-43667-macos-scriptonly-packages

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
server/datastore/mysql/software_installers.go (1)

2676-2689: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Preserve or block cross-platform setup selections during batch replacement.

The new direct delete guard covers DeleteSoftwareInstaller, but BatchSetSoftwareInstallers can still delete a linux .sh installer that is selected for macOS setup experience: the pre-delete check only counts software_installers.install_during_setup, and the direct delete of replaced installer IDs will cascade-remove setup_experience_software_installers rows. Add the cross-table to this guard and either re-point mappings to the replacement installer ID or return errDeleteInstallerInstalledDuringSetup when setup configuration is not being replaced. As per coding guidelines, SQL queries intended to return data for a specific entity must apply precise filtering criteria.

Directionally, include the cross-table in the guard
 			var countInstallDuringSetup int
 			if err := sqlx.GetContext(ctx, tx, &countInstallDuringSetup, stmt, args...); err != nil {
 				return ctxerr.Wrap(ctx, err, "check installers installed during setup")
 			}
 			if countInstallDuringSetup > 0 {
 				return errDeleteInstallerInstalledDuringSetup
 			}
+
+			stmt, args, err = sqlx.In(`
+				SELECT COUNT(*)
+				FROM setup_experience_software_installers seti
+				JOIN software_installers si ON si.id = seti.software_installer_id
+				WHERE si.global_or_team_id = ?
+				  AND si.title_id NOT IN (?)`,
+				globalOrTeamID, titleIDs,
+			)
+			if err != nil {
+				return ctxerr.Wrap(ctx, err, "build statement to check cross-platform setup installers")
+			}
+			var countCrossInstallDuringSetup int
+			if err := sqlx.GetContext(ctx, tx, &countCrossInstallDuringSetup, stmt, args...); err != nil {
+				return ctxerr.Wrap(ctx, err, "check cross-platform installers installed during setup")
+			}
+			if countCrossInstallDuringSetup > 0 {
+				return errDeleteInstallerInstalledDuringSetup
+			}
 		}

For the replaced-installer delete at Line 3180, migrate setup_experience_software_installers rows from the old installer ID to installerID before deleting, or block the replacement when the incoming installer is no longer an eligible linux .sh package.

Also applies to: 3172-3181

🤖 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 2676 - 2689, The
pre-delete guard at the countInstallDuringSetupNotInList check only validates
software_installers.install_during_setup but ignores cross-table references in
setup_experience_software_installers, allowing cascade deletion of setup
configurations when installers are replaced. Extend the guard check to include
the setup_experience_software_installers table to detect when a replaced
installer is selected for setup experience, and at the location where replaced
installer IDs are deleted (the direct delete operation around line 3180), either
migrate the setup_experience_software_installers rows to point to the
replacement installerID before deletion, or block the replacement entirely when
the incoming installer is not an eligible linux .sh package. Apply precise SQL
filtering criteria as per coding guidelines.

Source: Coding guidelines

server/datastore/mysql/setup_experience.go (1)

633-668: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use global_or_team_id consistently for no-team setup counts.

globalOrTeamID is derived for the new cross-table count, but the native installer, VPP, and script subqueries still use team_id = ? with the nullable teamID pointer. When teamID == nil, those predicates won't match no-team rows, so the setup count can report only cross-platform selections and miss native setup items.

Proposed fix
 		(
 			(SELECT COUNT(*)
 			FROM software_installers
-			WHERE team_id = ?
+			WHERE global_or_team_id = ?
 			AND install_during_setup = 1
 			AND platform = ?)
 			+
 			(SELECT COUNT(*)
 			FROM setup_experience_software_installers
@@
 		(
 			SELECT COUNT(*)
 			FROM vpp_apps_teams
-			WHERE team_id = ?
+			WHERE global_or_team_id = ?
 			AND platform = ?
 			AND install_during_setup = 1
 		) AS vpp,
 		(
 			SELECT COUNT(*)
 			FROM setup_experience_scripts
-			WHERE team_id = ?
+			WHERE global_or_team_id = ?
 		) AS scripts`
@@
 		ctx, ds.reader(ctx), sec, stmt,
-		teamID, platform,
+		globalOrTeamID, platform,
 		globalOrTeamID, platform,
-		teamID, platform,
-		teamID,
+		globalOrTeamID, platform,
+		globalOrTeamID,
 	); err != nil {
🤖 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/setup_experience.go` around lines 633 - 668, The VPP
and scripts subqueries in the setup experience count SQL use team_id = ? with
the nullable teamID pointer, which fails to match no-team rows when teamID is
nil. Update both subqueries to use global_or_team_id = ? instead of team_id = ?
in their WHERE clauses. Specifically, change the vpp_apps_teams subquery WHERE
clause from team_id = ? to global_or_team_id = ?, and change the
setup_experience_scripts subquery WHERE clause from team_id = ? to
global_or_team_id = ?. Then update the parameters passed to sqlx.GetContext to
use globalOrTeamID instead of teamID for these two subqueries, ensuring
consistent use of the derived globalOrTeamID value across all table references.
🤖 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.

Outside diff comments:
In `@server/datastore/mysql/setup_experience.go`:
- Around line 633-668: The VPP and scripts subqueries in the setup experience
count SQL use team_id = ? with the nullable teamID pointer, which fails to match
no-team rows when teamID is nil. Update both subqueries to use global_or_team_id
= ? instead of team_id = ? in their WHERE clauses. Specifically, change the
vpp_apps_teams subquery WHERE clause from team_id = ? to global_or_team_id = ?,
and change the setup_experience_scripts subquery WHERE clause from team_id = ?
to global_or_team_id = ?. Then update the parameters passed to sqlx.GetContext
to use globalOrTeamID instead of teamID for these two subqueries, ensuring
consistent use of the derived globalOrTeamID value across all table references.

In `@server/datastore/mysql/software_installers.go`:
- Around line 2676-2689: The pre-delete guard at the
countInstallDuringSetupNotInList check only validates
software_installers.install_during_setup but ignores cross-table references in
setup_experience_software_installers, allowing cascade deletion of setup
configurations when installers are replaced. Extend the guard check to include
the setup_experience_software_installers table to detect when a replaced
installer is selected for setup experience, and at the location where replaced
installer IDs are deleted (the direct delete operation around line 3180), either
migrate the setup_experience_software_installers rows to point to the
replacement installerID before deletion, or block the replacement entirely when
the incoming installer is not an eligible linux .sh package. Apply precise SQL
filtering criteria as per coding guidelines.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b4326520-44c1-4ecc-809a-e309eda6b94b

📥 Commits

Reviewing files that changed from the base of the PR and between bdd15d6 and ca0640d.

📒 Files selected for processing (9)
  • changes/43667-macos-script-only-setup-experience
  • server/datastore/mysql/migrations/tables/20260615135619_AddSetupExperienceSoftwareInstallers.go
  • server/datastore/mysql/migrations/tables/20260615135619_AddSetupExperienceSoftwareInstallers_test.go
  • server/datastore/mysql/schema.sql
  • server/datastore/mysql/setup_experience.go
  • server/datastore/mysql/setup_experience_test.go
  • server/datastore/mysql/software_installers.go
  • server/datastore/mysql/software_titles.go
  • server/service/integration_mdm_setup_experience_test.go

@jkatz01 jkatz01 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! Maybe we could drop the software_installers.install_during_setup flag and just use this new table if this gets more complicated in the future?

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.

macOS script-only packages are not available in the Setup experience

3 participants