From 62807bd61718c607cf40bfca0d2eab206d311755 Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:31:10 -0600 Subject: [PATCH] Add Blender as a macOS and Windows FMA (#36992) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This pull request adds support for managing Blender as a maintained app on both macOS and Windows platforms. It includes new app definitions, installation and uninstallation scripts, and integrates Blender’s icon into the frontend for display. **Blender app support (macOS & Windows):** * Added input definitions for Blender in `homebrew/blender.json` (macOS) and `winget/blender.json` (Windows), specifying installer formats and metadata. [[1]](diffhunk://#diff-891fa47f83954f81130e9a15f0fe60497916acb54a89b78478215cb08e9fb0e8R1-R8) [[2]](diffhunk://#diff-69ce1c29dce360a4dd0033a269811880ea700530661b9b73c1f02bbc37145f91R1-R10) * Updated `apps.json` to include Blender entries for both platforms, with descriptions and unique identifiers. * Created output manifests for Blender on macOS (`blender/darwin.json`) and Windows (`blender/windows.json`), including version info, installer URLs, SHA256 hashes, and install/uninstall scripts. [[1]](diffhunk://#diff-10617b0d1fdc87b778d38cf37a7a3ba479561eedb55b9c6cf8f5128abb224de1R1-R21) [[2]](diffhunk://#diff-a295dabd830631ee695b318097606d605b1d511cf32b46c9663c06ebbf24182bR1-R22) **Frontend integration:** * Added a new Blender SVG icon component (`Blender.tsx`) and registered it in the software icon map for display on the Software Page. [[1]](diffhunk://#diff-ba9ba14ef95d070975d2aa0e46b19d118bc9c167aeeb34b45ba48d2c1cc5848cR1-R14) [[2]](diffhunk://#diff-628095892e1d16090be1db6cc1a5c9cebc65248c32a8b1312385394818f2907bR22) [[3]](diffhunk://#diff-628095892e1d16090be1db6cc1a5c9cebc65248c32a8b1312385394818f2907bR179) --- .../inputs/homebrew/blender.json | 8 +++++++ ee/maintained-apps/inputs/winget/blender.json | 10 ++++++++ ee/maintained-apps/outputs/apps.json | 14 +++++++++++ .../outputs/blender/darwin.json | 21 +++++++++++++++++ .../outputs/blender/windows.json | 22 ++++++++++++++++++ .../SoftwarePage/components/icons/Blender.tsx | 14 +++++++++++ .../SoftwarePage/components/icons/index.ts | 2 ++ .../images/app-icon-blender-60x60@2x.png | Bin 0 -> 12728 bytes 8 files changed, 91 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/blender.json create mode 100644 ee/maintained-apps/inputs/winget/blender.json create mode 100644 ee/maintained-apps/outputs/blender/darwin.json create mode 100644 ee/maintained-apps/outputs/blender/windows.json create mode 100644 frontend/pages/SoftwarePage/components/icons/Blender.tsx create mode 100644 website/assets/images/app-icon-blender-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/blender.json b/ee/maintained-apps/inputs/homebrew/blender.json new file mode 100644 index 0000000000..2b173e28a0 --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/blender.json @@ -0,0 +1,8 @@ +{ + "name": "Blender", + "unique_identifier": "org.blenderfoundation.blender", + "token": "blender", + "installer_format": "dmg", + "slug": "blender/darwin", + "default_categories": ["Productivity"] +} diff --git a/ee/maintained-apps/inputs/winget/blender.json b/ee/maintained-apps/inputs/winget/blender.json new file mode 100644 index 0000000000..275daa0985 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/blender.json @@ -0,0 +1,10 @@ +{ + "name": "Blender", + "slug": "blender/windows", + "package_identifier": "BlenderFoundation.Blender", + "unique_identifier": "Blender", + "installer_arch": "x64", + "installer_type": "msi", + "installer_scope": "machine", + "default_categories": ["Productivity"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 8bb9605038..20d3017315 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -120,6 +120,20 @@ "unique_identifier": "com.bitwarden.desktop", "description": "Bitwarden is a desktop password and login vault." }, + { + "name": "Blender", + "slug": "blender/darwin", + "platform": "darwin", + "unique_identifier": "org.blenderfoundation.blender", + "description": "Blender is a 3D creation suite." + }, + { + "name": "Blender", + "slug": "blender/windows", + "platform": "windows", + "unique_identifier": "Blender", + "description": "Blender is a 3D creation suite." + }, { "name": "Box Drive", "slug": "box-drive/darwin", diff --git a/ee/maintained-apps/outputs/blender/darwin.json b/ee/maintained-apps/outputs/blender/darwin.json new file mode 100644 index 0000000000..9680c9a63d --- /dev/null +++ b/ee/maintained-apps/outputs/blender/darwin.json @@ -0,0 +1,21 @@ +{ + "versions": [ + { + "version": "5.0.0", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'org.blenderfoundation.blender';" + }, + "installer_url": "https://download.blender.org/release/Blender5.0/blender-5.0.0-macos-arm64.dmg", + "install_script_ref": "a73ff8be", + "uninstall_script_ref": "f1156a0f", + "sha256": "b23984753ec8354878acc25cd4073a41ae195522013f1649903a593bccbfa54b", + "default_categories": [ + "Productivity" + ] + } + ], + "refs": { + "a73ff8be": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n# functions\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\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\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_application 'org.blenderfoundation.blender'\nif [ -d \"$APPDIR/Blender.app\" ]; then\n\tsudo mv \"$APPDIR/Blender.app\" \"$TMPDIR/Blender.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Blender.app\" \"$APPDIR\"\n", + "f1156a0f": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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\nsudo rm -rf \"$APPDIR/Blender.app\"\nsudo rm -rf 'blender'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Blender'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/org.blenderfoundation.blender.savedState'\n" + } +} diff --git a/ee/maintained-apps/outputs/blender/windows.json b/ee/maintained-apps/outputs/blender/windows.json new file mode 100644 index 0000000000..ad2243d38c --- /dev/null +++ b/ee/maintained-apps/outputs/blender/windows.json @@ -0,0 +1,22 @@ +{ + "versions": [ + { + "version": "5.0.0", + "queries": { + "exists": "SELECT 1 FROM programs WHERE name = 'Blender' AND publisher = 'Blender Foundation';" + }, + "installer_url": "https://download.blender.org/release/Blender5.0/blender-5.0.0-windows-x64.msi", + "install_script_ref": "8959087b", + "uninstall_script_ref": "2560ee0b", + "sha256": "69f662f5c4adf3bfa3fa8ba60504ad6fed723ad1d33fe22d718cdeb47ef27895", + "default_categories": [ + "Productivity" + ], + "upgrade_code": "{004FFCA1-B269-51EE-8768-5C75526A8DE2}" + } + ], + "refs": { + "2560ee0b": "# Fleet uninstalls app by finding all related product codes for the specified upgrade code\n$inst = New-Object -ComObject \"WindowsInstaller.Installer\"\n$timeoutSeconds = 300 # 5 minute timeout per product\n\nforeach ($product_code in $inst.RelatedProducts(\"{004FFCA1-B269-51EE-8768-5C75526A8DE2}\")) {\n $process = Start-Process msiexec -ArgumentList @(\"/quiet\", \"/x\", $product_code, \"/norestart\") -PassThru\n \n # Wait for process with timeout\n $completed = $process.WaitForExit($timeoutSeconds * 1000)\n \n if (-not $completed) {\n Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue\n Exit 1603 # ERROR_UNINSTALL_FAILURE\n }\n \n # If the uninstall failed, bail\n if ($process.ExitCode -ne 0) {\n Write-Output \"Uninstall for $($product_code) exited $($process.ExitCode)\"\n Exit $process.ExitCode\n }\n}\n\n# All uninstalls succeeded; exit success\nExit 0\n", + "8959087b": "$logFile = \"${env:TEMP}/fleet-install-software.log\"\n\ntry {\n\n$installProcess = Start-Process msiexec.exe `\n -ArgumentList \"/quiet /norestart /lv ${logFile} /i `\"${env:INSTALLER_PATH}`\"\" `\n -PassThru -Verb RunAs -Wait\n\nGet-Content $logFile -Tail 500\n\nExit $installProcess.ExitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/Blender.tsx b/frontend/pages/SoftwarePage/components/icons/Blender.tsx new file mode 100644 index 0000000000..b55472e50d --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/Blender.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const Blender = (props: SVGProps) => ( + + + +); +export default Blender; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index 3c37e7aa1f..5554f3886a 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -19,6 +19,7 @@ import Asana from "./Asana"; import BBEdit from "./BBEdit"; import BeyondCompare from "./BeyondCompare"; import Bitwarden from "./Bitwarden"; +import Blender from "./Blender"; import Box from "./Box"; import Brave from "./Brave"; import CLion from "./CLion"; @@ -175,6 +176,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { bbedit: BBEdit, "beyond compare": BeyondCompare, bitwarden: Bitwarden, + blender: Blender, box: Box, brave: Brave, camtasia: Camtasia, diff --git a/website/assets/images/app-icon-blender-60x60@2x.png b/website/assets/images/app-icon-blender-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..237ad6372860140eaf8ef46281f304883bbaea73 GIT binary patch literal 12728 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 a(eRHy4-Uh;&f$N*Cgh})BrC;@0{#c%Kq4ss literal 0 HcmV?d00001