├── .clj-kondo ├── babashka │ ├── fs │ │ └── config.edn │ └── sci │ │ ├── config.edn │ │ └── sci │ │ └── core.clj ├── config.edn └── rewrite-clj │ └── rewrite-clj │ └── config.edn ├── .github └── workflows │ └── clojure.yml ├── .gitignore ├── LICENSE ├── README.md ├── VERSION ├── deps.edn ├── doc ├── config-options.md ├── internals.md └── target │ ├── add-module.md │ ├── check.md │ ├── clean.md │ ├── deploy.md │ ├── format-check.md │ ├── format-fix.md │ ├── init.md │ ├── install.md │ ├── jar.md │ ├── lint.md │ ├── merge-aliases.md │ ├── merge-deps.md │ ├── outdated.md │ ├── prep-self.md │ ├── prep.md │ ├── release.md │ ├── task.md │ ├── test.md │ └── uberjar.md ├── resources ├── .keep └── exoscale │ ├── module │ ├── root │ │ ├── .gitignore │ │ ├── deps.edn │ │ ├── resources │ │ │ └── .keep │ │ └── tests.edn │ ├── src │ │ └── main.clj │ └── template.edn │ └── project │ ├── root │ ├── .github │ │ └── workflows │ │ │ └── clojure.yml │ ├── .gitignore │ ├── .kaocha │ │ └── default.edn │ ├── Makefile │ ├── README.md │ ├── VERSION │ ├── deps.edn │ ├── doc │ │ └── intro.md │ ├── resources │ │ └── .keep │ └── tests.edn │ ├── src │ └── main.clj │ ├── template.edn │ └── test │ └── main_test.clj └── src └── exoscale └── tools ├── project.clj └── project ├── api.clj ├── api ├── deploy.clj ├── git.clj ├── jar.clj ├── java.clj ├── tasks.clj └── version.clj ├── dir.clj ├── io.clj ├── standalone.clj └── template.clj /.clj-kondo/babashka/fs/config.edn: -------------------------------------------------------------------------------- 1 | {:lint-as {babashka.fs/with-temp-dir clojure.core/let}} 2 | -------------------------------------------------------------------------------- /.clj-kondo/babashka/sci/config.edn: -------------------------------------------------------------------------------- 1 | {:hooks {:macroexpand {sci.core/copy-ns sci.core/copy-ns}}} 2 | -------------------------------------------------------------------------------- /.clj-kondo/babashka/sci/sci/core.clj: -------------------------------------------------------------------------------- 1 | (ns sci.core) 2 | 3 | (defmacro copy-ns 4 | ([ns-sym sci-ns] 5 | `(copy-ns ~ns-sym ~sci-ns nil)) 6 | ([ns-sym sci-ns opts] 7 | `[(quote ~ns-sym) 8 | ~sci-ns 9 | (quote ~opts)])) 10 | -------------------------------------------------------------------------------- /.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:linters 2 | {:clojure-lsp/unused-public-var {:exclude [exoscale.tools.project.api.java/compile 3 | exoscale.tools.project.api/version 4 | exoscale.tools.project.module/clean 5 | exoscale.tools.project.module/compile 6 | exoscale.tools.project.module/deploy 7 | exoscale.tools.project.module/install 8 | exoscale.tools.project.module/jar 9 | exoscale.tools.project.module/release 10 | exoscale.tools.project.module/task 11 | exoscale.tools.project.module/uberjar 12 | exoscale.tools.project.standalone/git-commit-version 13 | exoscale.tools.project.standalone/git-push 14 | exoscale.tools.project.standalone/git-tag-version 15 | exoscale.tools.project.standalone/release 16 | exoscale.tools.project.standalone/version-bump-and-snapshot 17 | exoscale.tools.project.standalone/version-remove-snapshot 18 | exoscale.tools.project.template/data-fn 19 | exoscale.tools.project.template/module-data-fn 20 | exoscale.tools.project.template/template-fn 21 | exoscale.tools.project/add-module 22 | exoscale.tools.project/check 23 | exoscale.tools.project/clean 24 | exoscale.tools.project/deploy 25 | exoscale.tools.project/format-check 26 | exoscale.tools.project/format-fix 27 | exoscale.tools.project/git-commit+tag-version 28 | exoscale.tools.project/git-commit-snapshot 29 | exoscale.tools.project/git-push 30 | exoscale.tools.project/info 31 | exoscale.tools.project/init 32 | exoscale.tools.project/install 33 | exoscale.tools.project/jar 34 | exoscale.tools.project/lint 35 | exoscale.tools.project/merge-aliases 36 | exoscale.tools.project/merge-deps 37 | exoscale.tools.project/outdated 38 | exoscale.tools.project/prep 39 | exoscale.tools.project/prep-self 40 | exoscale.tools.project/release 41 | exoscale.tools.project/revision-sha 42 | exoscale.tools.project/task 43 | exoscale.tools.project/test 44 | exoscale.tools.project/uberjar 45 | exoscale.tools.project/version 46 | exoscale.tools.project/version-bump-and-snapshot 47 | exoscale.tools.project/version-git-count-revs 48 | exoscale.tools.project/version-remove-snapshot]}} 49 | :skip-comments true 50 | :output {:exclude-files ["^resources/exoscale"]}} 51 | -------------------------------------------------------------------------------- /.clj-kondo/rewrite-clj/rewrite-clj/config.edn: -------------------------------------------------------------------------------- 1 | {:lint-as 2 | {rewrite-clj.zip/subedit-> clojure.core/-> 3 | rewrite-clj.zip/subedit->> clojure.core/->> 4 | rewrite-clj.zip/edit-> clojure.core/-> 5 | rewrite-clj.zip/edit->> clojure.core/->>}} 6 | -------------------------------------------------------------------------------- /.github/workflows/clojure.yml: -------------------------------------------------------------------------------- 1 | name: Clojure CI 2 | 3 | on: [push] 4 | 5 | env: 6 | GIT_SSH_COMMAND: "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | container: 14 | image: clojure:openjdk-17-tools-deps-slim-bullseye 15 | volumes: 16 | - ${{ github.workspace }}:${{ github.workspace }} 17 | 18 | steps: 19 | - uses: actions/checkout@v3.0.2 20 | name: "Checkout git repository" 21 | 22 | - name: Cache dependencies 23 | uses: actions/cache@v3 24 | id: cache-deps 25 | with: 26 | path: | 27 | /root/.gitlibs 28 | /root/.m2 29 | key: ${{ runner.os }}-${{ hashFiles('**/deps.edn') }} 30 | 31 | - name: Check 32 | id: check 33 | run: clojure -T:project check 34 | 35 | - name: Lint 36 | id: lint 37 | run: clojure -T:project lint 38 | 39 | - name: Format 40 | run: clojure -T:project format-check 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.cpcache/ 2 | .lsp 3 | .clj-kondo/.cache 4 | resources/git-version 5 | /target 6 | /modules/*/target 7 | .nrepl-port 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Exoscale 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # exoscale tools.project 2 | 3 | A `clojure.tool` that allows to work with projects in a streamlined way. 4 | It provides an api for common project management tasks, inspired by 5 | the conventions enforced by [leiningen](http://leiningen.org) 6 | 7 | ## Installation 8 | 9 | Add this to your deps.edn 10 | 11 | ``` clj 12 | :project {:deps {io.github.exoscale/tools.project {:git/sha "..."}} 13 | :ns-default exoscale.tools.project} 14 | ``` 15 | 16 | Or install the tool locally: 17 | 18 | ``` bash 19 | clojure -Ttools install io.github.exoscale/tools.project '{:git/sha "..."}' :as project 20 | ``` 21 | 22 | ## Usage 23 | 24 | With the tool installed, a new project can be initialized with: 25 | 26 | ``` bash 27 | clojure -Tproject init :name com.example/my-lib 28 | ``` 29 | 30 | Once installed, tools can called using standard tool call syntax, within the project 31 | directory: 32 | 33 | ``` bash 34 | clojure -T:project jar 35 | clojure -T:project uberjar 36 | ``` 37 | 38 | A convenience `Makefile` is provided with projects bootstrapped with 39 | `clojure -Tproject init`, which can assist in discovering the tool. 40 | 41 | ## Supported targets 42 | 43 | - [add-module](doc/target/add-module.md): creates a new submodule 44 | - [check](doc/target/check.md): validates that namespaces can be loaded 45 | - [clean](doc/target/clean.md): cleans target directories 46 | - [deploy](doc/target/deploy.md): deploys jars to remote repositories 47 | - [format-check](doc/target/format-check.md): source code format check 48 | - [format-fix](doc/target/format-fix.md): source code format fixing 49 | - [init](doc/target/init.md): initialize new project 50 | - [install](doc/target/install.md): local maven installation 51 | - [jar](doc/target/jar.md): JAR and POM generation 52 | - [lint](doc/target/lint.md): source code linting 53 | - [merge-deps](doc/target/merge-deps.md): managed dependency management 54 | - [merge-aliases](doc/target/merge-aliases.md): managed alias management 55 | - [outdated](doc/target/outdated.md): outdated version check 56 | - [prep](doc/target/prep.md): handle prep libs 57 | - [prep-self](doc/target/prep-self.md): handle prep task for the current project 58 | - [release](doc/target/release.md): project release 59 | - [task](doc/target/task.md): arbitrary task run 60 | - [test](doc/target/test.md): run tests 61 | - [uberjar](doc/target/uberjar.md): standalone JAR generation 62 | 63 | Note that **tools.project** does not provide a `repl` task, this is due to the 64 | fact that `deps.edn` files are always fully function when using this tool, the 65 | standard `clj` tool can be used to start a REPL, editor integration will also 66 | remain fully functional. 67 | 68 | ## Configuration 69 | 70 | A few keys control the configuration of the project. A minimum configuration 71 | is shown below: 72 | 73 | ```clojure 74 | {:exoscale.project/lib com.example/my-lib 75 | :exoscale.project/version-file "VERSION" 76 | :exoscale.project/uberjar? true 77 | 78 | :paths ["src" "resources"] 79 | :deps {org.clojure/clojure {:mvn/version "1.11.1"}}} 80 | ``` 81 | 82 | See [configuration options](doc/config-options.md) for a list of 83 | all valid options. 84 | 85 | ## Multi-module projects 86 | 87 | Some projects need to build several artifacts, or to expose different libraries. 88 | This is what we refer to as multi-module projects. 89 | 90 | **tools.project** supports multi-module projects out of the box, and provides the 91 | following functionality: 92 | 93 | - All targets adopt a default behavior that is dependent on whether they are called 94 | for a *multi-module*, or *standalone* project. 95 | - Submodules of a project *must* be transparent to tooling, i.e: each submodule of 96 | a project should have its own fully functional `deps.edn` file, requiring no 97 | additional tooling. 98 | - Default facilities are present to share dependencies and aliases across modules 99 | (through [deps-modules](https://github.com/exoscale/deps-modules). 100 | 101 | Under the cover, 102 | [deps-modules](https://github.com/exoscale/deps-modules) uses a 103 | mechanism to allow dependency expressions to inherit dependencies from 104 | the parent module. To ensure that submodules remain fully independent, 105 | deps files are rewritten (see [merge-deps](doc/target/merge-deps.md) 106 | and [merge-aliases](doc/target/merge-aliases.md) for details. 107 | 108 | Let's assume the following module structure: 109 | 110 | ``` 111 | superproject 112 | ├── deps.edn ;; com.example/superproject-common 113 | └── modules 114 | ├── client 115 | │   └── deps.edn ;; com.example/superproject-client 116 | └── server 117 | └── deps.edn ;; com.example/superproject-server 118 | ``` 119 | 120 | The corresponding top-level `deps.edn` file would 121 | add the following: 122 | 123 | ``` clojure 124 | {:exoscale.project/lib com.example/superproject-common 125 | :exoscale.project/version-file "VERSION" 126 | :exoscale.project/modules ["." "modules/client" "modules/server"] 127 | 128 | :paths ["src" "resources"] 129 | :deps {org.clojure/clojure {:mvn/version "1.11.1"}}} 130 | ``` 131 | 132 | **tools.project** supports seamless transitions from standalone projects 133 | to multi module ones. See [`add-module`](doc/target/add-module.md) for 134 | help on how to add a first module. 135 | 136 | ## Contributing 137 | 138 | When developing against **tools.project**, due to the presence of a 139 | `project.clj` namespace in the repository, if you use cider, you'll 140 | have to jack in from the top level directory, otherwise cider gets 141 | confused and tries to treat the repository as a leiningen one. 142 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.5.0-SNAPSHOT -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src" "resources"] 2 | :deps {org.clojure/clojure {:mvn/version "1.12.0"} 3 | io.github.seancorfield/deps-new {:git/tag "v0.4.9" 4 | :git/sha "ba30a76af8b42a45f8498a168bf89518a794fce5"} 5 | com.github.liquidz/antq {:mvn/version "1.7.798"} 6 | cljfmt/cljfmt {:mvn/version "0.8.0"} 7 | clj-kondo/clj-kondo {:mvn/version "2023.12.15"} 8 | timofreiberg/bultitude {:mvn/version "0.3.1"} 9 | io.github.clojure/tools.build {:git/tag "v0.10.5" :git/sha "2a21b7a"} 10 | io.github.exoscale/deps-version {:git/sha "e5caeb9dcb4b691de59b271c36e9c21093db6557"} 11 | com.exoscale/lingo {:mvn/version "1.0.0-alpha19"} 12 | io.github.slipset/deps-deploy {:git/sha "b87c9299761762984bd54a285ca8fa0aac81809f" 13 | :exclusions [s3-wagon-private/s3-wagon-private]} 14 | exoscale/sos-wagon-private {:mvn/version "1.3.2-exoscale11"} 15 | io.github.exoscale/deps-modules {:git/sha "7347a3bb40bf619176404866ffd75bd80e9a75a5"} 16 | org.clojure/tools.deps.alpha {:mvn/version "0.15.1254"} 17 | babashka/fs {:mvn/version "0.2.16"} 18 | rewrite-clj/rewrite-clj {:mvn/version "1.0.767-alpha"}} 19 | 20 | :aliases 21 | {:project {:deps {io.github.exoscale/tools.project {:local/root "."}} 22 | :ns-default exoscale.tools.project 23 | :jvm-opts ["-Dclojure.main.report=stderr"]}} 24 | 25 | :slipset.deps-deploy/exec-args 26 | {:installer :remote :sign-releases? false :repository "clojars"} 27 | 28 | :exoscale.project/lib com.exoscale/tools.project 29 | :exoscale.project/version-file "VERSION" 30 | 31 | :tools/usage 32 | {:ns-default exoscale.tools.project}} 33 | -------------------------------------------------------------------------------- /doc/config-options.md: -------------------------------------------------------------------------------- 1 | ## Project configuration 2 | 3 | ### Sample config 4 | 5 | ### Standalone project 6 | 7 | ``` clojure 8 | {:exoscale.project/lib com.example/my-lib 9 | :exoscale.project/version-file "VERSION" 10 | :exoscale.project/uberjar? true ;; false by default 11 | 12 | :paths ["src" "resources"] 13 | 14 | ;; exec-args as for slipset-deploy 15 | :slipset.deps-deploy/exec-args 16 | {:installer :remote :sign-releases? false :repository "clojars"} 17 | 18 | :deps {org.clojure/clojure {:mvn/version "1.11.1"}} 19 | 20 | :aliases 21 | {:test 22 | {:extra-deps {lambdaisland/kaocha {:exoscale.deps/inherit :all, :mvn/version "1.66.1034"}} 23 | 24 | :extra-paths ["test" "resources" "test/resources"] 25 | 26 | :exec-fn kaocha.runner/exec-fn} 27 | :project 28 | {:extra-deps 29 | {io.github.exoscale/tools.project {:git/sha "29011d6f6245beeabe95c4b2d3eaa1617937b7e0"}} 30 | :ns-default exoscale.tools.project}}} 31 | ``` 32 | 33 | ### Multi-module project 34 | 35 | ``` clojure 36 | {:exoscale.project/lib com.example/my-local-server 37 | :exoscale.project/version-file "VERSION" 38 | :exoscale.project/modules ["." "modules/client" "modules/server"] 39 | 40 | :exoscale.project/remote-deploy? false ;; true by default 41 | :exoscale.project/uberjar? true ;; false by default 42 | 43 | :paths ["src" "resources"] 44 | 45 | :deps {com.example/my-client {:local/root "modules/client", :exoscale.deps/inherit :all} 46 | com.example/my-server {:local/root "modules/server", :exoscale.deps/inherit :all}} 47 | 48 | :aliases 49 | {:test 50 | {:extra-deps {lambdaisland/kaocha {:exoscale.deps/inherit :all, :mvn/version "1.66.1034"}} 51 | 52 | :extra-paths ["test" "resources" "test/resources"] 53 | 54 | :exec-fn kaocha.runner/exec-fn 55 | :exoscale.deps/inherit :all} ;; 56 | :project 57 | {:extra-deps 58 | {io.github.exoscale/tools.project {:git/sha "29011d6f6245beeabe95c4b2d3eaa1617937b7e0"}} 59 | :exoscale.deps/inherit :all 60 | :ns-default exoscale.tools.project}} 61 | 62 | ;; Aliases that may need to be forwarded to downstream projects, only useful in 63 | ;; multi-module projects 64 | :exoscale.deps/managed-aliases 65 | {:project 66 | {:extra-deps 67 | {io.github.exoscale/tools.project {:git/sha "29011d6f6245beeabe95c4b2d3eaa1617937b7e0"}} 68 | :ns-default exoscale.tools.project} 69 | :test 70 | {:extra-deps {lambdaisland/kaocha {:exoscale.deps/inherit :all, :mvn/version "1.66.1034"}} 71 | 72 | :extra-paths ["test" "resources" "test/resources"] 73 | 74 | :exec-fn kaocha.runner/exec-fn 75 | :exoscale.deps/inherit :all}} 76 | 77 | ;; Dependencies that may need to be forwarded to downstream projects, only useful in 78 | ;; multi-module projects 79 | :exoscale.deps/managed-dependencies 80 | {org.clojure/clojure #:mvn{:version "1.11.1"} 81 | org.clojure/tools.logging #:mvn{:version "1.2.4"} 82 | 83 | com.example/my-client {:local/root "modules/client"} 84 | com.example/my-server {:local/root "modules/server"}}} 85 | ``` 86 | 87 | ### Project module 88 | 89 | ``` clojure 90 | {:exoscale.project/lib com.example/my-client 91 | :exoscale.project/version-file "../../VERSION" 92 | 93 | :paths ["src" "resources"] 94 | 95 | :slipset.deps-deploy/exec-args 96 | {:installer :remote :sign-releases? false :repository "clojars"} 97 | 98 | :deps {org.clojure/clojure {:exoscale.deps/inherit :all, :mvn/version "1.11.1"}} 99 | 100 | :aliases 101 | {:test 102 | {:extra-deps {lambdaisland/kaocha {:exoscale.deps/inherit :all, :mvn/version "1.66.1034"}} 103 | :exec-fn kaocha.runner/exec-fn 104 | :exoscale.deps/inherit :all} 105 | :project 106 | {:extra-deps 107 | {io.github.exoscale/tools.project {:git/sha "29011d6f6245beeabe95c4b2d3eaa1617937b7e0"}} 108 | :exoscale.deps/inherit :all 109 | :ns-default exoscale.tools.project}}} 110 | ``` 111 | -------------------------------------------------------------------------------- /doc/internals.md: -------------------------------------------------------------------------------- 1 | All commands are chainable. They take a context map an return a potentially 2 | updated one. 3 | 4 | Most commands use built'ins from clojure/tools.deps and clojure/tools.build. 5 | 6 | You can add preset project keys in a edn file that will be loaded for every 7 | project command. By default we assume you're doing that in `deps.edn` but you 8 | can specify an `:exoscale.project/file` at invocation time if you want to 9 | separate them (ex in a `project.edn` file alongside of `deps.edn`, **at Exoscale 10 | we tend to prefer to have everything in deps.edn**). You can also specify an 11 | :exoscale.project/keypath with the location in the loaded edn file. It defaults 12 | to root. 13 | -------------------------------------------------------------------------------- /doc/target/add-module.md: -------------------------------------------------------------------------------- 1 | ## `add-module` target 2 | 3 | Requires a `:name` argument on the command line 4 | 5 | ``` shell 6 | clojure -T:project add-module :name com.example/my-module 7 | ``` 8 | 9 | Adds a new module to the project. This target will perform the 10 | following actions: 11 | 12 | - Create a new directory within the `modules/` directory with a skeleton 13 | for the module 14 | - Add a reference to the module in the top level `deps.edn` 15 | 16 | You will likely want to run the [`merge-deps`](merge-deps.md) and 17 | [`merge-aliases`](merge-aliases.md) targets shortly after having ran 18 | this target. 19 | 20 | Note that once you add a first module, the top-level `deps.edn` file 21 | will be assumed not to contain anything relevant. If you want the 22 | top-level project to be part of the projects considered by targets, 23 | you will need to add `"."` to the collection of modules stored 24 | in `:exoscale.project/modules` 25 | -------------------------------------------------------------------------------- /doc/target/check.md: -------------------------------------------------------------------------------- 1 | ## `check` target 2 | 3 | Eagerly loads all project namespaces. Errors out if any namespaces 4 | fail to load. Resources directories are excluded from the namespace 5 | search. 6 | 7 | ### Usage 8 | 9 | ```bash 10 | clojure -T:project check 11 | ``` 12 | 13 | ### Multi-module project behavior 14 | 15 | When a `:exoscale.project/modules` key is present in the project's 16 | configuration, runs through all configured module to call the 17 | `check` target instead of running on the project. 18 | -------------------------------------------------------------------------------- /doc/target/clean.md: -------------------------------------------------------------------------------- 1 | ## `clean` target 2 | 3 | Removes target directories 4 | 5 | ### Usage 6 | 7 | ```bash 8 | clojure -T:project clean 9 | ``` 10 | 11 | ### Additional project configuration 12 | 13 | - `:exoscale.project/extra-clean-targets`: An optional collection of directories to 14 | clean. 15 | 16 | ### Multi-module project behavior 17 | 18 | When a `:exoscale.project/modules` key is present in the project's 19 | configuration, runs through all configured module to call the 20 | `clean` target instead of running on the project. 21 | -------------------------------------------------------------------------------- /doc/target/deploy.md: -------------------------------------------------------------------------------- 1 | ## `deploy` target 2 | 3 | Performs a remote deploy of the project's configured artifact 4 | through [deps-deploy](https://github.com/slipset/deps-deploy). 5 | 6 | ### Usage 7 | 8 | ```bash 9 | clojure -T:project deploy 10 | ``` 11 | 12 | ### Additional project configuration 13 | 14 | - `:exoscale.project/deploy?`: must be set to true for a deploy task to target a module or the root 15 | - `:slipset.deps-deploy/exec-args`: Exec arguments for [deps-deploy](https://github.com/slipset/deps-deploy). 16 | 17 | Note that by default if pushing to object storage, an Exoscale SOS bucket is assumed. 18 | 19 | ### Multi-module project behavior 20 | 21 | When a `:exoscale.project/modules` key is present in the project's 22 | configuration, runs through all configured module to call the 23 | `deploy` target instead of running on the project. 24 | -------------------------------------------------------------------------------- /doc/target/format-check.md: -------------------------------------------------------------------------------- 1 | ## `format-check` target 2 | 3 | Perform a format check with the help of [clj-fmt](https://github.com/weavejester/cljfmt) 4 | 5 | ### Usage 6 | 7 | ```bash 8 | clojure -T:project format-check 9 | ``` 10 | 11 | ### Multi-module project behavior 12 | 13 | When a `:exoscale.project/modules` key is present in the project's 14 | configuration, runs through all configured module to call the 15 | `format-check` target instead of running on the project. 16 | -------------------------------------------------------------------------------- /doc/target/format-fix.md: -------------------------------------------------------------------------------- 1 | ## `format-fix` target 2 | 3 | Perform a format check with the help of [clj-fmt](https://github.com/weavejester/cljfmt) 4 | 5 | ### Usage 6 | 7 | ```bash 8 | clojure -T:project format-fix 9 | ``` 10 | 11 | ### Configuration 12 | 13 | The following options may be provided 14 | 15 | - `:exoscale.project/source-path-exclusions`: A regex of paths to exclude, defaults to `#"(resources|^target|^classes)"` 16 | 17 | 18 | ### Multi-module project behavior 19 | 20 | When a `:exoscale.project/modules` key is present in the project's 21 | configuration, runs through all configured module to call the 22 | `format-fix` target instead of running on the project. 23 | -------------------------------------------------------------------------------- /doc/target/init.md: -------------------------------------------------------------------------------- 1 | ## `init` target 2 | 3 | This target will initialize a new project. This is the only 4 | target that is expected to be ran outside of a specific project. 5 | 6 | Requires a `:name` argument on the command line 7 | 8 | ``` shell 9 | clojure -T:project init :name com.example/my-project 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/target/install.md: -------------------------------------------------------------------------------- 1 | ## `install` target 2 | 3 | Perform a local install of jars 4 | 5 | ### Usage 6 | 7 | ```bash 8 | clojure -T:project install 9 | ``` 10 | 11 | ### Multi-module project behavior 12 | 13 | When a `:exoscale.project/modules` key is present in the project's 14 | configuration, runs through all configured module to call the 15 | `install` target instead of running on the project. 16 | -------------------------------------------------------------------------------- /doc/target/jar.md: -------------------------------------------------------------------------------- 1 | ## `jar` target 2 | 3 | Create JAR and POM files for the project 4 | 5 | ### Usage 6 | 7 | ```bash 8 | clojure -T:project jar 9 | ``` 10 | 11 | ### Multi-module project behavior 12 | 13 | When a `:exoscale.project/modules` key is present in the project's 14 | configuration, runs through all configured module to call the 15 | `jar` target instead of running on the project. 16 | -------------------------------------------------------------------------------- /doc/target/lint.md: -------------------------------------------------------------------------------- 1 | ## `lint` target 2 | 3 | Perform a 4 | Perform linting of source files through [clj-kondo](https://github.com/clj-kondo/clj-kondo). 5 | 6 | ### Usage 7 | 8 | ```bash 9 | clojure -T:project lint 10 | ``` 11 | 12 | ### Configuration 13 | 14 | The following options may be provided 15 | 16 | - `:exoscale.project/source-path-exclusions`: A regex of paths to exclude, defaults to `#"(resources|^target|^classes)"` 17 | 18 | ### Multi-module project behavior 19 | 20 | When a `:exoscale.project/modules` key is present in the project's 21 | configuration, runs through all configured module to call the 22 | `lint` task instead of running on the project. 23 | -------------------------------------------------------------------------------- /doc/target/merge-aliases.md: -------------------------------------------------------------------------------- 1 | ## `merge-aliases` target 2 | 3 | Handle dependency merging throughout profile with [deps-modules](https://github.com/exoscale/deps-modules). 4 | Modules to merge are inferred: 5 | 6 | - When `:exoscale.deps/deps-files` is present, its value is used; 7 | - Otherwise, when `:exoscale.project/modules` is present in configuration, deps-file is 8 | inferred from the list of modules; 9 | - Otherwise, `:exoscale.deps/deps-files` is assumed to be `["deps.edn"]` 10 | 11 | When inferring deps file configuration from the list of modules, it may be useful 12 | to add additional files to the list of entries processed. This can be done with 13 | the `:exoscale.deps/extra-deps-files` key. 14 | 15 | ### Usage 16 | 17 | ```bash 18 | clojure -T:project merge-aliases 19 | ``` 20 | 21 | ### Multi-module project behavior 22 | 23 | No specific behavior is enforced for multi-module or standalone repositories. 24 | -------------------------------------------------------------------------------- /doc/target/merge-deps.md: -------------------------------------------------------------------------------- 1 | ## `merge-deps` target 2 | 3 | Handle dependency merging throughout profile with 4 | [deps-modules](https://github.com/exoscale/deps-modules). Modules to 5 | merge are inferred: 6 | 7 | - When `:exoscale.deps/deps-files` is present, its value is used; 8 | - Otherwise, when `:exoscale.project/modules` is present in configuration, deps-file is 9 | inferred from the list of modules; 10 | - Otherwise, `:exoscale.deps/deps-files` is assumed to be `["deps.edn"]` 11 | 12 | When inferring deps file configuration from the list of modules, it may be useful 13 | to add additional files to the list of entries processed. This can be done with 14 | the `:exoscale.deps/extra-deps-files` key. 15 | 16 | ### Usage 17 | 18 | ```bash 19 | clojure -T:project merge-deps 20 | ``` 21 | 22 | ### Multi-module project behavior 23 | 24 | No specific behavior is enforced for multi-module or standalone repositories. 25 | -------------------------------------------------------------------------------- /doc/target/outdated.md: -------------------------------------------------------------------------------- 1 | ## `outdated` target 2 | 3 | Perform outdated version checks with [antq](https://github.com/liquidz/antq). 4 | 5 | ### Usage 6 | 7 | ```bash 8 | clojure -T:project outdated 9 | ``` 10 | 11 | ### Multi-module project behavior 12 | 13 | No specific behavior is enforced for multi-module or standalone repositories. 14 | -------------------------------------------------------------------------------- /doc/target/prep-self.md: -------------------------------------------------------------------------------- 1 | ## `prep-self` target 2 | 3 | Run the [prep task](https://clojure.org/guides/deps_and_cli#prep_libs) of the current 4 | project where applicable. 5 | To avoid unnecessarily running in all sub modules, predicated 6 | on `:deps/prep-lib` in the deps edn file. 7 | 8 | This is implicitly ran for `check`, `jar`, and `uberjar` and thus is only useful 9 | as a debugging facility. 10 | 11 | ### Usage 12 | 13 | ```bash 14 | clojure -T:project prep-self 15 | ``` 16 | 17 | ### Multi-module project behavior 18 | 19 | When a `:exoscale.project/modules` key is present in the project's 20 | configuration, runs through all configured module to call the 21 | `prep-self` target instead of running on the project, only accounting 22 | for those having `:deps/prep-lib` set to true. 23 | -------------------------------------------------------------------------------- /doc/target/prep.md: -------------------------------------------------------------------------------- 1 | ## `prep` target 2 | 3 | Run the [prep task](https://clojure.org/guides/deps_and_cli#prep_libs) of dependent libraries where applicable. 4 | To avoid unnecessarily running in all sub modules, predicated 5 | on `:exoscale.project/needs-prep?` in the deps edn file. 6 | 7 | This is implicitly ran for `check`, `jar`, and `uberjar` and thus is only useful 8 | as a debugging facility. 9 | 10 | ### Usage 11 | 12 | ```bash 13 | clojure -T:project prep 14 | ``` 15 | 16 | ### Multi-module project behavior 17 | 18 | When a `:exoscale.project/modules` key is present in the project's 19 | configuration, runs through all configured module to call the 20 | `prep` target instead of running on the project, only accounting 21 | for those having `:exoscale.project/needs-prep?` set to true. 22 | 23 | In standalone projects, the flag is not honored and `clojure -X:deps prep` 24 | is ran regardless. 25 | -------------------------------------------------------------------------------- /doc/target/release.md: -------------------------------------------------------------------------------- 1 | ## `release` target 2 | 3 | Perform a release of the project. This is merely a [task](../task.md) run 4 | for the `:release/single` task in standalone projects, and for the `:release/modules` 5 | task in multi-module projects. 6 | 7 | ### Usage release+tag task 8 | 9 | This is a simple release task that will release the artifact and not commit the 10 | version file (if any provided), and then git tag the release simply. That can be 11 | used with version-fn to create releases based on git-revs-count or timestamp 12 | schemes (see: `exoscale.tools.project.api.version/git-count-revs` & 13 | `exoscale.tools.project.api.version/epoch`). 14 | 15 | ```bash 16 | clojure -T:project release+tag 17 | ``` 18 | 19 | The default release+tag task is: 20 | 21 | ``` clojure 22 | [{:run :exoscale.tools.project.standalone/deploy} 23 | {:run :exoscale.tools.project.standalone/git-tag-version} 24 | {:run :exoscale.tools.project.standalone/git-push}] 25 | ``` 26 | 27 | 28 | ### Usage release task 29 | 30 | This task will trigger the update of the VERSION file provided and commit it, then trigger a release 31 | 32 | ```bash 33 | clojure -T:project release 34 | ``` 35 | 36 | ### Additional project configuration 37 | 38 | Since `release` is a task, the default task configuration can be overridden 39 | using standard task configuration: 40 | 41 | 42 | ``` clojure 43 | :exoscale.project/tasks 44 | {:release/single [{:shell ["echo noop"]}] 45 | ;; or 46 | :release/modules [{:shell ["echo module noop"]}]} 47 | ``` 48 | 49 | The default release task configuration is: 50 | 51 | ``` clojure 52 | [{:run :exoscale.tools.project.standalone/version-remove-snapshot} 53 | {:run :exoscale.tools.project.standalone/deploy} ;; {:ref :deploy} is used for multi module projects 54 | {:run :exoscale.tools.project.standalone/git-commit-version} 55 | {:run :exoscale.tools.project.standalone/git-tag-version} 56 | {:run :exoscale.tools.project.standalone/version-bump-and-snapshot} 57 | {:run :exoscale.tools.project.standalone/git-commit-version} 58 | {:run :exoscale.tools.project.standalone/git-push}] 59 | 60 | ``` 61 | -------------------------------------------------------------------------------- /doc/target/task.md: -------------------------------------------------------------------------------- 1 | ## Tasks 2 | 3 | You have the possibility to define/create tasks for your projects, under the 4 | `:exoscale.project/tasks` key. Tasks essentially allow you to run 5 | fns/shell-commands/other-tasks from clj directly and compose them. That is how, 6 | for instance most project level, complex, tasks are constructed (ex bump 7 | version + commit + release). 8 | 9 | Then in :exoscale.project/tasks we have tasks definitions, a map of task id -> 10 | task def. 11 | 12 | In your deps.edn 13 | ``` clj 14 | {:exoscale.project/tasks 15 | {:foo [{:run :some.fn/f :args {}}] 16 | :bar [{:run :some.fn/f 17 | :args {} 18 | :for-all [:exoscale.project/libs]} 19 | {:ref :foo} 20 | {:shell ["echo ho ho ho"] 21 | :for-all [:exoscale.project/deployables]}]}} 22 | ``` 23 | 24 | 25 | A Task is just a map `{...}` 26 | 27 | We have 3 type of tasks for now: 28 | 29 | * :run tasks `{:run some.ns/fn}` that will trigger an 30 | invocation of that task 31 | 32 | * :shell tasks `{:shell [\"ping foo.com\" \"ping bar.com\"]}` that will 33 | trigger an invocation of the shell commands listed in 34 | order 35 | 36 | * :ref tasks `{:ref :something}` that will invoke another task 37 | 38 | 39 | Great, but why are we doing this? 40 | 41 | Tasks can be repeated against a coll of values if you specify a :for-all key 42 | `{:run some.ns/fn :for-all :exoscale.project/libs]}` the 43 | task will then be run for all modules listed under that key in the deps.edn 44 | file, in (execution) context, in a single process potentially. Tasks can also 45 | refer each other. Essentially a declarative version of lein sub that supports 46 | composability and is not spawning as many processes as we have tasks. 47 | 48 | Filtering on the list of target modules can be performed: 49 | 50 | {:run some.ns/fn :for-all [:exoscale.project/modules] 51 | :when :exoscale.project/should-run?} 52 | 53 | Additionally, a default value can be provided: 54 | 55 | {:run some.ns/fn :for-all [:exoscale.project/modules] 56 | :when :exoscale.project/bypass?} 57 | 58 | Some implementation notes: 59 | 60 | * all commands run in the same process, with the exception of :shell commands of 61 | course, that means an equivalent of `lein sub install` runs quite fast (inside 62 | 1 jvm process). 63 | * we take care of setting the modules path via tools.deps & build for every 64 | command on separate modules. 65 | 66 | It's a tool, so invocation is of the `-T` form: `clj -T:project task :id 67 | :install`, meaning we potentially support run-time args as well, the classpath 68 | is also isolated from the lib cp. But because it's a tool we have no access to 69 | other aliases in context. 70 | -------------------------------------------------------------------------------- /doc/target/test.md: -------------------------------------------------------------------------------- 1 | ## `test` target 2 | 3 | This is a dummy target which calls `clojure -X:test` in the current 4 | project. In multi-module projects, it runs through all configured 5 | modules unless `:exoscale.project/testable?` is set to false in the 6 | project file and calls `clojure -X:test`. 7 | 8 | Note that it is still on each individual projects to set up the `:test` 9 | alias correctly. 10 | 11 | The default project templates wires kaocha as the runner for convenience. 12 | -------------------------------------------------------------------------------- /doc/target/uberjar.md: -------------------------------------------------------------------------------- 1 | ## `uberjar` target 2 | 3 | Create standalone JAR for the project 4 | 5 | ### Usage 6 | 7 | ```bash 8 | clojure -T:project uberjar 9 | ``` 10 | 11 | ### Additional project behavior 12 | 13 | - `:exoscale.project/uberjar?`: Only produce the uberjar when set to `true`. Defaults to `false`. 14 | - `:exoscale.project/compile-opts`: A map of options as for [`compile-clj`](https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-compile-clj). 15 | 16 | ### Multi-module project behavior 17 | 18 | When a `:exoscale.project/modules` key is present in the project's 19 | configuration, runs through all configured module to call the 20 | `uberjar` target instead of running on the project. 21 | Additionally, a `resources/VERSION` file is added to the uberjar, containing 22 | the specified project's version. 23 | -------------------------------------------------------------------------------- /resources/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exoscale/tools.project/8fdb31f6b7cd3a872087155383cee19b746ab15b/resources/.keep -------------------------------------------------------------------------------- /resources/exoscale/module/root/.gitignore: -------------------------------------------------------------------------------- 1 | .calva/output-window/ 2 | .classpath 3 | .clj-kondo/.cache 4 | .cpcache 5 | .eastwood 6 | .factorypath 7 | .hg/ 8 | .hgignore 9 | .java-version 10 | .lein-* 11 | .lsp/.cache 12 | .lsp/sqlite.db 13 | .nrepl-history 14 | .nrepl-port 15 | .project 16 | .rebel_readline_history 17 | .settings 18 | .socket-repl-port 19 | .sw* 20 | .vscode 21 | *.class 22 | *.jar 23 | *.swp 24 | *~ 25 | /checkouts 26 | /classes 27 | /target 28 | -------------------------------------------------------------------------------- /resources/exoscale/module/root/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src" "resources"] 2 | :deps {org.clojure/clojure {:exoscale.deps/inherit :all}} 3 | 4 | :exoscale.project/lib {{raw-name}} 5 | :exoscale.project/version-file "../../VERSION" 6 | 7 | :aliases 8 | {:project 9 | {:exoscale.deps/inherit :all} 10 | :test 11 | {:exoscale.deps/inherit :all}}} 12 | -------------------------------------------------------------------------------- /resources/exoscale/module/root/resources/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exoscale/tools.project/8fdb31f6b7cd3a872087155383cee19b746ab15b/resources/exoscale/module/root/resources/.keep -------------------------------------------------------------------------------- /resources/exoscale/module/root/tests.edn: -------------------------------------------------------------------------------- 1 | #include "../../.kaocha/default.edn" 2 | -------------------------------------------------------------------------------- /resources/exoscale/module/src/main.clj: -------------------------------------------------------------------------------- 1 | (ns {{top/ns}}.{{main/ns}}) 2 | 3 | (defn foo 4 | "I don't do a whole lot." 5 | [x] 6 | (prn x "Hello, World!")) 7 | -------------------------------------------------------------------------------- /resources/exoscale/module/template.edn: -------------------------------------------------------------------------------- 1 | {:description "FIXME: generated by the com.exoscale/project template." 2 | ;; this does not transform the data but does print a message: 3 | :data-fn exoscale.tools.project.template/module-data-fn 4 | ;; this does not transform the EDN but does print a message: 5 | :template-fn exoscale.tools.project.template/template-fn 6 | :transform 7 | [["src" "src/{{top/file}}" 8 | {"main.clj" "{{main/file}}.clj"}]]} 9 | -------------------------------------------------------------------------------- /resources/exoscale/project/root/.github/workflows/clojure.yml: -------------------------------------------------------------------------------- 1 | name: Clojure CI 2 | 3 | on: [push] 4 | 5 | env: 6 | GIT_SSH_COMMAND: "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | container: 14 | image: clojure:openjdk-17-tools-deps-slim-bullseye 15 | volumes: 16 | - ${{ github.workspace }}:${{ github.workspace }} 17 | 18 | steps: 19 | - uses: actions/checkout@v3.0.2 20 | name: "Checkout git repository" 21 | 22 | - name: Cache dependencies 23 | uses: actions/cache@v3 24 | id: cache-deps 25 | with: 26 | path: | 27 | /root/.gitlibs 28 | /root/.m2 29 | key: ${{ runner.os }}-${{ hashFiles('**/deps.edn') }} 30 | 31 | - name: Check 32 | id: check 33 | run: clojure -T:project check 34 | 35 | - name: Test 36 | id: test 37 | run: clojure -T:project test 38 | 39 | - name: Lint 40 | id: lint 41 | run: clojure -T:project lint 42 | 43 | - name: Format 44 | run: clojure -T:project format-check 45 | -------------------------------------------------------------------------------- /resources/exoscale/project/root/.gitignore: -------------------------------------------------------------------------------- 1 | .calva/output-window/ 2 | .classpath 3 | .clj-kondo/.cache 4 | .cpcache 5 | .eastwood 6 | .factorypath 7 | .hg/ 8 | .hgignore 9 | .java-version 10 | .lein-* 11 | .lsp/.cache 12 | .lsp/sqlite.db 13 | .nrepl-history 14 | .nrepl-port 15 | .project 16 | .rebel_readline_history 17 | .settings 18 | .socket-repl-port 19 | .sw* 20 | .vscode 21 | *.class 22 | *.jar 23 | *.swp 24 | *~ 25 | /checkouts 26 | /classes 27 | /target 28 | **/resources/git-version -------------------------------------------------------------------------------- /resources/exoscale/project/root/.kaocha/default.edn: -------------------------------------------------------------------------------- 1 | #kaocha/v1 2 | {} 3 | -------------------------------------------------------------------------------- /resources/exoscale/project/root/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: * 2 | .DEFAULT_GOAL:=help 3 | 4 | # Internal 5 | ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 6 | VERSION=$(shell cat $(ROOT_DIR)/VERSION) 7 | CLJ=clojure -J-Dclojure.main.report=stderr 8 | 9 | ##@ Testing 10 | 11 | test: ## Runs unit tests (alias to test-unit) 12 | make test-unit 13 | 14 | test-unit: ## Runs unit tests 15 | $(CLJ) -T:project test :kaocha.filter/focus '[:unit]' 16 | 17 | test-integration: ## Runs integration tests 18 | $(CLJ) -T:project test :kaocha.filter/focus '[:integration]' 19 | 20 | test-all: ## Runs unit+integration tests 21 | $(CLJ) -T:project test 22 | 23 | repl-test: ## Launch test repl 24 | $(CLJ) -A:test 25 | 26 | ##@ Dependencies 27 | 28 | deps: ## Show deps tree 29 | $(CLJ) -Stree 30 | 31 | check: ## Compile all namespaces to check for issues 32 | $(CLJ) -T:project check 33 | 34 | merge-deps: ## Merge dependencies on all modules from :managed-deps 35 | $(CLJ) -T:project merge-deps 36 | 37 | ##@ Misc. 38 | 39 | lint: ## runs linting on all modules 40 | $(CLJ) -T:project lint 41 | 42 | format: ## Format according to linter rules 43 | $(CLJ) -T:project format-check 44 | 45 | format-fix: ## Fix formatting errors found 46 | $(CLJ) -T:project format-fix 47 | 48 | outdated: ## run antq (aka 'ancient') task on all modules 49 | $(CLJ) -T:project outdated 50 | 51 | clean: ## Clean module target dirs 52 | $(CLJ) -T:project clean 53 | 54 | install: ## Install all modules to local maven repo 55 | $(CLJ) -T:project install 56 | 57 | version: ## Output project version 58 | @$(CLJ) -T:project version 59 | 60 | repl: ## Launch repl (no aliases) 61 | @clj 62 | 63 | ##@ CI/CD tasks 64 | 65 | uberjar: ## Build uberjar(s) 66 | $(CLJ) -T:project uberjar 67 | 68 | release: ## Release jar modules & tag versions 69 | git config --global --add safe.directory '*' 70 | $(CLJ) -T:project release 71 | 72 | build: uberjar 73 | 74 | ##@ Helpers 75 | 76 | .SILENT: info 77 | info: ## Show repo information 78 | @$(CLJ) -T:project info 79 | 80 | help: ## Display this help 81 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 82 | -------------------------------------------------------------------------------- /resources/exoscale/project/root/README.md: -------------------------------------------------------------------------------- 1 | # {{raw-name}} 2 | 3 | {{description}} 4 | 5 | ## Usage 6 | 7 | FIXME: write usage documentation! 8 | 9 | Run the project's tests (they'll fail until you edit them): 10 | 11 | $ make test 12 | 13 | Release a new version of the project: 14 | 15 | $ make release 16 | 17 | More help on default tasks can be found by running: 18 | 19 | $ make help 20 | -------------------------------------------------------------------------------- /resources/exoscale/project/root/VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0-SNAPSHOT -------------------------------------------------------------------------------- /resources/exoscale/project/root/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src" "resources"] 2 | :deps {org.clojure/clojure {:mvn/version "1.11.1" :exoscale.deps/inherit :all}} 3 | 4 | :exoscale.project/lib {{raw-name}} 5 | :exoscale.project/version-file "VERSION" 6 | 7 | :aliases 8 | {:project 9 | {:ns-default exoscale.tools.project 10 | :deps {io.github.exoscale/tools.project 11 | {:git/sha "29011d6f6245beeabe95c4b2d3eaa1617937b7e0"}} 12 | :exoscale.deps/inherit :all} 13 | :test 14 | {:extra-deps {lambdaisland/kaocha {:exoscale.deps/inherit :all, :mvn/version "1.66.1034"}} 15 | :extra-paths ["test" "test/resources"] 16 | :exec-fn kaocha.runner/exec-fn 17 | :exoscale.deps/inherit :all}} 18 | 19 | :exoscale.deps/managed-dependencies 20 | {org.clojure/clojure {:mvn/version "1.11.1"}} 21 | 22 | :exoscale.deps/managed-aliases 23 | {:project 24 | {:ns-default exoscale.tools.project 25 | :deps {io.github.exoscale/tools.project 26 | {:git/sha "29011d6f6245beeabe95c4b2d3eaa1617937b7e0"}}} 27 | 28 | :test 29 | {:extra-deps {lambdaisland/kaocha {:exoscale.deps/inherit :all, :mvn/version "1.66.1034"}} 30 | :extra-paths ["test" "test/resources"] 31 | :exec-fn kaocha.runner/exec-fn}}} 32 | -------------------------------------------------------------------------------- /resources/exoscale/project/root/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to {{raw-name}} 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /resources/exoscale/project/root/resources/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exoscale/tools.project/8fdb31f6b7cd3a872087155383cee19b746ab15b/resources/exoscale/project/root/resources/.keep -------------------------------------------------------------------------------- /resources/exoscale/project/root/tests.edn: -------------------------------------------------------------------------------- 1 | #include ".kaocha/default.edn" 2 | -------------------------------------------------------------------------------- /resources/exoscale/project/src/main.clj: -------------------------------------------------------------------------------- 1 | (ns {{top/ns}}.{{main/ns}}) 2 | 3 | (defn foo 4 | "I don't do a whole lot." 5 | [x] 6 | (prn x "Hello, World!")) 7 | -------------------------------------------------------------------------------- /resources/exoscale/project/template.edn: -------------------------------------------------------------------------------- 1 | {:description "FIXME: generated by the com.exoscale/project template." 2 | ;; this does not transform the data but does print a message: 3 | :data-fn exoscale.tools.project.template/data-fn 4 | ;; this does not transform the EDN but does print a message: 5 | :template-fn exoscale.tools.project.template/template-fn 6 | :transform 7 | [["src" "src/{{top/file}}" 8 | {"main.clj" "{{main/file}}.clj"}] 9 | ["test" "test/{{top/file}}" 10 | {"main_test.clj" "{{main/file}}_test.clj"}]]} 11 | -------------------------------------------------------------------------------- /resources/exoscale/project/test/main_test.clj: -------------------------------------------------------------------------------- 1 | (ns {{top/ns}}.{{main/ns}}-test 2 | (:require [clojure.test :refer [deftest is testing]])) 3 | 4 | (deftest a-test 5 | (testing "FIXME, I fail." 6 | (is (= 0 1)))) 7 | -------------------------------------------------------------------------------- /src/exoscale/tools/project.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project 2 | "All functions in this modules are meant to be used with -T. 3 | 4 | They will trigger invocation of exoscale.tools.project functions with the same 5 | name or tasks with same id. 6 | 7 | If we can infer that we are in a multi-module setup at the root we will invoke 8 | the corresponding task, if not we will simply call into the the 9 | exoscale.tools.project function. 10 | 11 | That essentially means that all fns here as dual purpose depending on which 12 | directory they are called from" 13 | (:refer-clojure :exclude [test]) 14 | (:require [clojure.edn :as edn] 15 | [exoscale.tools.project.standalone :as ps])) 16 | 17 | ;;; module aware commands 18 | 19 | (def ^:private root-edn 20 | (delay (edn/read-string (slurp "deps.edn")))) 21 | 22 | (defn- task-or-tool [opts task-id project-fn] 23 | (let [redn @root-edn] 24 | ;; if it's a multi-module deps.edn with a corresponding task use that 25 | ;; otherwise fallback on regular project fns 26 | (if (contains? redn :exoscale.project/modules) 27 | (ps/task (assoc opts :id task-id)) 28 | (project-fn opts)))) 29 | 30 | ;; Tasks go below 31 | 32 | (def add-module 33 | ps/add-module) 34 | 35 | (defn check 36 | [opts] 37 | (task-or-tool opts :check ps/check)) 38 | 39 | (defn clean 40 | [opts] 41 | (task-or-tool opts :clean ps/clean)) 42 | 43 | (defn deploy 44 | [opts] 45 | (task-or-tool opts :deploy ps/deploy)) 46 | 47 | (defn format-check 48 | [opts] 49 | (task-or-tool opts :format/check ps/format-check)) 50 | 51 | (defn format-fix 52 | [opts] 53 | (task-or-tool opts :format/fix ps/format-fix)) 54 | 55 | (def init 56 | ps/init) 57 | 58 | (def info 59 | ps/info) 60 | 61 | (defn install 62 | [opts] 63 | (task-or-tool opts :install ps/install)) 64 | 65 | (defn jar 66 | [opts] 67 | (task-or-tool opts :jar ps/jar)) 68 | 69 | (defn lint 70 | [opts] 71 | (task-or-tool opts :lint ps/lint)) 72 | 73 | (def merge-deps 74 | ps/merge-deps) 75 | 76 | (def merge-aliases 77 | ps/merge-aliases) 78 | 79 | (def outdated 80 | ps/outdated) 81 | 82 | (defn prep 83 | [opts] 84 | (task-or-tool opts :prep ps/prep)) 85 | 86 | (defn release 87 | [opts] 88 | (task-or-tool opts :release/modules ps/release)) 89 | 90 | (defn release+tag 91 | [opts] 92 | (task-or-tool opts :release+tag/modules ps/release+tag)) 93 | 94 | (defn revision-sha 95 | [opts] 96 | (task-or-tool opts :revision-sha ps/revision-sha)) 97 | 98 | (defn prep-self 99 | [opts] 100 | (task-or-tool opts :prep-self ps/prep-self)) 101 | 102 | (def task 103 | ps/task) 104 | 105 | (defn test 106 | [opts] 107 | (task-or-tool opts :test ps/test)) 108 | 109 | (defn uberjar 110 | [opts] 111 | (task-or-tool opts :uberjar ps/uberjar)) 112 | 113 | (def version 114 | ps/version) 115 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/api.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.api 2 | (:refer-clojure :exclude [test]) 3 | (:require [clojure.tools.build.api :as b] 4 | [clojure.edn :as edn] 5 | [clojure.spec.alpha :as s] 6 | [exoscale.lingo :as l] 7 | [babashka.fs :as fs] 8 | [cljfmt.main :as cljfmt] 9 | [clj-kondo.core :as kondo] 10 | [antq.core :as antq] 11 | [exoscale.tools.project.dir :as dir] 12 | [exoscale.tools.project.api.git :as git] 13 | [exoscale.tools.project.api.version :as version] 14 | [exoscale.tools.project.io :as pio])) 15 | 16 | (defn- find-source-dirs 17 | [{:keys [aliases paths] 18 | :exoscale.project/keys [source-path-exclusions] 19 | :or {source-path-exclusions #"(resources|^target|^classes)"}}] 20 | (into [] 21 | (comp cat 22 | (filter (comp fs/exists? dir/canonicalize)) 23 | (remove (partial re-find source-path-exclusions)) 24 | (map dir/canonicalize)) 25 | [paths (get-in aliases [:test :extra-paths])])) 26 | 27 | (defn clean 28 | [opts] 29 | (let [{:exoscale.tools.project.api.tasks/keys [dir]} opts 30 | target-dir (str (fs/path dir (:exoscale.project/target-dir opts))) 31 | extra-clean-targets (for [t (:exoscale.project/extra-clean-targets opts)] 32 | (str (fs/path dir t))) 33 | git-version (str (fs/path dir "resources" "git-version"))] 34 | (println "running clean for:" (:exoscale.project/lib opts)) 35 | (b/delete {:path target-dir}) 36 | (b/delete {:path git-version}) 37 | (doseq [t extra-clean-targets] 38 | (b/delete {:path t})) 39 | opts)) 40 | 41 | (defn revision-sha 42 | [opts] 43 | (try 44 | (let [revision-sha (git/revision-sha opts) 45 | {:exoscale.tools.project.api.tasks/keys [dir]} opts 46 | git-version-file (str (fs/path dir "resources" "git-version"))] 47 | (fs/create-dirs (fs/path dir "resources")) 48 | (spit git-version-file revision-sha) 49 | (println "storing git sha in" git-version-file)) 50 | (catch Exception e 51 | (println (format "failed to retrieve git version %s" (ex-message e))))) 52 | opts) 53 | 54 | (defn revision-version 55 | [opts] 56 | (let [{:exoscale.tools.project.api.tasks/keys [dir]} opts 57 | version (version/get-version opts) 58 | version-file (str (fs/path dir "resources" "VERSION"))] 59 | (fs/create-dirs (fs/path dir "resources")) 60 | (spit version-file version) 61 | (println "storing version in" version-file) 62 | opts)) 63 | 64 | (defn info 65 | [opts] 66 | (let [{:exoscale.project/keys [extra-deps-files version modules lib]} opts] 67 | (printf "%s project: %s version %s [%s]\n" 68 | (if (some? modules) "multi-module" "standalone") 69 | lib 70 | version 71 | (git/revision-sha opts)) 72 | (doseq [module modules] 73 | (println " submodule:" module)) 74 | (when (some? extra-deps-files) 75 | (println " additional managed deps files:" (vec extra-deps-files))) 76 | (flush) 77 | opts)) 78 | 79 | (defn prep 80 | [{:exoscale.tools.project.api.tasks/keys [dir] 81 | :exoscale.project/keys [lib] 82 | :or {dir "."} 83 | :as opts}] 84 | (println "running prep task for dependencies in:" lib) 85 | (pio/shell [["clojure" "-J-Dclojure.main.report=stderr" "-X:deps" "prep" ":log" "debug"]] {:dir dir}) 86 | opts) 87 | 88 | (defn prep-self 89 | [{:exoscale.project/keys [deps-file lib] 90 | :exoscale.tools.project.api.tasks/keys [dir] 91 | :or {dir "."} 92 | :as opts}] 93 | (let [{:deps/keys [prep-lib]} (-> (fs/path dir deps-file) str slurp edn/read-string) 94 | {f :fn :keys [alias ensure]} prep-lib] 95 | (when (some? prep-lib) 96 | (when-not (s/valid? :deps/prep-lib prep-lib) 97 | (binding [*out* *err*] 98 | (l/explain :deps/prep-lib prep-lib {:colors? true}) 99 | (flush) 100 | (System/exit 1))) 101 | (println "running prep task for:" lib) 102 | (pio/shell [["clojure" "-J-Dclojure.main.report=stderr" (str "-X" alias) (str f)]] {:dir dir}) 103 | (when (and (some? ensure) (not (fs/exists? (fs/path dir ensure)))) 104 | (binding [*out* *err*] 105 | (println "prep failed to produce the required output file or directory:" 106 | (str (fs/path dir ensure))) 107 | (flush) 108 | (System/exit 1))))) 109 | opts) 110 | 111 | (defn format-check 112 | [opts] 113 | (let [srcdirs (find-source-dirs opts)] 114 | (println "running format checks with cljfmt for:" (:exoscale.project/lib opts)) 115 | (cljfmt/check srcdirs cljfmt/default-options) 116 | opts)) 117 | 118 | (defn format-fix 119 | [opts] 120 | (let [srcdirs (find-source-dirs opts)] 121 | (println "running format checks with cljfmt for:" (:exoscale.project/lib opts)) 122 | (cljfmt/fix srcdirs cljfmt/default-options) 123 | opts)) 124 | 125 | (defn lint 126 | [opts] 127 | (let [srcdirs (find-source-dirs opts)] 128 | (println "running lint with clj-kondo for:" (:exoscale.project/lib opts)) 129 | (let [{:keys [summary] :as results} (kondo/run! {:lint srcdirs})] 130 | (kondo/print! results) 131 | (flush) 132 | (when (pos? (:error summary)) 133 | (System/exit 1))) 134 | opts)) 135 | 136 | (defn outdated 137 | [opts] 138 | (println "checking for outdated versions with antq for:" 139 | (:exoscale.project/lib opts)) 140 | (antq/main* (merge {:check-clojure-tools true 141 | :directory ["."] 142 | :reporter "table"} 143 | (:antq.core/options opts)) 144 | nil) 145 | opts) 146 | 147 | ;; We do this here to avoid forcing the registration of 148 | ;; the project tool as an extra-dep 149 | (def deps-check-config 150 | '{:aliases 151 | {:spootnik-deps-check 152 | {:extra-deps {org.spootnik/deps-check {:mvn/version "0.5.2"}}}}}) 153 | 154 | (defn check 155 | [opts] 156 | (let [dir (or (:exoscale.tools.project.api.tasks/dir opts) ".") 157 | source-dirs (find-source-dirs opts)] 158 | (pio/shell [["clojure" "-J-Dclojure.main.report=stderr" "-Sdeps" (pr-str deps-check-config) "-X:spootnik-deps-check:test" 159 | "spootnik.deps-check/check" ":paths" (pr-str source-dirs)]] 160 | {:dir dir}) 161 | opts)) 162 | 163 | (defn test 164 | [opts] 165 | (let [dir (or (:exoscale.tools.project.api.tasks/dir opts) ".") 166 | cmdline (reduce-kv 167 | (fn [cmdline k v] 168 | (-> cmdline 169 | (conj (pr-str k)) 170 | (conj (pr-str v)))) 171 | ["clojure" "-J-Dclojure.main.report=stderr" "-X:test"] 172 | (dissoc opts :exoscale.tools.project.api.tasks/dir))] 173 | (pio/shell [cmdline] {:dir dir})) 174 | opts) 175 | 176 | (defn version 177 | [opts] 178 | (println (:exoscale.project/version opts)) 179 | opts) 180 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/api/deploy.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.api.deploy 2 | (:require [clojure.tools.cli.api :as td] 3 | [deps-deploy.deps-deploy :as dd] 4 | [exoscale.tools.project.api.jar :as j] 5 | [exoscale.tools.project.dir :as dir])) 6 | 7 | (defn local 8 | [opts] 9 | (let [opts (j/jar opts)] 10 | (td/mvn-install {:jar (:exoscale.project/jar-file opts)}) 11 | opts)) 12 | 13 | ;; temporary until we have something more official, hence why its keys are ns'ed 14 | (defn remote 15 | [opts] 16 | (let [{:as opts 17 | :exoscale.project/keys [lib target-dir jar-file] 18 | :slipset.deps-deploy/keys [exec-args]} (j/jar opts)] 19 | (dd/deploy (into {:artifact (dir/canonicalize jar-file) 20 | :pom-file (dir/canonicalize (format "%s/classes/META-INF/maven/%s/pom.xml" 21 | target-dir 22 | lib))} 23 | exec-args)) 24 | opts)) 25 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/api/git.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.api.git 2 | (:require [clojure.string :as str] 3 | [clojure.tools.deps.alpha.util.dir :as td] 4 | [exoscale.tools.project.api.version :as v] 5 | [exoscale.tools.project.io :as pio])) 6 | 7 | (defn commit-version 8 | [opts] 9 | (pio/shell [(format "git add VERSION") 10 | "git commit -m \"Version $VERSION\""] 11 | {:dir td/*the-dir* 12 | :env {"VERSION" (v/get-version opts)}})) 13 | 14 | (defn tag-version 15 | [opts] 16 | (pio/shell ["git tag -a \"$VERSION\" --no-sign -m \"Release $VERSION\""] 17 | {:dir td/*the-dir* 18 | :env {"VERSION" (v/get-version opts)}})) 19 | 20 | (defn push 21 | [_] 22 | (pio/shell ["git pull" 23 | "git push --follow-tags"] 24 | {:dir td/*the-dir*})) 25 | 26 | (defn revision-sha 27 | [_] 28 | (-> (pio/shell ["git rev-parse HEAD"] 29 | {:dir td/*the-dir* 30 | :out :capture 31 | :fatal? false}) 32 | :out 33 | str/trim-newline)) 34 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/api/jar.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.api.jar 2 | (:require [clojure.tools.build.api :as b] 3 | [exoscale.tools.project.api.version :as v] 4 | [exoscale.tools.project.dir :as dir])) 5 | (defn- create-basis 6 | ([deps-file] 7 | (b/create-basis {:project deps-file})) 8 | ([] 9 | (create-basis "deps.edn"))) 10 | 11 | (defn jar-file* 12 | ([lib version] 13 | (jar-file* "target" lib version)) 14 | ([path lib version] 15 | (format "%s/%s-%s.jar" path lib version))) 16 | 17 | (defn uberjar-file* 18 | ([lib version] 19 | (uberjar-file* "target" lib version)) 20 | ([path lib version] 21 | (format "%s/%s-%s-standalone.jar" path lib version))) 22 | 23 | (defn jar [opts] 24 | (let [{:exoscale.project/keys [_env lib _version _version-file class-dir 25 | src-dirs basis jar-file deps-file pom-data] 26 | :as opts} opts 27 | version (v/get-version opts) 28 | deps-file (dir/canonicalize deps-file) 29 | basis (or basis (create-basis deps-file)) 30 | jar-file (dir/canonicalize (or jar-file (jar-file* (name lib) version))) 31 | class-dir (dir/canonicalize class-dir) 32 | src-dirs (map dir/canonicalize src-dirs)] 33 | 34 | (println "Writing pom.xml") 35 | (b/write-pom {:basis basis 36 | :class-dir class-dir 37 | :lib lib 38 | :pom-data pom-data 39 | :src-dirs (:exoscale.project/src-dirs opts) 40 | :version version}) 41 | (println "Copying src-dirs: " src-dirs) 42 | (b/copy-dir {:src-dirs src-dirs 43 | :target-dir class-dir}) 44 | (println "Creating jar:" jar-file) 45 | (b/jar {:class-dir class-dir 46 | :jar-file jar-file}) 47 | (into opts 48 | #:exoscale.project{:basis basis 49 | :version version 50 | :jar-file jar-file}))) 51 | 52 | (defn uberjar 53 | [opts] 54 | (let [{:exoscale.project/keys [_env lib _version _version-file main compile-opts 55 | src-dirs class-dir basis uberjar-file uber-opts deps-file 56 | ns-compile 57 | pom-data] 58 | :as opts} opts 59 | version (v/get-version opts) 60 | deps-file (dir/canonicalize deps-file) 61 | basis (or basis (create-basis deps-file)) 62 | uber-file (dir/canonicalize (or uberjar-file (uberjar-file* (name lib) version))) 63 | src-dirs (map dir/canonicalize src-dirs) 64 | class-dir (dir/canonicalize class-dir)] 65 | 66 | (println "Copying src-dirs") 67 | (b/copy-dir {:src-dirs src-dirs 68 | :target-dir class-dir}) 69 | (println "Writing pom.xml") 70 | (b/write-pom {:basis basis 71 | :class-dir class-dir 72 | :lib lib 73 | :pom-data pom-data 74 | :src-dirs (:exoscale.project/src-dirs opts) 75 | :version version}) 76 | (println "Compiling" src-dirs) 77 | (b/compile-clj (cond-> {:basis basis 78 | :class-dir class-dir 79 | :src-dirs src-dirs} 80 | (some? ns-compile) 81 | (assoc :ns-compile ns-compile) 82 | (some? compile-opts) 83 | (assoc :compile-opts compile-opts))) 84 | (println "Creating uberjar: " uber-file) 85 | (b/uber (merge uber-opts {:basis basis 86 | :class-dir class-dir 87 | :main main 88 | :uber-file uber-file})) 89 | (into opts 90 | #:exoscale.project{:basis basis 91 | :version version 92 | :uberjar-file uber-file 93 | :uber-opts uber-opts}))) 94 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/api/java.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.api.java 2 | (:refer-clojure :exclude [compile]) 3 | (:require [clojure.tools.build.api :as b] 4 | [exoscale.tools.project.dir :as dir])) 5 | 6 | (defn compile [opts] 7 | (let [{:as opts 8 | :exoscale.project/keys [basis class-dir java-src-dirs]} opts 9 | class-dir (dir/canonicalize class-dir) 10 | java-src-dirs (map dir/canonicalize java-src-dirs)] 11 | (b/javac {:basis basis 12 | :class-dir class-dir 13 | :src-dirs java-src-dirs}) 14 | (assoc opts 15 | :exoscale.project/class-dir class-dir 16 | :exoscale.project/java-src-dirs java-src-dirs))) 17 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/api/tasks.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.api.tasks 2 | "Tasks are meant to be used to compose exoscale.tools.project functions and 3 | potentially run them for a set of things. 4 | 5 | A Task is just a map. 6 | {...} 7 | 8 | We have 3 type of tasks for now: 9 | 10 | * :run tasks `{:run some.ns/fn}` that will trigger an invocation of that task 11 | * :shell tasks `{:shell [\"ping foo.com\" \"ping bar.com\"]}` that will trigger an invocation of the shell commands listed in order 12 | * :ref tasks `{:ref :something}` that will invoke another task 13 | 14 | Great, but why are we doing this? 15 | 16 | Tasks can be repeated for several \"sub projects\" if you specify a :for-all key 17 | `#:exoscale.project.task{:run some.ns/fn :for-all [:exoscale.project/modules]}` 18 | the task will then be run for all modules listed under that key in the 19 | deps.edn file, in (execution) context, in a single process 20 | potentially. Essentially a declarative version of lein sub that supports 21 | composability and is not spawning as many processes as we have tasks. 22 | 23 | Filtering on the list of target modules can be performed: 24 | 25 | {:run some.ns/fn :for-all [:exoscale.project/modules] 26 | :when :exoscale.project/should-run?} 27 | 28 | Inverted predicate filter is also supported: 29 | 30 | {:run some.ns/fn :for-all [:exoscale.project/modules] 31 | :unless :exoscale.project/bypass?} 32 | " 33 | 34 | (:require [clojure.edn :as edn] 35 | [clojure.java.io :as io] 36 | [clojure.spec.alpha :as s] 37 | [clojure.tools.build.api :as tb] 38 | [clojure.tools.deps.alpha.util.dir :as td] 39 | [exoscale.lingo :as l] 40 | [exoscale.tools.project.dir :as dir] 41 | [exoscale.tools.project.io :as pio])) 42 | 43 | (def default-tasks 44 | "Sets some desirable default tasks" 45 | {:install [{:run :exoscale.tools.project.standalone/install 46 | :for-all [:exoscale.project/modules]}] 47 | 48 | :deploy [{:run :exoscale.tools.project.standalone/deploy 49 | :when :exoscale.project/deploy? 50 | :for-all [:exoscale.project/modules]}] 51 | 52 | :jar [{:run :exoscale.tools.project.standalone/jar 53 | :for-all [:exoscale.project/modules]}] 54 | 55 | :uberjar [{:run :exoscale.tools.project.standalone/uberjar 56 | :when :exoscale.project/uberjar? 57 | :for-all [:exoscale.project/modules]}] 58 | 59 | :clean [{:run :exoscale.tools.project.standalone/clean 60 | :for-all [:exoscale.project/modules]}] 61 | 62 | :check [{:run :exoscale.tools.project.standalone/check 63 | :for-all [:exoscale.project/modules]}] 64 | 65 | :test [{:run :exoscale.tools.project.standalone/test 66 | :for-all [:exoscale.project/modules] 67 | :unless :exoscale.project/bypass-test?}] 68 | 69 | :format/check [{:run :exoscale.tools.project.standalone/format-check 70 | :for-all [:exoscale.project/modules]}] 71 | 72 | :format/fix [{:run :exoscale.tools.project.standalone/format-fix 73 | :for-all [:exoscale.project/modules]}] 74 | 75 | :lint [{:run :exoscale.tools.project.standalone/lint 76 | :for-all [:exoscale.project/modules]}] 77 | 78 | :prep [{:run :exoscale.tools.project.standalone/prep 79 | :for-all [:exoscale.project/modules] 80 | :when :exoscale.project/needs-prep?}] 81 | 82 | :revision-sha [{:run :exoscale.tools.project.standalone/revision-sha 83 | :for-all [:exoscale.project/modules]}] 84 | 85 | :release/single [{:run :exoscale.tools.project.standalone/version-remove-snapshot} 86 | {:run :exoscale.tools.project.standalone/deploy} 87 | {:run :exoscale.tools.project.standalone/git-commit-version} 88 | {:run :exoscale.tools.project.standalone/git-tag-version} 89 | {:run :exoscale.tools.project.standalone/version-bump-and-snapshot} 90 | {:run :exoscale.tools.project.standalone/git-commit-version} 91 | {:run :exoscale.tools.project.standalone/git-push}] 92 | 93 | :release/modules [{:run :exoscale.tools.project.standalone/version-remove-snapshot} 94 | {:ref :deploy} 95 | {:run :exoscale.tools.project.standalone/git-commit-version} 96 | {:run :exoscale.tools.project.standalone/git-tag-version} 97 | {:run :exoscale.tools.project.standalone/version-bump-and-snapshot} 98 | {:run :exoscale.tools.project.standalone/git-commit-version} 99 | {:run :exoscale.tools.project.standalone/git-push}] 100 | 101 | :release+tag/single 102 | [{:run :exoscale.tools.project.standalone/deploy} 103 | {:run :exoscale.tools.project.standalone/git-tag-version} 104 | {:run :exoscale.tools.project.standalone/git-push}] 105 | 106 | :release+tag/modules 107 | [{:ref :deploy} 108 | {:run :exoscale.tools.project.standalone/git-tag-version} 109 | {:run :exoscale.tools.project.standalone/git-push}] 110 | 111 | :prep-self [{:run :exoscale.tools.project.standalone/prep-self 112 | :for-all [:exoscale.project/modules] 113 | :when :deps/prep-lib}]}) 114 | 115 | (defn shell* 116 | [cmds {::keys [dir env]}] 117 | (pio/shell cmds {:dir dir :env env})) 118 | 119 | (defn run* 120 | [task {::keys [dir] :as opts}] 121 | (binding [tb/*project-root* dir 122 | td/*the-dir* (td/as-canonical (io/file dir))] 123 | ((requiring-resolve (symbol task)) opts))) 124 | 125 | (declare exoscale.tools.project.api.tasks/task) 126 | 127 | (s/def :exoscale.project/tasks 128 | (s/map-of keyword? (s/coll-of :exoscale.project/task))) 129 | 130 | (s/def :exoscale.project/task 131 | (s/or :ref (s/keys :req-un [:exoscale.project.task/ref]) 132 | :run (s/keys :req-un [:exoscale.project.task/run]) 133 | :shell (s/keys :req-un [:exoscale.project.task/shell]))) 134 | 135 | (s/def :exoscale.project.task/ref keyword?) 136 | (s/def :exoscale.project.task/run qualified-ident?) 137 | (s/def :exoscale.project.task/shell (s/coll-of string? :min-count 1)) 138 | 139 | (defn- run-task! 140 | [{:as task :keys [shell ref run args]} 141 | {::keys [dir] :as opts}] 142 | (let [ret (s/conform :exoscale.project/task task)] 143 | (case (first ret) 144 | :shell (shell* shell opts) 145 | :run (run* run (merge {} args opts)) 146 | :ref (binding [td/*the-dir* dir] 147 | (exoscale.tools.project.api.tasks/task (assoc opts :id ref) opts))))) 148 | 149 | (defn- filter-dir? 150 | [task sub-edn] 151 | (let [when' (if-let [when-k (:when task)] 152 | (get sub-edn when-k) 153 | true) 154 | unless' (get sub-edn (:unless task))] 155 | (and (not unless') 156 | when'))) 157 | 158 | (defn- relevant-dir? 159 | [task dir] 160 | (->> (str dir "/deps.edn") 161 | slurp 162 | edn/read-string 163 | (filter-dir? task))) 164 | 165 | (defn- task-relevant-dirs 166 | "Process for-all statement, accounting for `:exoscale.project/when` predicate" 167 | [deps-edn {:keys [for-all] :as task}] 168 | (let [dirs (or (get-in deps-edn for-all) 169 | (throw (ex-info (format "Missing for-all key %s" for-all) 170 | task)))] 171 | (filter (partial relevant-dir? task) 172 | dirs))) 173 | 174 | (defn task 175 | [opts args] 176 | ;; let's assume we have the full env here 177 | (let [{:as task-deps-edn 178 | :exoscale.project/keys [tasks lib] 179 | :keys [id]} (edn/read-string (slurp (dir/canonicalize "deps.edn"))) 180 | tasks (merge default-tasks tasks) 181 | task-id (keyword (:id opts)) 182 | task-def (get tasks task-id)] 183 | 184 | ;; validate early 185 | (when-not (s/valid? :exoscale.project/tasks tasks) 186 | (l/explain :exoscale.project/tasks tasks) 187 | (flush) 188 | (System/exit 1)) 189 | 190 | (when-not task-def 191 | (println (format "Task '%s' not found" task-id)) 192 | (System/exit 1)) 193 | 194 | (println "starting task:" task-id 195 | (if lib 196 | (str "for: " lib) 197 | "from `root`")) 198 | (flush) 199 | 200 | (doseq [{:as task :keys [for-all]} task-def 201 | :let [task (vary-meta task assoc :exoscale.tools.project.api.tasks/task-deps-edn task-deps-edn)]] 202 | (if (seq for-all) 203 | (doseq [dir (task-relevant-dirs task-deps-edn task)] 204 | (run-task! task (merge args {::dir (dir/canonicalize dir)}))) 205 | (run-task! task (merge args {::dir td/*the-dir*})))))) 206 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/api/version.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.api.version 2 | (:require [clojure.java.io :as io] 3 | [clojure.string :as str] 4 | [clojure.tools.build.api :as b] 5 | [clojure.tools.deps.alpha.util.dir :as td] 6 | [exoscale.deps-version :as version])) 7 | 8 | (defn run-version-fn [{:as opts :exoscale.project/keys [version-fn]}] 9 | (when-let [f (requiring-resolve (symbol version-fn))] 10 | (f opts))) 11 | 12 | (defn get-version 13 | [{:as opts :exoscale.project/keys [version-file version-fn version]}] 14 | (let [version-from-file (some-> version-file version/read-version-file*)] 15 | (cond 16 | (string? version) version 17 | (string? version-from-file) version-from-file 18 | (qualified-ident? version-fn) (run-version-fn opts)))) 19 | 20 | (defn remove-snapshot 21 | [{:as _opts 22 | :exoscale.project/keys [version-file] 23 | :or {version-file "VERSION"}}] 24 | (version/update-version {:file version-file :suffix nil})) 25 | 26 | (defn bump-and-snapshot 27 | [{:as _opts 28 | :exoscale.project/keys [version-file version-key version-suffix] 29 | :or {version-file "VERSION" 30 | version-key :patch 31 | version-suffix "SNAPSHOT"}}] 32 | (version/update-version {:file version-file 33 | :key version-key 34 | :suffix version-suffix})) 35 | 36 | (defn git-count-revs 37 | "To be used via version-fn, returns version as VERSION_TEMPLATE with number of 38 | commits on current branch replacing the GENERATED_VERSION placeholder" 39 | [{:as _opts :exoscale.project/keys [version-template-file] 40 | :or {version-template-file "VERSION_TEMPLATE"}}] 41 | (str/replace (slurp (td/canonicalize (io/file version-template-file))) 42 | "GENERATED_VERSION" 43 | (b/git-count-revs nil))) 44 | 45 | (defn epoch 46 | "To be used via version-fn, returns version as VERSION_TEMPLATE with number of 47 | seconds since epoch replacing the GENERATED_VERSION placeholder" 48 | [_opts] 49 | (.getEpochSecond (java.time.Instant/now))) 50 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/dir.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.dir 2 | (:require [clojure.tools.deps.alpha.util.dir :as td] 3 | [clojure.java.io :as io])) 4 | 5 | (defn canonicalize [f] 6 | (when f 7 | (str (td/canonicalize (io/file f))))) 8 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/io.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.io 2 | (:require [clojure.tools.build.tasks.process :as p])) 3 | 4 | (defn- check 5 | [{:keys [exit] :as res}] 6 | (when-not (zero? exit) 7 | (throw (ex-info "non zero exit" res))) 8 | res) 9 | 10 | (defn shell 11 | "Run shell commands contained in `cmds`. The first unsuccesful exit triggers a system exit." 12 | [cmds {:keys [dir env out fatal?] :or {fatal? true}}] 13 | (try 14 | (last 15 | (for [cmd cmds] 16 | (-> (p/process {:command-args (if (vector? cmd) cmd ["sh" "-c" cmd]) 17 | :dir dir 18 | :env env 19 | :out (or out :inherit)}) 20 | (check)))) 21 | (catch Exception _ 22 | ;; At this stage we already printed the relevant error to stdout 23 | (when (true? fatal?) 24 | (System/exit 1))))) 25 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/standalone.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.standalone 2 | (:refer-clojure :exclude [test]) 3 | (:require [babashka.fs :as fs] 4 | [clojure.edn :as edn] 5 | [clojure.java.io :as io] 6 | [clojure.spec.alpha :as s] 7 | [clojure.tools.deps.alpha.specs] 8 | [clojure.tools.deps.alpha.util.dir :as td] 9 | [exoscale.deps-modules :as deps-modules] 10 | [exoscale.deps-version :as version] 11 | [exoscale.lingo :as l] 12 | [exoscale.tools.project.api :as api] 13 | [exoscale.tools.project.api.deploy :as deploy] 14 | [exoscale.tools.project.api.git :as git] 15 | [exoscale.tools.project.api.jar :as jar] 16 | [exoscale.tools.project.api.tasks :as tasks] 17 | [exoscale.tools.project.api.version :as v] 18 | [exoscale.tools.project.template :as template])) 19 | 20 | (def default-opts 21 | #:exoscale.project{:file "deps.edn" 22 | :keypath [] 23 | :exoscale.deps-version/key :patch 24 | :target-dir "target" 25 | :class-dir "target/classes" 26 | :javac-opts ["-source" "11" "-target" "11"] 27 | :src-dirs ["src" "resources"] 28 | :java-src-dirs ["java"] 29 | :deps-file "deps.edn"}) 30 | 31 | (s/def :exoscale.project/lib qualified-ident?) 32 | (s/def :exoscale.project/version string?) 33 | (s/def :exoscale.project/version-file string?) 34 | (s/def :exoscale.project/version-fn qualified-ident?) 35 | (s/def :exoscale.project/target-dir string?) 36 | (s/def :exoscale.project/class-dir string?) 37 | (s/def :exoscale.project/javac-opts (s/coll-of string?)) 38 | (s/def :exoscale.project/src-dirs (s/coll-of string?)) 39 | (s/def :exoscale.project/java-src-dirs (s/coll-of string?)) 40 | (s/def :exoscale.project/deps-file string?) 41 | (s/def :exoscale.project/uber-opts map?) 42 | 43 | (s/def :exoscale.project/opts 44 | (s/keys :opt [:exoscale.project/lib 45 | :exoscale.project/version 46 | :exoscale.project/version-file 47 | :exoscale.project/version-fn 48 | :exoscale.project/target-dir 49 | :exoscale.project/class-dir 50 | :exoscale.project/javac-opts 51 | :exoscale.project/src-dirs 52 | :exoscale.project/java-src-dirs 53 | :exoscale.project/deps-file 54 | :exoscale.project/uber-opts])) 55 | 56 | (defn read-project 57 | [{:as _opts :exoscale.project/keys [file keypath]}] 58 | (try (some-> (td/canonicalize (io/file file)) 59 | slurp 60 | edn/read-string 61 | (get-in keypath)) 62 | (catch java.io.FileNotFoundException _fnf))) 63 | 64 | (defn assoc-version 65 | [{:as opts}] 66 | (let [v (v/get-version opts)] 67 | (cond-> opts 68 | (some? v) 69 | (assoc :exoscale.project/version v)))) 70 | 71 | (defn- assoc-deps-file 72 | "unless explicit options are given, prepare deps-module configuration" 73 | [{:exoscale.project/keys [modules extra-deps-files] 74 | :exoscale.deps/keys [deps-files] 75 | :as opts}] 76 | (cond-> opts 77 | (and (nil? deps-files) (nil? (:deps-files opts))) 78 | (assoc :deps-files 79 | (if (some? modules) 80 | (into [] 81 | cat 82 | [extra-deps-files 83 | (map (comp fs/normalize #(str % "/deps.edn")) modules)]) 84 | ["deps.edn"])))) 85 | 86 | (defn into-opts [opts] 87 | (let [opts (-> default-opts 88 | (merge (read-project (into default-opts opts)) opts) 89 | assoc-version 90 | assoc-deps-file)] 91 | (when-not (s/valid? :exoscale.project/opts opts) 92 | (let [msg (format "Invalid exoscale.project configuration in %s" 93 | (some-> (-> opts 94 | :exoscale.project/file 95 | io/file 96 | .getCanonicalPath)))] 97 | (println msg) 98 | (l/explain :exoscale.project/opts opts {:colors? true}) 99 | (flush) 100 | (System/exit 1))) 101 | opts)) 102 | 103 | ;; actions meant to be called from the command line 104 | 105 | (def ^{:arglists '([opts])} prep 106 | (comp api/prep into-opts)) 107 | 108 | (def ^{:arglists '([opts])} prep-self 109 | (comp api/prep-self into-opts)) 110 | 111 | (def ^{:arglists '([opts])} add-module 112 | (comp template/add-module into-opts)) 113 | 114 | (def ^{:arglists '([opts])} clean 115 | (comp api/clean into-opts)) 116 | 117 | (def ^{:arglists '([opts])} jar 118 | (comp jar/jar api/prep-self api/prep api/clean into-opts)) 119 | 120 | (def ^{:arglists '([opts])} init 121 | (comp template/init into-opts)) 122 | 123 | (def ^{:arglists '([opts])} info 124 | (comp api/info into-opts)) 125 | 126 | (def ^{:arglists '([opts])} install 127 | (comp deploy/local into-opts)) 128 | 129 | (def ^{:arglists '([opts])} deploy 130 | (comp deploy/remote into-opts)) 131 | 132 | (defn task 133 | [opts] 134 | (-> opts 135 | into-opts 136 | (tasks/task opts))) 137 | 138 | (defn release 139 | [opts] 140 | (-> opts 141 | into-opts 142 | (assoc :id :release/single) 143 | (tasks/task opts))) 144 | 145 | (defn release+tag 146 | [opts] 147 | (-> opts 148 | into-opts 149 | (assoc :id :release+tag/single) 150 | (tasks/task opts))) 151 | 152 | (def ^{:arglists '([opts])} version-bump-and-snapshot 153 | (comp v/bump-and-snapshot into-opts)) 154 | 155 | (def ^{:arglists '([opts])} version-remove-snapshot 156 | (comp v/remove-snapshot into-opts)) 157 | 158 | (def ^{:arglists '([opts])} git-commit-version 159 | (comp git/commit-version into-opts)) 160 | 161 | (def ^{:arglists '([opts])} git-tag-version 162 | (comp git/tag-version into-opts)) 163 | 164 | (def ^{:arglists '([opts])} git-push 165 | (comp git/push into-opts)) 166 | 167 | (def ^{:arglists '([opts])} format-check 168 | (comp api/format-check into-opts)) 169 | 170 | (def ^{:arglists '([opts])} format-fix 171 | (comp api/format-fix into-opts)) 172 | 173 | (def ^{:arglists '([opts])} lint 174 | (comp api/lint into-opts)) 175 | 176 | (defn merge-deps 177 | [opts] 178 | (doto (into-opts opts) 179 | (deps-modules/merge-deps))) 180 | 181 | (defn merge-aliases 182 | [opts] 183 | (doto (into-opts opts) 184 | (deps-modules/merge-aliases))) 185 | 186 | (def ^{:arglists '([opts])} outdated 187 | (comp api/outdated into-opts)) 188 | 189 | (def ^{:arglists '([opts])} revision-sha 190 | (comp api/revision-sha into-opts)) 191 | 192 | (def ^{:arglists '([opts])} check 193 | (comp api/check api/revision-version api/revision-sha api/prep-self api/prep into-opts)) 194 | 195 | (def ^{:arglists '([opts])} uberjar 196 | (comp jar/uberjar api/revision-version api/revision-sha api/prep-self api/prep api/clean into-opts)) 197 | 198 | (def ^{:arglists '([opts])} test 199 | api/test) 200 | 201 | (def ^{:arglists '([opts])} version 202 | (comp api/version into-opts)) 203 | -------------------------------------------------------------------------------- /src/exoscale/tools/project/template.clj: -------------------------------------------------------------------------------- 1 | (ns exoscale.tools.project.template 2 | (:require [org.corfield.new :as dnew] 3 | [rewrite-clj.zip :as z])) 4 | 5 | (def data-fn 6 | (constantly nil)) 7 | 8 | (defn module-data-fn 9 | [data] 10 | (update data :target-dir (partial str "modules/"))) 11 | 12 | (defn template-fn 13 | [edn _] 14 | ;; must return the whole EDN hash map 15 | edn) 16 | 17 | (defn init 18 | [opts] 19 | (dnew/create {:name (:name opts) :template "exoscale/project"}) 20 | opts) 21 | 22 | (defn- add-module-to-deps 23 | [deps-file shortname] 24 | (spit 25 | deps-file 26 | (let [zloc (z/of-string (slurp deps-file))] 27 | (z/root-string 28 | (if-let [modules (z/get zloc :exoscale.project/modules)] 29 | (z/assoc zloc 30 | :exoscale.project/modules 31 | (-> modules 32 | z/sexpr 33 | vec 34 | (conj (str "modules/" shortname)))) 35 | (z/assoc zloc :exoscale.project/modules [(str "modules/" shortname)])))))) 36 | 37 | (defn add-module 38 | [opts] 39 | (let [shortname (name (symbol (:name opts)))] 40 | (dnew/create {:name (:name opts) :template "exoscale/module"}) 41 | (add-module-to-deps "deps.edn" shortname)) 42 | opts) 43 | --------------------------------------------------------------------------------