diff --git a/build.clj b/build.clj new file mode 100644 index 0000000..175e2da --- /dev/null +++ b/build.clj @@ -0,0 +1,117 @@ +;; --------------------------------------------------------- +;; Build Script +;; +;; Build project and package for deployment +;; - `uberjar` - packaged application for deployment +;; - `clean` remove all build assets and jar files +;; +;; All functions are passed command line arguments +;; - `nil` is passed if there are no arguments +;; +;; +;; tools.build API commands +;; - `create-basis` create a project basis +;; - `copy-dir` copy Clojure source and resources into a working dir +;; - `compile-clj` compile Clojure source code to classes +;; - `delete` - remove path from file space +;; - `write-pom` - write pom.xml and pom.properties files +;; - `jar` - to jar up the working dir into a jar file +;; +;; --------------------------------------------------------- + +(ns build + (:require + [clojure.tools.build.api :as build-api] + [clojure.edn :as edn] + [clojure.pprint :as pprint])) + +;; --------------------------------------------------------- +;; Project configuration + +(def project-config + "Project configuration to support all tasks" + (-> (edn/read-string (slurp "deps.edn")) + :project + (merge + {:class-directory "target/classes" + :project-basis (build-api/create-basis) + :main-namespace 'org.domaindrivenarchitecture/build}))) + +(defn config + "Display build configuration" + [config] + (pprint/pprint (or config project-config))) + +;; End of Build configuration +;; --------------------------------------------------------- + +;; --------------------------------------------------------- +;; Build tasks + +(defn clean + "Remove a directory + - `:path '\"directory-name\"'` for a specific directory + - `nil` (or no command line arguments) to delete `target` directory + `target` is the default directory for build artefacts + Checks that `.` and `/` directories are not deleted" + [directory] + (when + (not (contains? #{"." "/"} directory)) + (build-api/delete {:path (or (:path directory) "target")}))) + +(defn jar [_] + (let [{:keys [name version project-basis class-directory license]} project-config + jar-file (format "target/%s-%s.jar" name version)] + (clean "target") + (build-api/write-pom {:class-dir class-directory + :lib name + :version version + :basis project-basis + :src-dirs ["src"]}) + (build-api/copy-dir {:src-dirs ["src" "resources"] + :target-dir class-directory}) + (build-api/jar {:class-dir class-directory + :jar-file jar-file}))) + +(defn install [_] + (let [{:keys [name version project-basis class-directory]} project-config + jar-file (format "target/%s-%s.jar" name version)] + (build-api/install {:basis project-basis + :lib name + :version version + :jar-file jar-file + :class-dir class-directory}))) + +(defn deploy [opts] + (let [{:keys [name version class-directory]} project-config + jar-file (format "target/%s-%s.jar" name version)] + ((requiring-resolve 'deps-deploy.deps-deploy/deploy) + (merge {:installer :remote + :artifact jar-file + :pom-file (build-api/pom-path {:lib name :class-dir class-directory})} + opts)) + opts)) + +(defn uberjar + "Create an archive containing Clojure and the build of the project + Merge command line configuration to the default project config" + [options] + (let [config (merge project-config options) + {:keys [class-directory name main-namespace project-basis class-directory]} project-config + uberjar-file (str "target/" name "-standalone.jar")] + (clean "target") + (build-api/copy-dir {:src-dirs ["src" "resources"] + :target-dir class-directory}) + + (build-api/compile-clj {:basis project-basis + :class-dir class-directory + :src-dirs ["src"]}) + + (build-api/uber {:basis project-basis + :class-dir class-directory + :main main-namespace + :uber-file uberjar-file}))) + +;; End of Build tasks +;; --------------------------------------------------------- + diff --git a/deps.edn b/deps.edn new file mode 100644 index 0000000..04ed35f --- /dev/null +++ b/deps.edn @@ -0,0 +1,42 @@ +{:project {:name org.domaindrivenarchitecture/build + :version "0.1.0"} + + :paths + ["src" "resources"] + + :deps + {org.clojure/clojure {:mvn/version "1.11.1"} + org.clojure/spec.alpha {:mvn/version "0.4.233"} + orchestra/orchestra {:mvn/version "2021.01.01-1"}} + + :aliases + {;; Clojure.main execution of application + :run/app + {:main-opts ["-m" "practicalli.playground"]} + + ;; Clojure.exec execution of specified function + :run/greet + {:exec-fn practicalli.playground/greet + :exec-args {:name "Clojure"}} + + ;; Add libraries and paths to support additional test tools + :test/env + {} + + ;; Test runner - local and CI + ;; call with :watch? true to start file watcher and re-run tests on saved changes + :test/run + {:extra-paths ["test"] + :extra-deps {lambdaisland/kaocha {:mvn/version "1.87.1366"}} + :main-opts ["-m" "kaocha.runner"] + :exec-fn kaocha.runner/exec-fn + :exec-args {:randomize? false + :fail-fast? true}} + + ;; tools.build `build.clj` built script + :build + {:replace-paths ["."] + :replace-deps {io.github.clojure/tools.build + {:git/tag "v0.10.5" :git/sha "2a21b7a"}} + :extra-deps {slipset/deps-deploy {:mvn/version "0.2.2"}} + :ns-default build}}} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0a1afb2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,8 @@ + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.html + + + diff --git a/src/dda/build/devops.clj b/src/dda/build/devops.clj new file mode 100644 index 0000000..091af34 --- /dev/null +++ b/src/dda/build/devops.clj @@ -0,0 +1,19 @@ +(ns dda.build.devops + (:require + [clojure.spec.alpha :as s] + [dda.build.devops.domain :as domain])) + +(s/def ::name ::domain/name) +(s/def ::module ::domain/module) +(s/def ::stage ::domain/stage) +(s/def ::project-root-path ::domain/project-root-path) +(s/def ::build-dir-name ::domain/build-dir-name) + +(s/def ::devops + (s/keys :req-un [::name] + :opt-un [::module ::stage ::project-root-path ::build-dir-name])) + +(def default {:name "dda-backup" + :project-root-path "." + :build-dir-name "target" + :stage "dev"}) diff --git a/src/dda/build/devops/domain.clj b/src/dda/build/devops/domain.clj new file mode 100644 index 0000000..4457858 --- /dev/null +++ b/src/dda/build/devops/domain.clj @@ -0,0 +1,35 @@ +(ns dda.build.devops.domain + (:require + [clojure.string :as str] + [clojure.spec.alpha :as s] + [orchestra.core :refer [defn-spec]])) + +(s/def ::name string?) +(s/def ::module string?) +(s/def ::stage string?) +(s/def ::project-root-path string?) +(s/def ::build-dir-name string?) + +(s/def ::devops + (s/keys :req-un [::name ::stage ::project-root-path ::build-dir-name] + :opt-un [::module])) + +(defn-spec build-path string? + [devops ::devops] + (let + [{:keys [project-root-path build-dir-name name module]} devops] + (str/join + "/" + (filter + some? + [project-root-path build-dir-name name module])))) + +(defn-spec clean-build-dir-command seq? + [devops ::devops] + (let [{:keys [name]} devops] + ["rm" "-rf" (build-path devops)])) + +(defn-spec create-build-dir-command seq? + [devops ::devops] + (let [{:keys [name]} devops] + ["mkdir" "-p" (build-path devops)])) diff --git a/src/dda/build/image.clj b/src/dda/build/image.clj new file mode 100644 index 0000000..f4bd5f6 --- /dev/null +++ b/src/dda/build/image.clj @@ -0,0 +1,17 @@ +(ns dda.build.image + (:require [orchestra.core :refer [defn-spec]] + [babashka.tasks :as t] + [dda.build.devops :as d] + [dda.build.devops.domain :as dd] + [dda.build.image.domain :as domain])) + +(def default + (merge d/default {})) + +(defn-spec dbuild nil? + [devops ::d/devops] + (let [final (merge default devops)] + (apply t/shell (dd/clean-build-dir-command final)) + (apply t/shell (dd/create-build-dir-command final)) + (apply t/shell (domain/copy-image-command final)) + (apply t/shell (domain/dbuild-command final)))) diff --git a/src/dda/build/image/domain.clj b/src/dda/build/image/domain.clj new file mode 100644 index 0000000..ac0cb04 --- /dev/null +++ b/src/dda/build/image/domain.clj @@ -0,0 +1,16 @@ +(ns dda.build.image.domain + (:require [clojure.spec.alpha :as s] + [orchestra.core :refer [defn-spec]] + [dda.build.devops.domain :as d])) + +(defn-spec copy-image-command seq? + [devops ::d/devops] + (let [{:keys [name]} devops] + ["cp" "-r" "image" (d/build-path devops)])) + +(defn-spec dbuild-command seq? + [devops ::d/devops] + (let [{:keys [name]} devops] + ["docker" "build" "-t" name "--file" + (str (d/build-path devops) "/image/Dockerfile") + (str (d/build-path devops) "/image")])) diff --git a/src/dda/build/terragrunt.clj b/src/dda/build/terragrunt.clj new file mode 100644 index 0000000..b9b95cf --- /dev/null +++ b/src/dda/build/terragrunt.clj @@ -0,0 +1,17 @@ +(ns dda.build.terragrunt + (:require [orchestra.core :refer [defn-spec]] + [babashka.tasks :as t] + [dda.build.devops :as d] + [dda.build.devops.domain :as dd] + [dda.build.terragrunt.domain :as dterra])) + +(def default + (merge d/default {})) + +(defn-spec plan nil? + [devops ::d/devops] + (let [final (merge default devops)] + (apply t/shell (dd/clean-build-dir-command final)) + (apply t/shell (dd/create-build-dir-command final)) + (apply t/shell (dterra/copy-terragrunt-command final)) + (apply t/shell (dterra/plan-command final)))) diff --git a/test/dda/build/devops/domain_test.clj b/test/dda/build/devops/domain_test.clj new file mode 100644 index 0000000..5134bfb --- /dev/null +++ b/test/dda/build/devops/domain_test.clj @@ -0,0 +1,40 @@ +(ns dda.build.devops.domain-test + (:require + [clojure.test :refer [deftest is are testing run-tests]] + [clojure.spec.test.alpha :as st] + [dda.build.devops.domain :as cut])) + +(st/instrument `cut/build-path) +(st/instrument `cut/create-build-dir-command) + +(deftest should-calculate-build-path + (is (= "../../target/dda-backup" + (cut/build-path {:name "dda-backup" + :project-root-path "../.." + :build-dir-name "target" + :version "4.11.8-dev" + :stage "dev"}))) + (is (= "../../target/dda/backup" + (cut/build-path {:name "dda" + :module "backup" + :project-root-path "../.." + :build-dir-name "target" + :version "4.11.8-dev" + :stage "dev"})))) + +(deftest should-calculate-clean-build-dir-command + (is (= ["rm" "-rf" "../../target/dda-backup"] + (cut/clean-build-dir-command {:name "dda-backup" + :project-root-path "../.." + :build-dir-name "target" + :version "4.11.8-dev" + :stage "dev"})))) + +(deftest should-calculate-create-build-dir-command + (is (= ["mkdir" "-p" "../../target/dda-backup"] + (cut/create-build-dir-command {:name "dda-backup" + :project-root-path "../.." + :build-dir-name "target" + :version "4.11.8-dev" + :stage "dev"})))) + diff --git a/test/dda/build/image/domain_test.clj b/test/dda/build/image/domain_test.clj new file mode 100644 index 0000000..e289efc --- /dev/null +++ b/test/dda/build/image/domain_test.clj @@ -0,0 +1,32 @@ +(ns dda.build.image.domain-test + (:require + [clojure.test :refer [deftest is are testing run-tests]] + [clojure.spec.test.alpha :as st] + [dda.build.image.domain :as cut])) + +(st/instrument `cut/copy-image-command) +(st/instrument `cut/dbuild-command) + + +(deftest should-calculate-copy-image-command + (is (= ["cp" "-r" "image" "../../target/dda-backup"] + (cut/copy-image-command {:name "dda-backup" + :project-root-path "../.." + :build-dir-name "target" + :version "4.11.8-dev" + :stage "dev"})))) + +(deftest should-calculate-dbuild-command + (is (= ["docker" + "build" + "-t" + "dda-backup" + "--file" + "../../target/dda-backup/image/Dockerfile" + "../../target/dda-backup/image"] + (cut/dbuild-command {:name "dda-backup" + :project-root-path "../.." + :build-dir-name "target" + :version "4.11.8-dev" + :stage "dev"})))) +