Install and uninstall plugins from file

Change plugins.edn to key off id as it is unique and all operations
work off of it.
Note: there are still some bugs with certain plugin installs
pull/6911/head
Gabriel Horner 2022-10-10 11:43:39 -04:00
parent da6c617a5b
commit bf79db9d54
8 changed files with 191 additions and 64 deletions

View File

@ -31,15 +31,15 @@
;; Get the latest release: /repos/{owner}/{repo}/releases/latest
;; Zipball https://api.github.com/repos/{owner}/{repo}/zipball
(defn fetch-latest-release-asset
[{:keys [repo theme]}]
(defn- fetch-release-asset
[{:keys [repo theme]} url-suffix]
(p/catch
(p/let [repo (some-> repo (string/trim) (string/replace #"^/+(.+?)/+$" "$1"))
api #(str "https://api.github.com/repos/" repo "/" %)
endpoint (api "releases/latest")
endpoint (api url-suffix)
^js res (fetch endpoint)
res (.json res)
_ (debug "[Release Latest] " endpoint)
_ (debug "[Release URL] " endpoint)
res (bean/->clj res)
version (:tag_name res)
asset (first (filter #(string/ends-with? (:name %) ".zip") (:assets res)))]
@ -56,6 +56,16 @@
(debug e)
(throw (js/Error. [:release-channel-issue (.-message e)])))))
(defn fetch-latest-release-asset
"Fetches latest release, normally when user clicks to install or update a plugin"
[item]
(fetch-release-asset item "releases/latest"))
(defn fetch-specific-release-asset
"Fetches a specific release asset, normally when installing specific versions from plugins.edn"
[{:keys [version] :as item}]
(fetch-release-asset item (str "releases/tags/" version)))
(defn download-asset-zip
[{:keys [id repo title author description effect sponsors]} dl-url dl-version dot-extract-to]
(p/catch
@ -130,11 +140,13 @@
(emit :lsp-installed {:status :error :payload e})
(throw e))))
;; repo is a github repo, not a logseq repo
(defn install-or-update!
[{:keys [version repo only-check] :as item}]
(when repo
[{:keys [version repo only-check plugin-action] :as item}]
(if repo
(let [coerced-version (and version (. semver coerce version))
updating? (and version (. semver valid coerced-version))]
updating? (and version (. semver valid coerced-version)
(not= plugin-action "install"))]
(debug (if updating? "Updating:" "Installing:") repo)
@ -142,7 +154,10 @@
(fn [resolve _reject]
;;(reset! *installing-or-updating item)
;; get releases
(-> (p/let [[asset latest-version notes] (fetch-latest-release-asset item)
(-> (p/let [[asset latest-version notes]
(if (= plugin-action "install")
(fetch-specific-release-asset item)
(fetch-latest-release-asset item))
_ (debug "[Release Asset] #" latest-version " =>" (:url asset))
@ -187,7 +202,8 @@
(resolve nil)))))
(p/finally
(fn []))))))
(fn []))))
(debug "Skip install because no repo was given for: " item)))
(defn uninstall!
[id]

View File

@ -225,7 +225,7 @@
:on-confirm (fn [_ {:keys [close-fn]}]
(close-fn)
(plugin-handler/unregister-plugin id)
(plugin-config/remove-plugin name))})]
(plugin-config/remove-plugin id))})]
(state/set-sub-modal! confirm-fn {:center? true}))}
(t :plugin/uninstall)]]]
@ -547,6 +547,9 @@
[{:title [:span.flex.items-center (ui/icon "world") (t :settings-page/network-proxy)]
:options {:on-click #(state/pub-event! [:go/proxy-settings agent-opts])}}]
[{:title [:span.flex.items-center (ui/icon "arrow-down-circle") (t :plugin/install-from-file)]
:options {:on-click plugin-config/open-sync-modal}}]
(when (state/developer-mode?)
[{:hr true}
{:title [:span.flex.items-center (ui/icon "file-code") "Open Preferences"]
@ -796,6 +799,36 @@
(and (seq unchecked)
(= (count unchecked) (count updates)))))])]))
(rum/defc plugins-from-file
< rum/reactive
[plugins]
[:div.cp__plugins-fom-file
[:h1.mb-4.text-2xl.p-1 "Install plugins from plugins.edn"]
(if (seq plugins)
[:div
[:div.mb-2.text-xl (util/format "The following %s plugin(s) will replace your %s plugin(s):"
(count (:install plugins))
(count (:uninstall plugins)))]
;; lists
[:ul
(for [it (:install plugins)
:let [k (str "lsp-it-" (name (:id it)))]]
[:li.flex.items-center
{:key k}
[:label.flex-1
{:for k}
[:strong.px-3 (str (:name it) " " (:version it))]]])]
;; actions
[:div.pt-5
(ui/button [:span "Install"]
:on-click #(do
(plugin-config/update-plugins plugins)
(state/close-sub-modal! "ls-plugins-from-file-modal")))]]
;; all done
[:div.py-4 [:strong.text-xl "\uD83C\uDF89 All synced!"]])])
(defn open-select-theme!
[]
(state/set-sub-modal! installed-themes))
@ -1041,6 +1074,14 @@
(waiting-coming-updates))
{:center? true}))
(defn open-plugins-from-file-modal!
[plugins]
(state/set-sub-modal!
(fn [_close!]
(plugins-from-file plugins))
{:center? true
:id "ls-plugins-from-file-modal"}))
(defn open-focused-settings-modal!
[title]
(state/set-sub-modal!

View File

@ -377,6 +377,7 @@
:plugin/marketplace-tips "If the plugin doesn't work correctly when first installed, try to restart Logseq."
:plugin/up-to-date "It's up to date"
:plugin/custom-js-alert "Found the custom.js file, is it allowed to execute? (If you don't understand the content of this file, it is recommended not to allow execution, which has certain security risks.)"
:plugin/install-from-file "Install from plugins.edn"
:pdf/copy-ref "Copy ref"
:pdf/copy-text "Copy text"

View File

@ -355,6 +355,9 @@
(defmethod handle :go/plugins-waiting-lists [_]
(plugin/open-waiting-updates-modal!))
(defmethod handle :go/plugins-from-file [[_ plugins]]
(plugin/open-plugins-from-file-modal! plugins))
(defmethod handle :go/plugins-settings [[_ pid nav? title]]
(if pid
(do

View File

@ -212,7 +212,11 @@
(p/then
(js/LSPluginCore.register (bean/->js {:key id :url dst}))
(fn [] (when theme (js/setTimeout #(select-a-plugin-theme id) 300))))
(plugin-config/add-or-update-plugin name (:installed-version payload))
(plugin-config/add-or-update-plugin
{:id id
:name (:name payload)
:version (:installed-version payload)
:repo (:repo payload)})
(notification/show!
(str (t :plugin/installed) (t :plugins) ": " name) :success)))))

View File

@ -7,38 +7,93 @@ This component depends on TODO"
[borkdude.rewrite-edn :as rewrite]
[frontend.fs :as fs]
[frontend.state :as state]
[electron.ipc :as ipc]
[clojure.edn :as edn]
[clojure.set :as set]
[clojure.pprint :as pprint]))
(defn- plugins-path
(defn- plugin-config-path
[]
(path/join (global-config-handler/global-config-dir) "plugins.edn"))
(path/join @global-config-handler/root-dir "plugins.edn"))
(defn add-or-update-plugin
[plugin-name plugin-version]
(p/let [content (fs/read-file "" (plugins-path))
[{:keys [id] :as plugin}]
(p/let [content (fs/read-file "" (plugin-config-path))
updated-content (-> content
rewrite/parse-string
(rewrite/assoc plugin-name {:version plugin-version})
(rewrite/assoc (keyword id) (dissoc plugin :id))
str)]
(fs/write-file! nil "" (plugins-path) updated-content {:skip-compare? true})))
;; fs protocols require repo and dir when they aren't necessary. For this component,
;; neither is needed so these are nil and blank respectively
(fs/write-file! nil "" (plugin-config-path) updated-content {:skip-compare? true})))
(defn remove-plugin
[plugin-name]
(p/let [content (fs/read-file "" (plugins-path))
updated-content (-> content rewrite/parse-string (rewrite/dissoc plugin-name) str)]
(fs/write-file! nil "" (plugins-path) updated-content {:skip-compare? true})))
[plugin-id]
(p/let [content (fs/read-file "" (plugin-config-path))
updated-content (-> content rewrite/parse-string (rewrite/dissoc (keyword plugin-id)) str)]
(fs/write-file! nil "" (plugin-config-path) updated-content {:skip-compare? true})))
(defn- create-global-config-file-if-not-exists
(defn- create-plugin-config-file-if-not-exists
[]
(let [content (->> (:plugin/installed-plugins @state/state)
vals
(map (fn [{:keys [name version]}]
[name {:version version}]))
(into {})
(let [content (-> (:plugin/installed-plugins @state/state)
(update-vals #(select-keys % [:name :version :repo]))
pprint/pprint
with-out-str)]
(fs/create-if-not-exists nil (global-config-handler/global-config-dir) (plugins-path) content)))
(fs/create-if-not-exists nil @global-config-handler/root-dir (plugin-config-path) content)))
(defn- determine-plugins-to-change
"Given installed plugins state and plugins from plugins.edn,
returns map of plugins to install and uninstall"
[installed-plugins edn-plugins]
(let [installed-plugins-set (->> installed-plugins
vals
(map #(assoc (select-keys % [:name :version :repo])
:id (keyword (:id %))))
set)
edn-plugins-set (->> edn-plugins
(map (fn [[k v]] (assoc v :id k)))
set)]
(if (= installed-plugins-set edn-plugins-set)
{}
{:install (mapv #(assoc % :plugin-action "install")
(set/difference edn-plugins-set installed-plugins-set))
:uninstall (set/difference installed-plugins-set edn-plugins-set)})))
(defn open-sync-modal
[]
(state/pub-event! [:go/plugins])
(p/let [edn-plugins (fs/read-file "" (plugin-config-path))
plugins-to-change (determine-plugins-to-change
(:plugin/installed-plugins @state/state)
(edn/read-string edn-plugins))]
(state/pub-event! [:go/plugins-from-file plugins-to-change])))
;; TODO: Extract from handler.plugin
(defn installed?
[id]
(and (contains? (:plugin/installed-plugins @state/state) (keyword id))
(get-in @state/state [:plugin/installed-plugins (keyword id) :iir])))
(defn install-marketplace-plugin
[{:keys [id] :as mft}]
; (prn :IN {:k1 (:plugin/installing @state/state)
; :k2 (installed? id)})
;; TODO:
(when-not (and (:plugin/installing @state/state)
(installed? id))
(p/create
(fn [resolve]
(state/set-state! :plugin/installing mft)
(ipc/ipc :installMarketPlugin mft)
(resolve id)))))
(defn update-plugins
[plugins]
(doseq [plugin (:uninstall plugins)]
(js/LSPluginCore.unregister (name (:id plugin))))
(doseq [plugin (:install plugins)]
(install-marketplace-plugin plugin)))
(defn start
[]
(create-global-config-file-if-not-exists))
(create-plugin-config-file-if-not-exists))

View File

@ -14,6 +14,7 @@
[frontend.handler.plugin :as plugin-handler]
[frontend.handler.export :as export-handler]
[frontend.handler.whiteboard :as whiteboard-handler]
[frontend.handler.plugin-config :as plugin-config]
[frontend.modules.shortcut.dicts :as dicts]
[frontend.modules.shortcut.before :as m]
[frontend.state :as state]
@ -408,6 +409,10 @@
:inactive (not plugin-handler/lsp-enabled?)
:fn plugin-handler/goto-plugins-dashboard!}
:ui/install-plugins-from-file {:binding false
:inactive (not plugin-handler/lsp-enabled?)
;; TODO: Remove dev convenience
:fn (fn [] (plugin-config/open-sync-modal))}
:editor/toggle-open-blocks {:binding "t o"
:fn editor-handler/toggle-open!}
@ -574,6 +579,7 @@
:ui/toggle-wide-mode
:ui/select-theme-color
:ui/goto-plugins
:ui/install-plugins-from-file
:editor/toggle-open-blocks
:ui/toggle-cards
:git/commit])

View File

@ -123,6 +123,7 @@
:ui/toggle-wide-mode "Toggle wide mode"
:ui/select-theme-color "Select available theme colors"
:ui/goto-plugins "Go to plugins dashboard"
:ui/install-plugins-from-file "Install plugins from plugins.edn"
:editor/toggle-open-blocks "Toggle open blocks (collapse or expand all blocks)"
:ui/toggle-cards "Toggle cards"
:git/commit "Create git commit with message"})