{"id":19345920,"url":"https://github.com/tolitius/tag","last_synced_at":"2025-04-23T04:36:36.814Z","repository":{"id":62434940,"uuid":"261910158","full_name":"tolitius/tag","owner":"tolitius","description":"tagging apps with immutable intel","archived":false,"fork":false,"pushed_at":"2025-03-21T01:12:01.000Z","size":591,"stargazers_count":24,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-02T08:22:32.513Z","etag":null,"topics":["build","git","maven"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tolitius.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-07T00:24:12.000Z","updated_at":"2025-03-21T01:12:06.000Z","dependencies_parsed_at":"2022-11-01T21:02:31.132Z","dependency_job_id":null,"html_url":"https://github.com/tolitius/tag","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Ftag","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Ftag/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Ftag/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Ftag/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tolitius","download_url":"https://codeload.github.com/tolitius/tag/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250372495,"owners_count":21419719,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build","git","maven"],"created_at":"2024-11-10T04:08:21.998Z","updated_at":"2025-04-23T04:36:36.147Z","avatar_url":"https://github.com/tolitius.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"## tag\n\ntagging apps with git, maven and other intel\n\n[![\u003c! release](https://img.shields.io/badge/dynamic/json.svg?label=release\u0026url=https%3A%2F%2Fclojars.org%2Ftolitius%2Ftag%2Flatest-version.json\u0026query=version\u0026colorB=blue)](https://github.com/tolitius/tag/releases)\n[![\u003c! clojars](https://img.shields.io/clojars/v/tolitius/tag.svg)](https://clojars.org/tolitius/tag)\n\n- [why](#why)\n- [how](#how)\n- [show me](#show-me)\n  - [look inside](#look-inside)\n- [composing tags](#composing-tags)\n- [access intel](#access-intel)\n- [yes, but why not in config?](#yes-but-why-not-in-config)\n- [license](#license)\n\n## why\n\nonce the app is built it is an immutable artifact. this artifact usually has quite a limitted way to describe itself:\n\n\u003e `name-version.jar` is not exactly very telling.\n\n`tag` is run right _before_ building an artifact to include additional useful intel about it.\n\nby the time artifact is built it would have an `about.edn` file that can be looked at by the app at runtime:\n\n\u003cimg src=\"doc/img/hubble-intel.png\" width=\"600px\"\u003e\n\n`described-at` in this case would be a build time.\n\n## how\n\n```clojure\n=\u003e (require '[tag.core :as t])\n```\n\n`tag` knows how to describe the app:\n\n```clojure\n=\u003e (t/describe \"hubble\" {:about \"I explore new worlds\"})\n\n{:about {:app-name \"hubble\",\n         \"what do I do?\" \"I explore new worlds\"},\n :git\n {:commit-id \"8b15218\",\n  :version/tag \"v0.1.42\",\n  :repo-url \"git@github.com:tolitius/hubble.git\",\n  :commit-time \"Wed May 6 20:26:03 2020 -0400\",\n  \"commit human (or not)\" \"'anatoly'\",\n  :commit-message \"readjust a low gain antena for the cat's eye nebula\"},\n  :maven\n {:group-id \"tolitius\",\n  :artifact-id \"hubble\",\n  :version \"0.1.42\",\n  :name \"hubble\",\n  :description \"I explore new worlds\",\n  :url \"https://github.com/tolitius/hubble\"}\n :described-at #inst \"2020-05-07T00:55:03.260-00:00\"}\n```\n\nand how to export it:\n\n```clojure\n=\u003e (-\u003e (t/describe \"hubble\")\n       (t/export-intel {:app-name \"tolitius/hubble\"}))\n\n{:intel-exported-to \"target/about/META-INF/tolitius/hubble/about.edn\"}\n```\n\nonce the jar is built the `edn` above will (by default) live _inside_ the jar in `META-INF/[app-name]/about.edn`.\n\nso 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.\n\n## show me\n\n`tag` _uses itself_ to include the intel in itself right before it is built. so let's look at what it does.\n\nit has [an alias](https://github.com/tolitius/tag/blob/master/deps.edn#L4) in deps.edn:\n\n```clojure\n:tag {:main-opts [\"-m\" \"tag.core\" \"tolitius/tag\" \"I tag apps with immutable intel\"]}\n```\n\nwhich calls out to `tag` and exports the intel.\n\nit also has a `:jar` alias with [an addition](https://github.com/tolitius/tag/blob/2bf572c5cb3fa95d1868ea2e0b2814670e21a648/deps.edn#L6) of an `:extra-paths`:\n\n```clojure\n:extra-paths [\"target/about\"]\n```\n\n`tag` will place \"`about.edn`\" to `target/about/META-INF` in order for a `jar` task to pick it up during the build.\n\nhence in order to tag and build we can do:\n\n```bash\n$ clj -A:tag\n$ clj -A:jar\n```\n\n### look inside\n\nlet's look at what inside this newly built `tag.jar`:\n\n```bash\n$ jar -tvf tag.jar\n     0 Thu May 07 00:17:10 EDT 2020 META-INF/\n     0 Thu May 07 00:17:10 EDT 2020 META-INF/tolitius/tag/\n   321 Thu May 07 00:17:06 EDT 2020 META-INF/tolitius/tag/about.edn     ## \u003c\u003c\u003c oh.. look who is here\n     0 Thu May 07 00:17:10 EDT 2020 tag/\n  1769 Thu May 07 00:16:58 EDT 2020 tag/core.clj\n    58 Thu May 07 00:17:10 EDT 2020 META-INF/MANIFEST.MF\n     0 Thu May 07 00:17:10 EDT 2020 META-INF/maven/\n     0 Thu May 07 00:17:10 EDT 2020 META-INF/maven/tolitius/\n     0 Thu May 07 00:17:10 EDT 2020 META-INF/maven/tolitius/tag/\n   110 Thu May 07 00:17:10 EDT 2020 META-INF/maven/tolitius/tag/pom.properties\n  1711 Thu May 07 00:17:10 EDT 2020 META-INF/maven/tolitius/tag/pom.xml\n```\n\n```bash\n$ jar -xvf tag.jar META-INF/tolitius/tag/about.edn\n inflated: META-INF/tolitius/tag/about.edn\n```\n\n```bash\n$ cat META-INF/tolitius/tag/about.edn\n\n{: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\"}\n```\n\ngreat success.\n\nthis way `tag` can also be used with lein:\n\n```clojure\n:dependencies [;; ...\n               [tolitius/tag \"x.x.x\" :scope \"provided\"]]\n```\n\n```clojure\n:prep-tasks [[\"run\" \"-m\" \"tag.core/-main\"\n                         \"tolitius/hubble\" \"I explore new worlds\"]\n             [\"compile\"]]\n\n\n:jar {:resource-paths [\"target/about\"] ;; ... }\n;; or and uberjar:\n:uberjar {:resource-paths [\"target/about\"] ;; ... }\n```\n\nboot, make, and other build tools.\n\n## composing tags\n\none intersing side effect of tagging libs or dependencies with `tag` is that the final app jar / uberjar has them all:\n\n```bash\n$ jar -tvf target/hubble-standalone.jar | grep about\n   369 Thu May 07 01:18:32 EDT 2020 META-INF/tolitius/hubble/about.edn\n   331 Thu May 07 01:06:21 EDT 2020 META-INF/tolitius/tag/about.edn\n   301 Thu May 07 01:02:25 EDT 2020 META-INF/tolitius/mount/about.edn\n   395 Thu May 07 01:01:42 EDT 2020 META-INF/tolitius/cprop/about.edn\n   384 Thu May 07 01:01:57 EDT 2020 META-INF/tolitius/lasync/about.edn\n   ...\n```\n\nhence 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.\n\n## access intel\n\na likely http route (in this case [reitit](https://github.com/metosin/reitit)) would look like this:\n\n```clojure\n[\"/about\"\n {:get (constantly\n         {:status 200\n          :body   (tools/edn-resource \"META-INF/tolitius/hubble/about.edn\")})}]]\n```\n\n`tools/edn-resource` might be something like this:\n\n```clojure\n(ns app.tools\n  (:require [clojure.java.io :as io]\n            [clojure.edn :as edn]))\n\n(defn slurp-resource [path]\n  (-\u003e path\n      io/resource\n      slurp))\n\n(defn edn-resource [path]\n  (-\u003e path\n      slurp-resource\n      edn/read-string))\n```\n\n## yes, but why not in config?\n\nconfiguration is usually overriden by system props, ENV vars, consul, etcd, etc.\n\nthe idea behind `tag` is to make sure the built artifact \"describes itself consistently\", or [constantly](https://clojuredocs.org/clojure.core/constantly) as we say in Clojure circles.\n\n## License\n\nCopyright © 2020 tolitius\n\nDistributed under the Eclipse Public License either version 1.0 or (at\nyour option) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftolitius%2Ftag","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftolitius%2Ftag","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftolitius%2Ftag/lists"}