From 1b36cab1645266b9e1c3d8c49544798d975c4d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Augusto=20Lissoni=20Lanjoni?= Date: Sun, 24 Nov 2024 14:33:50 -0300 Subject: [PATCH 1/6] feat: update deps --- .gitignore | 4 ++++ deps.edn | 2 +- project.clj | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index c53038e..c198cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ pom.xml.asc /.nrepl-port .hgignore .hg/ +.clj-kondo/ +.cpcache/ +.lsp/ +resources/ diff --git a/deps.edn b/deps.edn index bab6447..3dc4776 100644 --- a/deps.edn +++ b/deps.edn @@ -3,4 +3,4 @@ environ {:mvn/version "1.1.0"} camel-snake-kebab {:mvn/version "0.4.0"} org.clojure/tools.cli {:mvn/version "0.3.5"} - com.datomic/datomic-pro {:mvn/version "0.9.5561.62"}}} + com.datomic/peer {:mvn/version "1.0.7260"}}} diff --git a/project.clj b/project.clj index 34114f4..1f29961 100644 --- a/project.clj +++ b/project.clj @@ -6,13 +6,13 @@ :dependencies [[org.clojure/clojure "1.9.0"] [clj-time "0.14.2"] - [com.datomic/datomic-pro "0.9.5561.62"] + [com.datomic/peer "1.0.7260"] [environ "1.1.0"] [camel-snake-kebab "0.4.0"] [org.clojure/tools.cli "0.3.5"]] :eval-in-leiningen true - + :plugins [[lein-environ "1.1.0"]] :repositories {"my.datomic.com" {:url "https://my.datomic.com/repo" @@ -21,7 +21,7 @@ :min-lein-version "2.6.1" :target-path "target/%s" - + :uberjar-name "migrana.jar" :profiles {:uberjar {:aot :all}}) From feb1d4243b0524f4bd2d0554115e263e62eceaf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Augusto=20Lissoni=20Lanjoni?= Date: Sun, 24 Nov 2024 14:35:13 -0300 Subject: [PATCH 2/6] docs: fix square brackets balance --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 24a3a89..b5ee1ae 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Let's assume you have a Datomic schema file on `resources/schema.edn`: {:db/ident :relationship-status/single} {:db/ident :relationship-status/married} {:db/ident :relationship-status/divorced} - {:db/ident :relationship-status/widowed}]] + {:db/ident :relationship-status/widowed}] ``` Then run: @@ -144,7 +144,7 @@ Let's change our `resources/schema.edn` to: {:db/ident :relationship-status/single} {:db/ident :relationship-status/married} {:db/ident :relationship-status/divorced} - {:db/ident :relationship-status/widowed}]] + {:db/ident :relationship-status/widowed}] ``` After the change we run `lein migrana run` again: From 9f7b1a96eeec565a195988d75fd814840bae9c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Augusto=20Lissoni=20Lanjoni?= Date: Sun, 24 Nov 2024 16:04:16 -0300 Subject: [PATCH 3/6] feat: add cli-options for commands --- custom-schema.edn | 11 +++++++ deps.edn | 2 +- project.clj | 4 +-- src/leiningen/migrana.clj | 62 ++++++++++++++++++++++++++++++--------- src/migrana/core.clj | 58 +++++++++++++++++------------------- 5 files changed, 89 insertions(+), 48 deletions(-) create mode 100644 custom-schema.edn diff --git a/custom-schema.edn b/custom-schema.edn new file mode 100644 index 0000000..48b27ce --- /dev/null +++ b/custom-schema.edn @@ -0,0 +1,11 @@ +[{:db/ident :person/name + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one} + {:db/ident :person/relationship-status + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/one + :db/doc "Whether the person is married or single."} + {:db/ident :relationship-status/single} + {:db/ident :relationship-status/married} + {:db/ident :relationship-status/divorced} + {:db/ident :relationship-status/widowed}] diff --git a/deps.edn b/deps.edn index 3dc4776..0cfe146 100644 --- a/deps.edn +++ b/deps.edn @@ -2,5 +2,5 @@ {clj-time {:mvn/version "0.14.2"} environ {:mvn/version "1.1.0"} camel-snake-kebab {:mvn/version "0.4.0"} - org.clojure/tools.cli {:mvn/version "0.3.5"} + org.clojure/tools.cli {:mvn/version "1.1.230"} com.datomic/peer {:mvn/version "1.0.7260"}}} diff --git a/project.clj b/project.clj index 1f29961..60a4e9f 100644 --- a/project.clj +++ b/project.clj @@ -4,12 +4,12 @@ :license {:name "MIT" :url "https://opensource.org/licenses/MIT"} - :dependencies [[org.clojure/clojure "1.9.0"] + :dependencies [[org.clojure/clojure "1.12.0"] [clj-time "0.14.2"] [com.datomic/peer "1.0.7260"] [environ "1.1.0"] [camel-snake-kebab "0.4.0"] - [org.clojure/tools.cli "0.3.5"]] + [org.clojure/tools.cli "1.1.230"]] :eval-in-leiningen true diff --git a/src/leiningen/migrana.clj b/src/leiningen/migrana.clj index eb97bb8..b35595d 100644 --- a/src/leiningen/migrana.clj +++ b/src/leiningen/migrana.clj @@ -7,10 +7,14 @@ [migrana.core :as core])) (def ^:private cli-options - [["-s" "--schema SCHEMA_FILE" "Schema file (default resources/schema.edn)" - :id :schema] - ["-m" "--migrations MIGRATIONS_PATH" "Migrations path (default resources/migrations/)" - :id :migrations] + [["-s" "--schema SCHEMA_FILE" "Schema file" + :id :schema + :default "resources/schema.edn"] + + ["-m" "--migrations MIGRATIONS_PATH" "Migrations path" + :id :migrations + :default "resources/migrations/"] + [nil "--no-inference" "Runs with no schema change inference" :id :no-inference :default false]]) @@ -24,16 +28,26 @@ [project args] (if-let [uri (or (second args) (environ/env :datomic-uri))] (core/info uri) - (main/abort "Must specify . More details: $ lein help migrana info")) ) + (main/abort "Must specify . More details: $ lein help migrana info"))) (defn create "Creates new manual migration. Syntax: lein migrana create " [project args] - (if-let [n (second args)] - (core/create n) - (main/abort "Must specify . More details: $ lein help migrana name"))) + (let [{:keys [options arguments errors summary]} (parse-opts args cli-options) + n (second arguments)] + (cond + errors + (do + (println "Error parsing options:" (clojure.string/join "\n" errors)) + (main/abort summary)) + + (nil? n) + (main/abort "Must specify . More details: $ lein help migrana name") + + :else + (core/create n options)))) (defn dry-run "Simulates what `run` would do. @@ -42,9 +56,19 @@ defaults to environment variable $DATOMIC_URI if available." [project args] - (if-let [uri (or (second args) (environ/env :datomic-uri))] - (core/dry-run uri) - (main/abort "Must specify . More details: $ lein help migrana dry-run"))) + (let [{:keys [options arguments errors summary]} (parse-opts args cli-options) + uri (or (second arguments) (environ/env :datomic-uri))] + (cond + errors + (do + (println "Error parsing options:" (clojure.string/join "\n" errors)) + (main/abort summary)) + + (nil? uri) + (main/abort "Must specify . More details: $ lein help migrana dry-run") + + :else + (core/dry-run uri options)))) (defn run "Transacts pending migrations onto database. @@ -53,9 +77,19 @@ defaults to environment variable $DATOMIC_URI if available." [project args] - (if-let [uri (or (second args) (environ/env :datomic-uri))] - (core/run uri) - (main/abort "Must specify . More details: $ lein help migrana run"))) + (let [{:keys [options arguments errors summary]} (parse-opts args cli-options) + uri (or (second arguments) (environ/env :datomic-uri))] + (cond + errors + (do + (println "Error parsing options:" (clojure.string/join "\n" errors)) + (main/abort summary)) + + (nil? uri) + (main/abort "Must specify . More details: $ lein help migrana run") + + :else + (core/run uri options)))) (defn set-db "Sets the database timestamp. diff --git a/src/migrana/core.clj b/src/migrana/core.clj index bebe9bc..562d1e2 100644 --- a/src/migrana/core.clj +++ b/src/migrana/core.clj @@ -13,10 +13,6 @@ (def ^:private inference-suffix "_schema_inference.edn") -(def ^:private schema-path "resources/schema.edn") - -(def ^:private migrations-path "resources/migrations/") - (def ^:private migrana-schema [{:db/ident :migrana/migration :db/valueType :db.type/keyword @@ -76,14 +72,14 @@ (let [payload (concat (flatten-tx-data conn tx) [{:migrana/migration :current :migrana/timestamp (:timestamp tx) - :migrana/schema (prn-str (:schema tx))}])] + :migrana/schema (prn-str (:schema tx))}])] @(datomic/transact conn payload)))) (defn ^:private print-left-behind-changes [txs] (doseq [tx txs] (println "=> Would transact" (:timestamp tx)) - (if (:tx-fn tx) (println "... would evaluate" (:tx-fn tx) "for" )))) + (when (:tx-fn tx) (println "... would evaluate" (:tx-fn tx) "for")))) (defn ^:private new-time-stamp "Returns a new time stamp based on local time" @@ -101,16 +97,16 @@ (defn ^:private build-new-inference "Compares the schema in the DB and on disk and creates an inferred migration file if there are differences." - [conn] + [conn options] (let [{:keys [migrana/timestamp migrana/schema]} (current-db-info conn) - schema-on-disk (-> schema-path slurp edn/read-string) + schema-on-disk (-> (:schema options) slurp edn/read-string) diff (data/diff (set schema-on-disk) (set (edn/read-string schema))) gap-on-disk (vec (first diff))] - (if (> (count gap-on-disk) 0) + (if (> (count gap-on-disk) 0) (let [new-ts (new-time-stamp) - migration-name (str migrations-path new-ts inference-suffix)] + migration-name (str (:migrations options) new-ts inference-suffix)] (println "=> Schema changes detected") - (.mkdir (io/file migrations-path)) + (.mkdir (io/file (:migrations options))) (spit migration-name (with-out-str (pprint/pprint {:tx-data gap-on-disk @@ -120,9 +116,9 @@ false))) (defn ^:private dryrun-new-inference - [last-tx] + [last-tx options] (let [{:keys [timestamp schema]} last-tx - schema-on-disk (-> schema-path slurp edn/read-string) + schema-on-disk (-> (:schema options) slurp edn/read-string) diff (data/diff (set schema-on-disk) (set schema)) gap-on-disk (vec (first diff))] (if (> (count gap-on-disk) 0) @@ -139,11 +135,11 @@ (defn ^:private migration-files "Returns seq with all the migration files in chronological order" - [] + [options] (sort #(compare (.getName %1) (.getName %2)) - (.listFiles (io/file migrations-path)))) + (.listFiles (io/file (:migrations options))))) (defn ^:private pre-process-files [files] @@ -160,9 +156,9 @@ (defn ^:private pre-process-migrations "Returns the migrations that still need to be applied to the DB" - [conn] + [conn options] (let [{:keys [migrana/timestamp]} (current-db-info conn) - pre-processed-migrations (pre-process-files (migration-files))] + pre-processed-migrations (pre-process-files (migration-files options))] {:filtered-migrations (filter #(> (compare (:timestamp %) timestamp) 0) pre-processed-migrations) :last-migration (last pre-processed-migrations)})) @@ -170,8 +166,8 @@ (defn ^:private transact-to-latest "Transacts the DB to the latest state" [conn & args] - (let [{:keys [dryrun]} (apply hash-map args) - pre-processed-migrations (pre-process-migrations conn) + (let [{:keys [dryrun options]} (apply hash-map args) + pre-processed-migrations (pre-process-migrations conn options) left-behind-txs (:filtered-migrations pre-processed-migrations)] (if dryrun (print-left-behind-changes left-behind-txs) @@ -182,7 +178,7 @@ "Connects to URI, makes sure the DB exists and ensures bsasic migrana schema is in place" [uri] - (if-not uri (throw (Throwable. "Must have URI to connect to"))) + (when-not uri (throw (Throwable. "Must have URI to connect to"))) (println "=> Connecting to" uri) (-> uri ensure-db-exists @@ -192,13 +188,13 @@ (defn run "Connect to the DB, fast forwards it to the latest state in disk, infers new schema changes, creates extra migration if needed, and then fast forward to this new state" - [uri] + [uri options] (let [conn (base-uri-connect uri) {:keys [migrana/timestamp]} (current-db-info conn)] (println "=> DB is currently at" (or timestamp "N/A")) - (transact-to-latest conn) - (if (build-new-inference conn) - (transact-to-latest conn)) + (transact-to-latest conn :options options) + (when (build-new-inference conn options) + (transact-to-latest conn :options options)) (datomic/release conn) (println "=> DB is up-to-date!\n"))) @@ -213,14 +209,14 @@ (defn dry-run "Similar to apply-run but instead of applying the outstanding migrations it prints out what the migrations would do." - [uri] + [uri options] (let [conn (base-uri-connect uri) {:keys [migrana/timestamp]} (current-db-info conn)] (println "=> DB is currently at" (or timestamp "N/A")) - (let [last-tx (:last-migration (transact-to-latest conn :dryrun true)) - would-infer? (dryrun-new-inference last-tx)] + (let [last-tx (:last-migration (transact-to-latest conn :options options :dryrun true)) + would-infer? (dryrun-new-inference last-tx options)] (println "=> Last known migration at" (or (:timestamp last-tx) "N/A")) - (if would-infer? + (when would-infer? (println "=> Would transact inferred schema changes")) (if (and (= timestamp (:timestamp last-tx)) @@ -233,11 +229,11 @@ (defn create "Creates a migration named n" - [n] + [n {:keys [migrations]}] (let [new-ts (new-time-stamp) - migration-name (str migrations-path new-ts "_" + migration-name (str migrations new-ts "_" (->snake_case_string n) ".edn")] - (.mkdir (io/file migrations-path)) + (.mkdir (io/file migrations)) (spit migration-name (with-out-str (pprint/pprint {:tx-data []}))) From d3caaf184eda38f996463c4f879865ade40b811a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Augusto=20Lissoni=20Lanjoni?= Date: Sun, 24 Nov 2024 16:06:37 -0300 Subject: [PATCH 4/6] feat: bump some deps --- deps.edn | 4 ++-- project.clj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index 0cfe146..a46981a 100644 --- a/deps.edn +++ b/deps.edn @@ -1,6 +1,6 @@ {:deps - {clj-time {:mvn/version "0.14.2"} + {clj-time {:mvn/version "0.15.2"} environ {:mvn/version "1.1.0"} - camel-snake-kebab {:mvn/version "0.4.0"} + camel-snake-kebab {:mvn/version "0.4.3"} org.clojure/tools.cli {:mvn/version "1.1.230"} com.datomic/peer {:mvn/version "1.0.7260"}}} diff --git a/project.clj b/project.clj index 60a4e9f..d5207cb 100644 --- a/project.clj +++ b/project.clj @@ -5,10 +5,10 @@ :url "https://opensource.org/licenses/MIT"} :dependencies [[org.clojure/clojure "1.12.0"] - [clj-time "0.14.2"] + [clj-time "0.15.2"] [com.datomic/peer "1.0.7260"] [environ "1.1.0"] - [camel-snake-kebab "0.4.0"] + [camel-snake-kebab "0.4.3"] [org.clojure/tools.cli "1.1.230"]] :eval-in-leiningen true From e334851d691b78605ef39c6c199c604555351980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Augusto=20Lissoni=20Lanjoni?= Date: Sun, 24 Nov 2024 16:18:13 -0300 Subject: [PATCH 5/6] feat: update snake_case from lib --- src/leiningen/migrana.clj | 10 +++++----- src/migrana/core.clj | 9 ++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/leiningen/migrana.clj b/src/leiningen/migrana.clj index b35595d..95e633d 100644 --- a/src/leiningen/migrana.clj +++ b/src/leiningen/migrana.clj @@ -25,7 +25,7 @@ Syntax: lein migrana info defaults to environment variable $DATOMIC_URI if available." - [project args] + [_project args] (if-let [uri (or (second args) (environ/env :datomic-uri))] (core/info uri) (main/abort "Must specify . More details: $ lein help migrana info"))) @@ -34,7 +34,7 @@ "Creates new manual migration. Syntax: lein migrana create " - [project args] + [_project args] (let [{:keys [options arguments errors summary]} (parse-opts args cli-options) n (second arguments)] (cond @@ -55,7 +55,7 @@ Syntax: lein migrana dry-run defaults to environment variable $DATOMIC_URI if available." - [project args] + [_project args] (let [{:keys [options arguments errors summary]} (parse-opts args cli-options) uri (or (second arguments) (environ/env :datomic-uri))] (cond @@ -76,7 +76,7 @@ Syntax: lein migrana run defaults to environment variable $DATOMIC_URI if available." - [project args] + [_project args] (let [{:keys [options arguments errors summary]} (parse-opts args cli-options) uri (or (second arguments) (environ/env :datomic-uri))] (cond @@ -97,7 +97,7 @@ Syntax: lein migrana set-db defaults to environment variable $DATOMIC_URI if available." - [project args] + [_project args] (letfn [(abort [] (main/abort "Must specify and . More details: $ lein help migrana set-db"))] diff --git a/src/migrana/core.clj b/src/migrana/core.clj index 562d1e2..f35d472 100644 --- a/src/migrana/core.clj +++ b/src/migrana/core.clj @@ -1,6 +1,5 @@ (ns migrana.core - (:require [camel-snake-kebab.core :refer [->snake_case_string]] - [clj-time.core :as time] + (:require [camel-snake-kebab.core :as csk] [clj-time.format :as format] [clj-time.local :as local] [clojure.data :as data] @@ -98,7 +97,7 @@ "Compares the schema in the DB and on disk and creates an inferred migration file if there are differences." [conn options] - (let [{:keys [migrana/timestamp migrana/schema]} (current-db-info conn) + (let [{:keys [migrana/schema]} (current-db-info conn) schema-on-disk (-> (:schema options) slurp edn/read-string) diff (data/diff (set schema-on-disk) (set (edn/read-string schema))) gap-on-disk (vec (first diff))] @@ -117,7 +116,7 @@ (defn ^:private dryrun-new-inference [last-tx options] - (let [{:keys [timestamp schema]} last-tx + (let [{:keys [schema]} last-tx schema-on-disk (-> (:schema options) slurp edn/read-string) diff (data/diff (set schema-on-disk) (set schema)) gap-on-disk (vec (first diff))] @@ -232,7 +231,7 @@ [n {:keys [migrations]}] (let [new-ts (new-time-stamp) migration-name (str migrations new-ts "_" - (->snake_case_string n) ".edn")] + (csk/->snake_case n) ".edn")] (.mkdir (io/file migrations)) (spit migration-name (with-out-str From 4707d1840f3a56df3cf314e00704a676a96e202e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Augusto=20Lissoni=20Lanjoni?= Date: Sun, 24 Nov 2024 16:18:44 -0300 Subject: [PATCH 6/6] feat: remove unused custom-schema for tests --- custom-schema.edn | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 custom-schema.edn diff --git a/custom-schema.edn b/custom-schema.edn deleted file mode 100644 index 48b27ce..0000000 --- a/custom-schema.edn +++ /dev/null @@ -1,11 +0,0 @@ -[{:db/ident :person/name - :db/valueType :db.type/string - :db/cardinality :db.cardinality/one} - {:db/ident :person/relationship-status - :db/valueType :db.type/ref - :db/cardinality :db.cardinality/one - :db/doc "Whether the person is married or single."} - {:db/ident :relationship-status/single} - {:db/ident :relationship-status/married} - {:db/ident :relationship-status/divorced} - {:db/ident :relationship-status/widowed}]