Compare commits
7 Commits
1ff0cc923b
...
feature/re
Author | SHA1 | Date | |
---|---|---|---|
ce271b6ce9 | |||
62ea63618b | |||
5ecad255b7 | |||
4a8c154a53 | |||
92eb0cff39 | |||
22035b89e0 | |||
c6191c3505 |
74
.github/workflows/unittests.yaml
vendored
Normal file
74
.github/workflows/unittests.yaml
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
name: Pytest
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Unit Tests
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-22.04, windows-latest, macos-latest]
|
||||||
|
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
cache: pip
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pytest pytest-cov
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
pytest
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
name: Coverage Summary
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
cache: pip
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pytest pytest-cov
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Run Coverage
|
||||||
|
run: |
|
||||||
|
pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=semver | tee pytest-coverage.txt
|
||||||
|
|
||||||
|
- name: Comment Result Summary
|
||||||
|
uses: MishaKav/pytest-coverage-comment@main
|
||||||
|
with:
|
||||||
|
pytest-coverage-path: ./pytest-coverage.txt
|
||||||
|
junitxml-path: ./pytest.xml
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,7 +7,6 @@ dist/
|
|||||||
*.zip
|
*.zip
|
||||||
env
|
env
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
tests/
|
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
|
48
Dockerfile
48
Dockerfile
@ -1,29 +1,33 @@
|
|||||||
FROM centos/python-36-centos7
|
# ======= #
|
||||||
|
# Builder #
|
||||||
USER root
|
# ======= #
|
||||||
|
FROM python:3.11-slim as builder
|
||||||
|
COPY / /semver
|
||||||
|
RUN pip wheel --no-cache-dir --wheel-dir /wheels /semver
|
||||||
|
|
||||||
#Perform updates
|
# ======== #
|
||||||
RUN pip install --upgrade pip
|
# Finalize #
|
||||||
RUN yum update -y
|
# ======== #
|
||||||
RUN yum -y remove git
|
FROM python:3.11-slim
|
||||||
RUN yum -y install https://packages.endpoint.com/rhel/7/os/x86_64/endpoint-repo-1.7-1.x86_64.rpm
|
|
||||||
RUN yum -y install git
|
|
||||||
|
|
||||||
#Setup semver
|
# Update and install git
|
||||||
ADD / /semver
|
RUN apt-get update && apt-get install -y git
|
||||||
WORKDIR /semver
|
|
||||||
RUN python setup.py sdist
|
# Create user
|
||||||
RUN pip install dist/semver-*.tar.gz
|
RUN mkdir /semver && \
|
||||||
|
groupadd -g 10001 semver && \
|
||||||
|
useradd -u 10000 -g semver -d /semver semver \
|
||||||
|
&& chown -R semver:semver /semver
|
||||||
|
|
||||||
# Prep workspace
|
# Prep workspace
|
||||||
RUN mkdir /workspace
|
RUN mkdir /workspace && \
|
||||||
WORKDIR /workspace
|
chown -R semver:semver /workspace
|
||||||
VOLUME /workspace
|
VOLUME /workspace
|
||||||
|
|
||||||
#Permissions
|
# Setup semver
|
||||||
RUN useradd -d /semverUser semverUser
|
COPY --from=builder /wheels /semver/wheels
|
||||||
RUN chown -R semverUser:semverUser /workspace
|
RUN pip install --no-cache /semver/wheels/*
|
||||||
|
|
||||||
CMD [ "semver" ]
|
USER semver:semver
|
||||||
|
WORKDIR /workspace
|
||||||
USER semverUser
|
ENTRYPOINT [ "semver" ]
|
||||||
|
@ -3,176 +3,80 @@ import re
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from enum import IntEnum
|
from typing import List, Union
|
||||||
from semver.utils import get_tag_version
|
|
||||||
|
import toml
|
||||||
|
|
||||||
from semver.logger import logging, logger, console_logger
|
from semver.logger import logging, logger, console_logger
|
||||||
from semver.bump import bump_version
|
from semver.scm import SCM
|
||||||
|
from semver.scm.git import Git
|
||||||
|
|
||||||
try:
|
from semver.semver import SemVer
|
||||||
from configparser import ConfigParser
|
from semver.utils import setting_to_array
|
||||||
except ImportError:
|
|
||||||
# Python < 3
|
|
||||||
from ConfigParser import ConfigParser
|
|
||||||
|
|
||||||
version = '0.0.0'
|
from semver.exceptions import (
|
||||||
|
NoMergeFoundException,
|
||||||
|
NotMainBranchException,
|
||||||
|
NoGitFlowException,
|
||||||
|
)
|
||||||
|
|
||||||
class VersionType(IntEnum):
|
version = "0.0.0"
|
||||||
MAJOR = 0
|
|
||||||
MINOR = 1
|
|
||||||
PATCH = 2
|
|
||||||
|
|
||||||
# Define common exceptions;
|
|
||||||
NO_MERGE_FOUND = Exception('No merge found')
|
|
||||||
NOT_MAIN_BRANCH = Exception('Not merging into a main branch')
|
|
||||||
NO_GIT_FLOW = Exception('No git flow branch found')
|
|
||||||
|
|
||||||
# Important regex
|
|
||||||
GET_COMMIT_MESSAGE = re.compile(r"Merge (branch|pull request) '?([^']+)'? (into|from) (?:'(.+)'|[^\/]+\/([^\n\\]+))")
|
|
||||||
|
|
||||||
class SemVer(object):
|
|
||||||
|
|
||||||
# Merge pull request #1 from RightBrain-Networks/feature/PLAT-185-versioning
|
|
||||||
|
|
||||||
def __init__(self,global_user=False):
|
|
||||||
self.global_user = '--local' if global_user else '--global'
|
|
||||||
self.merged_branch = None
|
|
||||||
self.main_branch = None
|
|
||||||
self.version_type = None
|
|
||||||
|
|
||||||
self.main_branches = self._setting_to_array('main_branches')
|
|
||||||
self.major_branches = self._setting_to_array('major_branches')
|
|
||||||
self.minor_branches = self._setting_to_array('minor_branches')
|
|
||||||
self.patch_branches = self._setting_to_array('patch_branches')
|
|
||||||
|
|
||||||
def _setting_to_array(self, setting):
|
|
||||||
config = ConfigParser()
|
|
||||||
config.read('./.bumpversion.cfg')
|
|
||||||
value = config.get('semver', setting)
|
|
||||||
# filter() removes empty string which is what we get if setting is blank
|
|
||||||
return list(filter(bool, [v.strip() for v in value.split(',')]))
|
|
||||||
|
|
||||||
# based on commit message see what branches are involved in the merge
|
|
||||||
def get_branches(self):
|
|
||||||
p = subprocess.Popen(['git', 'log', '-1'], stdout=subprocess.PIPE,
|
|
||||||
cwd='.')
|
|
||||||
#check current branch
|
|
||||||
b = subprocess.Popen(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], stdout=subprocess.PIPE,
|
|
||||||
cwd='.')
|
|
||||||
message = str(p.stdout.read())
|
|
||||||
branch = b.stdout.read().decode('utf-8').rstrip()
|
|
||||||
logger.info('Main branch is ' + branch)
|
|
||||||
matches = GET_COMMIT_MESSAGE.search(message.replace('\\n','\n').replace('\\',''))
|
|
||||||
if matches:
|
|
||||||
if str(matches.group(4)) == branch:
|
|
||||||
self.merged_branch = matches.group(2)
|
|
||||||
else:
|
|
||||||
self.merged_branch = matches.group(5)
|
|
||||||
self.main_branch = branch
|
|
||||||
return bool(matches)
|
|
||||||
|
|
||||||
# based on branches involved see what type of versioning should be done
|
|
||||||
def get_version_type(self):
|
|
||||||
logger.info('Merged branch is ' + self.merged_branch)
|
|
||||||
|
|
||||||
merged_prefix = None
|
|
||||||
matches = re.findall("[^\/]*/", self.merged_branch)
|
|
||||||
if len(matches) >= 1:
|
|
||||||
merged_prefix = matches[-1][0:-1]
|
|
||||||
|
|
||||||
if merged_prefix:
|
|
||||||
for prefix in self.major_branches:
|
|
||||||
if prefix == merged_prefix:
|
|
||||||
self.version_type = VersionType.MAJOR
|
|
||||||
return self.version_type
|
|
||||||
for prefix in self.minor_branches:
|
|
||||||
if prefix == merged_prefix:
|
|
||||||
self.version_type = VersionType.MINOR
|
|
||||||
return self.version_type
|
|
||||||
for prefix in self.patch_branches:
|
|
||||||
if prefix == merged_prefix:
|
|
||||||
self.version_type = VersionType.PATCH
|
|
||||||
return self.version_type
|
|
||||||
return False
|
|
||||||
|
|
||||||
# setup git settings so we can commit and tag
|
|
||||||
def setup_git_user(self):
|
|
||||||
# setup git user
|
|
||||||
p = subprocess.Popen(['git', 'config', self.global_user, 'user.email',
|
|
||||||
'"versioner@semver.com"'],
|
|
||||||
cwd='.')
|
|
||||||
p = subprocess.Popen(['git', 'config', self.global_user, 'user.name',
|
|
||||||
'"Semantic Versioner"'],
|
|
||||||
cwd='.')
|
|
||||||
p.wait()
|
|
||||||
return self
|
|
||||||
|
|
||||||
# use bumpversion to increment the appropriate version type
|
|
||||||
def version_repo(self):
|
|
||||||
config_file = ""
|
|
||||||
with open(".bumpversion.cfg", "r") as file:
|
|
||||||
config_file = file.read()
|
|
||||||
|
|
||||||
# version repo
|
|
||||||
logger.debug("Running bumpversion of type: " + str(self.version_type.name))
|
|
||||||
bump_version(get_tag_version(), self.version_type)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def commit_and_push(self):
|
|
||||||
# push versioning commit
|
|
||||||
p = subprocess.Popen(['git', 'push', 'origin', self.main_branch],
|
|
||||||
cwd='.')
|
|
||||||
p.wait()
|
|
||||||
|
|
||||||
# push versioning tag
|
|
||||||
p = subprocess.Popen(['git', 'push', 'origin', '--tags'],
|
|
||||||
cwd='.')
|
|
||||||
p.wait()
|
|
||||||
return self
|
|
||||||
|
|
||||||
# 1) get branches from last commit message
|
|
||||||
# 2) see if we're merging into a main branch
|
|
||||||
# 3) see what type of versioning we should do
|
|
||||||
# 4) version the repo
|
|
||||||
def run(self,push=True):
|
|
||||||
if not self.get_branches():
|
|
||||||
raise NO_MERGE_FOUND
|
|
||||||
if self.main_branch not in self.main_branches:
|
|
||||||
raise NOT_MAIN_BRANCH
|
|
||||||
if not self.get_version_type():
|
|
||||||
raise NO_GIT_FLOW
|
|
||||||
if push:
|
|
||||||
self.setup_git_user()
|
|
||||||
self.version_repo()
|
|
||||||
if push:
|
|
||||||
self.commit_and_push()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description='Bump Semantic Version.')
|
"""Main entry point for the application"""
|
||||||
parser.add_argument('-n','--no-push', help='Do not try to push', action='store_false', dest='push')
|
parser = argparse.ArgumentParser(description="Bump Semantic Version.")
|
||||||
parser.add_argument('-g','--global-user', help='Set git user at a global level, helps in jenkins', action='store_true', dest='global_user')
|
parser.add_argument(
|
||||||
parser.add_argument('-D', '--debug', help='Sets logging level to DEBUG', action='store_true', dest='debug', default=False)
|
"-n", "--no-push", help="do not try to push", action="store_false", dest="push"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-g",
|
||||||
|
"--global-user",
|
||||||
|
help="set git user at a global level",
|
||||||
|
action="store_true",
|
||||||
|
dest="global_user",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-D",
|
||||||
|
"--debug",
|
||||||
|
help="sets logging level to DEBUG",
|
||||||
|
action="store_true",
|
||||||
|
dest="debug",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
scm: SCM = Git(global_user=args.global_user)
|
||||||
|
|
||||||
|
app = SemVer(
|
||||||
|
scm=scm,
|
||||||
|
main_branches=setting_to_array("main_branches"),
|
||||||
|
major_branches=setting_to_array("major_branches"),
|
||||||
|
minor_branches=setting_to_array("minor_branches"),
|
||||||
|
patch_branches=setting_to_array("patch_branches"),
|
||||||
|
)
|
||||||
|
|
||||||
if args.debug:
|
if args.debug:
|
||||||
console_logger.setLevel(logging.DEBUG)
|
console_logger.setLevel(logging.DEBUG)
|
||||||
try:
|
try:
|
||||||
SemVer(global_user=args.global_user).run(push=args.push)
|
app.run(push=args.push)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
if args.debug:
|
if args.debug:
|
||||||
tb = sys.exc_info()[2]
|
tb = sys.exc_info()[2]
|
||||||
traceback.print_tb(tb)
|
traceback.print_tb(tb)
|
||||||
if e == NO_MERGE_FOUND:
|
if e is NoMergeFoundException:
|
||||||
exit(1)
|
exit(1)
|
||||||
elif e == NOT_MAIN_BRANCH:
|
elif e == NotMainBranchException:
|
||||||
exit(2)
|
exit(2)
|
||||||
elif e == NO_GIT_FLOW:
|
elif e == NoGitFlowException:
|
||||||
exit(3)
|
exit(3)
|
||||||
else:
|
else:
|
||||||
exit(128)
|
exit(128)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
try: main()
|
if __name__ == "__main__":
|
||||||
except: raise
|
try:
|
||||||
|
main()
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
from enum import IntEnum
|
|
||||||
import subprocess, os
|
|
||||||
from semver.logger import logging, logger, console_logger
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
from configparser import ConfigParser
|
|
||||||
except ImportError:
|
|
||||||
# Python < 3
|
|
||||||
from ConfigParser import ConfigParser
|
|
||||||
|
|
||||||
def bump_version(version, index=2, tag_repo = True, update_files=True):
|
|
||||||
v = version.split('.')
|
|
||||||
|
|
||||||
# Bump version
|
|
||||||
v[index] = str(int(v[index]) + 1)
|
|
||||||
|
|
||||||
# Reset subversions
|
|
||||||
i = len(v) - 1
|
|
||||||
while i > index:
|
|
||||||
v[i] = '0'
|
|
||||||
i = i - 1
|
|
||||||
|
|
||||||
# Get new version
|
|
||||||
new_version = '.'.join(v)
|
|
||||||
|
|
||||||
# Tag new version
|
|
||||||
if tag_repo and version != new_version:
|
|
||||||
p = subprocess.Popen(['git', 'tag', new_version], cwd='.')
|
|
||||||
p.wait()
|
|
||||||
|
|
||||||
# Update local files
|
|
||||||
if update_files:
|
|
||||||
update_file_version(new_version, version)
|
|
||||||
|
|
||||||
return new_version
|
|
||||||
|
|
||||||
def update_file_version(new_version, version="0.0.0"):
|
|
||||||
# Open up config file
|
|
||||||
config = ConfigParser()
|
|
||||||
config.read('./.bumpversion.cfg')
|
|
||||||
|
|
||||||
for section in config.sections():
|
|
||||||
if len(section) > 17 and section[0:17] == "bumpversion:file:":
|
|
||||||
file_name = section[17:]
|
|
||||||
if os.path.isfile(file_name):
|
|
||||||
# Get search val from config
|
|
||||||
search_val = config.get(section, "search")
|
|
||||||
search_val = process_config_string(search_val, new_version, version)
|
|
||||||
|
|
||||||
# Get replace val from config
|
|
||||||
replace_val = config.get(section, "replace")
|
|
||||||
replace_val = process_config_string(replace_val, new_version, version)
|
|
||||||
|
|
||||||
# Update replace values in file
|
|
||||||
with open(file_name, 'r') as file:
|
|
||||||
filedata = file.read()
|
|
||||||
filedata =filedata.replace(search_val,replace_val)
|
|
||||||
with open(file_name, 'w') as file:
|
|
||||||
file.write(filedata)
|
|
||||||
else:
|
|
||||||
logger.warning("Tried to version file: `" + file_name + "` but it doesn't exist!")
|
|
||||||
|
|
||||||
def process_config_string(cfg_string, new_version, version):
|
|
||||||
return cfg_string.replace("{new_version}", new_version).replace("{current_version}", version)
|
|
18
semver/exceptions.py
Normal file
18
semver/exceptions.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
class SemverException(Exception):
|
||||||
|
"""Semver base exception"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoMergeFoundException(SemverException):
|
||||||
|
"""No merge found in commit message"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NotMainBranchException(SemverException):
|
||||||
|
"""Not on main branch"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoGitFlowException(SemverException):
|
||||||
|
"""No git flow branch found"""
|
||||||
|
pass
|
@ -1,62 +1,57 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import re
|
|
||||||
import subprocess
|
from semver.logger import logging, console_logger
|
||||||
from semver.logger import logging, logger, console_logger
|
|
||||||
from semver.utils import get_tag_version, get_file_version, DEVNULL
|
|
||||||
from semver import SemVer
|
from semver import SemVer
|
||||||
from semver.bump import bump_version
|
from semver.utils import setting_to_array
|
||||||
|
from semver.scm.git import Git
|
||||||
def get_version(build=0,version_format=None,dot=False):
|
|
||||||
version = get_tag_version()
|
|
||||||
|
|
||||||
# Get the commit hash of the version
|
|
||||||
v_hash = subprocess.Popen(['git', 'rev-list', '-n', '1', version], stdout=subprocess.PIPE,
|
|
||||||
stderr=DEVNULL, cwd='.').stdout.read().decode('utf-8').rstrip()
|
|
||||||
# Get the current commit hash
|
|
||||||
c_hash = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE,
|
|
||||||
stderr=DEVNULL, cwd='.').stdout.read().decode('utf-8').rstrip()
|
|
||||||
|
|
||||||
# If the version commit hash and current commit hash
|
|
||||||
# do not match return the branch name else return the version
|
|
||||||
if v_hash != c_hash:
|
|
||||||
logger.debug("v_hash and c_hash do not match!")
|
|
||||||
branch = subprocess.Popen(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], stdout=subprocess.PIPE,
|
|
||||||
stderr=DEVNULL, cwd='.').stdout.read().decode('utf-8').rstrip()
|
|
||||||
semver = SemVer()
|
|
||||||
semver.merged_branch = branch
|
|
||||||
logger.debug("merged branch is: {}".format(semver.merged_branch))
|
|
||||||
version_type = semver.get_version_type()
|
|
||||||
logger.debug("version type is: {}".format(version_type))
|
|
||||||
if version_type:
|
|
||||||
|
|
||||||
next_version = bump_version(get_tag_version(), version_type, False, False)
|
|
||||||
|
|
||||||
if version_format in ('npm','docker'):
|
|
||||||
return "{}-{}.{}".format(next_version,re.sub(r'[/_]', '-', branch),build)
|
|
||||||
if version_format == 'maven':
|
|
||||||
qualifier = 'SNAPSHOT' if build == 0 else build
|
|
||||||
return "{}-{}-{}".format(next_version,re.sub(r'[/_]', '-', branch),qualifier)
|
|
||||||
if dot:
|
|
||||||
branch = branch.replace('/','.')
|
|
||||||
return branch
|
|
||||||
return version
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description='Get Version or Branch.')
|
parser = argparse.ArgumentParser(description="Get Version or Branch.")
|
||||||
parser.add_argument('-d', '--dot', help='Switch out / for . to be used in docker tag', action='store_true', dest='dot')
|
parser.add_argument(
|
||||||
parser.add_argument('-D', '--debug', help='Sets logging level to DEBUG', action='store_true', dest='debug', default=False)
|
"-d",
|
||||||
parser.add_argument('-f', '--format', help='Format for pre-release version syntax', choices=['npm','maven','docker'], default=None)
|
"--dot",
|
||||||
parser.add_argument('-b', '--build-number', help='Build number, used in pre-releases', default=0)
|
help="switch out / for . to be used in docker tag",
|
||||||
|
action="store_true",
|
||||||
|
dest="dot",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-D",
|
||||||
|
"--debug",
|
||||||
|
help="sets logging level to DEBUG",
|
||||||
|
action="store_true",
|
||||||
|
dest="debug",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-f",
|
||||||
|
"--format",
|
||||||
|
help="format for pre-release version syntax",
|
||||||
|
choices=["npm", "maven", "docker"],
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-b", "--build-number", help="build number, used in pre-releases", default=0
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.debug:
|
if args.debug:
|
||||||
console_logger.setLevel(logging.DEBUG)
|
console_logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
print(get_version(args.build_number,args.format,args.dot))
|
semver = SemVer(
|
||||||
|
scm=Git(),
|
||||||
|
main_branches=setting_to_array("main_branches"),
|
||||||
|
major_branches=setting_to_array("major_branches"),
|
||||||
|
minor_branches=setting_to_array("minor_branches"),
|
||||||
|
patch_branches=setting_to_array("patch_branches"),
|
||||||
|
)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
print(semver.get_version(args.build_number, args.format, args.dot))
|
||||||
try: main()
|
|
||||||
except: raise
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
@ -2,7 +2,7 @@ import logging
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
# create logger
|
# create logger
|
||||||
logger = logging.getLogger('simple_example')
|
logger = logging.getLogger("simple_example")
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
# create console handler and set level to INFO
|
# create console handler and set level to INFO
|
||||||
@ -10,10 +10,10 @@ console_logger = logging.StreamHandler()
|
|||||||
console_logger.setLevel(logging.INFO)
|
console_logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
# create formatter
|
# create formatter
|
||||||
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
||||||
|
|
||||||
# add formatter to console_logger
|
# add formatter to console_logger
|
||||||
console_logger.setFormatter(formatter)
|
console_logger.setFormatter(formatter)
|
||||||
|
|
||||||
# add console_logger to logger
|
# add console_logger to logger
|
||||||
logger.addHandler(console_logger)
|
logger.addHandler(console_logger)
|
||||||
|
76
semver/scm/__init__.py
Normal file
76
semver/scm/__init__.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
from semver.version_type import VersionType
|
||||||
|
from semver.logger import logger
|
||||||
|
|
||||||
|
|
||||||
|
class SCM(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def get_tag_version(self) -> str:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_branch(self) -> str:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_merge_branch(self) -> Union[str, None]:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def commit_and_push(self, branch: str) -> None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def tag_version(self, version: str) -> None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_version_hash(self, version: str) -> str:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_hash(self) -> str:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def get_file_version(self, config: dict) -> str:
|
||||||
|
"""
|
||||||
|
:param config: The bumpversion config as a dict
|
||||||
|
:return: The current version from the config file
|
||||||
|
"""
|
||||||
|
bump_version: Union[str, None] = config.get("bumpversion", None)
|
||||||
|
version: str = (
|
||||||
|
bump_version.get("current_version", "0.0.0") if bump_version else "0.0.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
return version
|
||||||
|
|
||||||
|
def get_version_type(
|
||||||
|
self,
|
||||||
|
merged_branch: str,
|
||||||
|
major_branches: List[str],
|
||||||
|
minor_branches: List[str],
|
||||||
|
patch_branches: List[str],
|
||||||
|
) -> Union[VersionType, None]:
|
||||||
|
"""
|
||||||
|
Get the version type based on the branches involved in the merge
|
||||||
|
:param merged_branch: The branch that was merged
|
||||||
|
:param major_branches: List of prefixes for major branches
|
||||||
|
:param minor_branches: List of prefixes for minor branches
|
||||||
|
:param patch_branches: List of prefixes for patch branches
|
||||||
|
:return: The version type
|
||||||
|
"""
|
||||||
|
logger.info(f"Merged branch is {merged_branch}")
|
||||||
|
|
||||||
|
merged_prefix = merged_branch.split("/")[-1].rstrip("/")
|
||||||
|
|
||||||
|
version_type: Union[VersionType, None] = None
|
||||||
|
if merged_prefix:
|
||||||
|
if merged_prefix in major_branches:
|
||||||
|
version_type = VersionType.MAJOR
|
||||||
|
if merged_prefix in minor_branches:
|
||||||
|
version_type = VersionType.MINOR
|
||||||
|
if merged_prefix in patch_branches:
|
||||||
|
version_type = VersionType.PATCH
|
||||||
|
return version_type
|
168
semver/scm/git.py
Normal file
168
semver/scm/git.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from typing import Union, List
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
import toml
|
||||||
|
|
||||||
|
from semver.scm import SCM
|
||||||
|
from semver.logger import logger
|
||||||
|
from semver.version_type import VersionType
|
||||||
|
from semver.exceptions import SemverException
|
||||||
|
from semver.utils import get_settings
|
||||||
|
|
||||||
|
|
||||||
|
class Git(SCM):
|
||||||
|
def __init__(self, global_user: bool = False) -> None:
|
||||||
|
self.git_commit_pattern = re.compile(
|
||||||
|
r"Merge (branch|pull request) '?([^']+)'? (into|from) (?:'(.+)'|[^\/]+\/([^\n\\]+))"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.git_bin = "git"
|
||||||
|
|
||||||
|
self.global_user: bool = global_user
|
||||||
|
self.git_email: str = "versioner@semver.com"
|
||||||
|
self.git_user: str = "Semantic Versioner"
|
||||||
|
self._setup_git_user()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def _run_command(
|
||||||
|
self, *args: str, throwExceptions: bool = True
|
||||||
|
) -> subprocess.CompletedProcess:
|
||||||
|
return subprocess.run(
|
||||||
|
args,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=throwExceptions,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _setup_git_user(self) -> None:
|
||||||
|
self._run_command(
|
||||||
|
self.git_bin,
|
||||||
|
"config",
|
||||||
|
"--global" if self.global_user else "--local",
|
||||||
|
"user.email",
|
||||||
|
f'"{self.git_email}"',
|
||||||
|
)
|
||||||
|
|
||||||
|
self._run_command(
|
||||||
|
self.git_bin,
|
||||||
|
"config",
|
||||||
|
"--global" if self.global_user else "--local",
|
||||||
|
"user.name",
|
||||||
|
f'"{self.git_user}"',
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_tag_version(self) -> str:
|
||||||
|
"""
|
||||||
|
Get the latest tagged version from git tags
|
||||||
|
:return: The latest tagged version
|
||||||
|
"""
|
||||||
|
config: dict = get_settings()
|
||||||
|
|
||||||
|
tag_expression: str = config["bumpversion"]["tag_name"].replace(
|
||||||
|
"{new_version}", "[0-9]*.[0-9]*.[0-9]*"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(f"Tag expression: {tag_expression}")
|
||||||
|
|
||||||
|
# Default version is `0.0.0` or what is found in
|
||||||
|
version = self.get_file_version(config)
|
||||||
|
|
||||||
|
# If a version is found in git tags, use that the latest tagged version
|
||||||
|
tagged_versions: Union[List[str], None] = None
|
||||||
|
try:
|
||||||
|
proc = self._run_command(
|
||||||
|
self.git_bin, "tag", "--sort=v:refname", "-l", tag_expression
|
||||||
|
)
|
||||||
|
tagged_versions = proc.stdout.rstrip().split("\n")
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise SemverException(
|
||||||
|
f"Error getting latest tagged git version: {str(e.stderr).rstrip()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
tagged_versions is not None
|
||||||
|
and len(tagged_versions) > 0
|
||||||
|
and tagged_versions[-1] != ""
|
||||||
|
):
|
||||||
|
version = tagged_versions[-1]
|
||||||
|
|
||||||
|
logger.debug(f"Tag Version: {version}")
|
||||||
|
return version
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def get_branch(self) -> str:
|
||||||
|
"""
|
||||||
|
Get the main branch
|
||||||
|
:return: The main branch
|
||||||
|
"""
|
||||||
|
proc = self._run_command(self.git_bin, "rev-parse", "--abbrev-ref", "HEAD")
|
||||||
|
return proc.stdout.rstrip()
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def get_merge_branch(self) -> Union[str, None]:
|
||||||
|
"""
|
||||||
|
Get the branches involved in the merge
|
||||||
|
:return: The branch involved in the merge
|
||||||
|
"""
|
||||||
|
proc = self._run_command(self.git_bin, "log", "-1")
|
||||||
|
message: str = proc.stdout
|
||||||
|
|
||||||
|
branch: str = self.get_branch()
|
||||||
|
|
||||||
|
matches = self.git_commit_pattern.search(
|
||||||
|
message.replace("\\n", "\n").replace("\\", "")
|
||||||
|
)
|
||||||
|
merged_branch: Union[str, None] = None
|
||||||
|
if matches:
|
||||||
|
merged_branch = str(
|
||||||
|
matches.group(2) if matches.group(4) == branch else matches.group(5)
|
||||||
|
)
|
||||||
|
|
||||||
|
return merged_branch
|
||||||
|
|
||||||
|
def commit_and_push(self, branch: str) -> None:
|
||||||
|
"""
|
||||||
|
Commit and push the versioning changes
|
||||||
|
:param branch: The branch to push
|
||||||
|
"""
|
||||||
|
proc = self._run_command(
|
||||||
|
self.git_bin, "push", "origin", branch, throwExceptions=False
|
||||||
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise SemverException(
|
||||||
|
f"Error pushing versioning changes to {branch}: {proc.stderr}"
|
||||||
|
)
|
||||||
|
proc = self._run_command(
|
||||||
|
self.git_bin, "push", "origin", "--tags", throwExceptions=False
|
||||||
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise SemverException(
|
||||||
|
f"Error pushing versioning changes to {branch}: {proc.stderr}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def tag_version(self, version: str) -> None:
|
||||||
|
"""
|
||||||
|
Creates a git tag at HEAD with the given version
|
||||||
|
:param version: The version to tag
|
||||||
|
"""
|
||||||
|
self._run_command(self.git_bin, "tag", version)
|
||||||
|
|
||||||
|
def get_version_hash(self, version: str) -> str:
|
||||||
|
"""
|
||||||
|
Get the hash of the commit that has the given version
|
||||||
|
:param version: The version to get the hash for
|
||||||
|
:return: The hash of the commit that has the given version
|
||||||
|
"""
|
||||||
|
proc = self._run_command(self.git_bin, "rev-list", "-n", "1", version)
|
||||||
|
return proc.stdout.rstrip()
|
||||||
|
|
||||||
|
def get_hash(self) -> str:
|
||||||
|
"""
|
||||||
|
Get the hash of the current commit
|
||||||
|
:return: The hash of the current commit
|
||||||
|
"""
|
||||||
|
proc = self._run_command(self.git_bin, "rev-parse", "HEAD")
|
||||||
|
return proc.stdout.rstrip()
|
26
semver/scm/mock.py
Normal file
26
semver/scm/mock.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from semver.scm import SCM
|
||||||
|
|
||||||
|
|
||||||
|
class MockSCM(SCM):
|
||||||
|
def get_tag_version(self) -> str:
|
||||||
|
return "1.0.0"
|
||||||
|
|
||||||
|
def get_branch(self) -> str:
|
||||||
|
return "main"
|
||||||
|
|
||||||
|
def get_merge_branch(self) -> Union[str, None]:
|
||||||
|
return "main"
|
||||||
|
|
||||||
|
def commit_and_push(self, branch: str) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tag_version(self, version: str) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_version_hash(self, version: str) -> str:
|
||||||
|
return "HASH"
|
||||||
|
|
||||||
|
def get_hash(self) -> str:
|
||||||
|
return "HASH"
|
0
semver/scm/tests/__init__.py
Normal file
0
semver/scm/tests/__init__.py
Normal file
117
semver/scm/tests/test_git.py
Normal file
117
semver/scm/tests/test_git.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from semver.version_type import VersionType
|
||||||
|
from semver.exceptions import SemverException
|
||||||
|
from semver.scm import SCM
|
||||||
|
from semver.scm.git import Git
|
||||||
|
|
||||||
|
|
||||||
|
class TestMockSCM(unittest.TestCase):
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def setUp(self, mock_subprocess_run: mock.Mock):
|
||||||
|
# Mock the subprocess.run function to avoid
|
||||||
|
# running actual git commands
|
||||||
|
mock_subprocess_run.return_value.returncode = 0
|
||||||
|
mock_subprocess_run.return_value.stdout = ""
|
||||||
|
|
||||||
|
self.scm = Git()
|
||||||
|
|
||||||
|
def test_run_command(self):
|
||||||
|
proc: subprocess.CompletedProcess[str] = self.scm._run_command("echo", "hello")
|
||||||
|
self.assertEqual(proc.stdout, "hello\n")
|
||||||
|
|
||||||
|
@mock.patch("toml.load")
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_get_tag_version(
|
||||||
|
self, mock_subprocess_run: mock.Mock, mock_toml_load: mock.Mock
|
||||||
|
):
|
||||||
|
mock_toml_load.return_value = {"bumpversion": {"tag_name": "v{new_version}"}}
|
||||||
|
mock_subprocess_run.return_value.stdout = "v1.0.0\n"
|
||||||
|
|
||||||
|
expected_version = "v1.0.0"
|
||||||
|
version = self.scm.get_tag_version()
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
@mock.patch("toml.load")
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_get_tag_version_git_fail(
|
||||||
|
self, mock_subprocess_run: mock.Mock, mock_toml_load: mock.Mock
|
||||||
|
):
|
||||||
|
mock_toml_load.return_value = {"bumpversion": {"tag_name": "v{new_version}"}}
|
||||||
|
mock_subprocess_run.return_value.returncode = 1
|
||||||
|
mock_subprocess_run.side_effect = subprocess.CalledProcessError(
|
||||||
|
1, "git", "git error"
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(SemverException):
|
||||||
|
self.scm.get_tag_version()
|
||||||
|
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_get_branch(self, mock_subprocess_run: mock.Mock):
|
||||||
|
mock_subprocess_run.return_value.stdout = "main\n"
|
||||||
|
|
||||||
|
expected_branch = "main"
|
||||||
|
branch = self.scm.get_branch()
|
||||||
|
self.assertEqual(branch, expected_branch)
|
||||||
|
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_get_merge_branch(self, mock_subprocess_run: mock.Mock):
|
||||||
|
mock_subprocess_run.return_value.stdout = (
|
||||||
|
"Merge pull request #1 from RightBrain-Networks/feature/example\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_merge_branch = "feature/example"
|
||||||
|
merge_branch = self.scm.get_merge_branch()
|
||||||
|
self.assertEqual(merge_branch, expected_merge_branch)
|
||||||
|
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_commit_and_push(self, mock_subprocess_run: mock.Mock):
|
||||||
|
mock_subprocess_run.return_value.returncode = 0
|
||||||
|
|
||||||
|
branch = "main"
|
||||||
|
self.scm.commit_and_push(branch)
|
||||||
|
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_commit_and_push_git_fail(self, mock_subprocess_run: mock.Mock):
|
||||||
|
mock_subprocess_run.return_value.returncode = 1
|
||||||
|
|
||||||
|
branch = "main"
|
||||||
|
with self.assertRaises(SemverException):
|
||||||
|
self.scm.commit_and_push(branch)
|
||||||
|
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_commit_and_push_git_fail_tags(self, mock_subprocess_run: mock.Mock):
|
||||||
|
mock_subprocess_run.side_effect = [
|
||||||
|
mock.Mock(returncode=0),
|
||||||
|
mock.Mock(returncode=1),
|
||||||
|
]
|
||||||
|
|
||||||
|
branch = "main"
|
||||||
|
with self.assertRaises(SemverException):
|
||||||
|
self.scm.commit_and_push(branch)
|
||||||
|
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_tag_version(self, mock_subprocess_run: mock.Mock):
|
||||||
|
mock_subprocess_run.return_value.returncode = 0
|
||||||
|
|
||||||
|
version = "1.0.0"
|
||||||
|
self.scm.tag_version(version)
|
||||||
|
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_get_version_hash(self, mock_subprocess_run: mock.Mock):
|
||||||
|
mock_subprocess_run.return_value.stdout = "HASH\n"
|
||||||
|
|
||||||
|
version = "1.0.0"
|
||||||
|
expected_hash = "HASH"
|
||||||
|
version_hash = self.scm.get_version_hash(version)
|
||||||
|
self.assertEqual(version_hash, expected_hash)
|
||||||
|
|
||||||
|
@mock.patch("subprocess.run")
|
||||||
|
def test_get_hash(self, mock_subprocess_run: mock.Mock):
|
||||||
|
mock_subprocess_run.return_value.stdout = "HASH\n"
|
||||||
|
|
||||||
|
expected_hash = "HASH"
|
||||||
|
version_hash = self.scm.get_hash()
|
||||||
|
self.assertEqual(version_hash, expected_hash)
|
45
semver/scm/tests/test_mock.py
Normal file
45
semver/scm/tests/test_mock.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
from semver.version_type import VersionType
|
||||||
|
from semver.scm import SCM
|
||||||
|
from semver.scm.mock import MockSCM
|
||||||
|
|
||||||
|
|
||||||
|
class TestMockSCM(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.scm: SCM = MockSCM()
|
||||||
|
|
||||||
|
def test_get_tag_version(self):
|
||||||
|
expected_version = "1.0.0"
|
||||||
|
version = self.scm.get_tag_version()
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
def test_get_branch(self):
|
||||||
|
expected_branch = "main"
|
||||||
|
branch = self.scm.get_branch()
|
||||||
|
self.assertEqual(branch, expected_branch)
|
||||||
|
|
||||||
|
def test_get_merge_branch(self):
|
||||||
|
expected_merge_branch = "main"
|
||||||
|
merge_branch = self.scm.get_merge_branch()
|
||||||
|
self.assertEqual(merge_branch, expected_merge_branch)
|
||||||
|
|
||||||
|
def test_commit_and_push(self):
|
||||||
|
branch = "main"
|
||||||
|
self.scm.commit_and_push(branch)
|
||||||
|
|
||||||
|
def test_tag_version(self):
|
||||||
|
version = "1.0.0"
|
||||||
|
self.scm.tag_version(version)
|
||||||
|
|
||||||
|
def test_get_version_hash(self):
|
||||||
|
version = "1.0.0"
|
||||||
|
expected_hash = "HASH"
|
||||||
|
version_hash = self.scm.get_version_hash(version)
|
||||||
|
self.assertEqual(version_hash, expected_hash)
|
||||||
|
|
||||||
|
def test_get_hash(self):
|
||||||
|
expected_hash = "HASH"
|
||||||
|
version_hash = self.scm.get_hash()
|
||||||
|
self.assertEqual(version_hash, expected_hash)
|
72
semver/scm/tests/test_scm.py
Normal file
72
semver/scm/tests/test_scm.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from semver.version_type import VersionType
|
||||||
|
from semver.scm import SCM
|
||||||
|
from semver.scm.mock import MockSCM
|
||||||
|
|
||||||
|
|
||||||
|
class TestSCM(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.scm: SCM = MockSCM()
|
||||||
|
|
||||||
|
def test_get_file_version_existing_config(self):
|
||||||
|
config = {"bumpversion": {"current_version": "1.2.3"}}
|
||||||
|
expected_version = "1.2.3"
|
||||||
|
version = self.scm.get_file_version(config)
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
def test_get_file_version_no_config(self):
|
||||||
|
config = {}
|
||||||
|
expected_version = "0.0.0"
|
||||||
|
version = self.scm.get_file_version(config)
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
def test_get_file_version_no_version(self):
|
||||||
|
config = {"bumpversion": {}}
|
||||||
|
expected_version = "0.0.0"
|
||||||
|
version = self.scm.get_file_version(config)
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
def test_get_version_type_major(self):
|
||||||
|
merged_branch = "main"
|
||||||
|
major_branches = ["main"]
|
||||||
|
minor_branches = ["develop"]
|
||||||
|
patch_branches = ["hotfix"]
|
||||||
|
expected_version_type = VersionType.MAJOR
|
||||||
|
version_type = self.scm.get_version_type(
|
||||||
|
merged_branch, major_branches, minor_branches, patch_branches
|
||||||
|
)
|
||||||
|
self.assertEqual(version_type, expected_version_type)
|
||||||
|
|
||||||
|
def test_get_version_type_minor(self):
|
||||||
|
merged_branch = "develop"
|
||||||
|
major_branches = ["main"]
|
||||||
|
minor_branches = ["develop"]
|
||||||
|
patch_branches = ["hotfix"]
|
||||||
|
expected_version_type = VersionType.MINOR
|
||||||
|
version_type = self.scm.get_version_type(
|
||||||
|
merged_branch, major_branches, minor_branches, patch_branches
|
||||||
|
)
|
||||||
|
self.assertEqual(version_type, expected_version_type)
|
||||||
|
|
||||||
|
def test_get_version_type_patch(self):
|
||||||
|
merged_branch = "hotfix"
|
||||||
|
major_branches = ["main"]
|
||||||
|
minor_branches = ["develop"]
|
||||||
|
patch_branches = ["hotfix"]
|
||||||
|
expected_version_type = VersionType.PATCH
|
||||||
|
version_type = self.scm.get_version_type(
|
||||||
|
merged_branch, major_branches, minor_branches, patch_branches
|
||||||
|
)
|
||||||
|
self.assertEqual(version_type, expected_version_type)
|
||||||
|
|
||||||
|
def test_get_version_type_none(self):
|
||||||
|
merged_branch = "feature"
|
||||||
|
major_branches = ["main"]
|
||||||
|
minor_branches = ["develop"]
|
||||||
|
patch_branches = ["hotfix"]
|
||||||
|
expected_version_type = None
|
||||||
|
version_type = self.scm.get_version_type(
|
||||||
|
merged_branch, major_branches, minor_branches, patch_branches
|
||||||
|
)
|
||||||
|
self.assertEqual(version_type, expected_version_type)
|
218
semver/semver.py
Normal file
218
semver/semver.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
from typing import List, Union
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
import toml
|
||||||
|
|
||||||
|
from semver.logger import logger
|
||||||
|
from semver.scm import SCM
|
||||||
|
from semver.version_type import VersionType
|
||||||
|
from semver.exceptions import (
|
||||||
|
NoMergeFoundException,
|
||||||
|
NotMainBranchException,
|
||||||
|
NoGitFlowException,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SemVer:
|
||||||
|
"""Primary class for handling auto-semver processing"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
scm: SCM,
|
||||||
|
main_branches: Union[List[str], None] = None,
|
||||||
|
major_branches: Union[List[str], None] = None,
|
||||||
|
minor_branches: Union[List[str], None] = None,
|
||||||
|
patch_branches: Union[List[str], None] = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize the SemVer object
|
||||||
|
:param global_user: Toggles git user at a global level, useful for build servers
|
||||||
|
:param scm: The source control manager to use
|
||||||
|
:param main_branches: Branches to run versioning on
|
||||||
|
:param major_branches: List of prefixes for major branches
|
||||||
|
:param minor_branches: List of prefixes for minor branches
|
||||||
|
:param patch_branches: List of prefixes for patch branches
|
||||||
|
"""
|
||||||
|
self._merged_branch: Union[str, None] = None
|
||||||
|
self._branch: Union[str, None] = None
|
||||||
|
self._version_type: Union[VersionType, None] = None
|
||||||
|
|
||||||
|
self._main_branches: List[str] = main_branches if main_branches else []
|
||||||
|
self._major_branches: List[str] = major_branches if major_branches else []
|
||||||
|
self._minor_branches: List[str] = minor_branches if minor_branches else []
|
||||||
|
self._patch_branches: List[str] = patch_branches if patch_branches else []
|
||||||
|
|
||||||
|
self._scm: SCM = scm
|
||||||
|
|
||||||
|
def _version_repo(self) -> str:
|
||||||
|
"""
|
||||||
|
Use bump_version to update the repo version
|
||||||
|
:return: The new version
|
||||||
|
"""
|
||||||
|
version = self._scm.get_tag_version()
|
||||||
|
if not self._version_type:
|
||||||
|
raise NoMergeFoundException()
|
||||||
|
|
||||||
|
logger.debug(f"Running bumpversion of type: {self._version_type.name}")
|
||||||
|
return self._bump_version(version, self._version_type)
|
||||||
|
|
||||||
|
def _process_config_string(self, cfg_string, new_version, version):
|
||||||
|
return cfg_string.replace("{new_version}", new_version).replace(
|
||||||
|
"{current_version}", version
|
||||||
|
)
|
||||||
|
|
||||||
|
def _bump_version(
|
||||||
|
self,
|
||||||
|
version: str,
|
||||||
|
index: VersionType = VersionType.MINOR,
|
||||||
|
tag_repo: bool = True,
|
||||||
|
update_files: bool = True,
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Bump the version of the repo
|
||||||
|
:param version: The current version
|
||||||
|
:param index: The index of the version to bump
|
||||||
|
:param tag_repo: Whether or not to tag the repo
|
||||||
|
:param update_files: Whether or not to update the files
|
||||||
|
:return: The new version
|
||||||
|
"""
|
||||||
|
v: List[str] = version.split(".")
|
||||||
|
|
||||||
|
# Bump version
|
||||||
|
v[index] = str(int(v[index]) + 1)
|
||||||
|
|
||||||
|
# Reset subversions
|
||||||
|
i = len(v) - 1
|
||||||
|
while i > index:
|
||||||
|
v[i] = "0"
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
# Get new version
|
||||||
|
new_version = ".".join(v)
|
||||||
|
|
||||||
|
# Tag new version
|
||||||
|
if tag_repo and version != new_version:
|
||||||
|
self._scm.tag_version(new_version)
|
||||||
|
|
||||||
|
# Update local files
|
||||||
|
if update_files:
|
||||||
|
self._update_file_version(new_version, version)
|
||||||
|
|
||||||
|
return new_version
|
||||||
|
|
||||||
|
def _update_file_version(self, new_version: str, version: str = "0.0.0"):
|
||||||
|
"""
|
||||||
|
Update the version in the config file
|
||||||
|
:param new_version: The new version
|
||||||
|
:param version: The current version
|
||||||
|
"""
|
||||||
|
# Open up config file
|
||||||
|
config = toml.load("./.bumpversion.cfg")
|
||||||
|
|
||||||
|
bump_version_file_prefix = "bumpversion:file:"
|
||||||
|
bump_version_file_prefix_len = len(bump_version_file_prefix)
|
||||||
|
for section in config:
|
||||||
|
if section.startswith(bump_version_file_prefix):
|
||||||
|
file_path = Path(section[bump_version_file_prefix_len:])
|
||||||
|
section_data = config[section]
|
||||||
|
if file_path.is_file():
|
||||||
|
# Get search val from config
|
||||||
|
search_val = section_data["search"]
|
||||||
|
search_val = self._process_config_string(
|
||||||
|
search_val, new_version, version
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get replace val from config
|
||||||
|
replace_val = section_data["replace"]
|
||||||
|
replace_val = self._process_config_string(
|
||||||
|
replace_val, new_version, version
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update replace values in file
|
||||||
|
with open(file_path, "r") as file:
|
||||||
|
filedata = file.read()
|
||||||
|
filedata = filedata.replace(search_val, replace_val)
|
||||||
|
with open(file_path, "w") as file:
|
||||||
|
file.write(filedata)
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
f"Tried to version file: '{file_path}' but it doesn't exist!"
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_version(
|
||||||
|
self, build: int = 0, version_format: Union[str, None] = None, dot: bool = False
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get the version of the repo
|
||||||
|
:param build: The build number
|
||||||
|
:param version_format: The format of the version
|
||||||
|
:param dot: Whether or not to replace / with .
|
||||||
|
:return: The version
|
||||||
|
"""
|
||||||
|
version = self._scm.get_tag_version()
|
||||||
|
|
||||||
|
# Get the commit hash of the version
|
||||||
|
v_hash = self._scm.get_version_hash(version)
|
||||||
|
# Get the current commit hash
|
||||||
|
c_hash = self._scm.get_hash()
|
||||||
|
|
||||||
|
# If the version commit hash and current commit hash
|
||||||
|
# do not match return the branch name else return the version
|
||||||
|
if v_hash != c_hash:
|
||||||
|
logger.debug("v_hash and c_hash do not match!")
|
||||||
|
branch = self._scm.get_branch()
|
||||||
|
logger.debug("merged branch is: {}".format(branch))
|
||||||
|
version_type = self._scm.get_version_type(
|
||||||
|
branch, self._major_branches, self._minor_branches, self._patch_branches
|
||||||
|
)
|
||||||
|
logger.debug("version type is: {}".format(version_type))
|
||||||
|
if version_type:
|
||||||
|
next_version = self._bump_version(
|
||||||
|
self._scm.get_tag_version(), version_type, False, False
|
||||||
|
)
|
||||||
|
|
||||||
|
if version_format in ("npm", "docker"):
|
||||||
|
return "{}-{}.{}".format(
|
||||||
|
next_version, re.sub(r"[/_]", "-", branch), build
|
||||||
|
)
|
||||||
|
if version_format == "maven":
|
||||||
|
qualifier = "SNAPSHOT" if build == 0 else build
|
||||||
|
return "{}-{}-{}".format(
|
||||||
|
next_version, re.sub(r"[/_]", "-", branch), qualifier
|
||||||
|
)
|
||||||
|
if dot:
|
||||||
|
branch = branch.replace("/", ".")
|
||||||
|
return branch
|
||||||
|
return version
|
||||||
|
|
||||||
|
def run(self, push=True):
|
||||||
|
"""
|
||||||
|
Run the versioning process
|
||||||
|
1) get branches from last commit message
|
||||||
|
2) see if we're merging into a main branch
|
||||||
|
3) see what type of versioning we should do
|
||||||
|
4) version the repo
|
||||||
|
:param push: Whether or not to push the changes
|
||||||
|
"""
|
||||||
|
self._branch = self._scm.get_branch()
|
||||||
|
self._merged_branch = self._scm.get_merge_branch()
|
||||||
|
|
||||||
|
if not self._merged_branch:
|
||||||
|
raise NoMergeFoundException("No merge found")
|
||||||
|
if self._branch not in self._main_branches:
|
||||||
|
raise NotMainBranchException("Not a main branch")
|
||||||
|
|
||||||
|
self._version_type = self._scm.get_version_type(
|
||||||
|
self._branch,
|
||||||
|
self._major_branches,
|
||||||
|
self._minor_branches,
|
||||||
|
self._patch_branches,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self._version_type:
|
||||||
|
raise NoGitFlowException("Could not determine version type")
|
||||||
|
|
||||||
|
self._version_repo()
|
||||||
|
if push:
|
||||||
|
self._scm.commit_and_push(self._branch)
|
246
semver/tests.py
246
semver/tests.py
@ -1,246 +0,0 @@
|
|||||||
import unittest, os, subprocess, re, semver
|
|
||||||
from semver.logger import logging, logger, console_logger
|
|
||||||
|
|
||||||
from semver import bump, get_version, utils, NO_MERGE_FOUND, GET_COMMIT_MESSAGE
|
|
||||||
|
|
||||||
config_data = """
|
|
||||||
[bumpversion]
|
|
||||||
current_version = 0.0.0
|
|
||||||
commit = False
|
|
||||||
tag = True
|
|
||||||
tag_name = {new_version}
|
|
||||||
|
|
||||||
[bumpversion:file:file.txt]
|
|
||||||
search = 0.0.0
|
|
||||||
replace = {new_version}
|
|
||||||
|
|
||||||
[semver]
|
|
||||||
main_branches = master
|
|
||||||
major_branches = major
|
|
||||||
minor_branches = minor
|
|
||||||
patch_branches = patch
|
|
||||||
"""
|
|
||||||
|
|
||||||
test_directory = "test"
|
|
||||||
|
|
||||||
|
|
||||||
class TestSemverObject(unittest.TestCase):
|
|
||||||
def test_get_version_type_major_merge(self):
|
|
||||||
semver_object = semver.SemVer()
|
|
||||||
semver_object.merged_branch = "major/unittest"
|
|
||||||
semver_object.get_version_type()
|
|
||||||
self.assertEqual(semver_object.version_type, semver.VersionType.MAJOR)
|
|
||||||
def test_get_version_type_minor_merge(self):
|
|
||||||
semver_object = semver.SemVer()
|
|
||||||
semver_object.merged_branch = "minor/unittest"
|
|
||||||
semver_object.get_version_type()
|
|
||||||
self.assertEqual(semver_object.version_type, semver.VersionType.MINOR)
|
|
||||||
def test_get_version_type_patch_merge(self):
|
|
||||||
semver_object = semver.SemVer()
|
|
||||||
semver_object.merged_branch = "patch/unittest"
|
|
||||||
semver_object.get_version_type()
|
|
||||||
self.assertEqual(semver_object.version_type, semver.VersionType.PATCH)
|
|
||||||
def test_run_no_merge(self):
|
|
||||||
semver_object = semver.SemVer()
|
|
||||||
try:
|
|
||||||
result = semver_object.run(False)
|
|
||||||
except Exception as e:
|
|
||||||
if e == NO_MERGE_FOUND:
|
|
||||||
self.assertTrue(True)
|
|
||||||
else:
|
|
||||||
self.assertTrue(False)
|
|
||||||
|
|
||||||
class TestGetVersion(unittest.TestCase):
|
|
||||||
def test_get_branch_version(self):
|
|
||||||
create_git_environment()
|
|
||||||
branch = get_version.get_version()
|
|
||||||
self.assertEqual(branch, "master")
|
|
||||||
def test_branch_dotting(self):
|
|
||||||
create_git_environment()
|
|
||||||
subprocess.call(['git', 'checkout', '-b', 'test/branch'])
|
|
||||||
branch = get_version.get_version(dot=True)
|
|
||||||
self.assertEqual(branch, "test.branch")
|
|
||||||
def test_branch_dotting_false(self):
|
|
||||||
create_git_environment()
|
|
||||||
subprocess.call(['git', 'checkout', '-b', 'test/branch'])
|
|
||||||
branch = get_version.get_version(dot=False)
|
|
||||||
self.assertEqual(branch, "test/branch")
|
|
||||||
def test_branch_npm_pre_release(self):
|
|
||||||
create_git_environment()
|
|
||||||
subprocess.call(['git', 'checkout', '-b', 'patch/branch'])
|
|
||||||
branch = get_version.get_version(version_format='npm')
|
|
||||||
self.assertEqual(branch, "0.0.1-patch-branch.0")
|
|
||||||
def test_branch_docker_pre_release(self):
|
|
||||||
create_git_environment()
|
|
||||||
subprocess.call(['git', 'checkout', '-b', 'patch/branch'])
|
|
||||||
branch = get_version.get_version(build=2,version_format='docker')
|
|
||||||
self.assertEqual(branch, "0.0.1-patch-branch.2")
|
|
||||||
def test_branch_maven_pre_release(self):
|
|
||||||
create_git_environment()
|
|
||||||
subprocess.call(['git', 'checkout', '-b', 'minor/branch'])
|
|
||||||
branch = get_version.get_version(version_format='maven')
|
|
||||||
self.assertEqual(branch, "0.1.0-minor-branch-SNAPSHOT")
|
|
||||||
def test_branch_maven_bad_branch(self):
|
|
||||||
create_git_environment()
|
|
||||||
subprocess.call(['git', 'checkout', '-b', 'test/branch'])
|
|
||||||
branch = get_version.get_version(version_format='maven')
|
|
||||||
self.assertEqual(branch, "test/branch")
|
|
||||||
def test_get_version_run(self):
|
|
||||||
create_git_environment()
|
|
||||||
val = subprocess.Popen(['python', '../get_version.py', '-d'], stdout=subprocess.PIPE,
|
|
||||||
stderr=open(os.devnull, 'wb'), cwd='.').stdout.read().decode('utf-8').rstrip()
|
|
||||||
self.assertEqual(val, "master")
|
|
||||||
|
|
||||||
|
|
||||||
class TestGetTagVersion(unittest.TestCase):
|
|
||||||
def test_get_version_tag(self):
|
|
||||||
create_git_environment()
|
|
||||||
subprocess.call(['git', 'tag', '1.0.0'])
|
|
||||||
tag = utils.get_tag_version()
|
|
||||||
self.assertEqual(tag, "1.0.0")
|
|
||||||
def test_get_version_multiple(self):
|
|
||||||
create_git_environment()
|
|
||||||
subprocess.call(['git', 'tag', '0.1.0'])
|
|
||||||
subprocess.call(['git', 'tag', '0.1.1'])
|
|
||||||
subprocess.call(['git', 'tag', '0.1.2'])
|
|
||||||
subprocess.call(['git', 'tag', '0.1.3'])
|
|
||||||
subprocess.call(['git', 'tag', '0.2.0'])
|
|
||||||
subprocess.call(['git', 'tag', '0.3.0'])
|
|
||||||
subprocess.call(['git', 'tag', '0.3.1'])
|
|
||||||
subprocess.call(['git', 'tag', '1.0.0'])
|
|
||||||
subprocess.call(['git', 'tag', '1.1.0'])
|
|
||||||
subprocess.call(['git', 'tag', '1.2.0'])
|
|
||||||
subprocess.call(['git', 'tag', '1.2.1'])
|
|
||||||
tag = utils.get_tag_version()
|
|
||||||
self.assertEqual(tag, "1.2.1")
|
|
||||||
def test_get_version_out_of_order(self):
|
|
||||||
subprocess.call(['git', 'tag', '0.1.0'])
|
|
||||||
subprocess.call(['git', 'tag', '0.1.1'])
|
|
||||||
subprocess.call(['git', 'tag', '0.5.2'])
|
|
||||||
subprocess.call(['git', 'tag', '0.1.3'])
|
|
||||||
subprocess.call(['git', 'tag', '8.1.0'])
|
|
||||||
subprocess.call(['git', 'tag', '0.3.8'])
|
|
||||||
subprocess.call(['git', 'tag', '3.3.1'])
|
|
||||||
subprocess.call(['git', 'tag', '1.4.0'])
|
|
||||||
subprocess.call(['git', 'tag', '1.1.7'])
|
|
||||||
subprocess.call(['git', 'tag', '1.2.0'])
|
|
||||||
subprocess.call(['git', 'tag', '0.2.1'])
|
|
||||||
tag = utils.get_tag_version()
|
|
||||||
self.assertEqual(tag, "8.1.0")
|
|
||||||
def test_default_get_version_tag(self):
|
|
||||||
create_git_environment()
|
|
||||||
tag = utils.get_tag_version()
|
|
||||||
self.assertEqual(tag, "0.0.0")
|
|
||||||
|
|
||||||
class TestGetCommitMessageRegex(unittest.TestCase):
|
|
||||||
def test_github_message(self):
|
|
||||||
matches = GET_COMMIT_MESSAGE.search("Merge pull request #1 from user/branch")
|
|
||||||
if matches:
|
|
||||||
self.assertEqual(matches.group(4), None)
|
|
||||||
self.assertEqual(matches.group(5), "branch")
|
|
||||||
else:
|
|
||||||
self.assertTrue(False)
|
|
||||||
pass
|
|
||||||
def test_gitlab_message(self):
|
|
||||||
matches = GET_COMMIT_MESSAGE.search("Merge branch 'branch' into 'master'")
|
|
||||||
if matches:
|
|
||||||
self.assertEqual(matches.group(4), "master")
|
|
||||||
self.assertEqual(matches.group(2), "branch")
|
|
||||||
else:
|
|
||||||
self.assertTrue(False)
|
|
||||||
def test_branch_in_message(self):
|
|
||||||
matches = GET_COMMIT_MESSAGE.search(str(b'commit examplehash\nMerge: example\nAuthor: Test <test@nodomain.rightbrainnetworks.com>\nDate: Mon Jun 15 18:15:22 2020 -0400\n\n Merge pull request #45 from user/branch\n \n user/branch\n'))
|
|
||||||
if matches:
|
|
||||||
self.assertEqual(matches.group(4), None)
|
|
||||||
self.assertEqual(matches.group(5), "branch")
|
|
||||||
else:
|
|
||||||
self.assertTrue(False)
|
|
||||||
def test_non_merge_message(self):
|
|
||||||
matches = GET_COMMIT_MESSAGE.search("Example unrelated commit message that should get 0 matches")
|
|
||||||
self.assertEqual(matches, None)
|
|
||||||
def test_gitlab_merge_with_double_quotes(self):
|
|
||||||
matches = GET_COMMIT_MESSAGE.search("Merge branch 'branch' into 'master'\n\n\"Message in quotes!\"")
|
|
||||||
if matches:
|
|
||||||
self.assertEqual(matches.group(4), "master")
|
|
||||||
self.assertEqual(matches.group(2), "branch")
|
|
||||||
else:
|
|
||||||
self.assertTrue(False)
|
|
||||||
|
|
||||||
class TestVersionBumping(unittest.TestCase):
|
|
||||||
def test_patch_bump(self):
|
|
||||||
self.assertEqual("0.0.1", bump.bump_version("0.0.0", semver.VersionType.PATCH, False))
|
|
||||||
self.assertEqual("0.0.2", bump.bump_version("0.0.1", semver.VersionType.PATCH, False))
|
|
||||||
self.assertEqual("0.1.1", bump.bump_version("0.1.0", semver.VersionType.PATCH, False))
|
|
||||||
self.assertEqual("1.0.1", bump.bump_version("1.0.0", semver.VersionType.PATCH, False))
|
|
||||||
self.assertEqual("1.2.4", bump.bump_version("1.2.3", semver.VersionType.PATCH, False))
|
|
||||||
self.assertEqual("0.0.11", bump.bump_version("0.0.10", semver.VersionType.PATCH, False))
|
|
||||||
self.assertEqual("0.10.1", bump.bump_version("0.10.0", semver.VersionType.PATCH, False))
|
|
||||||
self.assertEqual("10.0.1", bump.bump_version("10.0.0", semver.VersionType.PATCH, False))
|
|
||||||
def test_minor_bump(self):
|
|
||||||
self.assertEqual("0.1.0", bump.bump_version("0.0.0", semver.VersionType.MINOR, False))
|
|
||||||
self.assertEqual("0.1.0", bump.bump_version("0.0.1", semver.VersionType.MINOR, False))
|
|
||||||
self.assertEqual("0.2.0", bump.bump_version("0.1.0", semver.VersionType.MINOR, False))
|
|
||||||
self.assertEqual("1.1.0", bump.bump_version("1.0.0", semver.VersionType.MINOR, False))
|
|
||||||
self.assertEqual("1.3.0", bump.bump_version("1.2.3", semver.VersionType.MINOR, False))
|
|
||||||
self.assertEqual("0.1.0", bump.bump_version("0.0.10", semver.VersionType.MINOR, False))
|
|
||||||
self.assertEqual("0.11.0", bump.bump_version("0.10.0", semver.VersionType.MINOR, False))
|
|
||||||
self.assertEqual("10.1.0", bump.bump_version("10.0.0", semver.VersionType.MINOR, False))
|
|
||||||
def test_major_bump(self):
|
|
||||||
self.assertEqual("1.0.0", bump.bump_version("0.0.0", semver.VersionType.MAJOR, False))
|
|
||||||
self.assertEqual("1.0.0", bump.bump_version("0.0.1", semver.VersionType.MAJOR, False))
|
|
||||||
self.assertEqual("1.0.0", bump.bump_version("0.1.0", semver.VersionType.MAJOR, False))
|
|
||||||
self.assertEqual("2.0.0", bump.bump_version("1.0.0", semver.VersionType.MAJOR, False))
|
|
||||||
self.assertEqual("2.0.0", bump.bump_version("1.2.3", semver.VersionType.MAJOR, False))
|
|
||||||
self.assertEqual("1.0.0", bump.bump_version("0.0.10", semver.VersionType.MAJOR, False))
|
|
||||||
self.assertEqual("1.0.0", bump.bump_version("0.10.0", semver.VersionType.MAJOR, False))
|
|
||||||
self.assertEqual("11.0.0", bump.bump_version("10.0.0", semver.VersionType.MAJOR, False))
|
|
||||||
class TestFileVersioning(unittest.TestCase):
|
|
||||||
def test_file_bump(self):
|
|
||||||
with open('file.txt', 'w') as f:
|
|
||||||
f.write("0.0.0")
|
|
||||||
bump.update_file_version("12.34.56")
|
|
||||||
|
|
||||||
file_data = ""
|
|
||||||
with open('file.txt', 'r') as f:
|
|
||||||
file_data = f.read()
|
|
||||||
|
|
||||||
self.assertEqual("12.34.56", file_data)
|
|
||||||
def test_file_bump_with_text(self):
|
|
||||||
with open('file.txt', 'w') as f:
|
|
||||||
f.write("version = 0.0.0")
|
|
||||||
bump.update_file_version("12.34.56")
|
|
||||||
|
|
||||||
file_data = ""
|
|
||||||
with open('file.txt', 'r') as f:
|
|
||||||
file_data = f.read()
|
|
||||||
|
|
||||||
self.assertEqual("version = 12.34.56", file_data)
|
|
||||||
def test_file_bump_with_multiline(self):
|
|
||||||
with open('file.txt', 'w') as f:
|
|
||||||
f.write("version = 0.0.0\n#An example second line\nThird line!")
|
|
||||||
bump.update_file_version("12.34.56")
|
|
||||||
|
|
||||||
file_data = ""
|
|
||||||
with open('file.txt', 'r') as f:
|
|
||||||
file_data = f.read()
|
|
||||||
|
|
||||||
self.assertEqual("version = 12.34.56", file_data.split('\n')[0])
|
|
||||||
|
|
||||||
def create_git_environment():
|
|
||||||
subprocess.call(['rm', '-rf', './.git'])
|
|
||||||
subprocess.call(['git', 'init'])
|
|
||||||
subprocess.call(['touch', 'file.txt'])
|
|
||||||
subprocess.call(['git', 'add', 'file.txt'])
|
|
||||||
subprocess.call(['git', 'commit', '-m', 'file.txt'])
|
|
||||||
subprocess.call(['git', 'remote', 'add', 'origin', os.getcwd()+'/.git'])
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
console_logger.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
subprocess.call(['rm', '-rf', test_directory])
|
|
||||||
subprocess.call(['mkdir', test_directory])
|
|
||||||
os.chdir(test_directory)
|
|
||||||
with open('.bumpversion.cfg', "w") as config:
|
|
||||||
config.write(config_data)
|
|
||||||
unittest.main()
|
|
||||||
os.chdir("..")
|
|
0
semver/tests/__init__.py
Normal file
0
semver/tests/__init__.py
Normal file
189
semver/tests/test_semver.py
Normal file
189
semver/tests/test_semver.py
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from semver.version_type import VersionType
|
||||||
|
from semver.scm import SCM
|
||||||
|
from semver.scm.mock import MockSCM
|
||||||
|
from semver.semver import SemVer
|
||||||
|
from semver.exceptions import (
|
||||||
|
NoMergeFoundException,
|
||||||
|
NoGitFlowException,
|
||||||
|
NotMainBranchException,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSemVer(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
scm = mock.MagicMock(MockSCM())
|
||||||
|
self.semver: SemVer = SemVer(scm=scm)
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._bump_version")
|
||||||
|
def test_version_repo(self, mock_bump_version: mock.Mock):
|
||||||
|
self.semver._scm.get_tag_version.return_value = "1.0.0"
|
||||||
|
self.semver._version_type = VersionType.PATCH
|
||||||
|
|
||||||
|
expected_version = "1.0.1"
|
||||||
|
mock_bump_version.return_value = expected_version
|
||||||
|
version = self.semver._version_repo()
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._bump_version")
|
||||||
|
def test_version_repo_no_tag(self, mock_bump_version: mock.Mock):
|
||||||
|
self.semver._scm.get_tag_version.return_value = "1.0.0"
|
||||||
|
self.semver._version_type = None
|
||||||
|
|
||||||
|
with self.assertRaises(NoMergeFoundException):
|
||||||
|
self.semver._version_repo()
|
||||||
|
|
||||||
|
def test_process_config_string(self):
|
||||||
|
expected_version = "v1.0.0"
|
||||||
|
version = self.semver._process_config_string("v{new_version}", "1.0.0", "1.0.1")
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
expected_version = "v1.0.1"
|
||||||
|
version = self.semver._process_config_string(
|
||||||
|
"v{current_version}", "1.0.0", "1.0.1"
|
||||||
|
)
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._update_file_version")
|
||||||
|
def test_bump_version_major(self, mock_update_file_version: mock.Mock):
|
||||||
|
expected_version = "2.0.0"
|
||||||
|
version = self.semver._bump_version("1.0.0", VersionType.MAJOR)
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._update_file_version")
|
||||||
|
def test_bump_version_minor(self, mock_update_file_version: mock.Mock):
|
||||||
|
expected_version = "1.1.0"
|
||||||
|
version = self.semver._bump_version("1.0.0", VersionType.MINOR)
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._update_file_version")
|
||||||
|
def test_bump_version_patch(self, mock_update_file_version: mock.Mock):
|
||||||
|
expected_version = "1.0.1"
|
||||||
|
version = self.semver._bump_version("1.0.0", VersionType.PATCH)
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
@mock.patch("toml.load")
|
||||||
|
@mock.patch("pathlib.Path.is_file")
|
||||||
|
@mock.patch("builtins.open", mock.mock_open())
|
||||||
|
def test_update_file_version(
|
||||||
|
self,
|
||||||
|
mock_path_is_file: mock.Mock,
|
||||||
|
mock_toml_load: mock.Mock,
|
||||||
|
):
|
||||||
|
mock_toml_load.return_value = {
|
||||||
|
"bumpversion": {"current_version": "1.0.0"},
|
||||||
|
"bumpversion:file:VERSION": {
|
||||||
|
"search": "0.0.0",
|
||||||
|
"replace": "{new_version}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mock_path_is_file.return_value = True
|
||||||
|
self.semver._update_file_version("1.0.1", "1.0.0")
|
||||||
|
|
||||||
|
mock_path_is_file.return_value = False
|
||||||
|
self.semver._update_file_version("1.0.1", "1.0.0")
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._version_repo", mock.MagicMock())
|
||||||
|
def test_run_ok(self):
|
||||||
|
self.semver._version_repo = mock.MagicMock()
|
||||||
|
self.semver._version_repo.return_value = "1.0.1"
|
||||||
|
self.semver._scm.get_branch.return_value = "main"
|
||||||
|
self.semver._scm.get_merge_branch.return_value = "main"
|
||||||
|
self.semver._scm.get_version_type.return_value = VersionType.MINOR
|
||||||
|
self.semver._scm.commit_and_push.return_value = None
|
||||||
|
|
||||||
|
self.semver._main_branches = ["main"]
|
||||||
|
self.semver.run()
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._version_repo", mock.MagicMock())
|
||||||
|
def test_run_not_merge(self):
|
||||||
|
self.semver._version_repo = mock.MagicMock()
|
||||||
|
self.semver._version_repo.return_value = "1.0.1"
|
||||||
|
self.semver._scm.get_branch.return_value = "main"
|
||||||
|
self.semver._scm.get_merge_branch.return_value = None
|
||||||
|
self.semver._scm.get_version_type.return_value = VersionType.MINOR
|
||||||
|
self.semver._scm.commit_and_push.return_value = None
|
||||||
|
|
||||||
|
self.semver._main_branches = ["main"]
|
||||||
|
|
||||||
|
with self.assertRaises(NoMergeFoundException):
|
||||||
|
self.semver.run()
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._version_repo", mock.MagicMock())
|
||||||
|
def test_run_not_version_type(self):
|
||||||
|
self.semver._version_repo = mock.MagicMock()
|
||||||
|
self.semver._version_repo.return_value = "1.0.1"
|
||||||
|
self.semver._scm.get_branch.return_value = "feature/example"
|
||||||
|
self.semver._scm.get_merge_branch.return_value = "main"
|
||||||
|
self.semver._scm.get_version_type.return_value = VersionType.MINOR
|
||||||
|
self.semver._scm.commit_and_push.return_value = None
|
||||||
|
|
||||||
|
self.semver._main_branches = ["main"]
|
||||||
|
|
||||||
|
with self.assertRaises(NotMainBranchException):
|
||||||
|
self.semver.run()
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._version_repo", mock.MagicMock())
|
||||||
|
def test_run_not_main_branch(self):
|
||||||
|
self.semver._version_repo = mock.MagicMock()
|
||||||
|
self.semver._version_repo.return_value = "1.0.1"
|
||||||
|
self.semver._scm.get_branch.return_value = "main"
|
||||||
|
self.semver._scm.get_merge_branch.return_value = "main"
|
||||||
|
self.semver._scm.get_version_type.return_value = None
|
||||||
|
self.semver._scm.commit_and_push.return_value = None
|
||||||
|
|
||||||
|
self.semver._main_branches = ["main"]
|
||||||
|
|
||||||
|
with self.assertRaises(NoGitFlowException):
|
||||||
|
self.semver.run()
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._bump_version")
|
||||||
|
def test_get_version(self, mock_bump_version: mock.Mock):
|
||||||
|
self.semver._scm.get_branch.return_value = "feature/example"
|
||||||
|
self.semver._scm.get_tag_version.return_value = "1.0.0"
|
||||||
|
self.semver._scm.get_version_hash.return_value = "HASH"
|
||||||
|
self.semver._scm.get_hash.return_value = "ALT_HASH"
|
||||||
|
|
||||||
|
mock_bump_version.return_value = "1.0.1"
|
||||||
|
|
||||||
|
expected_version = "1.0.0+HASH"
|
||||||
|
version = self.semver.get_version(dot=True)
|
||||||
|
self.assertEqual(version, "feature.example")
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._bump_version")
|
||||||
|
def test_get_version_docker(self, mock_bump_version: mock.Mock):
|
||||||
|
self.semver._scm.get_branch.return_value = "feature/example"
|
||||||
|
self.semver._scm.get_tag_version.return_value = "1.0.0"
|
||||||
|
self.semver._scm.get_version_hash.return_value = "HASH"
|
||||||
|
self.semver._scm.get_hash.return_value = "ALT_HASH"
|
||||||
|
|
||||||
|
mock_bump_version.return_value = "1.0.1"
|
||||||
|
|
||||||
|
expected_version = "1.0.0+HASH"
|
||||||
|
version = self.semver.get_version(version_format="docker")
|
||||||
|
self.assertEqual(version, "1.0.1-feature-example.0")
|
||||||
|
|
||||||
|
@mock.patch("semver.semver.SemVer._bump_version")
|
||||||
|
def test_get_version_maven(self, mock_bump_version: mock.Mock):
|
||||||
|
self.semver._scm.get_branch.return_value = "feature/example"
|
||||||
|
self.semver._scm.get_tag_version.return_value = "1.0.0"
|
||||||
|
self.semver._scm.get_version_hash.return_value = "HASH"
|
||||||
|
self.semver._scm.get_hash.return_value = "ALT_HASH"
|
||||||
|
|
||||||
|
mock_bump_version.return_value = "1.0.1"
|
||||||
|
|
||||||
|
expected_version = "1.0.1-feature-example-SNAPSHOT"
|
||||||
|
version = self.semver.get_version(version_format="maven")
|
||||||
|
self.assertEqual(version, expected_version)
|
||||||
|
|
||||||
|
def test_get_version_no_hash(self):
|
||||||
|
self.semver._scm.get_branch.return_value = "main"
|
||||||
|
self.semver._scm.get_tag_version.return_value = "1.0.0"
|
||||||
|
self.semver._scm.get_version_hash.return_value = "HASH"
|
||||||
|
self.semver._scm.get_hash.return_value = "HASH"
|
||||||
|
|
||||||
|
expected_version = "1.0.0"
|
||||||
|
version = self.semver.get_version()
|
||||||
|
self.assertEqual(version, expected_version)
|
59
semver/tests/test_utils.py
Normal file
59
semver/tests/test_utils.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from semver.utils import get_settings, setting_to_array
|
||||||
|
from semver.exceptions import SemverException
|
||||||
|
|
||||||
|
|
||||||
|
class TestUtils(unittest.TestCase):
|
||||||
|
@mock.patch("toml.load")
|
||||||
|
@mock.patch("pathlib.Path.is_file")
|
||||||
|
def test_get_settings_toml(self, mock_is_file: mock.Mock, mock_toml: mock.Mock):
|
||||||
|
get_settings.cache_clear()
|
||||||
|
|
||||||
|
mock_is_file.side_effect = [True, False]
|
||||||
|
|
||||||
|
mock_toml.return_value = {"1": {"a": "alpha", "fruit": "apple"}}
|
||||||
|
settings = get_settings()
|
||||||
|
self.assertEqual(settings, {"1": {"a": "alpha", "fruit": "apple"}})
|
||||||
|
|
||||||
|
@mock.patch("configparser.ConfigParser")
|
||||||
|
@mock.patch("pathlib.Path.is_file")
|
||||||
|
def test_get_settings_cfg(
|
||||||
|
self, mock_is_file: mock.Mock, mock_config_parser: mock.Mock
|
||||||
|
):
|
||||||
|
get_settings.cache_clear()
|
||||||
|
|
||||||
|
mock_is_file.side_effect = [False, True]
|
||||||
|
|
||||||
|
mock_config_parser.return_value.read.return_value = ["./.bumpversion.cfg"]
|
||||||
|
mock_config_parser.return_value.sections.return_value = ["1", "2", "3"]
|
||||||
|
mock_config_parser.return_value.items.side_effect = [
|
||||||
|
[("a", "alpha"), ("fruit", "apple")],
|
||||||
|
[("b", "bravo"), ("fruit", "banana")],
|
||||||
|
[("c", "charlie"), ("fruit", "cherry")],
|
||||||
|
]
|
||||||
|
|
||||||
|
settings = get_settings()
|
||||||
|
self.assertEqual(
|
||||||
|
settings,
|
||||||
|
{
|
||||||
|
"1": {"a": "alpha", "fruit": "apple"},
|
||||||
|
"2": {"b": "bravo", "fruit": "banana"},
|
||||||
|
"3": {"c": "charlie", "fruit": "cherry"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch("pathlib.Path.is_file")
|
||||||
|
def test_get_settings_no_file(self, mock_is_file: mock.Mock):
|
||||||
|
get_settings.cache_clear()
|
||||||
|
|
||||||
|
mock_is_file.side_effect = [False, False]
|
||||||
|
with self.assertRaises(SemverException):
|
||||||
|
get_settings()
|
||||||
|
|
||||||
|
@mock.patch("semver.utils.get_settings")
|
||||||
|
def test_setting_to_array(self, mock_get_settings: mock.Mock):
|
||||||
|
mock_get_settings.return_value = {"semver": {"test": "test1, test2"}}
|
||||||
|
settings = setting_to_array("test")
|
||||||
|
self.assertEqual(settings, ["test1", "test2"])
|
@ -1,41 +1,38 @@
|
|||||||
import subprocess
|
from typing import List
|
||||||
from semver.logger import logging, logger, console_logger
|
from pathlib import Path
|
||||||
|
from functools import lru_cache
|
||||||
|
import configparser
|
||||||
|
|
||||||
try:
|
import toml
|
||||||
from configparser import ConfigParser
|
|
||||||
except ImportError:
|
|
||||||
# Python < 3
|
|
||||||
from ConfigParser import ConfigParser
|
|
||||||
|
|
||||||
try:
|
from semver.exceptions import SemverException
|
||||||
from subprocess import DEVNULL # py3k
|
|
||||||
except ImportError:
|
|
||||||
import os
|
|
||||||
DEVNULL = open(os.devnull, 'wb')
|
|
||||||
|
|
||||||
def get_tag_version():
|
|
||||||
config = ConfigParser()
|
|
||||||
config.read('./.bumpversion.cfg')
|
|
||||||
tag_expression = config.get('bumpversion','tag_name').replace('{new_version}','[0-9]*.[0-9]*.[0-9]*')
|
|
||||||
|
|
||||||
logger.debug("Tag expression: " + str(tag_expression))
|
@lru_cache(maxsize=None)
|
||||||
|
def get_settings() -> dict:
|
||||||
|
"""
|
||||||
|
Get the settings from the config file
|
||||||
|
:return: The settings from the config file
|
||||||
|
"""
|
||||||
|
if Path("./.bumpversion.toml").is_file():
|
||||||
|
return toml.load("./.bumpversion.toml")
|
||||||
|
if Path("./.bumpversion.cfg").is_file():
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read("./.bumpversion.cfg")
|
||||||
|
|
||||||
# Default version is `0.0.0` or what is found in
|
return {section: dict(config.items(section)) for section in config.sections()}
|
||||||
version = get_file_version(config)
|
|
||||||
|
|
||||||
# If a version is found in tags, use that the lastest tagged version
|
raise SemverException("No config file found")
|
||||||
tagged_versions = subprocess.Popen(['git','tag','--sort=v:refname', '-l',tag_expression],
|
|
||||||
stdout=subprocess.PIPE, stderr=DEVNULL, cwd=".").stdout.read().decode('utf-8').rstrip().split('\n')
|
|
||||||
if len(tagged_versions) > 0 and tagged_versions[-1] != "":
|
|
||||||
version = tagged_versions[-1]
|
|
||||||
|
|
||||||
logger.debug("Tag Version: " + str(version))
|
|
||||||
return version
|
|
||||||
|
|
||||||
def get_file_version(config):
|
def setting_to_array(setting) -> List[str]:
|
||||||
version = config.get('bumpversion','current_version')
|
"""
|
||||||
if not version:
|
Get a setting from the config file and return it as a list
|
||||||
config.set('bumpversion', 'current_version', '0.0.0')
|
:param setting: The setting to get from the config file
|
||||||
version = '0.0.0'
|
:return: The setting as a list
|
||||||
return version
|
"""
|
||||||
|
config: dict = get_settings()
|
||||||
|
semver: dict = config.get("semver", {})
|
||||||
|
value: str = semver.get(setting, "")
|
||||||
|
|
||||||
|
return [v.strip() for v in value.split(",") if v.strip()]
|
||||||
|
7
semver/version_type.py
Normal file
7
semver/version_type.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class VersionType(IntEnum):
|
||||||
|
MAJOR = 0
|
||||||
|
MINOR = 1
|
||||||
|
PATCH = 2
|
41
setup.cfg
Normal file
41
setup.cfg
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
[metadata]
|
||||||
|
name = semver
|
||||||
|
author = RightBrain Networks
|
||||||
|
author_email = cloud@rightbrainnetworks.com
|
||||||
|
description = Automatic Semantic Versioner
|
||||||
|
long_description = file: README.md, license.txt
|
||||||
|
url = https://github.com/RightBrain-Networks/auto-semver
|
||||||
|
|
||||||
|
license = Apache2.0
|
||||||
|
keywords = Semantic, Version, Git, Auto-Versioning
|
||||||
|
classifiers =
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
License :: OSI Approved :: Apache Software License
|
||||||
|
|
||||||
|
# Development Status
|
||||||
|
Development Status :: 3 - Alpha
|
||||||
|
|
||||||
|
# Audience
|
||||||
|
Intended Audience :: Developers
|
||||||
|
Intended Audience :: System Administrators
|
||||||
|
Topic :: Software Development :: Build Tools
|
||||||
|
|
||||||
|
|
||||||
|
[options]
|
||||||
|
include_package_data = True
|
||||||
|
packages = find:
|
||||||
|
python_requires = >=3.8, <4
|
||||||
|
install_requires =
|
||||||
|
toml
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
exclude =
|
||||||
|
contrib*
|
||||||
|
docs*
|
||||||
|
tests*
|
||||||
|
|
||||||
|
|
||||||
|
[options.entry_points]
|
||||||
|
console_scripts =
|
||||||
|
semver = semver:main
|
||||||
|
semver_get_version = semver.get_version:main
|
52
setup.py
52
setup.py
@ -1,11 +1,12 @@
|
|||||||
import re
|
import re
|
||||||
from pathlib import Path, StrPath
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
# To use a consistent encoding
|
# To use a consistent encoding
|
||||||
from codecs import open
|
from codecs import open
|
||||||
|
|
||||||
# Always prefer setuptools over distutils
|
# Always prefer setuptools over distutils
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup
|
||||||
|
|
||||||
|
|
||||||
here = Path(__file__).resolve().parent
|
here = Path(__file__).resolve().parent
|
||||||
@ -15,7 +16,7 @@ with open(Path(here, "README.md"), encoding="utf-8") as f:
|
|||||||
long_description = f.read()
|
long_description = f.read()
|
||||||
|
|
||||||
|
|
||||||
def find_version(*file_path: StrPath) -> str:
|
def find_version(*file_path: List[str]) -> str:
|
||||||
"""
|
"""
|
||||||
Searches for the semantic version within the given path
|
Searches for the semantic version within the given path
|
||||||
:param file_path: Path to the file to search
|
:param file_path: Path to the file to search
|
||||||
@ -29,47 +30,4 @@ def find_version(*file_path: StrPath) -> str:
|
|||||||
return version_match.group(1)
|
return version_match.group(1)
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(version=find_version("semver", "__init__.py"))
|
||||||
name="semver",
|
|
||||||
version=find_version("semver", "__init__.py"),
|
|
||||||
description="Automatic Semantic Versioner",
|
|
||||||
long_description=long_description,
|
|
||||||
url="https://github.com/RightBrain-Networks/auto-semver",
|
|
||||||
# Author details
|
|
||||||
author="RightBrain Networks",
|
|
||||||
author_email="cloud@rightbrainnetworks.com",
|
|
||||||
# Choose your license
|
|
||||||
license="Apache2.0",
|
|
||||||
# ======== #
|
|
||||||
# Metadata #
|
|
||||||
# ======== #
|
|
||||||
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
|
||||||
classifiers=[
|
|
||||||
"License :: OSI Approved :: Apache Software License",
|
|
||||||
# Development Status
|
|
||||||
"Development Status :: 3 - Alpha",
|
|
||||||
# Audience
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"Intended Audience :: System Administrators",
|
|
||||||
"Topic :: Software Development :: Build Tools",
|
|
||||||
# Supported Python Versions
|
|
||||||
"Programming Language :: Python :: 3.6",
|
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
|
||||||
],
|
|
||||||
keywords=["Semantic", "Version", "Git", "Auto-Versioning"],
|
|
||||||
# ======= #
|
|
||||||
# Package #
|
|
||||||
# ======= #
|
|
||||||
packages=find_packages(exclude=["contrib", "docs", "tests"]),
|
|
||||||
install_requires=["argparse>=1.4.0"],
|
|
||||||
package_data={},
|
|
||||||
entry_points={
|
|
||||||
"console_scripts": [
|
|
||||||
"semver = semver:main",
|
|
||||||
"semver_get_version = semver.get_version:main",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
Reference in New Issue
Block a user