diff --git a/bbin b/bbin index 9665024..33d545d 100755 --- a/bbin +++ b/bbin @@ -22,6 +22,89 @@ (ns babashka.bbin.meta) (def min-bb-version "This def was generated by the bbin build script." "0.9.162") (def version "This def was generated by the bbin build script." "0.2.0") +(ns babashka.bbin.dirs + (:require [babashka.fs :as fs] + [clojure.string :as str])) + +(defn user-home [] + (System/getProperty "user.home")) + +(defn print-legacy-path-warning [] + (binding [*out* *err*] + (println (str/triml " +WARNING: In bbin 0.2.0, we now use the XDG Base Directory Specification by +WARNING: default. This means the ~/.babashka/bbin/bin path is deprecated in +WARNING: favor of ~/.local/bin. +WARNING: +WARNING: To remove this message, run `bbin migrate` for further instructions. +WARNING: (We won't make any changes without asking you first.) +")))) + +(def ^:dynamic *legacy-bin-dir* nil) + +(defn- legacy-override-dir [] + (some-> (or (System/getenv "BABASHKA_BBIN_DIR") + (some-> (System/getenv "XDG_DATA_HOME") (fs/file ".babashka" "bbin"))) + (fs/canonicalize {:nofollow-links true}))) + +(defn legacy-bin-dir-base [] + (if-let [override (legacy-override-dir)] + (fs/file override "bin") + (fs/file (user-home) ".babashka" "bbin" "bin"))) + +(defn legacy-bin-dir [] + (or *legacy-bin-dir* (legacy-bin-dir-base))) + +(defn using-legacy-paths? [] + (fs/exists? (legacy-bin-dir))) + +(def ^:dynamic *legacy-jars-dir* nil) + +(defn legacy-jars-dir-base [] + (if-let [override (legacy-override-dir)] + (fs/file override "jars") + (fs/file (user-home) ".babashka" "bbin" "jars"))) + +(defn legacy-jars-dir [] + (or *legacy-jars-dir* (legacy-jars-dir-base))) + +(defn check-legacy-paths [] + (when (using-legacy-paths?) + (print-legacy-path-warning))) + +(def ^:dynamic *xdg-bin-dir* nil) + +(defn xdg-bin-dir [_] + (or *xdg-bin-dir* + (if-let [override (System/getenv "BABASHKA_BBIN_BIN_DIR")] + (fs/file (fs/expand-home override)) + (fs/file (user-home) ".local" "bin")))) + +(defn bin-dir [opts] + (if (using-legacy-paths?) + (legacy-bin-dir) + (xdg-bin-dir opts))) + +(def ^:dynamic *xdg-jars-dir* nil) + +(defn xdg-jars-dir [_] + (or *xdg-jars-dir* + (if-let [override (System/getenv "BABASHKA_BBIN_JARS_DIR")] + (fs/file (fs/expand-home override)) + (fs/file (fs/xdg-cache-home) "babashka" "bbin" "jars")))) + +(defn jars-dir [opts] + (if (using-legacy-paths?) + (legacy-jars-dir) + (xdg-jars-dir opts))) + +(defn ensure-bbin-dirs [cli-opts] + (fs/create-dirs (bin-dir cli-opts))) + +(defn ensure-xdg-dirs [cli-opts] + (fs/create-dirs (xdg-bin-dir cli-opts)) + (fs/create-dirs (xdg-jars-dir cli-opts))) + (ns babashka.bbin.protocols) (defprotocol Script @@ -1092,6 +1175,153 @@ (uninstall [_] (common/delete-files cli-opts))) +(ns babashka.bbin.scripts + (:require + [babashka.bbin.scripts.common :as common] + [babashka.bbin.util :as util] + [babashka.bbin.dirs :as dirs] + [babashka.bbin.protocols :as p] + [babashka.bbin.scripts.git-dir :refer [map->GitDir]] + [babashka.bbin.scripts.local-file :refer [map->LocalFile]] + [babashka.bbin.scripts.local-dir :refer [map->LocalDir]] + [babashka.bbin.scripts.http-file :refer [map->HttpFile]] + [babashka.bbin.scripts.http-jar :refer [map->HttpJar]] + [babashka.bbin.scripts.local-jar :refer [map->LocalJar]] + [babashka.bbin.scripts.maven-jar :refer [map->MavenJar]] + [babashka.fs :as fs] + [clojure.edn :as edn] + [clojure.string :as str] + [rads.deps-info.summary :as deps-info-summary] + [selmer.filters :as filters])) + +;; selmer filter for clojure escaping for e.g. files +(filters/add-filter! :pr-str (comp pr-str str)) + +(defn parse-script [s] + (let [lines (str/split-lines s) + prefix (if (str/ends-with? (first lines) "bb") ";" "#")] + (->> lines + (drop-while #(not (re-seq (re-pattern (str "^" prefix " *:bbin/start")) %))) + next + (take-while #(not (re-seq (re-pattern (str "^" prefix " *:bbin/end")) %))) + (map #(str/replace % (re-pattern (str "^" prefix " *")) "")) + (str/join "\n") + edn/read-string))) + +(defn load-scripts [dir] + (->> (file-seq dir) + (filter #(.isFile %)) + (map (fn [x] [(symbol (str (fs/relativize dir x))) + (parse-script (slurp x))])) + (filter second) + (into {}))) + + +(defn ls [cli-opts] + (let [scripts (load-scripts (dirs/bin-dir cli-opts))] + (if (:edn cli-opts) + (util/pprint scripts cli-opts) + (do + (println) + (util/print-scripts (util/printable-scripts scripts) cli-opts) + (println))))) + +(defn bin [cli-opts] + (println (str (dirs/bin-dir cli-opts)))) + +(defn- throw-invalid-script [summary cli-opts] + (let [{:keys [procurer artifact]} summary] + (throw (ex-info "Invalid script coordinates.\nIf you're trying to install from the filesystem, make sure the path actually exists." + {:script/lib (:script/lib cli-opts) + :procurer procurer + :artifact artifact})))) + +(defn- new-script [cli-opts] + (let [summary (deps-info-summary/summary cli-opts) + {:keys [procurer artifact]} summary] + (case [procurer artifact] + [:git :dir] (map->GitDir {:cli-opts cli-opts :summary summary}) + [:http :file] (map->HttpFile {:cli-opts cli-opts}) + [:http :jar] (map->HttpJar {:cli-opts cli-opts}) + [:local :dir] (map->LocalDir {:cli-opts cli-opts :summary summary}) + [:local :file] (map->LocalFile {:cli-opts cli-opts}) + [:local :jar] (map->LocalJar {:cli-opts cli-opts}) + [:maven :jar] (map->MavenJar {:cli-opts cli-opts}) + (throw-invalid-script summary cli-opts)))) + +(defn install [cli-opts] + (if-not (:script/lib cli-opts) + (util/print-help) + (do + (dirs/ensure-bbin-dirs cli-opts) + (when-not (util/edn? cli-opts) + (println) + (println (util/bold "Starting install..." cli-opts))) + (let [cli-opts' (util/canonicalized-cli-opts cli-opts) + script (new-script cli-opts')] + (p/install script))))) + +(defn- default-script [cli-opts] + (reify + p/Script + (install [_]) + (upgrade [_] + (throw (ex-info "Not implemented" {}))) + (uninstall [_] + (common/delete-files cli-opts)))) + +(defn- load-script [cli-opts] + (let [script-name (:script/lib cli-opts) + script-file (fs/file (fs/canonicalize (fs/file (dirs/bin-dir cli-opts) script-name) {:nofollow-links true})) + parsed (parse-script (slurp script-file))] + (cond + (-> parsed :coords :bbin/url) + (let [summary (deps-info-summary/summary {:script/lib (-> parsed :coords :bbin/url)}) + {:keys [procurer artifact]} summary] + (case [procurer artifact] + [:git :dir] (map->GitDir {:cli-opts cli-opts :summary summary :coords (:coords parsed)}) + [:http :file] (map->HttpFile {:cli-opts cli-opts :coords (:coords parsed)}) + [:http :jar] (map->HttpJar {:cli-opts cli-opts :coords (:coords parsed)}) + [:local :dir] (map->LocalDir {:cli-opts cli-opts :summary summary}) + [:local :file] (map->LocalFile {:cli-opts cli-opts}) + [:local :jar] (map->LocalJar {:cli-opts cli-opts}) + (throw-invalid-script summary cli-opts))) + + (-> parsed :coords :mvn/version) + (map->MavenJar {:cli-opts cli-opts :lib (:lib parsed)}) + + (-> parsed :coords :git/tag) + (let [summary (deps-info-summary/summary {:script/lib (:lib parsed) + :git/tag (-> parsed :coords :git/tag)})] + (map->GitDir {:cli-opts cli-opts :summary summary :coords (:coords parsed)})) + + (-> parsed :coords :git/sha) + (let [summary (deps-info-summary/summary {:script/lib (:lib parsed) + :git/sha (-> parsed :coords :git/sha)})] + (map->GitDir {:cli-opts cli-opts :summary summary :coords (:coords parsed)})) + + :else (default-script cli-opts)))) + +(defn upgrade [cli-opts] + (if-not (:script/lib cli-opts) + (util/print-help) + (do + (dirs/ensure-bbin-dirs cli-opts) + (let [script (load-script cli-opts)] + (p/upgrade script))))) + +(defn uninstall [cli-opts] + (if-not (:script/lib cli-opts) + (util/print-help) + (do + (dirs/ensure-bbin-dirs cli-opts) + (let [script-name (:script/lib cli-opts) + script-file (fs/canonicalize (fs/file (dirs/bin-dir cli-opts) script-name) {:nofollow-links true})] + (when (fs/delete-if-exists script-file) + (when util/windows? (fs/delete-if-exists (fs/file (str script-file common/windows-wrapper-extension)))) + (fs/delete-if-exists (fs/file (dirs/jars-dir cli-opts) (str script-name ".jar"))) + (println "Removing" (str script-file))))))) + (ns babashka.bbin.migrate (:require [babashka.bbin.dirs :as dirs] [babashka.bbin.scripts :as scripts] @@ -1316,153 +1546,6 @@ Otherwise, you can either a) migrate manually or b) override: :root (migrate-help cli-opts) :auto (migrate-auto cli-opts)))) -(ns babashka.bbin.scripts - (:require - [babashka.bbin.scripts.common :as common] - [babashka.bbin.util :as util] - [babashka.bbin.dirs :as dirs] - [babashka.bbin.protocols :as p] - [babashka.bbin.scripts.git-dir :refer [map->GitDir]] - [babashka.bbin.scripts.local-file :refer [map->LocalFile]] - [babashka.bbin.scripts.local-dir :refer [map->LocalDir]] - [babashka.bbin.scripts.http-file :refer [map->HttpFile]] - [babashka.bbin.scripts.http-jar :refer [map->HttpJar]] - [babashka.bbin.scripts.local-jar :refer [map->LocalJar]] - [babashka.bbin.scripts.maven-jar :refer [map->MavenJar]] - [babashka.fs :as fs] - [clojure.edn :as edn] - [clojure.string :as str] - [rads.deps-info.summary :as deps-info-summary] - [selmer.filters :as filters])) - -;; selmer filter for clojure escaping for e.g. files -(filters/add-filter! :pr-str (comp pr-str str)) - -(defn parse-script [s] - (let [lines (str/split-lines s) - prefix (if (str/ends-with? (first lines) "bb") ";" "#")] - (->> lines - (drop-while #(not (re-seq (re-pattern (str "^" prefix " *:bbin/start")) %))) - next - (take-while #(not (re-seq (re-pattern (str "^" prefix " *:bbin/end")) %))) - (map #(str/replace % (re-pattern (str "^" prefix " *")) "")) - (str/join "\n") - edn/read-string))) - -(defn load-scripts [dir] - (->> (file-seq dir) - (filter #(.isFile %)) - (map (fn [x] [(symbol (str (fs/relativize dir x))) - (parse-script (slurp x))])) - (filter second) - (into {}))) - - -(defn ls [cli-opts] - (let [scripts (load-scripts (dirs/bin-dir cli-opts))] - (if (:edn cli-opts) - (util/pprint scripts cli-opts) - (do - (println) - (util/print-scripts (util/printable-scripts scripts) cli-opts) - (println))))) - -(defn bin [cli-opts] - (println (str (dirs/bin-dir cli-opts)))) - -(defn- throw-invalid-script [summary cli-opts] - (let [{:keys [procurer artifact]} summary] - (throw (ex-info "Invalid script coordinates.\nIf you're trying to install from the filesystem, make sure the path actually exists." - {:script/lib (:script/lib cli-opts) - :procurer procurer - :artifact artifact})))) - -(defn- new-script [cli-opts] - (let [summary (deps-info-summary/summary cli-opts) - {:keys [procurer artifact]} summary] - (case [procurer artifact] - [:git :dir] (map->GitDir {:cli-opts cli-opts :summary summary}) - [:http :file] (map->HttpFile {:cli-opts cli-opts}) - [:http :jar] (map->HttpJar {:cli-opts cli-opts}) - [:local :dir] (map->LocalDir {:cli-opts cli-opts :summary summary}) - [:local :file] (map->LocalFile {:cli-opts cli-opts}) - [:local :jar] (map->LocalJar {:cli-opts cli-opts}) - [:maven :jar] (map->MavenJar {:cli-opts cli-opts}) - (throw-invalid-script summary cli-opts)))) - -(defn install [cli-opts] - (if-not (:script/lib cli-opts) - (util/print-help) - (do - (dirs/ensure-bbin-dirs cli-opts) - (when-not (util/edn? cli-opts) - (println) - (println (util/bold "Starting install..." cli-opts))) - (let [cli-opts' (util/canonicalized-cli-opts cli-opts) - script (new-script cli-opts')] - (p/install script))))) - -(defn- default-script [cli-opts] - (reify - p/Script - (install [_]) - (upgrade [_] - (throw (ex-info "Not implemented" {}))) - (uninstall [_] - (common/delete-files cli-opts)))) - -(defn- load-script [cli-opts] - (let [script-name (:script/lib cli-opts) - script-file (fs/file (fs/canonicalize (fs/file (dirs/bin-dir cli-opts) script-name) {:nofollow-links true})) - parsed (parse-script (slurp script-file))] - (cond - (-> parsed :coords :bbin/url) - (let [summary (deps-info-summary/summary {:script/lib (-> parsed :coords :bbin/url)}) - {:keys [procurer artifact]} summary] - (case [procurer artifact] - [:git :dir] (map->GitDir {:cli-opts cli-opts :summary summary :coords (:coords parsed)}) - [:http :file] (map->HttpFile {:cli-opts cli-opts :coords (:coords parsed)}) - [:http :jar] (map->HttpJar {:cli-opts cli-opts :coords (:coords parsed)}) - [:local :dir] (map->LocalDir {:cli-opts cli-opts :summary summary}) - [:local :file] (map->LocalFile {:cli-opts cli-opts}) - [:local :jar] (map->LocalJar {:cli-opts cli-opts}) - (throw-invalid-script summary cli-opts))) - - (-> parsed :coords :mvn/version) - (map->MavenJar {:cli-opts cli-opts :lib (:lib parsed)}) - - (-> parsed :coords :git/tag) - (let [summary (deps-info-summary/summary {:script/lib (:lib parsed) - :git/tag (-> parsed :coords :git/tag)})] - (map->GitDir {:cli-opts cli-opts :summary summary :coords (:coords parsed)})) - - (-> parsed :coords :git/sha) - (let [summary (deps-info-summary/summary {:script/lib (:lib parsed) - :git/sha (-> parsed :coords :git/sha)})] - (map->GitDir {:cli-opts cli-opts :summary summary :coords (:coords parsed)})) - - :else (default-script cli-opts)))) - -(defn upgrade [cli-opts] - (if-not (:script/lib cli-opts) - (util/print-help) - (do - (dirs/ensure-bbin-dirs cli-opts) - (let [script (load-script cli-opts)] - (p/upgrade script))))) - -(defn uninstall [cli-opts] - (if-not (:script/lib cli-opts) - (util/print-help) - (do - (dirs/ensure-bbin-dirs cli-opts) - (let [script-name (:script/lib cli-opts) - script-file (fs/canonicalize (fs/file (dirs/bin-dir cli-opts) script-name) {:nofollow-links true})] - (when (fs/delete-if-exists script-file) - (when util/windows? (fs/delete-if-exists (fs/file (str script-file common/windows-wrapper-extension)))) - (fs/delete-if-exists (fs/file (dirs/jars-dir cli-opts) (str script-name ".jar"))) - (println "Removing" (str script-file))))))) - (ns babashka.bbin.cli (:require [babashka.cli :as cli] [babashka.bbin.scripts :as scripts] diff --git a/dev/babashka/bbin/gen_script.clj b/dev/babashka/bbin/gen_script.clj index bc212fc..4a5fafd 100644 --- a/dev/babashka/bbin/gen_script.clj +++ b/dev/babashka/bbin/gen_script.clj @@ -47,6 +47,7 @@ (def all-scripts [prelude-str meta-str + (slurp "src/babashka/bbin/dirs.clj") (slurp "src/babashka/bbin/protocols.clj") (slurp "src/babashka/bbin/specs.clj") (slurp "src/babashka/bbin/util.clj") @@ -58,8 +59,8 @@ (slurp "src/babashka/bbin/scripts/local_file.clj") (slurp "src/babashka/bbin/scripts/local_jar.clj") (slurp "src/babashka/bbin/scripts/maven_jar.clj") - (slurp "src/babashka/bbin/migrate.clj") (slurp "src/babashka/bbin/scripts.clj") + (slurp "src/babashka/bbin/migrate.clj") (slurp "src/babashka/bbin/cli.clj")]) (defn gen-script []