From 6045f9f2147fa40d4000be126b2c014c8ddca3bf Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Mon, 17 Nov 2025 11:43:53 -0600 Subject: [PATCH] Add Nudge as a macOS FMA (#35741) **Related issue:** Resolves # # 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](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [ ] Added/updated automated tests - [ ] 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) - [ ] QA'd all new/changed functionality manually For unreleased bug fixes in a release candidate, one of: - [ ] Confirmed that the fix is not expected to adversely impact load test results - [ ] Alerted the release DRI if additional load testing is needed ## 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`). ## New Fleet configuration settings - [ ] Setting(s) is/are explicitly excluded from GitOps If you didn't check the box above, follow this checklist for GitOps-enabled settings: - [ ] Verified that the setting is exported via `fleetctl generate-gitops` - [ ] Verified the setting is documented in a separate PR to [the GitOps documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485) - [ ] 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) - [ ] Verified that any relevant UI is disabled when GitOps mode is enabled ## fleetd/orbit/Fleet Desktop - [ ] Verified compatibility with the latest released version of Fleet (see [Must rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md)) - [ ] If the change applies to only one platform, confirmed that `runtime.GOOS` is used as needed to isolate changes - [ ] Verified that fleetd runs on macOS, Linux and Windows - [ ] Verified auto-update works from the released version of component to the new version (see [tools/tuf/test](../tools/tuf/test/README.md)) --- ee/maintained-apps/inputs/homebrew/nudge.json | 8 +++++++ ee/maintained-apps/outputs/apps.json | 7 ++++++ ee/maintained-apps/outputs/nudge/darwin.json | 21 ++++++++++++++++++ .../SoftwarePage/components/icons/Nudge.tsx | 14 ++++++++++++ .../assets/images/app-icon-nudge-60x60@2x.png | Bin 0 -> 6159 bytes 5 files changed, 50 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/nudge.json create mode 100644 ee/maintained-apps/outputs/nudge/darwin.json create mode 100644 frontend/pages/SoftwarePage/components/icons/Nudge.tsx create mode 100644 website/assets/images/app-icon-nudge-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/nudge.json b/ee/maintained-apps/inputs/homebrew/nudge.json new file mode 100644 index 0000000000..a4dc0c5b78 --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/nudge.json @@ -0,0 +1,8 @@ +{ + "name": "Nudge", + "unique_identifier": "com.github.macadmins.Nudge", + "token": "nudge", + "installer_format": "pkg", + "slug": "nudge/darwin", + "default_categories": ["Productivity"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 159e058f31..91c69a2a80 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -260,6 +260,13 @@ "unique_identifier": "notion.id", "description": "Notion is an all-in-one workspace for writing, planning, collaborating, and organizing." }, + { + "name": "Nudge", + "slug": "nudge/darwin", + "platform": "darwin", + "unique_identifier": "com.github.macadmins.Nudge", + "description": "Nudge is an application for enforcing OS updates." + }, { "name": "Omnissa Horizon Client", "slug": "omnissa-horizon-client/darwin", diff --git a/ee/maintained-apps/outputs/nudge/darwin.json b/ee/maintained-apps/outputs/nudge/darwin.json new file mode 100644 index 0000000000..451c5a9faa --- /dev/null +++ b/ee/maintained-apps/outputs/nudge/darwin.json @@ -0,0 +1,21 @@ +{ + "versions": [ + { + "version": "2.0.12.81807", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.github.macadmins.Nudge';" + }, + "installer_url": "https://github.com/macadmins/nudge/releases/download/v2.0.12.81807/Nudge-2.0.12.81807.pkg", + "install_script_ref": "a271b2d1", + "uninstall_script_ref": "7e3910cf", + "sha256": "508b65fe484ba01fce0e12f42e4758a61ce47e0e1fcf5991611942464bb0a055", + "default_categories": [ + "Productivity" + ] + } + ], + "refs": { + "7e3910cf": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil \u003c\u003c\u003c \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nexpand_pkgid_and_map() {\n local PKGID=\"$1\"\n local FUNC=\"$2\"\n if [[ \"$PKGID\" == *\"*\" ]]; then\n local prefix=\"${PKGID%\\*}\"\n echo \"Expanding wildcard for PKGID: $PKGID\"\n for receipt in $(pkgutil --pkgs | grep \"^${prefix}\"); do\n echo \"Processing $receipt\"\n \"$FUNC\" \"$receipt\"\n done\n else\n \"$FUNC\" \"$PKGID\"\n fi\n}\n\nforget_pkg() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" forget_receipt\n}\n\nforget_receipt() {\n local PKGID=\"$1\"\n sudo pkgutil --forget \"$PKGID\"\n}\n\nremove_pkg_files() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" remove_receipt_files\n}\n\nremove_receipt_files() {\n local PKGID=\"$1\"\n local PKGINFO VOLUME INSTALL_LOCATION FULL_INSTALL_LOCATION\n\n echo \"pkgutil --pkg-info-plist \\\"$PKGID\\\"\"\n PKGINFO=$(pkgutil --pkg-info-plist \"$PKGID\")\n VOLUME=$(echo \"$PKGINFO\" | awk '/\u003ckey\u003evolume\u003c\\/key\u003e/ {getline; gsub(/.*\u003cstring\u003e|\u003c\\/string\u003e.*/, \"\"); print}')\n INSTALL_LOCATION=$(echo \"$PKGINFO\" | awk '/\u003ckey\u003einstall-location\u003c\\/key\u003e/ {getline; gsub(/.*\u003cstring\u003e|\u003c\\/string\u003e.*/, \"\"); print}')\n\n if [ -z \"$INSTALL_LOCATION\" ] || [ \"$INSTALL_LOCATION\" = \"/\" ]; then\n FULL_INSTALL_LOCATION=\"$VOLUME\"\n else\n FULL_INSTALL_LOCATION=\"$VOLUME/$INSTALL_LOCATION\"\n FULL_INSTALL_LOCATION=$(echo \"$FULL_INSTALL_LOCATION\" | sed 's|//|/|g')\n fi\n\n echo \"sudo pkgutil --only-files --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-files --files \"$PKGID\" | sed \"s|^|/${INSTALL_LOCATION}/|\" | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n echo \"sudo pkgutil --only-dirs --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | grep '\\\\.app$' | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-dirs --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | grep '\\.app$' | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n root_app_dir=$(\n sudo pkgutil --only-dirs --files \"$PKGID\" \\\n | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" \\\n | grep 'Applications' \\\n | awk '{ print length, $0 }' \\\n | sort -n \\\n | head -n1 \\\n | cut -d' ' -f2-\n )\n if [ -n \"$root_app_dir\" ]; then\n echo \"sudo rmdir -p \\\"$root_app_dir\\\" 2\u003e/dev/null || :\"\n sudo rmdir -p \"$root_app_dir\" 2\u003e/dev/null || :\n fi\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nremove_pkg_files 'com.github.macadmins.Nudge'\nforget_pkg 'com.github.macadmins.Nudge'\nsudo rm -rf 'nudge'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.github.macadmins.Nudge.plist'\n", + "a271b2d1": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n\n# install pkg files\nsudo installer -pkg \"$TMPDIR/Nudge-2.0.12.81807.pkg\" -target /\n" + } +} \ No newline at end of file diff --git a/frontend/pages/SoftwarePage/components/icons/Nudge.tsx b/frontend/pages/SoftwarePage/components/icons/Nudge.tsx new file mode 100644 index 0000000000..b3849fe8cc --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/Nudge.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const Nudge = (props: SVGProps) => ( + + + +); +export default Nudge; diff --git a/website/assets/images/app-icon-nudge-60x60@2x.png b/website/assets/images/app-icon-nudge-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2060b325a6416df69c04464159d3992f60e0bb50 GIT binary patch literal 6159 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 literal 0 HcmV?d00001