In the previous tutorial we explained how to make a
library compliant with
Reader Conditionals. As
an example, we exploited the
valip
library that we already
used in the modern-cljs
project for validating the input fields of
the Login and Shopping forms we adopted as CLJ/CLJS playground.
Even if we reached a decent result, we left some work to be done, namely the deployment of the updated library to clojars, the notorious community repository for open source Clojure libraries.
Unless you completed the previous tutorial and finally committed
your work in the reader-conditionals
branch of the valip
project
you created while following the tutorial itself, to be able to work on
this new tutorial, you need to do as follows:
git clone https://github.com/magomimmo/valip.git
cd valip
git checkout se-tutorial-19
git checkout -b reader-conditionals
In this tutorial we're going to fill that gap. But first we have to
digress on a couple of topics we left uncovered about the boot
build
tool, because they constitute a prerequisite to the clojars
deployment itself, namely:
- POM (Project Object Model)
- Dependency Scope
To quickly finish the
previous tutorial,
we switched to leiningen
and locally installed the updated version
of valip
by using the lein install
task. This was because the
project.clj
build file used by leiningen
already had the
minimal information
needed to create and package the valip
library, while we had not
updated the corresponding build.boot
build file used by boot
with
the same information, namely:
-
the
groupId:
it has to follow the java package name rules; e.g.,org.clojars.magomimmo
; -
the
artifactId
: it is the name of thejar
file without version; e.g.,valip
; -
the
version
: it is suggested to follow the Semantic Version Specification; e.g., "0.4.0-SNAPSHOT"NOTE 1:
-SNAPSHOT
qualifies a version "as-yet-unreleased". Under the hood,maven
, on which bothleiningen
andboot
are based, will fetch the most recently deployed-SNAPSHOT
version. Even if this behavior slows down the build process, in a continuous integration scenario it guarantees up-to-date builds, while minimizing the amount of rebuilding that is required for each integration step.
In boot
, to create a project's POM, package it in a jar
and
finally install it in the local maven
repository on your machine,
you can chain the following three
built-in
boot
tasks:
pom
jar
install
Let's look at the pom
docstring first:
cd /path/to/valip
boot pom -h
Create project pom.xml file.
The project and version must be specified to make a pom.xml.
Options:
-h, --help Print this help info.
-p, --project SYM Set the project id (eg. foo/bar) to SYM.
-v, --version VER Set the project version to VER.
-d, --description DESC Set the project description to DESC.
-u, --url URL Set the project homepage url to URL.
-s, --scm KEY=VAL Conj [KEY VAL] onto the project scm map (KEY is one of url, tag, connection, developerConnection).
-l, --license NAME:URL Conj [NAME URL] onto the map {name url} of project licenses.
-o, --developers NAME:EMAIL Conj [NAME EMAIL] onto the map {name email} of project developers.
-D, --dependencies SYM:VER Conj [SYM VER] onto the project dependencies vector (overrides boot env dependencies).
As you see, to create a minimal pom.xml
file for a project you need
to specify at least the -p
and the -v
command line options. Before
testing the pom
task, let's first get rid of the stuff generated in
the
previous tutorial,
when we ran the lein install
command:
# delete files generated in the project directory
cd /path/to/valip
lein clean
rm -rf pom.xml
# delete the jar file from the local maven repository
rm -rf ~/.m2/repository/org/clojars/magomimmo/valip
Now launch the pom
task at the command line, by providing the minimal
information needed to generate the pom.xml
file:
boot pom -p org.clojars.magomimmo/valip -v 0.4.0-SNAPSHOT
Writing pom.xml and pom.properties...
NOTE 2: remember to substitute
magomimmo
with your github name asgroupId
Even if the command reported the writing of the pom.xml
and the
pom.properties
files, which is a misleading message to me, if you
take a look at the project directory you won't see them:
tree
.
├── README.md
├── boot.properties
├── build.boot
├── project.clj
├── src
│ └── valip
│ ├── core.cljc
│ ├── macros.clj
│ └── predicates.cljc
└── test
└── valip
└── test
├── core.cljc
└── predicates.cljc
NOTE 3: after I wrote the previous tutorial, I decided to move the
def.clj
file containing thevalip
macros to a more conventional place and rename it asmacros.clj
. You could do the same thing as a very simple exercise.
This is because we did not chain the target
task after the pom
one. Before the 2.5.5.
release, boot
did not have a target
task.
The default behavior was to automatically write to an implicit
target
directory, which is not the case anymore when you set, as we
did in the
previous tutorial,
the BOOT_EMIT_TARGET
environment to no
in the boot.properties
file of the valip
project.
In boot
, the local installation process of a project doesn't need to
create a target directory in your project directory to succeed. If you
want to see what is going on in the boot
's
fileset before and
after the execution of any boot
task, you can use the show
built-in task:
Let's view the docstring for the show
task:
boot show -h
Print project/build info (e.g. dependency graph, etc).
Options:
-h, --help Print this help info.
-C, --fake-classpath Print the project's fake classpath.
-c, --classpath Print the project's full classpath.
-d, --deps Print project dependency graph.
-e, --env Print the boot env map.
-f, --fileset Print the build fileset object.
-l, --list-pods Print the names of all active pods.
-p, --pedantic Print graph of dependency conflicts.
-P, --pods REGEX Set the name filter used to select which pods to inspect to REGEX.
-U, --update-snapshots Include snapshot versions in updates searches.
-u, --updates Print newer releases of outdated dependencies.
Wow, this is a very handy task indeed. Let's play on the
command line by chaining it before and after the pom
task as
follows:
boot show -f pom -p org.clojars.magomimmo/valip -v 0.4.0-SNAPSHOT show -f
└── valip
├── core.cljc
├── macros.clj
└── predicates.cljc
Writing pom.xml and pom.properties...
├── valip
│ ├── core.cljc
│ ├── macros.clj
│ └── predicates.cljc
└── META-INF
└── maven
└── org.clojars.magomimmo
└── valip
├── pom.properties
└── pom.xmlc
Initially, the fileset
includes the valip
source code files
only. Then, the pom
task adds the META-INF
directory containing
what later will be needed to package the project into a jar
file.
Let's now view the jar
docstring:
boot jar -h
Build a jar file for the project.
Options:
-h, --help Print this help info.
-f, --file PATH Set the target jar file name to PATH.
-M, --manifest KEY=VAL Conj [KEY VAL] onto the jar manifest map.
-m, --main MAIN Set the namespace containing the -main function to MAIN.
At the moment, we're not interested in any of its command line
options. The jar
's help does not tell you, but if you do not specify
a file name with the -f
option, the jar
task will
concatenate
the project's artifactId
and version
as
default name,
which is what we want.
Try it:
boot pom -p org.clojars.magomimmo/valip -v 0.4.0-SNAPSHOT jar show -f
Writing pom.xml and pom.properties...
Writing valip-0.4.0-SNAPSHOT.jar...
├── valip
│ ├── core.cljc
│ ├── macros.clj
│ └── predicates.cljc
├── META-INF
│ └── maven
│ └── org.clojars.magomimmo
│ └── valip
│ ├── pom.properties
│ └── pom.xml
└── valip-0.4.0-SNAPSHOT.jar
Do you see the valip-0.4.0-SNAPSHOT.jar
file shown by the show -f
task?
We are almost done. install
is the next built-in task to be chained
for installing the valip
library in the maven
local
repository. Let's view its docstring:
boot install -h
Install project jar to local Maven repository.
The --file option allows installation of arbitrary jar files. If no
file option is given then any jar artifacts created during the build
will be installed.
The pom.xml file that's required when installing a jar can usually be
found in the jar itself. However, sometimes a jar might contain more
than one pom.xml file or may not contain one at all.
The --pom option can be used in these situations to specify which
pom.xml file to use. The optarg denotes either the path to a pom.xml
file in the filesystem or a subdir of the META-INF/maven/ dir in which
the pom.xml contained in the jar resides.
Example:
Given a jar file (warp-0.1.0.jar) with the following contents:
.
├── META-INF
│ ├── MANIFEST.MF
│ └── maven
│ └── tailrecursion
│ └── warp
│ ├── pom.properties
│ └── pom.xml
└── tailrecursion
└── warp.clj
The jar could be installed with the following boot command:
$ boot install -f warp-0.1.0.jar -p tailrecursion/warp
Options:
-h, --help Print this help info.
-f, --file PATH Set the jar file to install to PATH.
-p, --pom PATH Set the pom.xml file to use to PATH.
This help is mostly about corner cases which are not in our
scenario. We'll stay with the default behavior, without passing any
options to the install
task:
boot pom -p org.clojars.<your_github_name>/valip -v 0.4.0-SNAPSHOT jar install
Writing pom.xml and pom.properties...
Writing valip-0.4.0-SNAPSHOT.jar...
Installing valip-0.4.0-SNAPSHOT.jar...
Now take a look at your local maven
repository:
tree ~/.m2/repository/org/clojars/<your_github_name>/valip
├── 0.4.0-SNAPSHOT
│ ├── _maven.repositories
│ ├── maven-metadata-local.xml
│ ├── valip-0.4.0-SNAPSHOT.jar
│ └── valip-0.4.0-SNAPSHOT.pom
└── maven-metadata-local.xml
It is worth nothing that the valip
directory is still clean, because
we did not chain any target
task:
tree
.
├── README.md
├── boot.properties
├── build.boot
├── project.clj
├── src
│ └── valip
│ ├── core.cljc
│ ├── macros.clj
│ └── predicates.cljc
└── test
└── valip
└── test
├── core.cljc
└── predicates.cljc
The previous paragraphs explained the local installation of valip
step by step, but it is not something you'd like to repeat again and
again after any project change.
From a build tool such as boot
you would expect something like the lein install
command we used in the previous tutorial: enter
task-options!
.
task-options!
allows us to add any task option to the build.boot
build file. If you're only using built-in tasks, you can place it
just after the set-env!
form. If you are using other tasks, you'll
place task-options!
after the requirement form. Following is the
complete updated version of the build.boot
build file for the
valip
project:
(set-env!
:source-paths #{"src"}
:dependencies '[[org.clojure/clojure "1.8.0"]
[adzerk/boot-test "1.2.0"]
[org.clojure/clojurescript "1.9.494"]
[adzerk/boot-cljs "1.7.228-2"]
[crisptrutski/boot-cljs-test "0.3.0"]
[doo "0.1.7"]]
)
(require '[adzerk.boot-test :refer [test]]
'[adzerk.boot-cljs :refer [cljs]]
'[crisptrutski.boot-cljs-test :refer [test-cljs]])
(task-options!
pom {:project 'org.clojars.magomimmo/valip
:version "0.4.0-SNAPSHOT"
:description "Functional validation library for Clojure and ClojureScript.
Forked from https://github.com/cemerick/valip"
:url "http://github.com/magomimmo/valip"
:scm {:url "http://github.com/magomimmo/valip"}
:license {"Eclipse Public License" "http://www.eclipse.org/legal/epl-v10.html"}}
test {:namespaces #{'valip.test.core 'valip.test.predicates}}
test-cljs {:namespaces #{'valip.test.core 'valip.test.predicates}})
(deftask testing
[]
(merge-env! :source-paths #{"test"})
identity)
(deftask tdd
"Launch a CLJ TDD Environment"
[]
(comp
(testing)
(watch)
(test-cljs)
(test)))
NOTE 4: we moved the
test
and thetest-cljs
option arguments into thetask-options!
form as well.
NOTE 5: we enriched the
pom
task with more information, including optional fields like:description
,:url
,:scm
and:license
that are not part of a minimal POM required for the project to be packaged and installed.
We can now run the tdd
task to verify that everything is still
working:
boot tdd
Compiling ClojureScript...
• cljs_test/generated_test_suite.js
;; ======================================================================
;; Testing with Phantom:
Testing valip.test.core
Testing valip.test.predicates
Ran 20 tests containing 86 assertions.
0 failures, 0 errors.
Testing valip.test.core
Testing valip.test.predicates
Ran 21 tests containing 90 assertions.
0 failures, 0 errors.
Elapsed time: 15.959 sec
It worked. Now stop the running boot
process and try to reinstall
valip
in the local maven
repository by chaining the three cited
tasks as follows:
boot pom jar install
Writing pom.xml and pom.properties...
Writing valip-0.4.0-SNAPSHOT.jar...
Installing valip-0.4.0-SNAPSHOT.jar...
Verify that the installation process succeeded:
tree ~/.m2/repository/org/clojars/<your_github_name/valip/
├── 0.4.0-SNAPSHOT
│ ├── _maven.repositories
│ ├── maven-metadata-local.xml
│ ├── valip-0.4.0-SNAPSHOT.jar
│ └── valip-0.4.0-SNAPSHOT.pom
└── maven-metadata-local.xml
1 directory, 5 files
Nice job, but if you now think we're done, you're wrong! At least,
we have to test the local installation of the valip
library in the
context of a project, as we already did in the
previous tutorial.
First, if not already done, clone the modern-cljs
project in a
temporary directory and checkout the se-tutorial-18
branch as
follows:
cd /path/to/tmp
git clone https://github.com/magomimmo/modern-cljs.git
cd modern-cljs
git checkout se-tutorial-18
Then edit its build.boot
file by substituting the valip
dependency
with the newly updated one:
(set-env!
...
:dependencies '[
...
[org.clojars.<your_github_name>/valip "0.4.0-SNAPSHOT"]
...
])
Next, start the TDD environment as usual:
boot tdd
Starting reload server on ws://localhost:60606
Writing boot_reload.cljs...
Writing boot_cljs_repl.cljs...
2016-01-10 14:12:42.402:INFO::clojure-agent-send-off-pool-0: Logging initialized @10813ms
clojure.lang.ExceptionInfo: java.io.FileNotFoundException: Could not locate valip/core__init.class or valip/core.clj on classpath., compiling:(modern_cljs/login/validators.cljc:1:1)
data: {:file
"/var/folders/17/1jg3ghkx73q4jtgw4z500www0000gp/T/boot.user2098590056263435211.clj",
:line 31}
java.util.concurrent.ExecutionException: java.io.FileNotFoundException: Could not locate valip/core__init.class or valip/core.clj on classpath., compiling:(modern_cljs/login/validators.cljc:1:1)
clojure.lang.Compiler$CompilerException: java.io.FileNotFoundException: Could not locate valip/core__init.class or valip/core.clj on classpath., compiling:(modern_cljs/login/validators.cljc:1:1)
java.io.FileNotFoundException: Could not locate valip/core__init.class or valip/core.clj on classpath.
...
Elapsed time: 26.573 sec
Argh. This is not so lovely! What's is happening here? It seems that
the boot tdd
task is not able to find the source files of the
valip
library in the classpath
.
Previously, we should have taken a look at the content of the
generated jar
file installed by boot
in the local repository,
instead of quickly proceeding with the valip
installation:
jar -tvf ~/.m2/repository/org/clojars/magomimmo/valip/0.4.0-SNAPSHOT/valip-0.4.0-SNAPSHOT.jar
0 Mon Mar 06 18:01:43 CET 2017 META-INF/
0 Mon Mar 06 18:01:43 CET 2017 META-INF/maven/
0 Mon Mar 06 18:01:43 CET 2017 META-INF/maven/org.clojars.magomimmo/
0 Mon Mar 06 18:01:43 CET 2017 META-INF/maven/org.clojars.magomimmo/valip/
2136 Mon Mar 06 18:01:43 CET 2017 META-INF/maven/org.clojars.magomimmo/valip/pom.xml
158 Mon Mar 06 18:01:43 CET 2017 META-INF/maven/org.clojars.magomimmo/valip/pom.properties
25 Mon Mar 06 18:01:43 CET 2017 META-INF/MANIFEST.MF
That's very bad. The locally installed jar
package for the valip
library does not contain any valip
source files. How this could
happen? The answer, my friend, is blowin' in the wind, namely in the
boot
wiki:
:resource-paths
: A set of path strings. These paths will be on the classpath and the files contained will be emitted as final artifacts;:source-paths
: A set of path strings. These paths will be on the classpath but the files contained will not be emitted as final artifacts.
Ahah! Even if the :source-paths
set of directories will be on the
classpath
of the project, as we already knew from the fact that
valip
was able to be compiled and tested, the contained source files
would never be emitted in the final artifacts, as we just discovered
by listing the content of the generated jar
file.
On the contrary, the files contained in the set of directories of the
:resource-paths
would be emitted in the final jar
file.
Now, go back to valip
's build.boot
build file. By just setting
the :resource-paths
environment variable with the same #{"src"}
value of the :source-paths
one, we should be able to solve the
problem of including valip
's source files into the jar
. While
we are at it, let's define a new install-jar
task, which first sets
the :resource-paths
environment variable and then composes the
pom
, jar
and install
built-in tasks:
;; append at the end of `build.boot`
(deftask install-jar
[]
(merge-env! :resource-paths #{"src"})
(comp
(pom)
(jar)
(install)))
Let's see if it works:
cd /path/to/valip
boot install-jar
Writing pom.xml and pom.properties...
Writing valip-0.4.0-SNAPSHOT.jar...
Installing valip-0.4.0-SNAPSHOT.jar...
Now list the content of the generated jar
package file again
jar -tvf ~/.m2/repository/org/clojars/magomimmo/valip/0.4.0-SNAPSHOT/valip-0.4.0-SNAPSHOT.jar
0 Mon Mar 06 18:09:25 CET 2017 META-INF/
0 Mon Mar 06 18:09:25 CET 2017 META-INF/maven/
0 Mon Mar 06 18:09:25 CET 2017 META-INF/maven/org.clojars.magomimmo/
0 Mon Mar 06 18:09:25 CET 2017 META-INF/maven/org.clojars.magomimmo/valip/
2136 Mon Mar 06 18:09:25 CET 2017 META-INF/maven/org.clojars.magomimmo/valip/pom.xml
158 Mon Mar 06 18:09:25 CET 2017 META-INF/maven/org.clojars.magomimmo/valip/pom.properties
0 Mon Mar 06 18:09:25 CET 2017 valip/
4961 Mon Mar 06 16:47:54 CET 2017 valip/predicates.cljc
1027 Mon Mar 06 16:47:31 CET 2017 valip/macros.clj
995 Tue Feb 28 18:17:17 CET 2017 valip/core.cljc
25 Mon Mar 06 18:09:25 CET 2017 META-INF/MANIFEST.MF
That's much better. Run the modern-cljs
again project to see the
result:
cd /path/to/modern-cljs
boot tdd
Starting reload server on ws://localhost:46523
Writing boot_cljs_repl.cljs...
2017-03-06 18:15:26.943:INFO::clojure-agent-send-off-pool-0: Logging initialized @21591ms
2017-03-06 18:15:27.073:INFO:oejs.Server:clojure-agent-send-off-pool-0: jetty-9.2.10.v20150310
2017-03-06 18:15:27.137:INFO:oejs.ServerConnector:clojure-agent-send-off-pool-0: Started ServerConnector@2a6e421a{HTTP/1.
1}{0.0.0.0:3000}
2017-03-06 18:15:27.138:INFO:oejs.Server:clojure-agent-send-off-pool-0: Started @21786ms
Started Jetty on http://localhost:3000
Starting file watcher (CTRL-C to quit)...
Writing adzerk/boot_reload.cljs to connect to ws://localhost:46523...
nREPL server started on port 38395 on host 127.0.0.1 - nrepl://127.0.0.1:38395
Writing clj_test/suite.cljs...
Writing main.cljs.edn...
Compiling ClojureScript...
WARNING: Replacing ClojureScript compiler option :main with automatically set value.
• main.js
Running cljs tests...
Testing modern-cljs.login.validators-test
Testing modern-cljs.shopping.validators-test
Ran 3 tests containing 55 assertions.
0 failures, 0 errors.
Testing modern-cljs.login.validators-test
Testing modern-cljs.shopping.validators-test
Ran 4 tests containing 56 assertions.
0 failures, 0 errors.
Writing target dir(s)...
Elapsed time: 34.338 sec
Now we are talking. Just to be sure that everything is still working as at the end of the Tutorial 18, visit the Shopping Calculator URL to play with the Shopping Calculator.
You can now stop the boot
process and go back again to the valip
project directory, because there is something new to learn before
deploying the valip
library to clojars
and making it available to
anybody else.
When we made the valip
library compliant with the Reader
Conditionals extension, we changed its version identifier from
"0.3.2"
to "0.4.0-SNAPSHOT"
. The reason why there are so many
0.x.y
versioned libraries around, it's because until their APIs get
stable, they never get versioned with a major version number. In the
mean time, as dictated by the
The Semantic Version Specification, anything
could change. By considering that a lot of open source libraries stay
as unstable for a long time, their minor version number is generally
treated as major version number, meaning that minor version number
increments do not guarantee any backward compatibility. This is why we
incremented the minor version only, and not the major version as well,
even if our valip
version is not backward compatible with the
0.3.2
Let's first summarize what we already did with valip
:
- we forked and cloned the
valip
library by Chas Emerick; - we added the remote upstream repo;
- we created the
reader-conditionals
branch; - we modified the leiningen
project.clj
build file to update the CLJ dependency from1.4.0
to1.8.0
; - we made substantial changes to the
valip
source and test files to make it compatible with the Reader Conditionals extension of CLJ/CLJS compilers and to introduce few corner cases tests; - we incremented the minor-version only of the library, because, even if
it would break any preexisting use of the
valip
library, its major version is still0
; - we qualified the new minor version as SNAPSHOT, because it's as-yet-unreleased;
- we bootified
valip
by creating the correspondingbuild.boot
build file and theboot.properties
file. - we tested the
valip
library in the context of themodern-cljs
project by installing it into the local maven repository.
In a real world scenario, the next step would be to submit a pull
request to the upstream valip
repository, and wait until the owner
of the repo would eventually accept and merge our pull request.
But what if the owner is lazy or for any reason she/he does not agree to merge our pull requests?
Currently, the modified valip
library lives on your computer only,
and it can't be directly shared with other developers or even with
other computers.
You have a couple of options:
- you can publish the updated library to clojars public repository. This way the library will be available to everybody;
- you can publish a lib on a private repository. This way the
accessibility to the updated
valip
library is governed by the rules defined in the repository itself. Generally, this is the right choice when you want to make a library available to other devs without making it public.
In the next part of this tutorial we're going to inspect the first option only.
The process of publishing a CLJ/CLJS lib to clojars is pretty simple. As already said, keep in mind that any release ending in "-SNAPSHOT" is not an official release and you should rely on them only when you really need (which it is not our fictional scenario). Also remember that by adding a SNAPSHOT dependency to your project, you will cause any build tool to slow down its dependencies search.
Clojars offers two repositories, Classic and Releases. The Classic repository, which is the one we're going to use, has no restrictions and anyone may publish a lib to it.
That said, if you want to push your own version of somebody else's
library, which is our case, do not use the original groupId
: use
your personal groupId
, e.g., org.clojars.<your_clojars_name>
.
To publish a library to clojars's Classic Repository, you first need
to register with it and you're almost
ready. Actually, there is another very handy boot
task to be used:
bootlaces
.
The bootlaces
task is aimed at simplifying the typical workflow of
publishing a library to clojars.
Open the build.boot
file to add the bootlaces
task and to require
its main namespace as well:
(set-env!
:source-paths #{"src"}
:dependencies '[...
[adzerk/bootlaces "0.1.13"]])
(require '...
'[adzerk.bootlaces :refer [bootlaces! build-jar push-snapshot]])
Now edit the valip
's build.boot
file as suggested by bootlaces
's
README.md file
(set-env! ...)
(require ...)
(def +version+ "0.4.0-SNAPSHOT")
(bootlaces! +version+)
and you're ready to go:
boot build-jar push-snapshot
Writing pom.xml and pom.properties...
Writing valip-0.4.0-SNAPSHOT.jar...
Installing valip-0.4.0-SNAPSHOT.jar...
CLOJARS_USER and CLOJARS_PASS were not set; please enter your Clojars credentials.
Username:
Password:
clojure.lang.ExceptionInfo: java.lang.AssertionError: Assert failed: current git branch is reader-conditionals but must be master
(or (not ensure-branch) (= b ensure-branch))
data: {:file
"/var/folders/8z/yj2xnrdj0hb5mswc1kfyjmw00000gn/T/boot.user8233124078905763746.clj",
:line 33}
java.util.concurrent.ExecutionException: java.lang.AssertionError: Assert failed: current git branch is reader-conditionals but must be master
(or (not ensure-branch) (= b ensure-branch))
java.lang.AssertionError: Assert failed: current git branch is reader-conditionals but must be master
(or (not ensure-branch) (= b ensure-branch))
boot.task.built-in/fn/fn/fn/fn built_in.clj: 782
adzerk.bootlaces/eval715/fn/fn/fn bootlaces.clj: 54
adzerk.bootlaces/eval754/fn/fn/fn bootlaces.clj: 62
boot.task.built-in/fn/fn/fn/fn built_in.clj: 716
boot.task.built-in/fn/fn/fn/fn built_in.clj: 618
boot.task.built-in/fn/fn/fn/fn built_in.clj: 342
boot.core/run-tasks core.clj: 794
boot.core/boot/fn core.clj: 804
clojure.core/binding-conveyor-fn/fn core.clj: 1916
...
Uhm, not such a nice shot. After having required your clojars
credentials, boot
complained about the fact that your current branch
is not the master. As default, bootlaces
assumes that you only
publish a snapshot release from a master branch, but you can overwrite
the default with task-options!
. Indeed, the push-snapshot
task
internally uses the built-in push
task:
(deftask push-snapshot
"Deploy snapshot version to Clojars."
[f file PATH str "The jar file to deploy."]
(comp (collect-clojars-credentials)
(push :file file :ensure-snapshot true)))
Now take a look at the push
docstring and pay
attention on the --ensure-*
options:
boot push -h
Deploy jar file to a Maven repository.
...
Options:
...
-B, --ensure-branch BRANCH Set the required current git branch to BRANCH.
-C, --ensure-clean Ensure that the project git repo is clean.
-R, --ensure-release Ensure that the current version is not a snapshot.
-S, --ensure-snapshot Ensure that the current version is a snapshot.
-T, --ensure-tag TAG Set the SHA1 of the commit the pom's scm tag must contain to TAG.
-V, --ensure-version VER Set the version the jar's pom must contain to VER.
Interesting, we can easily change the push
task's behavior by just
setting the :ensure-branch
options to nil
in the task-options!
section we already used previosly to configure the pom
, test
and
test-cljs
tasks:
(task-options!
push {:ensure-branch nil}
pom {...}
test {...}
test-cljs {...})
Shoot again:
boot build-jar push-snapshot
Writing pom.xml and pom.properties...
Writing valip-0.4.0-SNAPSHOT.jar...
Installing valip-0.4.0-SNAPSHOT.jar...
CLOJARS_USER and CLOJARS_PASS were not set; please enter your Clojars credentials.
Username:
Password:
clojure.lang.ExceptionInfo: java.lang.AssertionError: Assert failed: project repo is not clean
(or (not ensure-clean) clean?)
...
This time, the push-snapshot
task complains about the fact that the git
branch is not clean. This is something that we should like, because
generally speaking you're not publishing something that is still to be
committed, right? So, let's be nice to ourselves and commit the work
we have done so far
cd /path/to/valip
git commit -am "prepare for publish to clojar"
and shoot the snapshot again:
boot build-jar push-snapshot
Writing pom.xml and pom.properties...
Writing valip-0.4.0-SNAPSHOT.jar...
Installing valip-0.4.0-SNAPSHOT.jar...
CLOJARS_USER and CLOJARS_PASS were not set; please enter your Clojars credentials.
Username:
Password:
Deploying valip-0.4.0-SNAPSHOT.jar...
Even if we already checked the Reader Conditionals compliant valip
library within the modern-cljs
context, we want to be sure
modern-cljs
is still working with the new snaphost release of
valip
by downloading it from clojars
, instead of using the one we
installed in the local maven
repository of our machine. The
accomplishment of this assignment is very easy.
First, delete the installed valip
from the local maven
repository
rm -rf ~/.m2/repository/org/clojars/<your_github_name/valip
then re-run the tdd
task from the modern-cljs
project home
directory:
cd /path/to/modern-cljs
boot tdd
Retrieving valip-0.4.0-20160111.164819-6.jar from https://clojars.org/repo/
...
Elapsed time: 36.476 sec
Boom! Did you note the Retrieving valip-....
notification? Now,
interactively test the usual
Shopping Calculator to verify
that everything is still working. Then stop the boot
process, but if
you think you're done, you're wrong again!
The very last topic of this tutorial has to do with dependency
management. boot
, being based on maven
, uses the same semantics as
maven
when dealing with dependency scope. Take into account that
the dependency scope controls the dependency transitivity as well. In
maven
there are 6 scopes available:
- compile
- provided
- runtime
- test
- system
- import
I have to admit that I never saw more than a couple of them, namely
"test" and "provided", in the context of boot
build files and even
less with leiningen
, which offers
profiles
for such a thing.
NOTE 6: when you don't specify a
:scope
,maven
assumes"compile"
The test scope indicates that a dependency is only required for the compilation and the test phases of the library itself and it's not required to consume the library from another application.
Let's illustrate this concept within the valip
library by
analyzing its dependencies starting from the ones characterized by a
very clear role. All the boot
tasks play a role in the building,
testing and publishing of a library, but they are not consumed by an
application using the library itself. We can safely say that all of
them should have the :scope
set to "test"
But what about Clojure and ClojureScript compilers? Obviously, we need
them to compile valip
. Any application consuming the valip
source
code needs to compile it as well, but it will provide those compilers by
itself. We can safely say that both the CLJ and the CLJS compilers should have the :scope
set to "provided"
.
Here is the entire build.boot
build file complete with dependencies scoping.
(set-env!
:source-paths #{"src"}
:dependencies '[[org.clojure/clojure "1.8.0" :scope "provided"]
[adzerk/boot-test "1.2.0" :scope "test"]
[org.clojure/clojurescript "1.9.494" :scope "provided"]
[adzerk/boot-cljs "1.7.228-2" :scope "test"]
[crisptrutski/boot-cljs-test "0.3.0" :scope "test"]
[doo "0.1.7" :scope "test"]
[adzerk/bootlaces "0.1.13" :scope "test"]])
(require '[adzerk.boot-test :refer [test]]
'[adzerk.boot-cljs :refer [cljs]]
'[crisptrutski.boot-cljs-test :refer [test-cljs]]
'[adzerk.bootlaces :refer [bootlaces! build-jar push-snapshot]])
(def +version+ "0.4.0-SNAPSHOT")
(bootlaces! +version+)
(task-options!
push {:ensure-branch nil}
pom {:project 'org.clojars.magomimmo/valip
:version +version+
:description "Functional validation library for Clojure and ClojureScript.
Forked from https://github.com/cemerick/valip"
:url "http://github.com/magomimmo/valip"
:scm {:url "http://github.com/magomimmo/valip"}
:license {"Eclipse Public License" "http://www.eclipse.org/legal/epl-v10.html"}}
test {:namespaces #{'valip.test.core 'valip.test.predicates}}
test-cljs {:namespaces #{'valip.test.core 'valip.test.predicates}})
(deftask testing
[]
(merge-env! :source-paths #{"test"})
identity)
(deftask tdd
"Launch a CLJ TDD Environment"
[]
(comp
(testing)
(watch)
(test-cljs)
(test)))
(deftask install-jar
[]
(merge-env! :resource-paths #{"src"})
(comp
(pom)
(jar)
(install)))
You can now re-publish valip
to clojars
boot build-jar push-snapshot
Writing pom.xml and pom.properties...
Writing valip-0.4.0-SNAPSHOT.jar...
Installing valip-0.4.0-SNAPSHOT.jar...
CLOJARS_USER and CLOJARS_PASS were not set; please enter your Clojars credentials.
Username:
Password:
Deploying valip-0.4.0-SNAPSHOT.jar...
That's all folks.
The next tutorial will begin guiding you through porting the Official React tutorial to Reagent step by step.
Copyright © Mimmo Cosenza, 2012-15. Released under the Eclipse Public License, the same as Clojure.