Add Zotero as fleet-maintained app (#41370)
## Summary - Adds Zotero (reference/research management tool) as a fleet-maintained app with macOS and Windows support. - **macOS**: Uses Homebrew cask `zotero` with DMG installer format (bundle identifier: `org.zotero.zotero`). - **Windows**: Uses WinGet package `DigitalScholar.Zotero` with NSIS (exe) installer, including custom install/uninstall PowerShell scripts with `/S` silent flag. ## Files added | File | Purpose | |------|---------| | `ee/maintained-apps/inputs/homebrew/zotero.json` | macOS input manifest | | `ee/maintained-apps/inputs/winget/zotero.json` | Windows input manifest | | `ee/maintained-apps/inputs/winget/scripts/zotero_install.ps1` | Windows silent install script (NSIS /S) | | `ee/maintained-apps/inputs/winget/scripts/zotero_uninstall.ps1` | Windows silent uninstall script (NSIS /S) | ## Remaining steps (per FMA contributing guide) - [ ] Run `go run cmd/maintained-apps/main.go --slug="zotero/darwin" --debug` to generate macOS output - [ ] Run `go run cmd/maintained-apps/main.go --slug="zotero/windows" --debug` to generate Windows output - [ ] Generate and add app icon using the `tools/software/icons/` script - [ ] Add description to `outputs/apps.json` > **Note:** The WinGet package identifier for Zotero is `DigitalScholar.Zotero` (the community-maintained identifier in the winget-pkgs repository). Built for [Mitch Francese](https://fleetdm.slack.com/archives/D0AG92RJGHY/p1773163983187599?thread_ts=1773163736.129729&cid=D0AG92RJGHY) by [Kilo for Slack](https://kilo.ai/features/slack-integration) --------- Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> Co-authored-by: Mitch Francese <2227948+tux234@users.noreply.github.com>
This commit is contained in:
co-authored by
kiloconnect[bot]
Mitch Francese
parent
db9b16aeeb
commit
f0ba17c1a2
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Zotero",
|
||||
"unique_identifier": "org.zotero.zotero",
|
||||
"token": "zotero",
|
||||
"installer_format": "dmg",
|
||||
"slug": "zotero/darwin",
|
||||
"default_categories": ["Productivity"]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# Learn more about .exe install scripts:
|
||||
# http://fleetdm.com/learn-more-about/exe-install-scripts
|
||||
|
||||
$exeFilePath = "${env:INSTALLER_PATH}"
|
||||
|
||||
try {
|
||||
|
||||
# Add argument to install silently
|
||||
# NSIS (Nullsoft) installers require /S flag for silent installation
|
||||
$processOptions = @{
|
||||
FilePath = "$exeFilePath"
|
||||
ArgumentList = "/S"
|
||||
PassThru = $true
|
||||
Wait = $true
|
||||
}
|
||||
|
||||
# Start process and track exit code
|
||||
$process = Start-Process @processOptions
|
||||
$exitCode = $process.ExitCode
|
||||
|
||||
# Prints the exit code
|
||||
Write-Host "Install exit code: $exitCode"
|
||||
Exit $exitCode
|
||||
|
||||
} catch {
|
||||
Write-Host "Error: $_"
|
||||
Exit 1
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
# Fleet extracts name from installer (EXE) and saves it to PACKAGE_ID
|
||||
# variable
|
||||
$softwareName = $PACKAGE_ID
|
||||
|
||||
# It is recommended to use exact software name here if possible to avoid
|
||||
# uninstalling unintended software.
|
||||
$softwareNameLike = "*Zotero*"
|
||||
|
||||
# NSIS installers require /S flag for silent uninstall
|
||||
$uninstallArgs = "/S"
|
||||
|
||||
$paths = @(
|
||||
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
|
||||
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
|
||||
)
|
||||
|
||||
$exitCode = 0
|
||||
|
||||
try {
|
||||
|
||||
[array]$uninstallKeys = Get-ChildItem `
|
||||
-Path $paths `
|
||||
-ErrorAction SilentlyContinue |
|
||||
ForEach-Object { Get-ItemProperty $_.PSPath }
|
||||
|
||||
$foundUninstaller = $false
|
||||
foreach ($key in $uninstallKeys) {
|
||||
# If needed, add -notlike to the comparison to exclude certain similar
|
||||
# software
|
||||
if ($key.DisplayName -like $softwareNameLike) {
|
||||
$foundUninstaller = $true
|
||||
# Get the uninstall command. Some uninstallers do not include
|
||||
# 'QuietUninstallString' and require a flag to run silently.
|
||||
$uninstallCommand = if ($key.QuietUninstallString) {
|
||||
$key.QuietUninstallString
|
||||
} else {
|
||||
$key.UninstallString
|
||||
}
|
||||
|
||||
# The uninstall command may contain command and args, like:
|
||||
# "C:\Program Files\Software\uninstall.exe" /S
|
||||
# Split the command and args
|
||||
$splitArgs = $uninstallCommand.Split('"')
|
||||
if ($splitArgs.Length -gt 1) {
|
||||
if ($splitArgs.Length -eq 3) {
|
||||
$existingArgs = $splitArgs[2].Trim()
|
||||
# Append /S if not already present
|
||||
if ($existingArgs -notmatch '\b/S\b') {
|
||||
$uninstallArgs = "$existingArgs /S".Trim()
|
||||
} else {
|
||||
$uninstallArgs = $existingArgs
|
||||
}
|
||||
} elseif ($splitArgs.Length -gt 3) {
|
||||
Throw `
|
||||
"Uninstall command contains multiple quoted strings. " +
|
||||
"Please update the uninstall script.`n" +
|
||||
"Uninstall command: $uninstallCommand"
|
||||
}
|
||||
$uninstallCommand = $splitArgs[1]
|
||||
} else {
|
||||
# No quotes, check if /S is already in the command
|
||||
if ($uninstallCommand -notmatch '\b/S\b') {
|
||||
$uninstallArgs = "/S"
|
||||
} else {
|
||||
$uninstallArgs = ""
|
||||
}
|
||||
}
|
||||
Write-Host "Uninstall command: $uninstallCommand"
|
||||
Write-Host "Uninstall args: $uninstallArgs"
|
||||
|
||||
$processOptions = @{
|
||||
FilePath = $uninstallCommand
|
||||
PassThru = $true
|
||||
Wait = $true
|
||||
}
|
||||
|
||||
if ($uninstallArgs -ne '') {
|
||||
$processOptions.ArgumentList = $uninstallArgs
|
||||
}
|
||||
|
||||
$process = Start-Process @processOptions
|
||||
$exitCode = $process.ExitCode
|
||||
Write-Host "Uninstall exit code: $exitCode"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $foundUninstaller) {
|
||||
Write-Host "Uninstall entry not found for $softwareNameLike"
|
||||
Exit 0
|
||||
}
|
||||
|
||||
Exit $exitCode
|
||||
|
||||
} catch {
|
||||
Write-Host "Error: $_"
|
||||
Exit 1
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "Zotero",
|
||||
"slug": "zotero/windows",
|
||||
"package_identifier": "DigitalScholar.Zotero",
|
||||
"unique_identifier": "Zotero",
|
||||
"install_script_path": "ee/maintained-apps/inputs/winget/scripts/zotero_install.ps1",
|
||||
"uninstall_script_path": "ee/maintained-apps/inputs/winget/scripts/zotero_uninstall.ps1",
|
||||
"installer_arch": "x64",
|
||||
"installer_type": "exe",
|
||||
"installer_scope": "machine",
|
||||
"default_categories": ["Productivity"]
|
||||
}
|
||||
@@ -1813,6 +1813,20 @@
|
||||
"platform": "windows",
|
||||
"unique_identifier": "Zoom Workplace (X64)",
|
||||
"description": "Zoom is a leading video communication platform for meetings, webinars, and collaboration."
|
||||
},
|
||||
{
|
||||
"name": "Zotero",
|
||||
"slug": "zotero/darwin",
|
||||
"platform": "darwin",
|
||||
"unique_identifier": "org.zotero.zotero",
|
||||
"description": "Zotero is a free tool for collecting, organizing, citing, and sharing research."
|
||||
},
|
||||
{
|
||||
"name": "Zotero",
|
||||
"slug": "zotero/windows",
|
||||
"platform": "windows",
|
||||
"unique_identifier": "Zotero",
|
||||
"description": "Zotero is a free tool for collecting, organizing, citing, and sharing research."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"version": "8.0.4",
|
||||
"queries": {
|
||||
"exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'org.zotero.zotero';"
|
||||
},
|
||||
"installer_url": "https://download.zotero.org/client/release/8.0.4/Zotero-8.0.4.dmg",
|
||||
"install_script_ref": "52a11eaf",
|
||||
"uninstall_script_ref": "24de4c91",
|
||||
"sha256": "54ef47ec82e9125b80165078565c25d3eca6d5dad190206ef5ab32b8e4493a1a",
|
||||
"default_categories": [
|
||||
"Productivity"
|
||||
]
|
||||
}
|
||||
],
|
||||
"refs": {
|
||||
"24de4c91": "#!/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/Zotero.app\"\nsudo rmdir '~/Zotero'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/org.zotero.SafariExtensionApp.SafariExtension'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Zotero'\ntrash $LOGGED_IN_USER '~/Library/Caches/Zotero'\ntrash $LOGGED_IN_USER '~/Library/Containers/org.zotero.SafariExtensionApp.SafariExtension'\ntrash $LOGGED_IN_USER '~/Library/Preferences/org.zotero.zotero.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/org.zotero.zotero.savedState'\n",
|
||||
"52a11eaf": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\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 eval \"export $var_name=0\"\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 eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\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\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; 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 relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Try to launch the application\n if osascript -e \"tell application id \\\"$bundle_id\\\" to activate\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\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_and_track_application 'org.zotero.zotero'\nif [ -d \"$APPDIR/Zotero.app\" ]; then\n\tsudo mv \"$APPDIR/Zotero.app\" \"$TMPDIR/Zotero.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Zotero.app\" \"$APPDIR\"\nrelaunch_application 'org.zotero.zotero'\n"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"version": "8.0.4",
|
||||
"queries": {
|
||||
"exists": "SELECT 1 FROM programs WHERE name = 'Zotero' AND publisher = 'Corporation for Digital Scholarship';"
|
||||
},
|
||||
"installer_url": "https://download.zotero.org/client/release/8.0.4/Zotero-8.0.4_x64_setup.exe",
|
||||
"install_script_ref": "d29b62d2",
|
||||
"uninstall_script_ref": "599566de",
|
||||
"sha256": "43632093fc0c59f30d94e312b8b8af2f68d4d529458b0be7bbe1fe629f1edfaa",
|
||||
"default_categories": [
|
||||
"Productivity"
|
||||
]
|
||||
}
|
||||
],
|
||||
"refs": {
|
||||
"599566de": "# Fleet extracts name from installer (EXE) and saves it to PACKAGE_ID\n# variable\n$softwareName = $PACKAGE_ID\n\n# It is recommended to use exact software name here if possible to avoid\n# uninstalling unintended software.\n$softwareNameLike = \"*Zotero*\"\n\n# NSIS installers require /S flag for silent uninstall\n$uninstallArgs = \"/S\"\n\n$paths = @(\n 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'\n)\n\n$exitCode = 0\n\ntry {\n\n[array]$uninstallKeys = Get-ChildItem `\n -Path $paths `\n -ErrorAction SilentlyContinue |\n ForEach-Object { Get-ItemProperty $_.PSPath }\n\n$foundUninstaller = $false\nforeach ($key in $uninstallKeys) {\n # If needed, add -notlike to the comparison to exclude certain similar\n # software\n if ($key.DisplayName -like $softwareNameLike) {\n $foundUninstaller = $true\n # Get the uninstall command. Some uninstallers do not include\n # 'QuietUninstallString' and require a flag to run silently.\n $uninstallCommand = if ($key.QuietUninstallString) {\n $key.QuietUninstallString\n } else {\n $key.UninstallString\n }\n\n # The uninstall command may contain command and args, like:\n # \"C:\\Program Files\\Software\\uninstall.exe\" /S\n # Split the command and args\n $splitArgs = $uninstallCommand.Split('\"')\n if ($splitArgs.Length -gt 1) {\n if ($splitArgs.Length -eq 3) {\n $existingArgs = $splitArgs[2].Trim()\n # Append /S if not already present\n if ($existingArgs -notmatch '\\b/S\\b') {\n $uninstallArgs = \"$existingArgs /S\".Trim()\n } else {\n $uninstallArgs = $existingArgs\n }\n } elseif ($splitArgs.Length -gt 3) {\n Throw `\n \"Uninstall command contains multiple quoted strings. \" +\n \"Please update the uninstall script.`n\" +\n \"Uninstall command: $uninstallCommand\"\n }\n $uninstallCommand = $splitArgs[1]\n } else {\n # No quotes, check if /S is already in the command\n if ($uninstallCommand -notmatch '\\b/S\\b') {\n $uninstallArgs = \"/S\"\n } else {\n $uninstallArgs = \"\"\n }\n }\n Write-Host \"Uninstall command: $uninstallCommand\"\n Write-Host \"Uninstall args: $uninstallArgs\"\n\n $processOptions = @{\n FilePath = $uninstallCommand\n PassThru = $true\n Wait = $true\n }\n\n if ($uninstallArgs -ne '') {\n $processOptions.ArgumentList = $uninstallArgs\n }\n\n $process = Start-Process @processOptions\n $exitCode = $process.ExitCode\n Write-Host \"Uninstall exit code: $exitCode\"\n break\n }\n}\n\nif (-not $foundUninstaller) {\n Write-Host \"Uninstall entry not found for $softwareNameLike\"\n Exit 0\n}\n\nExit $exitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n",
|
||||
"d29b62d2": "# Learn more about .exe install scripts:\n# http://fleetdm.com/learn-more-about/exe-install-scripts\n\n$exeFilePath = \"${env:INSTALLER_PATH}\"\n\ntry {\n\n# Add argument to install silently\n# NSIS (Nullsoft) installers require /S flag for silent installation\n$processOptions = @{\n FilePath = \"$exeFilePath\"\n ArgumentList = \"/S\"\n PassThru = $true\n Wait = $true\n}\n \n# Start process and track exit code\n$process = Start-Process @processOptions\n$exitCode = $process.ExitCode\n\n# Prints the exit code\nWrite-Host \"Install exit code: $exitCode\"\nExit $exitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -234,6 +234,7 @@ import Zed from "./Zed";
|
||||
import Zeplin from "./Zeplin";
|
||||
import ZeroOneZeroEditor from "./010Editor";
|
||||
import Zoom from "./Zoom";
|
||||
import Zotero from "./Zotero";
|
||||
|
||||
// SOFTWARE_NAME_TO_ICON_MAP list "special" applications that have a defined
|
||||
// icon for them, keys refer to application names, and are intended to be fuzzy
|
||||
@@ -479,6 +480,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = {
|
||||
"yubikey manager": YubikeyManager,
|
||||
zed: Zed,
|
||||
zeplin: Zeplin,
|
||||
zotero: Zotero,
|
||||
} as const;
|
||||
|
||||
// Maps all known Linux platforms to the LinuxOS icon
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Reference in New Issue
Block a user