Automatic version management and state recovery solution for any application agnostic to language or architecture
vmn is compliant with Semver (https://semver.org) semantics
Let people know that your repository is managed by vmn by including this badge in your readme.
[](https://github.com/final-israel/vmn)- What is VMN?
- Play around with VMN
- Contribute
- Key features
- Usage by Lean Examples
- Detailed Documentation
- Version auto-embedding
- Generic version backends
- Configuration
- Thank yous and Contributors
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 :)
# 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_appCheck VMN Official GitHub Action Repo
Example workflow snippet:
steps:
- uses: actions/checkout@v3
- uses: progovoy/vmn-action@v1
with:
app-name: my_cool_app
do-stamp: true
stamp_release: true# 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# install Docker
# For Ubuntu - sudo apt install docker.io
# Then run:
./tests/run_pytest.sh
# If it runs successfully, you are good to go- Stamping of versions of type:
major.minor.patch, e.g.,1.6.0[Semvercompliant] - Stamping of versions of type:
major.minor.patch-prerelease, e.g.,1.6.0-rc23[Semvercompliant] - Stamping of versions of type:
major.minor.patch.hotfix, e.g.,1.6.7.4[Semverextension] - Bringing back the repository / repositories state to the state they were when the project was stamped (
see
gotosection) - Stamping of micro-services-like project topologies (
see
Root appssection) - Stamping of a project depending on multiple git repositories (
see
Configuration: depssection) - Version auto-embedding into supported backends during the
vmn stampphase ( seeVersion auto-embeddingsection) - Addition of
buildmetadatafor an existing version, e.g.,1.6.0-rc23+build01.Info[Semvercompliant] - Support for
conventional commitsfor automatic release mode detection (optionalorstrict) - Support for
conventional commitsfor automatic release notes generation -
WIPSupport "root apps" that are located in different repositories
Note: init-app and stamp both support --dry-run flag for testing purposes.
pip3 install vmn
# Another option (and a better one) is to use pipX (https://github.com/pypa/pipx):
pipx install vmncd to/your/repository
## Needed only once per repository.
vmn init## Needed only once per app-name
# will start from 0.0.0
vmn init-app <app-name>
# example for starting from version 1.6.8
vmn init-app -v 1.6.8 <app-name2># will stamp 0.0.1
vmn stamp -r patch <app-name>
# will stamp 1.7.0
vmn stamp -r minor <app-name2>In case you desire to use conventional commits for the stamp command to recognize the release_mode automatically, provide the following in app's conf.yml file:
"conventional_commits": {
"default_release_mode": "optional",
}
default_release_mode may be optional(for --orm) or strict(for -r).
Now you will be able to run:
vmn stamp <app-name> and vmn will deduce the proper release_mode based on your conventional commits.
## 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>## 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>## 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>## 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>## 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>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
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.
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>
# Or release directly from the prerelease commit using --stamp
vmn release --stamp <app-name>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.
vmn init-app my_root_app/service1
vmn stamp -r patch my_root_app/service1vmn init-app my_root_app/service2
vmn stamp -r patch my_root_app/service2vmn init-app my_root_app/service3
vmn stamp -r patch my_root_app/service3Next 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
Promote a prerelease stamped with vmn stamp to a final release.
Use -v/--version to specify the prerelease version or
--stamp to release the most recently stamped prerelease.
# Run release when you ready - will stamp 2.0.0 (from the same commit)
vmn release -v 2.0.0-mybeta1 <app-name>
# Or release directly from the prerelease commit using --stamp
vmn release --stamp <app-name>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>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>Generates version output file based on jinja2 template
vmn gen -t path/to/jinja_template.j2 -o path/to/output.txt app_name
{
"_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,
"release_notes": ""
}"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"
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>
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 supports auto-embedding the version string during the vmn stamp phase for supported backends:
There are two generic version backends types: generic_jinja and 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 keywords 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.
{
"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*)$"
}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}}version_backends:
generic_jinja:
- input_file_path: f1.jinja2
output_file_path: jinja_out.txt
custom_keys_path: custom.ymlThe parameters here are the same but are talking about jinja2 files.
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"| Field | Description | Example |
|---|---|---|
template |
The template configuration string can be customized and will be applied on the "raw" vmn version.vmn will display the version based on the template. |
vmn show my_root_app/service3 will output 0.0 however running: vmn show --raw my_root_app/service3 will output 0.0.1 |
deps |
In 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_info |
Setting this to true will make vmn output usefull data about the host on which vmn has stamped the version.Note This feature is not very popular and may be remove / altered in the future. |
See example conf.yml file above |
create_verinfo_files |
Tells 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_hotfix |
Tells 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_backends |
Tells 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 |
vmn can enforce policies during stamping and releasing. A common policy is to restrict these operations to specific branches.
policies:
whitelist_release_branches: ["main"]Attempting to stamp or release from branches that are not listed will trigger an error.
To Pavel Rogovoy for being the main contributor for this project.
To semver.org: we used semver.org as a blueprint for the structure of this specification.
Thanks goes to these wonderful people ✨
If you like and use this project, please consider starring it ✨
Thanks!