From 69ac47369417942e8768cf197de2da59ef6c4561 Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:28:35 -0600 Subject: [PATCH] Add DB Browser for SQLite as a macOS FMA (#37057) This pull request adds support for "DB Browser for SQLite" to the maintained apps system and frontend, including metadata, installer details, and icon integration. The changes ensure that "DB Browser for SQLite" is now recognized, installable, and visually represented in the software catalog. **Backend: App Metadata and Installer Integration** * Added a new input file `db-browser-for-sqlite.json` with metadata for "DB Browser for SQLite" (`ee/maintained-apps/inputs/homebrew/db-browser-for-sqlite.json`). * Generated output files for the app, including listing in `apps.json` and a detailed installer/uninstaller script with version info and SHA256 checksum (`ee/maintained-apps/outputs/apps.json`, `ee/maintained-apps/outputs/db-browser-for-sqlite/darwin.json`). [[1]](diffhunk://#diff-4c1446cfc02c6bb0bda874481e333c65b84e184fcea52f656b49a6489f73c9c2R403-R409) [[2]](diffhunk://#diff-4f92dcbeb9f303f8a2b0337097c2c11892b2eb2f73023dc26845f6971d34aa6aR1-R21) **Frontend: Icon and Mapping Integration** * Added a new React SVG icon component for "DB Browser for SQLite" (`DbBrowserForSqLite.tsx`). * Imported the new icon into the icons index and mapped it in `SOFTWARE_NAME_TO_ICON_MAP` for correct display on the software page (`frontend/pages/SoftwarePage/components/icons/index.ts`). [[1]](diffhunk://#diff-628095892e1d16090be1db6cc1a5c9cebc65248c32a8b1312385394818f2907bR56) [[2]](diffhunk://#diff-628095892e1d16090be1db6cc1a5c9cebc65248c32a8b1312385394818f2907bR234) --- .../homebrew/db-browser-for-sqlite.json | 8 +++++++ ee/maintained-apps/outputs/apps.json | 7 ++++++ .../outputs/db-browser-for-sqlite/darwin.json | 21 ++++++++++++++++++ .../components/icons/DbBrowserForSqLite.tsx | 14 ++++++++++++ .../SoftwarePage/components/icons/index.ts | 2 ++ ...pp-icon-db-browser-for-sqlite-60x60@2x.png | Bin 0 -> 7206 bytes 6 files changed, 52 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/db-browser-for-sqlite.json create mode 100644 ee/maintained-apps/outputs/db-browser-for-sqlite/darwin.json create mode 100644 frontend/pages/SoftwarePage/components/icons/DbBrowserForSqLite.tsx create mode 100644 website/assets/images/app-icon-db-browser-for-sqlite-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/db-browser-for-sqlite.json b/ee/maintained-apps/inputs/homebrew/db-browser-for-sqlite.json new file mode 100644 index 0000000000..1ecf41b683 --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/db-browser-for-sqlite.json @@ -0,0 +1,8 @@ +{ + "name": "DB Browser for SQLite", + "unique_identifier": "com.sqlitebrowser.sqlitebrowser", + "token": "db-browser-for-sqlite", + "installer_format": "dmg", + "slug": "db-browser-for-sqlite/darwin", + "default_categories": ["Developer tools"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 2923871eea..d8ab194049 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -400,6 +400,13 @@ "unique_identifier": "com.jetbrains.datagrip", "description": "DataGrip is a powerful cross-platform IDE for relational and NoSQL databases." }, + { + "name": "DB Browser for SQLite", + "slug": "db-browser-for-sqlite/darwin", + "platform": "darwin", + "unique_identifier": "com.sqlitebrowser.sqlitebrowser", + "description": "DB Browser for SQLite is a high quality, visual, open source tool designed for people who want to create, search, and edit SQLite or SQLCipher databases." + }, { "name": "DBeaver", "slug": "dbeaver-community/darwin", diff --git a/ee/maintained-apps/outputs/db-browser-for-sqlite/darwin.json b/ee/maintained-apps/outputs/db-browser-for-sqlite/darwin.json new file mode 100644 index 0000000000..d244f42eeb --- /dev/null +++ b/ee/maintained-apps/outputs/db-browser-for-sqlite/darwin.json @@ -0,0 +1,21 @@ +{ + "versions": [ + { + "version": "3.13.1", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.sqlitebrowser.sqlitebrowser';" + }, + "installer_url": "https://github.com/sqlitebrowser/sqlitebrowser/releases/download/v3.13.1/DB.Browser.for.SQLite-v3.13.1.dmg", + "install_script_ref": "924000d3", + "uninstall_script_ref": "c014d2d5", + "sha256": "a641cfbfcc2ce609f07de44a35134dab53485ecc18e6d9afa297b514d74bd75e", + "default_categories": [ + "Developer tools" + ] + } + ], + "refs": { + "924000d3": "#!/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 'com.sqlitebrowser.sqlitebrowser'\nif [ -d \"$APPDIR/DB Browser for SQLite.app\" ]; then\n\tsudo mv \"$APPDIR/DB Browser for SQLite.app\" \"$TMPDIR/DB Browser for SQLite.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/DB Browser for SQLite.app\" \"$APPDIR\"\n", + "c014d2d5": "#!/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/DB Browser for SQLite.app\"\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.sqlitebrowser.sqlitebrowser.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/net.sourceforge.sqlitebrowser.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/net.sourceforge.sqlitebrowser.savedState'\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/DbBrowserForSqLite.tsx b/frontend/pages/SoftwarePage/components/icons/DbBrowserForSqLite.tsx new file mode 100644 index 0000000000..678eb92d43 --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/DbBrowserForSqLite.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const DbBrowserForSqLite = (props: SVGProps) => ( + + + +); +export default DbBrowserForSqLite; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index c3f73c63d1..b85c404cd0 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -53,6 +53,7 @@ import CreativeCloud from "./AdobeCreativeCloud"; import Cursor from "./Cursor"; import Cyberduck from "./Cyberduck"; import DataGrip from "./DataGrip"; +import DbBrowserForSqLite from "./DbBrowserForSqLite"; import DBeaver from "./DBeaver"; import DBeaverEe from "./DBeaverEe"; import DBeaverLite from "./DBeaverLite"; @@ -230,6 +231,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { cursor: Cursor, cyberduck: Cyberduck, datagrip: DataGrip, + "db browser for sqlite": DbBrowserForSqLite, "dbeaver community": DBeaver, "dbeaver enterprise edition": DBeaverEe, "dbeaver lite edition": DBeaverLite, diff --git a/website/assets/images/app-icon-db-browser-for-sqlite-60x60@2x.png b/website/assets/images/app-icon-db-browser-for-sqlite-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fda6ad6c738bf13298a3a6adeb4bbc3d84d1a45c GIT binary patch literal 7206 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