From 18a783f5a1f830b1287561247b4455cf2edf3cda Mon Sep 17 00:00:00 2001 From: Allen Houchins Date: Wed, 10 Jun 2026 22:57:29 -0500 Subject: [PATCH 1/2] Add Logi Tune as a macOS FMA Add support for Logi Tune: include a Homebrew input manifest and install/uninstall scripts, add a transformer to override the installer URL to Logitech's enterprise PKG (and set SHA256 to "no_check"), and register the app in outputs. Also add darwin output refs with version, installer URL and embedded script refs, update apps.json to list Logi Tune, and add a frontend icon component + PNG asset and icon map entry. The PKG override is used because the Homebrew DMG contains a GUI-only installer without a silent mode; version is still sourced from Homebrew. --- .../ingesters/homebrew/external_refs/main.go | 12 +++++ .../inputs/homebrew/logitune.json | 10 ++++ .../homebrew/scripts/logitune-install.sh | 43 ++++++++++++++++ .../homebrew/scripts/logitune-uninstall.sh | 48 ++++++++++++++++++ ee/maintained-apps/outputs/apps.json | 7 +++ .../outputs/logitune/darwin.json | 22 ++++++++ .../components/icons/LogiTune.tsx | 14 +++++ .../SoftwarePage/components/icons/index.ts | 2 + .../images/app-icon-logitune-60x60@2x.png | Bin 0 -> 10501 bytes 9 files changed, 158 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/logitune.json create mode 100644 ee/maintained-apps/inputs/homebrew/scripts/logitune-install.sh create mode 100644 ee/maintained-apps/inputs/homebrew/scripts/logitune-uninstall.sh create mode 100644 ee/maintained-apps/outputs/logitune/darwin.json create mode 100644 frontend/pages/SoftwarePage/components/icons/LogiTune.tsx create mode 100644 website/assets/images/app-icon-logitune-60x60@2x.png diff --git a/ee/maintained-apps/ingesters/homebrew/external_refs/main.go b/ee/maintained-apps/ingesters/homebrew/external_refs/main.go index 607ea8bc53f..9eaa7b0d662 100644 --- a/ee/maintained-apps/ingesters/homebrew/external_refs/main.go +++ b/ee/maintained-apps/ingesters/homebrew/external_refs/main.go @@ -39,6 +39,7 @@ var Funcs = map[string][]func(*maintained_apps.FMAManifestApp) (*maintained_apps "mysqlworkbench/darwin": {MySQLWorkbenchVersionTransformer}, "lens/darwin": {LensVersionTransformer}, "grammarly-desktop/darwin": {GrammarlyDesktopVersionShortener}, + "logitune/darwin": {LogiTunePKGInstaller}, "anka-virtualization/darwin": {AnkaVersionShortener}, "pd/darwin": {PdVersionTransformer}, } @@ -83,6 +84,17 @@ func ZoomPKGInstaller(app *maintained_apps.FMAManifestApp) (*maintained_apps.FMA return app, nil } +func LogiTunePKGInstaller(app *maintained_apps.FMAManifestApp) (*maintained_apps.FMAManifestApp, error) { + // Override installer URL to use Logitech's enterprise PKG installer instead of Homebrew's DMG, + // which only contains a GUI installer app with no silent mode. + // Version is kept from Homebrew (not set to "latest") + app.InstallerURL = "https://software.vc.logitech.com/downloads/tune/LogiTuneInstaller.pkg" + // Set SHA256 to "no_check" since we're using a different installer URL than Homebrew + app.SHA256 = "no_check" + + return app, nil +} + func WhatsAppInstallerURL(app *maintained_apps.FMAManifestApp) (*maintained_apps.FMAManifestApp, error) { // Override installer URL to always use the specified URL app.InstallerURL = "https://web.whatsapp.com/desktop/mac_native/release/?configuration=Release&src=whatsapp_downloads_page" diff --git a/ee/maintained-apps/inputs/homebrew/logitune.json b/ee/maintained-apps/inputs/homebrew/logitune.json new file mode 100644 index 00000000000..c4f8095a9f0 --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/logitune.json @@ -0,0 +1,10 @@ +{ + "name": "Logi Tune", + "unique_identifier": "com.logitech.logitune", + "token": "logitune", + "installer_format": "pkg", + "slug": "logitune/darwin", + "default_categories": ["Communication"], + "install_script_path": "ee/maintained-apps/inputs/homebrew/scripts/logitune-install.sh", + "uninstall_script_path": "ee/maintained-apps/inputs/homebrew/scripts/logitune-uninstall.sh" +} diff --git a/ee/maintained-apps/inputs/homebrew/scripts/logitune-install.sh b/ee/maintained-apps/inputs/homebrew/scripts/logitune-install.sh new file mode 100644 index 00000000000..03c9684c514 --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/scripts/logitune-install.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +quit_application() { + local bundle_id="$1" + local console_user="$2" + local timeout_duration=10 + + if [[ $EUID -eq 0 && "$console_user" == "root" ]]; then + echo "Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'." + return + fi + + echo "Quitting application '$bundle_id'..." + + # try to quit the application within the timeout period + local quit_success=false + SECONDS=0 + while (( SECONDS < timeout_duration )); do + if osascript -e "tell application id \"$bundle_id\" to quit" >/dev/null 2>&1; then + if ! pgrep -f "$bundle_id" >/dev/null 2>&1; then + echo "Application '$bundle_id' quit successfully." + quit_success=true + break + fi + fi + sleep 1 + done + + if [[ "$quit_success" = false ]]; then + echo "Application '$bundle_id' did not quit." + fi +} + +CONSOLE_USER=$(stat -f "%Su" /dev/console 2>/dev/null || echo "") + +# Quit Logi Tune gracefully before the PKG's preinstall force-kills it. The +# PKG's default RUNAPP choice relaunches the app for logged-in console users +# after installation, so no relaunch step is needed here. +if osascript -e "application id \"com.logitech.logitune\" is running" 2>/dev/null; then + quit_application 'com.logitech.logitune' "$CONSOLE_USER" +fi + +installer -pkg "$INSTALLER_PATH" -target / diff --git a/ee/maintained-apps/inputs/homebrew/scripts/logitune-uninstall.sh b/ee/maintained-apps/inputs/homebrew/scripts/logitune-uninstall.sh new file mode 100644 index 00000000000..d2b542edb74 --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/scripts/logitune-uninstall.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Logi Tune installs a vendor uninstall script that unloads its launchd +# services, kills running processes, removes the RightSight daemon, and +# deletes the app bundle, support files, and pkg receipts. Prefer it when +# present (any install of Logi Tune 3.4+ has it). +VENDOR_UNINSTALLER="/Library/Application Support/logitune/uninstall_app.sh" + +if [ -f "$VENDOR_UNINSTALLER" ]; then + sh "$VENDOR_UNINSTALLER" + exit $? +fi + +# Fallback cleanup for installs missing the vendor uninstaller. + +# stop and remove per-user launch agents +for agent in com.logitech.logitune.launcher com.logitech.logitune.agent; do + for user in $(who | grep console | awk '{print $1}'); do + user_id=$(id -u "$user") + launchctl asuser "$user_id" launchctl unload "/Library/LaunchAgents/${agent}.plist" 2>/dev/null + done + rm -f "/Library/LaunchAgents/${agent}.plist" +done + +# stop and remove launch daemons (incl. the bundled RightSight daemon) +for daemon in com.logitech.logitune.updater com.logitech.logitune.crashpad com.logitech.LogiRightSight; do + launchctl unload "/Library/LaunchDaemons/${daemon}.plist" 2>/dev/null + rm -f "/Library/LaunchDaemons/${daemon}.plist" +done + +# kill any remaining processes +for process in LogiTune LogiTuneAgent LogiTuneUpdater LogiTuneCrashpadHandler; do + pkill -9 -x "$process" 2>/dev/null +done + +# remove the app bundle (current and legacy install paths) and support files +rm -rf "/Applications/Logi Tune.app" +rm -rf "/Applications/LogiTune.app" +rm -rf "/Applications/LogiTuneInstaller.app" +rm -rf "/Library/Application Support/logitune" +rm -rf "/Users/Shared/logitune" +rm -f "/Users/Shared/LogiTuneInstallerStarted.txt" + +# forget pkg receipts +pkgutil --forget com.logitech.pkg.logitune 2>/dev/null +pkgutil --forget com.logitech.logitune.installer 2>/dev/null + +exit 0 diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 909548ded69..e573dc91f39 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -2878,6 +2878,13 @@ "unique_identifier": "Logi Options+", "description": "Logi Options+ is software for managing Logitech devices." }, + { + "name": "Logi Tune", + "slug": "logitune/darwin", + "platform": "darwin", + "unique_identifier": "com.logitech.logitune", + "description": "Logi Tune is software for managing Logitech webcams, headsets, and docks for video meetings." + }, { "name": "Loom", "slug": "loom/darwin", diff --git a/ee/maintained-apps/outputs/logitune/darwin.json b/ee/maintained-apps/outputs/logitune/darwin.json new file mode 100644 index 00000000000..8947b5270b0 --- /dev/null +++ b/ee/maintained-apps/outputs/logitune/darwin.json @@ -0,0 +1,22 @@ +{ + "versions": [ + { + "version": "3.11.89", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.logitech.logitune';", + "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = 'com.logitech.logitune' AND version_compare(bundle_short_version, '3.11.89') < 0);" + }, + "installer_url": "https://software.vc.logitech.com/downloads/tune/LogiTuneInstaller.pkg", + "install_script_ref": "584e269e", + "uninstall_script_ref": "8a23a81e", + "sha256": "no_check", + "default_categories": [ + "Communication" + ] + } + ], + "refs": { + "584e269e": "#!/bin/bash\n\nquit_application() {\n local bundle_id=\"$1\"\n local console_user=\"$2\"\n local timeout_duration=10\n\n if [[ $EUID -eq 0 && \"$console_user\" == \"root\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\nCONSOLE_USER=$(stat -f \"%Su\" /dev/console 2>/dev/null || echo \"\")\n\n# Quit Logi Tune gracefully before the PKG's preinstall force-kills it. The\n# PKG's default RUNAPP choice relaunches the app for logged-in console users\n# after installation, so no relaunch step is needed here.\nif osascript -e \"application id \\\"com.logitech.logitune\\\" is running\" 2>/dev/null; then\n quit_application 'com.logitech.logitune' \"$CONSOLE_USER\"\nfi\n\ninstaller -pkg \"$INSTALLER_PATH\" -target /\n", + "8a23a81e": "#!/bin/bash\n\n# Logi Tune installs a vendor uninstall script that unloads its launchd\n# services, kills running processes, removes the RightSight daemon, and\n# deletes the app bundle, support files, and pkg receipts. Prefer it when\n# present (any install of Logi Tune 3.4+ has it).\nVENDOR_UNINSTALLER=\"/Library/Application Support/logitune/uninstall_app.sh\"\n\nif [ -f \"$VENDOR_UNINSTALLER\" ]; then\n sh \"$VENDOR_UNINSTALLER\"\n exit $?\nfi\n\n# Fallback cleanup for installs missing the vendor uninstaller.\n\n# stop and remove per-user launch agents\nfor agent in com.logitech.logitune.launcher com.logitech.logitune.agent; do\n for user in $(who | grep console | awk '{print $1}'); do\n user_id=$(id -u \"$user\")\n launchctl asuser \"$user_id\" launchctl unload \"/Library/LaunchAgents/${agent}.plist\" 2>/dev/null\n done\n rm -f \"/Library/LaunchAgents/${agent}.plist\"\ndone\n\n# stop and remove launch daemons (incl. the bundled RightSight daemon)\nfor daemon in com.logitech.logitune.updater com.logitech.logitune.crashpad com.logitech.LogiRightSight; do\n launchctl unload \"/Library/LaunchDaemons/${daemon}.plist\" 2>/dev/null\n rm -f \"/Library/LaunchDaemons/${daemon}.plist\"\ndone\n\n# kill any remaining processes\nfor process in LogiTune LogiTuneAgent LogiTuneUpdater LogiTuneCrashpadHandler; do\n pkill -9 -x \"$process\" 2>/dev/null\ndone\n\n# remove the app bundle (current and legacy install paths) and support files\nrm -rf \"/Applications/Logi Tune.app\"\nrm -rf \"/Applications/LogiTune.app\"\nrm -rf \"/Applications/LogiTuneInstaller.app\"\nrm -rf \"/Library/Application Support/logitune\"\nrm -rf \"/Users/Shared/logitune\"\nrm -f \"/Users/Shared/LogiTuneInstallerStarted.txt\"\n\n# forget pkg receipts\npkgutil --forget com.logitech.pkg.logitune 2>/dev/null\npkgutil --forget com.logitech.logitune.installer 2>/dev/null\n\nexit 0\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/LogiTune.tsx b/frontend/pages/SoftwarePage/components/icons/LogiTune.tsx new file mode 100644 index 00000000000..b0c20739793 --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/LogiTune.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const LogiTune = (props: SVGProps) => ( + + + +); +export default LogiTune; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index 543ab78a34b..1ee056f4e2e 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -330,6 +330,7 @@ import Linear from "./Linear"; import LinuxOS from "./LinuxOS"; import LittleSnitch from "./LittleSnitch"; import Logioptionsplus from "./Logioptionsplus"; +import LogiTune from "./LogiTune"; import Loom from "./Loom"; import LuLu from "./LuLu"; import Maccy from "./Maccy"; @@ -995,6 +996,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { linear: Linear, "little snitch": LittleSnitch, "logi options+": Logioptionsplus, + "logi tune": LogiTune, loom: Loom, lulu: LuLu, maccy: Maccy, diff --git a/website/assets/images/app-icon-logitune-60x60@2x.png b/website/assets/images/app-icon-logitune-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d375174759b95ad55c774fdeea3defddfd04a95b GIT binary patch literal 10501 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 A;{X5v literal 0 HcmV?d00001 From ceb75cfb1c059ed61e371430a3c83d5969710bf1 Mon Sep 17 00:00:00 2001 From: Allen Houchins Date: Wed, 10 Jun 2026 23:06:53 -0500 Subject: [PATCH 2/2] Update darwin.go --- cmd/maintained-apps/validate/darwin.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmd/maintained-apps/validate/darwin.go b/cmd/maintained-apps/validate/darwin.go index 6caecef4e88..08e89c66023 100644 --- a/cmd/maintained-apps/validate/darwin.go +++ b/cmd/maintained-apps/validate/darwin.go @@ -282,6 +282,18 @@ func appExists(ctx context.Context, logger *slog.Logger, appName, uniqueAppIdent } } + // Logi Tune: the installer URL always serves the latest release, while the Homebrew + // cask version lags behind (its livecheck scrapes a Logitech support article that is + // updated less often than the download). The installed version is therefore newer + // than the manifest version. If version doesn't match but app is installed, fall + // back to existence-only validation. + if uniqueAppIdentifier == "com.logitech.logitune" { + if !checkVersionMatch(appVersion, result.Version, result.BundledVersion) { + logger.InfoContext(ctx, "Logi Tune detected - version mismatch but app is installed, falling back to existence-only validation") + return true, nil + } + } + // Check various version matching strategies if checkVersionMatch(appVersion, result.Version, result.BundledVersion) { return true, nil