Home

Awesome

<h1 align="center" style="border-bottom: none;">🥇🏷️Automatic version management solution</h1> <h3 align="center">Automatic version management and state recovery solution for any application agnostic to language or architecture</h3> <p align="center">

Project Status: Active – The project has reached a stable, usable state and is being actively developed. codecov <a href="#badge"> <img alt="vmn" src="https://img.shields.io/github/pipenv/locked/python-version/final-israel/vmn"> </a> <a href="#badge"> <img alt="vmn: pypi downloads" src="https://img.shields.io/pypi/dw/vmn"> </a> <a href="#badge"> <img alt="vmn: supported platfroms" src="https://img.shields.io/badge/vmn-linux%20%7C%20macos%20%7C%20windows%20-brightgreen"> </a>

vmn is compliant with Semver (https://semver.org) semantics

</p>

Badge

Let people know that your repository is managed by vmn by including this badge in your readme.

vmn: automatic versioning

[![vmn: automatic versioning](https://img.shields.io/badge/vmn-automatic%20versioning-blue)](https://github.com/final-israel/vmn)
<p align="center"> <br> <img width="800" src="https://i.imgur.com/g3wYIk8.png"> <br> </p>

What is vmn?

vmn is a CLI tool and a Python library that is used for handling project versioning needs.

Now go ahead and read vmn's docs :)

Play around with vmn

Option 1: Create a playground

# Install vmn
pip install vmn

# Create fake remote
mkdir remote
cd remote
git init --bare

# Clone from fake remote
cd ..
git clone ./remote ./local
cd local

# Mimic user commit
echo a >> ./a.txt ; git add ./a.txt ; git commit -m "wip" ; git push origin master

# Initialize vmn for first time
vmn init
# Initialize app for first time
vmn init-app my_cool_app

# First stamp
vmn stamp -r patch my_cool_app

# Mimic user commit
echo a >> ./a.txt ; git add ./a.txt ; git commit -m "wip" ; git push origin master

# Second stamp
vmn stamp -r patch my_cool_app

Option 2: Use VMN Action with GitHub Action

Check VMN Official GitHub Action Repo

Contribute

Create a dev environment

# After cloning vmn repo:
cd ./vmn
# For Ubuntu:
#   sudo apt install python3-venv
python3 -m venv ./venv
source ./venv/bin/activate

pip install -r  ./tests/requirements.txt 
pip install -r  ./tests/test_requirements.txt 
pip install -e  ./
vmn --version # Should see 0.0.0 if installed successfully

Run tests

# install Docker 
#   For Ubuntu - sudo apt install docker.io
# Then run:
./tests/run_pytest.sh
# If it runs successfully, you are good to go

Key features

Usage

1. Installation

pip3 install vmn

# Another option (and a better one) is to use pipX (https://github.com/pypa/pipx):
pipx install vmn

2. cd into your git repository

cd to/your/repository

3. vmn init

## Needed only once per repository.
vmn init

4. vmn stamp

## Needed only once per app-name
# will start from 0.0.0
vmn init-app <app-name>

# will stamp 0.0.1
vmn stamp -r patch <app-name>

# example for starting from version 1.6.8
vmn init-app -v 1.6.8 <app-name2>

# will stamp 1.7.0
vmn stamp -r minor <app-name2>

Note

init-app and stamp both support --dry-run flag

You can also use vmn as a python lib by importing it

from contextlib import redirect_stdout, redirect_stderr
import io
import version_stamp.vmn as vmn

out = io.StringIO()
err = io.StringIO()
with redirect_stdout(out), redirect_stderr(err):
    ret, vmn_ctx = vmn.vmn_run(["show", "vmn"])
out_s = out.getvalue()
err_s = err.getvalue()

explore vmn_ctx object to see what you can get from it. Vars starting with _ are private and may change with time

Supported env vars

VMN_WORKING_DIR - Set it and vmn will run from this directory

VMN_LOCK_FILE_PATH - Set this to make vmn use this lockfile when it runs. The default is to use a lock file per repo to avoid running multiple vmn commands simultaneously.

Detailed Documentation

vmn stamp for release candidates

vmn supports Semver's prerelease notion of version stamping, enabling you to release non-mature versions and only then release the final version.

# will start from 1.6.8
vmn init-app -v 1.6.8 <app-name>

# will stamp 2.0.0-alpha1
vmn stamp -r major --pr alpha <app-name>

# will stamp 2.0.0-alpha2
vmn stamp --pr alpha <app-name>

# will stamp 2.0.0-mybeta1
vmn stamp --pr mybeta <app-name>

# Run release when you ready - will stamp 2.0.0 (from the same commit)
vmn release -v 2.0.0-mybeta1 <app-name>

vmn stamp for "root apps" or microservices

vmn supports stamping of something called a "root app" which can be useful for managing version of multiple services that are logically located under the same solution.

Example

vmn init-app my_root_app/service1
vmn stamp -r patch my_root_app/service1
vmn init-app my_root_app/service2
vmn stamp -r patch my_root_app/service2
vmn init-app my_root_app/service3
vmn stamp -r patch my_root_app/service3

Next we'll be able to use vmn show to display everything we need:

vmn show --verbose my_root_app/service3

vmn_info:
  description_message_version: '1'
  vmn_version: <the version of vmn itself that has stamped the application>
stamping:
  msg: 'my_root_app/service3: update to version 0.0.1'
  app:
    name: my_root_app/service3
    _version: 0.0.1
    release_mode: patch
    prerelease: release
    previous_version: 0.0.0
    stamped_on_branch: master
    changesets:
      .:
        hash: 8bbeb8a4d3ba8499423665ba94687b551309ea64
        remote: <remote url>
        vcs_type: git
    info: {}
  root_app:
    name: my_root_app
    version: 5
    latest_service: my_root_app/service3
    services:
      my_root_app/service1: 0.0.1
      my_root_app/service2: 0.0.1
      my_root_app/service3: 0.0.1
    external_services: {}

vmn show my_root_app/service3 will output 0.0.1

vmn show --root my_root_app will output 5

vmn show

Use vmn show for displaying version information of previous vmn stamp commands

vmn show <app-name>
vmn show --verbose <app-name>
vmn show -v 1.0.1 <app-name>

vmn goto

Similar to git checkout but also supports checking out all configured dependencies. This way you can easily go back to the exact state of you entire code for a specific version even when multiple git repositories are involved.

vmn goto -v 1.0.1 <app-name>

vmn gen

Generates version output file based on jinja2 template

vmn gen -t path/to/jinja_template.j2 -o path/to/output.txt app_name

Available jinja2 keywords

{
 "_version": "0.0.1",
 "base_version": "0.0.1",
 "changesets": {".": {"hash": "d6377170ae767cd025f6c623b838c7a99efbe7f8",
                      "remote": "../test_repo_remote",
                      "state": {"modified"},
                      "vcs_type": "git"}},
 "info": {},
 "name": "test_app2/s1",
 "prerelease": "release",
 "prerelease_count": {},
 "previous_version": "0.0.0",
 "release_mode": "patch",
 "stamped_on_branch": "main",
 "version": "0.0.1",
 "root_latest_service": "test_app2/s1",
 "root_name": "test_app2",
 "root_services": 
 {
   "test_app2/s1": "0.0.1"
 }, 
 "root_version": 1,
}

vmn gen jinja template example

"VERSION: {{version}} \n" \
"NAME: {{name}} \n" \
"BRANCH: {{stamped_on_branch}} \n" \
"RELEASE_MODE: {{release_mode}} \n" \
"{% for k,v in changesets.items() %} \n" \
"    <h2>REPO: {{k}}\n" \
"    <h2>HASH: {{v.hash}}</h2> \n" \
"    <h2>REMOTE: {{v.remote}}</h2> \n" \
"    <h2>VCS_TYPE: {{v.vcs_type}}</h2> \n" \
"{% endfor %}\n"

vmn gen output example

VERSION: 0.0.1
NAME: test_app2/s1
BRANCH: master
RELEASE_MODE: patch

    <h2>REPO: .
    <h2>HASH: ef4c6f4355d0190e4f516230f65a79ec24fc7396</h2>
    <h2>REMOTE: ../test_repo_remote</h2>
    <h2>VCS_TYPE: git</h2>

Version auto-embedding

vmn supports auto-embedding the version string during the vmn stamp phase for supported backends:

BackendDescription
alt text
Will embed version string to package.json file within the vmn stamp command
alt text
Will embed version string to Cargo.toml file within the vmn stamp command
Poetry
Will embed version string to Poetry's pyproject.toml file within the vmn stamp command

Generic version backends

There are two generic version backends types: generic_jinja and generic_selectors.

generic_selectors

vmn has a comprehensive regex for matching any vmn compliant version string. You may use it if you'd like.

(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:\.(?P<hotfix>0|[1-9]\d*))?(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)\.(?P<rcn>(?:0|[1-9]\d*)))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?

You can play with it here:

https://regex101.com/r/JoEvaN/1

version_backends:
    generic_selectors:
    - paths_section:
      - input_file_path: in.txt
        output_file_path: in.txt
        custom_keys_path: custom.yml
      selectors_section:
      - regex_selector: '(version: )(\d+\.\d+\.\d+)'
        regex_sub: \1{{version}}
      - regex_selector: '(Custom: )([0-9]+)'
        regex_sub: \1{{k1}}

input_file_path is the file you want to read and output_file_path is the file you want to write to.

custom_keys_path is a path to a yaml file containing any custom jinja2 kewords you would like to use. This field is optional.

A regex_selector is a regex that will match the desired part in the input_file_path file and regex_sub is a regex that states what the matched part should be replaced with. In this particular example, putting {{version}} tells vmn to inject the correct version while stamping. vmn will create an intermidiate jinja2 template and render it to output_file_path file.

Supported regex vars

{
   "VMN_VERSION_REGEX":"(?P<major>0|[1-9]\\d*)\\.(?P<minor>0|[1-9]\\d*)\\.(?P<patch>0|[1-9]\\d*)(?:\\.(?P<hotfix>0|[1-9]\\d*))?(?:-(?P<prerelease>(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)\\.(?P<rcn>(?:0|[1-9]\\d*)))?(?:\\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?",
   "VMN_ROOT_VERSION_REGEX":"^(?P<version>0|[1-9]\\d*)$"
}
Usage
version_backends:
    generic_selectors:
    - paths_section:
      - input_file_path: in.txt
        output_file_path: in.txt
        custom_keys_path: custom.yml
      selectors_section:
      - regex_selector: '(version: )({{VMN_VERSION_REGEX}})'
        regex_sub: \1{{version}}
      - regex_selector: '(Custom: )([0-9]+)'
        regex_sub: \1{{k1}}

generic_jinja

version_backends:
  generic_jinja:
  - input_file_path: f1.jinja2
    output_file_path: jinja_out.txt
    custom_keys_path: custom.yml

The parameters here are the same but are talking about jinja2 files.

Configuration

vmn auto generates a conf.yml file that can be modified later by the user.

An example of a possible conf.yml file:

# Autogenerated by vmn. You can edit this configuration file
conf:
  template: '[{major}][.{minor}]'
  deps:
    ../:
      <repo dir name>:
        vcs_type: git
        # branch: branch_name
        # tag: tag_name
        # hash: specific_hash
  extra_info: false
  create_verinfo_files: false
  hide_zero_hotfix: true
  version_backends: 
    npm:
      path: "relative_path/to/package.json"
FieldDescriptionExample
templateThe template configuration string can be customized and will be applied on the "raw" vmn version.<br>vmn will display the version based on the template.vmn show my_root_app/service3 will output 0.0 <br>however running:<br>vmn show --raw my_root_app/service3 will output 0.0.1
depsIn deps you can specify other repositories as your dependencies and vmn will consider them when stamping and performing goto.See example conf.yml file above
extra_infoSetting this to true will make vmn output usefull data about the host on which vmn has stamped the version.<br>Note This feature is not very popular and may be remove / altered in the future.See example conf.yml file above
create_verinfo_filesTells vmn to create file for each stamped version. vmn show --from-file will work with these files instead of working with git tags.See example conf.yml file above
hide_zero_hotfixTells vmn to hide the fourth version octa when it is equal to zero. This way you will never see the fourth octa unless you will specifically stamp with vmn stamp -r hotfix. True by default.See example conf.yml file above
version_backendsTells vmn to auto-embed the version string into one of the supported backends' files during the vmn stamp command. For instance, vmn will auto-embed the version string into package.json file if configured for npm projects.See example conf.yml file above