Add Linear as a Windows FMA (#43521)

Add Windows support for Linear: new winget input
(ee/maintained-apps/inputs/winget/linear.json) with installer metadata
and category, plus install/uninstall PowerShell scripts. Add output
metadata (ee/maintained-apps/outputs/linear/windows.json) including a
version entry, installer URL, sha256 and script refs, and register the
app in apps.json. Update frontend icon component to reference a new PNG
and add the image asset.

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43501
This commit is contained in:
Allen Houchins 2026-04-14 09:57:23 -05:00 committed by GitHub
parent ef897e2b56
commit 3d067afb47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 182 additions and 2 deletions

View file

@ -0,0 +1,13 @@
{
"name": "Linear",
"slug": "linear/windows",
"package_identifier": "LinearOrbit.Linear",
"unique_identifier": "Linear",
"fuzzy_match_name": true,
"installer_arch": "x64",
"installer_type": "exe",
"installer_scope": "machine",
"install_script_path": "ee/maintained-apps/inputs/winget/scripts/linear_install.ps1",
"uninstall_script_path": "ee/maintained-apps/inputs/winget/scripts/linear_uninstall.ps1",
"default_categories": ["Productivity"]
}

View file

@ -0,0 +1,26 @@
# Learn more about .exe install scripts:
# http://fleetdm.com/learn-more-about/exe-install-scripts
$exeFilePath = "${env:INSTALLER_PATH}"
try {
# Linear uses a Nullsoft installer:
# - /S for silent installation
# - /allusers for machine-scope installs
$processOptions = @{
FilePath = "$exeFilePath"
ArgumentList = "/S /allusers"
PassThru = $true
Wait = $true
}
$process = Start-Process @processOptions
$exitCode = $process.ExitCode
Write-Host "Install exit code: $exitCode"
Exit $exitCode
}
catch {
Write-Host "Error: $_"
Exit 1
}

View file

@ -0,0 +1,112 @@
# Attempts to locate Linear's uninstaller from registry and execute it silently
$displayName = "Linear"
$productCode = "e614e7b4-b4f2-531a-be3f-ebb7fc898755"
$paths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
)
$uninstall = $null
foreach ($p in $paths) {
$codeCandidates = @(
"$p\$productCode",
"$p\{$productCode}"
)
foreach ($candidate in $codeCandidates) {
if (Test-Path $candidate) {
$uninstall = Get-ItemProperty -Path $candidate -ErrorAction SilentlyContinue
if ($uninstall) {
break
}
}
}
if ($uninstall) {
break
}
$items = Get-ItemProperty "$p\*" -ErrorAction SilentlyContinue | Where-Object {
$_.DisplayName -and ($_.DisplayName -eq $displayName -or $_.DisplayName -like "$displayName*")
}
if ($items) {
$uninstall = $items | Select-Object -First 1
break
}
}
if (-not $uninstall) {
Write-Host "Uninstall entry not found"
Exit 0
}
$uninstallString = if ($uninstall.QuietUninstallString) {
$uninstall.QuietUninstallString
}
else {
$uninstall.UninstallString
}
if (-not $uninstallString) {
Write-Host "Uninstall command not found"
Exit 0
}
# Stop the app before uninstalling.
Stop-Process -Name "Linear" -Force -ErrorAction SilentlyContinue
$exePath = ""
$arguments = ""
if ($uninstallString -match '^"([^"]+)"(.*)') {
$exePath = $matches[1]
$arguments = $matches[2].Trim()
}
elseif ($uninstallString -match '^([^\s]+)(.*)') {
$exePath = $matches[1]
$arguments = $matches[2].Trim()
}
else {
Write-Host "Error: Could not parse uninstall string: $uninstallString"
Exit 1
}
$argumentList = @()
if ($arguments -ne '') {
$argumentList += $arguments -split '\s+'
}
# NSIS uninstallers require /S for silent mode.
if ($argumentList -notcontains "/S" -and $arguments -notmatch '\b/S\b') {
$argumentList += "/S"
}
Write-Host "Uninstall executable: $exePath"
Write-Host "Uninstall arguments: $($argumentList -join ' ')"
try {
$processOptions = @{
FilePath = $exePath
NoNewWindow = $true
PassThru = $true
Wait = $true
}
if ($argumentList.Count -gt 0) {
$processOptions.ArgumentList = $argumentList
}
$process = Start-Process @processOptions
$exitCode = $process.ExitCode
Write-Host "Uninstall exit code: $exitCode"
Exit $exitCode
}
catch {
Write-Host "Error running uninstaller: $_"
Exit 1
}

View file

@ -1002,6 +1002,13 @@
"unique_identifier": "com.linear",
"description": "Linear is an app to manage software development and track bugs."
},
{
"name": "Linear",
"slug": "linear/windows",
"platform": "windows",
"unique_identifier": "Linear",
"description": "Linear is an app to manage software development and track bugs."
},
{
"name": "Little Snitch",
"slug": "little-snitch/darwin",
@ -1955,4 +1962,4 @@
"description": "Zotero is a free tool for collecting, organizing, citing, and sharing research."
}
]
}
}

View file

@ -0,0 +1,22 @@
{
"versions": [
{
"version": "1.28.13",
"queries": {
"exists": "SELECT 1 FROM programs WHERE name LIKE 'Linear %' AND publisher = 'Linear Orbit, Inc.';",
"patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name LIKE 'Linear %' AND publisher = 'Linear Orbit, Inc.' AND version_compare(version, '1.28.13') < 0);"
},
"installer_url": "https://download.todesktop.com/200315glz2793v6/Linear%20Setup%201.28.13%20-%20Build%20260318pho1bttxr-x64.exe",
"install_script_ref": "166c7345",
"uninstall_script_ref": "b1906016",
"sha256": "6279df1ceab3d4c66ff9185b8a3bb121137d52e0abae80db5a0b9708d0eb2352",
"default_categories": [
"Productivity"
]
}
],
"refs": {
"166c7345": "# 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 # Linear uses a Nullsoft installer:\n # - /S for silent installation\n # - /allusers for machine-scope installs\n $processOptions = @{\n FilePath = \"$exeFilePath\"\n ArgumentList = \"/S /allusers\"\n PassThru = $true\n Wait = $true\n }\n\n $process = Start-Process @processOptions\n $exitCode = $process.ExitCode\n\n Write-Host \"Install exit code: $exitCode\"\n Exit $exitCode\n}\ncatch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n",
"b1906016": "# Attempts to locate Linear's uninstaller from registry and execute it silently\n\n$displayName = \"Linear\"\n$productCode = \"e614e7b4-b4f2-531a-be3f-ebb7fc898755\"\n\n$paths = @(\n 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'\n)\n\n$uninstall = $null\n\nforeach ($p in $paths) {\n $codeCandidates = @(\n \"$p\\$productCode\",\n \"$p\\{$productCode}\"\n )\n\n foreach ($candidate in $codeCandidates) {\n if (Test-Path $candidate) {\n $uninstall = Get-ItemProperty -Path $candidate -ErrorAction SilentlyContinue\n if ($uninstall) {\n break\n }\n }\n }\n\n if ($uninstall) {\n break\n }\n\n $items = Get-ItemProperty \"$p\\*\" -ErrorAction SilentlyContinue | Where-Object {\n $_.DisplayName -and ($_.DisplayName -eq $displayName -or $_.DisplayName -like \"$displayName*\")\n }\n\n if ($items) {\n $uninstall = $items | Select-Object -First 1\n break\n }\n}\n\nif (-not $uninstall) {\n Write-Host \"Uninstall entry not found\"\n Exit 0\n}\n\n$uninstallString = if ($uninstall.QuietUninstallString) {\n $uninstall.QuietUninstallString\n}\nelse {\n $uninstall.UninstallString\n}\n\nif (-not $uninstallString) {\n Write-Host \"Uninstall command not found\"\n Exit 0\n}\n\n# Stop the app before uninstalling.\nStop-Process -Name \"Linear\" -Force -ErrorAction SilentlyContinue\n\n$exePath = \"\"\n$arguments = \"\"\n\nif ($uninstallString -match '^\"([^\"]+)\"(.*)') {\n $exePath = $matches[1]\n $arguments = $matches[2].Trim()\n}\nelseif ($uninstallString -match '^([^\\s]+)(.*)') {\n $exePath = $matches[1]\n $arguments = $matches[2].Trim()\n}\nelse {\n Write-Host \"Error: Could not parse uninstall string: $uninstallString\"\n Exit 1\n}\n\n$argumentList = @()\nif ($arguments -ne '') {\n $argumentList += $arguments -split '\\s+'\n}\n\n# NSIS uninstallers require /S for silent mode.\nif ($argumentList -notcontains \"/S\" -and $arguments -notmatch '\\b/S\\b') {\n $argumentList += \"/S\"\n}\n\nWrite-Host \"Uninstall executable: $exePath\"\nWrite-Host \"Uninstall arguments: $($argumentList -join ' ')\"\n\ntry {\n $processOptions = @{\n FilePath = $exePath\n NoNewWindow = $true\n PassThru = $true\n Wait = $true\n }\n\n if ($argumentList.Count -gt 0) {\n $processOptions.ArgumentList = $argumentList\n }\n\n $process = Start-Process @processOptions\n $exitCode = $process.ExitCode\n\n Write-Host \"Uninstall exit code: $exitCode\"\n Exit $exitCode\n}\ncatch {\n Write-Host \"Error running uninstaller: $_\"\n Exit 1\n}\n"
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB