Liking cljdoc? Tell your friends :D

tag

tagging apps with git, maven and other intel

<! release <! clojars

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 "git@github.com: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 "git@github.com: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.

Can you improve this documentation? These fine people already did:
Anatoly & anatoly
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close