{"id":30690265,"url":"https://github.com/jgrodziski/metav","last_synced_at":"2025-09-02T02:10:56.208Z","repository":{"id":59683170,"uuid":"165516154","full_name":"jgrodziski/metav","owner":"jgrodziski","description":"Release and Versioning of Clojure projects using tools.deps","archived":false,"fork":false,"pushed_at":"2022-09-22T14:06:01.000Z","size":368,"stargazers_count":68,"open_issues_count":11,"forks_count":12,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-08-09T10:54:09.644Z","etag":null,"topics":["artefacts","build-tool","clojure","deps","monorepo","monorepos","release","scm","tagging","tools-deps","version"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jgrodziski.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-13T14:39:03.000Z","updated_at":"2024-06-03T12:00:27.000Z","dependencies_parsed_at":"2022-09-19T14:42:02.872Z","dependency_job_id":null,"html_url":"https://github.com/jgrodziski/metav","commit_stats":null,"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/jgrodziski/metav","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgrodziski%2Fmetav","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgrodziski%2Fmetav/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgrodziski%2Fmetav/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgrodziski%2Fmetav/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jgrodziski","download_url":"https://codeload.github.com/jgrodziski/metav/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgrodziski%2Fmetav/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273218437,"owners_count":25065915,"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","status":"online","status_checked_at":"2025-09-02T02:00:09.530Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["artefacts","build-tool","clojure","deps","monorepo","monorepos","release","scm","tagging","tools-deps","version"],"created_at":"2025-09-02T02:10:50.515Z","updated_at":"2025-09-02T02:10:56.200Z","avatar_url":"https://github.com/jgrodziski.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Metav\n\n\u003e “The assumption of an absolute determinism is the essential foundation of every scientific enquiry.” \n\u003e ― _Max Planck_\n\nMetav is a library that cleanly version Clojure projects based on git tags and helps the release process, particularly the one using [tools.deps](https://github.com/clojure/tools.deps.alpha) and a _Monorepo_ style (see [Rationale](#rationale)).\n\n[![Clojars Project](https://img.shields.io/clojars/v/metav.svg)](https://clojars.org/metav)\n[![cljdoc badge](https://cljdoc.org/badge/metav/metav)](https://cljdoc.org/d/metav/metav/CURRENT)\n[![CircleCI](https://circleci.com/gh/jgrodziski/metav/tree/master.svg?style=svg)](https://circleci.com/gh/jgrodziski/metav/tree/master)\n[![License](https://img.shields.io/badge/License-EPL%201.0-blue.svg)](https://opensource.org/licenses/EPL-1.0)\n\n\u003cdiv id=\"TOC\"\u003e\n\u003cul\u003e \n\u003cli\u003e\u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#display-modules-name-and-current-version\"\u003eDisplay current version\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#release\"\u003eRelease\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#spit-or-render-current-version-in-a-file\"\u003eSpit or render current version in a file\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#init-an-existing-codebase-with-a-specific-version\"\u003eInit an existing codebase with a specific version\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#behavior\"\u003eBehavior\u003c/a\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#version-bumping\"\u003eVersion bumping\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#module-naming\"\u003eModule Naming\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#tagging-behavior\"\u003eTagging behavior\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#meta-management\"\u003eMeta management\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#metav-interface\"\u003eMetav interface\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#rationale\"\u003eRationale\u003c/a\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#release-semantic\"\u003eRelease semantic\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#change-level-major-minor-patch\"\u003eChange level (major, minor, patch)\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#repository-organization\"\u003eRepository organization\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#version\"\u003eVersion\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#inspiration\"\u003eInspiration\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\n# Installation\n\nLatest version: 1.6.7 see the [CHANGELOG](https://github.com/jgrodziski/metav/blob/master/CHANGELOG.md).\n\n[deps.edn](https://clojure.org/guides/deps_and_cli) dependency information:\n```\n{metav {:mvn/version \"1.6.7\"}}\n```\n\nUsing [tools.deps](https://github.com/clojure/tools.deps.alpha), add several alias in your `deps.edn` for each main task (display, spit, release) like this with git ref:\n\n```clojure\n{:aliases {:metav {:extra-deps {jgrodziski/metav {:git/url \"https://github.com/jgrodziski/metav.git\" :sha \"e4c322d4f89358cba3d16bab430fb01b2bf192c7\"}}}\n           :artifact-name {:extra-deps {jgrodziski/metav {:git/url \"https://github.com/jgrodziski/metav.git\" :sha \"e4c322d4f89358cba3d16bab430fb01b2bf192c7\"}}\n                           :main-opts [\"-m\" \"metav.display\"]}\n           :release {:extra-deps {jgrodziski/metav {:git/url \"https://github.com/jgrodziski/metav.git\" :sha \"e4c322d4f89358cba3d16bab430fb01b2bf192c7\"}}\n                     :main-opts [\"-m\" \"metav.release\"]}}}\n```\n\nOr using the clojars version `{metav {:mvn/version \"1.6.7\"}}` :\n```clojure\n{:aliases {:metav {:extra-deps {metav {:mvn/version \"1.6.7\"}}}\n           :artifact-name {:extra-deps {metav {:mvn/version \"1.6.7\"}}\n                           :main-opts [\"-m\" \"metav.display\"]}\n           :release {:extra-deps {metav {:mvn/version \"1.6.7\"}}\n                     :main-opts [\"-m\" \"metav.release\"]}\n           :spit     {:extra-deps {metav {:mvn/version \"1.6.7\"}}\n                      :main-opts [\"-m\" \"metav.spit\"\n                                  \"--output-dir\" \"src\"\n                                  \"--namespace\" \"yournamespace.meta\"\n                                  \"--formats\" \"clj\"]}}}\n```\n\n# Usage\n\n## Display module's name and current version\n\nOne liner:\n```\nclj -Sdeps '{:deps {jgrodziski/metav {:git/url \"https://github.com/jgrodziski/metav.git\" :sha  \"e4c322d4f89358cba3d16bab430fb01b2bf192c7\"}}}' -m metav.display\n```\n\nIf you've installed Metav's dependency in `deps.edn` like in the above Installation section, just run:\n```\nclj -A:artifact-name\n```\nYou should get something like:\n```\nmyawesomesys-backend        1.3.4\n```\nThe module name is deduced from the path: each directory name from the toplevel to the module dir is concatenated in the module name separated with a hyphen ('-'). Example: a module sitting in the directory `/myawesomesys/backend` would automatically give the module name `myawesomesys-backend`. You also have the possibility to override the module name with the following option (available on the three tasks: display, release, spit):\n```\n  -r, --module-name-override MODULE-NAME Module Name Override\n```\nThe tab character between the module name and version makes it easy to use `cut -f1` and `cut -f2` to extract the data in shell script. \n\nTo avoid problem with logging libraries that can mess up the stdout and make it difficult to extract the module name and version, you can output the metadata in a file with the `spit` function and then extract the values to build the artifact name. Example:\n\n```shell\nclj -A:metav -m \"metav.spit\" \"-o\" \"target\" \"-f\" \"json\"\nARTIFACT_ID=$(cat target/meta.json | jq -r '.\"module-name\"')\nARTIFACT_VERSION=$(cat target/meta.json | jq -r '.\"version\"')\nARTIFACT_TAG=$(cat target/meta.json | jq -r '.\"tag\"')\nJAR_FILENAME=\"$ARTIFACT_TAG.jar\"\n\n```\n\n## Release\n\n_Release_ is the process invoked by the developer when a code related to a change is ready for prime time, hence releasable. The release process does the following:\n- **Check** everything is committed (no untracked or uncommitted file(s) otherwise the release process is aborted)\n- **Bump** the current version according to the release level of the change (major, minor or patch)\n- **[Optionaly: Spit and Commit metadata]**: Spit metadata in file(s) (version, tag, timestamp, module-name, etc.) with the `-s, --spit` option flag (presence means spitting metadata).\n- **Tag** the repo with that version. In case of monorepo, prefix the version with the module name (automatically deduced from the module's path or provided). The git tag can be signed with GPG or not, default behavior is to sign the tag.\n- **Push** the tag\n\nOne liner:\n```\nclj -Sdeps '{:deps {jgrodziski/metav {:git/url \"https://github.com/jgrodziski/metav.git\" :sha \"42ed18317ec843813e5084b4a4b616eb03e9679e\"}}}' -m metav.release\n```\n\nIf you've installed Metav's dependency in `deps.edn` like in the above Installation section, just run:\n```\nclj -A:release minor\n```\nWill execute the _release_ process described above, the tag used for the release is then printed in the standard output.\n\nThe `release` can also output metadata files like the `spit` function does, the CLI options are the same than `spit` there is a boolean flag option indicating that the `spit` is done (default to `false`).\n\nThe `release` usage is:\n```\nMetav's \"release\" function does the following:\n  - assert the command is invoked with a deps.edn in the working directory\n  - assert everything is committed (no untracked or uncommitted files).\n  - bump the version\n  - [optional: spit and commit the version metadata (module-name, tag, version, sha, timestamp) in file(s)]\n  - tag the repo with the version prefixed by the module-name in cas of a monorepo (the git tag can be signed with GPG or not, default behavior is to sign the tag)\n  - push everything\n\nUsage: metav.release [options] \u003clevel\u003e\nwith \u003clevel\u003e: major, minor or patch\n\nOptions:\n      --without-sign                      Should the git tag used for release be signed with the current user's GPG key configured with git\n  -s, --spit                              Indicates the release process should spit the metadata file as with the \"spit\" task, in that case the spit options must be provided\n  -o, --output-dir DIR_PATH  resources    Output Directory\n  -n, --namespace NS         meta         Namespace used in code output\n  -f, --formats FORMATS      edn          Comma-separated list of output formats (clj, cljc, cljs, edn, json)\n  -t, --template TEMPLATE                 Template used for rendering (must follows mustache format, spitted data is available during template rendering)\n  -d, --rendering-output RENDERING-OUTPUT File to render template in\n  -r, --module-name-override MODULE-NAME  Module Name Override\n  -v, --verbose                           Verbose, output the metadata as json in stdout if the option is present\n  -h, --help                              Help\n\n```\n\n## Spit or render current version in a file\n\nThe spit feature output the current state of the module in the repo in one or several files that can be directly Clojure source code (`clj`, `cljc` and `cljs` formats) or data literals structure like EDN or JSON (`edn` and `json` format).\nYou can also render a mustache template with the `-t` and `-d` option (see options below) to output a file with the metadata used in the template (like the version). A typical use case would be to link to a specific version of an artefact (like a link to a JS bundle in a HTML file).\n\n### Example of _spitted_ output as source code\n\n```clojure\n(ns metav.meta)\n\n(def module-name \"metav\")\n(def path \".\")\n(def version \"1.1.2\")\n(def tag \"v1.1.2\")\n(def generated-at \"2019-02-02T22:57:54Z\")\n```\n\n### Usage\n\n```\nclj -A:metav -m metav.spit --output-dir src --namespace metav.meta -formats clj\n;will output src/metav/meta.clj\n;or\nclj -A:metav -m metav.spit --output-dir resources --namespace meta -formats edn,json\n;will output resources/meta.edn and resources.json\n```\n\nThe `spit` usage help is (`clj -Ametav -m metav.spit --help`):\n\n```\nThe spit function of Metav output module's metadata in files according the given formats among: clj, cljc, cljs, edn or json.\nThe metadata is composed of: module-name, tag, version, path, timestamp\n\nUsage: metav.spit [options]\n\nOptions:\n  -o, --output-dir DIR_PATH  resources    Output Directory\n  -n, --namespace NS         meta         Namespace used in code output\n  -f, --formats FORMATS      edn          Comma-separated list of output formats (clj, cljc, cljs, edn, json)\n  -t, --template TEMPLATE                 Template used for rendering (must follows mustache format, spitted data is available during template rendering)\n  -d, --rendering-output RENDERING-OUTPUT File to render template in\n  -r, --module-name-override MODULE-NAME  Module Name Override\n  -v, --verbose                           Verbose, output the metadata as json in stdout if the option is present\n  -h, --help                              Help\n```\n\n## Init an existing codebase with a specific version\n\nSet a git annotated tag with the current version with `git tag -a v1.7.5 -m \"version 1.7.5\"` (the `-a` flag is important to set the tag as an annotated one, otherwise metav would not see it), then you can release it as usual with the `release` feature, the next version will be set starting from the specific setted version.\n\n# Behavior\n\n\u003e __Every artifact should be reproduceable from the source code hash ([git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References))__\n\n## Version bumping\n\nVersion is deduced from the current state of the SCM working copy: \n\n- is the source code on a tag? version is `1.5.2` \n- on a commit made after the tag? (possibly several commits, compute the distance from last tag and use it as patch number) version is `1.5.2-f34b91` (use the commit hash in version number)\n- with uncommitted change (DIRTY state)? `1.5.2-f34b91-DIRTY`\n\nThe version is never persisted somewhere in source code to avoid any desynchronisation between SCM state and version number. _However_, the library can optionaly spit the metadata (module name and version) in file to be included in an artefact during the build process.\n\n* **The SCM state drives the version.**\n* **Version uses the [Semantic Versioning scheme](https://semver.org).**\n\nWe never use SNAPSHOT in version number as it's difficult to know what's really inside the binary artefact.\n\n\n## Module Naming\n\nWe believe repo layout should follows some convention regarding the system, container, component organization and relationships (following the [C4 model](https://c4model.com) for example, but any other layout should be possible). Hence the naming scheme should reflect that organization, if we take the same example used in the C4 model documentation, the folders in the monorepo should be:\n\n- `Monorepo root`\n    - `Internet_Banking` (_System_ in C4 model)\n    - `Web_Application`\n        - `Single_Page_Application`\n        - `Mobile_App`\n        - `API` (_Container_ in C4 model, actual project that delivers an artefact whose name is `Internet_Banking-API`)\n            - `src/`\n            - `test/`\n            - `resources/`\n            - `deps.edn`\n        - `Database`\n    - `Mainframe_Banking`\n    - `Email`\n    \nModule's name is by default deduced from the repo path layout (but can also be overriden): each directory name from the toplevel to the module dir is concatenated in the module name separated with a hyphen ('-'). Example: a module sitting in the directory `/myawesomesys/backend` would automatically give the module name `myawesomesys-backend`. \nIn case of a dedicated repo, Metav takes only the folder name containing the working copy (aka. containing the `.git` folder), e.g. if your repo sits in the `awesomerepo` dir then the module's name will be `awesomerepo`.  \n\n## Tagging behavior\n\nEach _release_ invocation tags the current SCM state with the following naming scheme: `system-container-version`.\nThe tagging function use [git annotated tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) using the naming scheme describe previously, the message contains an EDN data structure described the module that is tagged: \n\n```clojure\n{:module-name \"Internet_banking-API\" \n :version \"1.5.2\"\n :path \"Internet_Banking/API\"\n :msg \"Add new attachment feature in the message part of the system\"}\n``` \n\nThe metadata in the tag message is stored as JSON and can later be extracted for use in shell script like so:\n```\ngit tag -l --format %(contents:subject) v1.0.3 | jq '.\"module-name\"'\n```\nDon't forget to start the command with a `noglob` if you use `zsh` as the `%(...)` will be interpreted otherwise.\n\n## Meta management\n\nMetadata, like module name and version, should be deduced from the SCM and included in the binary artefact (JAR, docker image). Metadata file can be named `meta.edn` for example.\n\nMetadata are:\n\n- Module name\n- Version number\n- Tag\n- Timestamp\n- Path in the repo\n\nSee [spit function](#spit-current-version-in-a-file).\n\n# Rationale\n\n* **SCM reference (hash) should give -\u003e Artefact.**\n* **Artefact should give -\u003e SCM reference (Hash).** \n    * We should be able to link a SCM hash to a software's binary artefact and the inverse: link a binary artefact to a reference in the SCM tree.\n* **Version is derived from git state instead of the other way around** (like a file versioned in the repo, with all the desynchronisation risks)\n* **The library should accomodate a Monorepo style organization** where several modules (directory containing a `deps.edn` file) lives under a top level repository, hence mixing the version and tag in it.\n* **Artifact construction from the source code SCM state should be deterministic.** An SCM reference should always give the same artefact.\n\n## Release semantic\n\n_Release_ means changes in source code in one or several commits are stable and ready to be \"published\" for later deployment. The _Release_ process takes as an input the semantic of the release (major, minor or patch, see below) and then assigns a proper version number according to this semantic, tags the repo with it and push the changes. The _Release_ task is invoked by developer when she considers changes in source code are ready and stable and she provides the semantic as parameter. Pushing binary artefact (JAR, docker image, etc.) somewhere is out of the scope of the _Release_ process and should be the responsibility of the CI system.\n\n\n## Change level (major, minor, patch)\n\nWhen releasing, developer indicates the characteristic of the changes regarding the breaks potentially introduced (*major* level change), whether new features were pushed (*minor* level with no breaking change) or just a fix with no new features nor breaking changes (_patch_ level). The _Release_ process takes care of dealing with the SCM and version number to left the developer only decides what she's releasing to the world. \n\n## Repository organization\n\nSCM repository organization is important, with many decisions to make: mono or multirepos, modules slicing, links with the CI and build process. Monorepos are a popular way of organizing source code at the moment to promote better code sharing behavior, knowledge spreading, refactoring, etc. (see the article [\"Monorepos and the fallacy of scale\"](https://presumably.de/monorepos-and-the-fallacy-of-scale.html)).\n\n**The library is intended to accomodate Monorepos and Multirepos style of organization**, in case of _Monorepos_ style Metav's tagging behavior ensures isolation between components living in the same repo. Many tools implicitly depends on having a dedicated repository per component, in our case the way we manage the version and release from the source code should be independant of whether the source code is in a dedicated repo (Multirepos) or a shared one (Monorepos).\n\nMonorepo layout makes it difficult to tag using only a version as several modules versions can collide, the solution used by metav is to prefix the tag name with the module name then the version like so: `sys-container-version`. The annotation message of the tag can also contain some metadata in the form of an EDN data structure.\n\n## Version\n\nEach version should gives a clear semantic about the content of the change, [Semantic Versioning](https://semver.org) is a great way to do that.\nI'm fond of using git tags to denote the current version of a component whether we use a Monorepo or a Multirepo.\n\nExtract from the [semver](https://semver.org) website:\n\n\u003e Given a version number MAJOR.MINOR.PATCH, increment the:\n\n\u003e - MAJOR version when you make incompatible API changes,\n\u003e - MINOR version when you add functionality in a backwards-compatible manner, and\n\u003e - PATCH version when you make backwards-compatible bug fixes.\n\u003e Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.\n\n## Inspiration\n\nMetav was inspired from these existing libraries in the Leiningen ecosystem:\n- [lein-git-version](https://github.com/arrdem/lein-git-version)\n- [lein-v](https://github.com/roomkey/lein-v) I used that one some times ago and Metav borrowed the SemVer and Maven version handling code.\n\nThe monorepo concern also has solutions like:\n- [lein-modules](https://github.com/jcrossley3/lein-modules)\n- [lein-monolith](https://github.com/amperity/lein-monolith)\nand of course the [release task of lein](https://github.com/technomancy/leiningen/blob/master/doc/DEPLOY.md#releasing-simplified), all of that was the inception of Metav.\n\n\n# License\n\nCopyright © 2020 Jeremie Grodziski\n\nDistributed under the Eclipse Public License either version 1.0 or (at your option) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjgrodziski%2Fmetav","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjgrodziski%2Fmetav","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjgrodziski%2Fmetav/lists"}