tag
tag copied to clipboard
tagging apps with immutable intel
tag
tagging apps with git, maven and other intel
- why
- how
-
show me
- look inside
- composing tags
- access intel
- yes, but why not in config?
- license
why
once the app is built it is an immutable artifact. this artifact usually has quite a limitted way to describe itself:
name-version.jar
is not exactly very telling.
tag
is run right before building an artifact to include additional useful intel about it.
by the time artifact is built it would have an about.edn
file that can be looked at by the app at runtime:

described-at
in this case would be a build time.
how
=> (require '[tag.core :as t])
tag
knows how to describe the app:
=> (t/describe "hubble" {:about "I explore new worlds"})
{:about {:app-name "hubble",
"what do I do?" "I explore new worlds"},
:git
{:commit-id "8b15218",
:version/tag "v0.1.42",
:repo-url "[email protected]:tolitius/hubble.git",
:commit-time "Wed May 6 20:26:03 2020 -0400",
"commit human (or not)" "'anatoly'",
:commit-message "readjust a low gain antena for the cat's eye nebula"},
:maven
{:group-id "tolitius",
:artifact-id "hubble",
:version "0.1.42",
:name "hubble",
:description "I explore new worlds",
:url "https://github.com/tolitius/hubble"}
:described-at #inst "2020-05-07T00:55:03.260-00:00"}
and how to export it:
=> (-> (t/describe "hubble")
(t/export-intel {:app-name "tolitius/hubble"}))
{:intel-exported-to "target/about/META-INF/tolitius/hubble/about.edn"}
once the jar is built the edn
above will (by default) live inside the jar in META-INF/[app-name]/about.edn
.
so for example if your, say http, app has an /about
endpoint, it could read this file on start or at runtime and display the immutable intel above.
show me
tag
uses itself to include the intel in itself right before it is built. so let's look at what it does.
it has an alias in deps.edn:
:tag {:main-opts ["-m" "tag.core" "tolitius/tag" "I tag apps with immutable intel"]}
which calls out to tag
and exports the intel.
it also has a :jar
alias with an addition of an :extra-paths
:
:extra-paths ["target/about"]
tag
will place "about.edn
" to target/about/META-INF
in order for a jar
task to pick it up during the build.
hence in order to tag and build we can do:
$ clj -A:tag
$ clj -A:jar
look inside
let's look at what inside this newly built tag.jar
:
$ jar -tvf tag.jar
0 Thu May 07 00:17:10 EDT 2020 META-INF/
0 Thu May 07 00:17:10 EDT 2020 META-INF/tolitius/tag/
321 Thu May 07 00:17:06 EDT 2020 META-INF/tolitius/tag/about.edn ## <<< oh.. look who is here
0 Thu May 07 00:17:10 EDT 2020 tag/
1769 Thu May 07 00:16:58 EDT 2020 tag/core.clj
58 Thu May 07 00:17:10 EDT 2020 META-INF/MANIFEST.MF
0 Thu May 07 00:17:10 EDT 2020 META-INF/maven/
0 Thu May 07 00:17:10 EDT 2020 META-INF/maven/tolitius/
0 Thu May 07 00:17:10 EDT 2020 META-INF/maven/tolitius/tag/
110 Thu May 07 00:17:10 EDT 2020 META-INF/maven/tolitius/tag/pom.properties
1711 Thu May 07 00:17:10 EDT 2020 META-INF/maven/tolitius/tag/pom.xml
$ jar -xvf tag.jar META-INF/tolitius/tag/about.edn
inflated: META-INF/tolitius/tag/about.edn
$ cat META-INF/tolitius/tag/about.edn
{:about {:app-name "tolitius/tag", "what do I do?" "I tag apps with immutable intel"}, :git {:commit-id "58df09d", :version/tag "v0.1.0", :repo-url "[email protected]:tolitius/tag.git", :commit-time "Wed May 6 23:41:33 2020 -0400", "commit human (or not)" "'Anatoly'", :commit-message "[docs]: add lein :prep-tasks example"}, :described-at #inst "2020-05-07T04:17:06.081-00:00"}
great success.
this way tag
can also be used with lein:
:dependencies [;; ...
[tolitius/tag "x.x.x" :scope "provided"]]
:prep-tasks [["run" "-m" "tag.core/-main"
"tolitius/hubble" "I explore new worlds"]
["compile"]]
:jar {:resource-paths ["target/about"] ;; ... }
;; or and uberjar:
:uberjar {:resource-paths ["target/about"] ;; ... }
boot, make, and other build tools.
composing tags
one intersing side effect of tagging libs or dependencies with tag
is that the final app jar / uberjar has them all:
$ jar -tvf target/hubble-standalone.jar | grep about
369 Thu May 07 01:18:32 EDT 2020 META-INF/tolitius/hubble/about.edn
331 Thu May 07 01:06:21 EDT 2020 META-INF/tolitius/tag/about.edn
301 Thu May 07 01:02:25 EDT 2020 META-INF/tolitius/mount/about.edn
395 Thu May 07 01:01:42 EDT 2020 META-INF/tolitius/cprop/about.edn
384 Thu May 07 01:01:57 EDT 2020 META-INF/tolitius/lasync/about.edn
...
hence building something like /about/[dependency-name]
app endponts is straightforward and allows to have reliable intel about not just the app itself but its dependencies as well.
access intel
a likely http route (in this case reitit) would look like this:
["/about"
{:get (constantly
{:status 200
:body (tools/edn-resource "META-INF/tolitius/hubble/about.edn")})}]]
tools/edn-resource
might be something like this:
(ns app.tools
(:require [clojure.java.io :as io]
[clojure.edn :as edn]))
(defn slurp-resource [path]
(-> path
io/resource
slurp))
(defn edn-resource [path]
(-> path
slurp-resource
edn/read-string))
yes, but why not in config?
configuration is usually overriden by system props, ENV vars, consul, etcd, etc.
the idea behind tag
is to make sure the built artifact "describes itself consistently", or constantly as we say in Clojure circles.
License
Copyright © 2020 tolitius
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.