diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bf018ccdfedb48b6bfe927483242fa44cb888251..29c4be0267154630dd6f106d4cab2dc6b22aee9f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,8 +1,83 @@
 stages:
   - test
+  - build
+  - check
+  - deploy
 
 mdcheck:
   stage: test
   image: it4innovations/docker-mdcheck:latest
   script:
     - mdl *.md
+
+pycodestyle:
+  stage: test
+  image: it4innovations/docker-pycheck:latest
+  script:
+  - pycodestyle --max-line-length=100 *.py
+
+pylint:
+  stage: test
+  image: it4innovations/docker-pycheck:latest
+  before_script:
+    - export PYTHONIOENCODING=UTF-8
+    - export LC_CTYPE=en_US.UTF-8
+    - python setup.py egg_info
+    - pip install $(paste -d " " -s pipdeps.egg-info/requires.txt)
+  script:
+    - pylint $(find . -type f -not -path "*/.eggs/*" -name "*.py")
+
+build:
+  stage: build
+  image: it4innovations/docker-pypi:latest
+  artifacts:
+    expire_in: 1 day
+    paths:
+      - dist/pipdeps*tar.gz
+  before_script:
+    - export PYTHONIOENCODING=UTF-8
+    - export LC_CTYPE=en_US.UTF-8
+    - pip install mustache pystache
+  script:
+    - echo "output_engine = mustache(\"restructuredtext\")" >> .gitchangelog.rc
+    - gitchangelog >> HISTORY.txt
+    - cat HISTORY.txt
+    - echo "output_engine = mustache(\"markdown\")" >> .gitchangelog.rc
+    - echo >> README.md
+    - gitchangelog | sed -r '1,/^# Changelog$/s/^(# Changelog)$/#\1/' >> README.md
+    - python setup.py --version
+    - echo "version = '$(python setup.py --version)'" >> version.py
+    - python setup.py sdist
+
+Install test:
+  stage: check
+  image: it4innovations/docker-pypi:latest
+  script:
+    - pip install dist/pipdeps*tar.gz
+    - pipdeps -l || true
+    - pipdeps -u
+
+versioncheck:
+  stage: check
+  image: it4innovations/docker-pypi:latest
+  before_script:
+    - pip install packaging
+  script:
+    - python setup.py --version
+    - export BUILD_VERSION="$(python setup.py --version)"
+    - >-
+         export PUBLISHED_VERSION="$(pip install pipdeps== 2>&1 | grep -oE "\(from versions: .*)" | sed "s/(from versions: //" | sed "s/)//" | tr ", " "\n" | tail -n1)"
+    #- CMP_VERSION="$(cmp-version $BUILD_VERSION $PUBLISHED_VERSION)"
+    #- if [ $CMP_VERSION -eq 1 ]; then true; else echo 'Git tag is older/same version as module already available from public pypi repository. Please run git tag -a <version> -m "<version>"'; false; fi
+    - >-
+         echo -e "import sys\nfrom packaging.version import Version, LegacyVersion\nver1=Version('$BUILD_VERSION')\nver2=Version('$PUBLISHED_VERSION')\nif ver1>ver2: sys.exit(0)\nif ver1<ver2: sys.exit(1)\nif ver1==ver2: sys.exit(2)" | python
+    - if [ $? -eq 0 ]; then true; else echo 'Git tag is older/same version as module already available from public pypi repository. Please run git tag -a <version> -m "<version>"'; false; fi
+
+upload:
+  stage: deploy
+  image: it4innovations/docker-pypi:latest
+  script:
+    - twine upload -u "$PYPI_USERNAME" -p "$PYPI_PASSWORD" dist/pipdeps*tar.gz
+  only:
+    - master
+  when: manual
diff --git a/LICENSE b/LICENSE
index e76b29762f914dea666f9a46fece03dc993d630c..4b457196f03b30eb739a4d6afa6a41bad35f4074 100644
--- a/LICENSE
+++ b/LICENSE
@@ -631,7 +631,7 @@ to attach them to the start of each source file to most effectively
 state the exclusion of warranty; and each file should have at least
 the "copyright" line and a pointer to where the full notice is found.
 
-    pip-upgradedependencies
+    pipdeps
     Copyright (C) 2019  IT4Innovations National Supercomputing Center,
     VSB - Technical University of Ostrava, Czech Republic
 
@@ -653,7 +653,7 @@ Also add information on how to contact you by electronic and paper mail.
   If the program does terminal interaction, make it output a short
 notice like this when it starts in an interactive mode:
 
-    pip-upgradedependencies  Copyright (C) 2019  
+    pipdeps  Copyright (C) 2019  
     IT4Innovations National Supercomputing Center,
     VSB - Technical University of Ostrava, Czech Republic
     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
diff --git a/README.md b/README.md
index a20bc7b24abf2ed06c644564b4337926e42dca32..c3914badc5e24445ee8c2edec41850a9d18d6560 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,22 @@
-# pip-upgradedependencies
+# pipdeps
 
-Pip-upgradedependencies upgrades all outdated packages with respect to existing dependencies.
+Pipdeps shows/upgrades outdated packages with respect to existing dependencies.
 
 Python 2.7 is required.
 
 ## Usage
 
 ```console
-pip-upgradedependencies
+$ pipdeps.py --help
+usage: pipdeps.py [-h] (-l | -u | -s SHOW [SHOW ...])
+
+Pipdeps shows/upgrades outdated packages with respect to existing
+dependencies.
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -l, --list            show upgradeable packages and versions
+  -u, --upgrade         upgrade upgradeable packages
+  -s SHOW [SHOW ...], --show SHOW [SHOW ...]
+                        show detailed info about upgradeable packages
 ```
diff --git a/pipdeps/__init__.py b/pipdeps/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2da04136d4d43e6cab79b88e7d2e8c882b7972e
--- /dev/null
+++ b/pipdeps/__init__.py
@@ -0,0 +1,4 @@
+"""
+pipdeps init
+"""
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/pipdeps/pipdeps.py b/pipdeps/pipdeps.py
new file mode 100644
index 0000000000000000000000000000000000000000..073a2168e8dd82b30c0cf9aa023e038eb7e0ea6e
--- /dev/null
+++ b/pipdeps/pipdeps.py
@@ -0,0 +1,374 @@
+"""
+pipdeps
+"""
+import argparse
+import json
+import distutils.version
+import os
+import pprint
+import re
+import subprocess
+import sys
+import urllib2
+import tarfile
+import tempfile
+import zipfile
+
+import tabulate
+import packaging.specifiers
+import packaging.version
+
+def arg_parse():
+    """
+    argument parser
+    """
+    parser = argparse.ArgumentParser(
+        description="Pipdeps shows/upgrades outdated packages with respect to existing \
+                     dependencies."
+    )
+    group = parser.add_mutually_exclusive_group(required=True)
+    group.add_argument('-l', '--list',
+                       action='store_true',
+                       help="show upgradeable packages and versions")
+    group.add_argument('-u', '--upgrade',
+                       action='store_true',
+                       help="upgrade upgradeable packages")
+    group.add_argument('-s', '--show',
+                       nargs='+',
+                       help="show detailed info about upgradeable packages")
+    return parser.parse_args()
+
+def get_pyver():
+    """
+    return running python version
+    """
+    return ".".join(map(str, sys.version_info[:3]))
+
+def is_strict_version(version):
+    """
+    Return true if version is strict, otherwise return false
+    """
+    try:
+        distutils.version.StrictVersion(version)
+    except ValueError:
+        return False
+    return True
+
+def version_conform_specifiers(version, specifiers):
+    """
+    check if version conforms specifiers
+    """
+    if not specifiers:
+        return True
+    elif version is None:
+        return True
+    else:
+        ver = packaging.version.Version(version)
+        spec = packaging.specifiers.SpecifierSet(",".join(specifiers))
+        if spec.contains(ver):
+            return True
+    return False
+
+def upgrade_package(package, versions):
+    """
+    pip install --upgrade "<package><versions>"
+    """
+    subprocess.check_call(
+        ["pip", "install", "--upgrade", "%s==%s" % (package, "".join(versions))],
+        stderr=subprocess.STDOUT
+    )
+
+def get_pip_list():
+    """
+    pip list
+    """
+    outdated_packages = subprocess.check_output(["pip", "list"])
+    return [line.split()[0] for line in outdated_packages.strip().split("\n")[2:]]
+
+def file_download(url):
+    """
+    Download file from url as temporary file
+    It returns file object
+    """
+    tmp_file = tempfile.NamedTemporaryFile(delete=False)
+    rfile = urllib2.urlopen(url)
+    with tmp_file as output:
+        output.write(rfile.read())
+    return tmp_file
+
+def get_jsonpipdeptree():
+    """
+    pipdeptree --json-tree
+    """
+    pipdeptree = subprocess.check_output(
+        ["pipdeptree", "--json-tree"],
+        stderr=subprocess.STDOUT
+    )
+    return json.loads(pipdeptree.strip())
+
+def get_json(url):
+    """
+    Return url json
+    """
+    return json.load(urllib2.urlopen(urllib2.Request(url)))
+
+def json_search(jsonpipdeptree, package, key):
+    """
+    find package dependencies in json tree
+    """
+    if isinstance(jsonpipdeptree, dict):
+        keys = jsonpipdeptree.keys()
+        if 'package_name' in keys and key in keys:
+            if re.search(r'^%s$' % package, jsonpipdeptree['package_name'], re.IGNORECASE):
+                yield jsonpipdeptree[key]
+        for child_val in json_search(jsonpipdeptree['dependencies'], package, key):
+            yield child_val
+    elif isinstance(jsonpipdeptree, list):
+        for item in jsonpipdeptree:
+            for item_val in json_search(item, package, key):
+                yield item_val
+
+def get_highest_version(package, data):
+    """
+    Return upgradeable version if possible, otherwise return installed version
+    """
+    try:
+        version = data[package]['upgradeable_version']
+    except KeyError:
+        version = data[package]['installed_version']
+    return version
+
+def find_available_vers(package_name, pyver):
+    """
+    Return descending list of available strict version
+    """
+    versions = []
+    data = get_json("https://pypi.python.org/pypi/%s/json" % (package_name,))
+    releases = data["releases"].keys()
+    for release in releases:
+        requires_python = []
+        for item in data["releases"][release]:
+            if item['requires_python'] is not None:
+                requires_python.append(item['requires_python'])
+        if is_strict_version(release) and version_conform_specifiers(pyver, requires_python):
+            versions.append(release)
+    return sorted(versions, key=distutils.version.StrictVersion, reverse=True)
+
+def get_newer_vers(available_version, required_version, installed_version=None):
+    """
+    Return list of newer versions which conforms pipdeptree dependencies, otherwise return none.
+    """
+    if [rver for rver in required_version if re.search(r'(^==.*|^\d.*)', rver) is not None]:
+        return None
+    result = []
+    av_version = list(available_version)
+    while True:
+        try:
+            version = av_version.pop(0)
+        except IndexError:
+            break
+        aver = packaging.version.Version(version)
+        rver = packaging.specifiers.SpecifierSet(",".join(required_version))
+        if rver.contains(aver):
+            if installed_version is not None:
+                iver = packaging.version.Version(installed_version)
+                if aver == iver:
+                    break
+                elif aver > iver:
+                    result.append(version)
+            else:
+                result.append(version)
+    if result:
+        return sorted(result, key=distutils.version.StrictVersion, reverse=True)
+    return None
+
+def parse_requires_txt(package, version):
+    """
+    Return content of requires.txt until first [ appears
+    """
+    content = None
+    release_data = get_json("https://pypi.python.org/pypi/%s/%s/json" % (package, version,))
+    for item in release_data['releases'][version]:
+        if item['packagetype'] == 'sdist':
+            tmp_file = file_download(item['url'])
+            try:
+                tar_file = tarfile.open(tmp_file.name, 'r')
+                for member in tar_file.getmembers():
+                    if 'requires.txt' in member.name:
+                        content = tar_file.extractfile(member)
+            except tarfile.ReadError:
+                zip_file = zipfile.ZipFile(tmp_file.name, 'r')
+                for member in zip_file.namelist():
+                    if 'requires.txt' in member:
+                        content = zip_file.read(member)
+    if content is not None:
+        par = []
+        for line in content:
+            if '[' in line:
+                break
+            else:
+                par.append(line.strip())
+        content = "\n".join(par)
+    os.unlink(tmp_file.name)
+    return content
+
+def find_new_dependencies(package, version, package_list, pyver):
+    """
+    Return package dependencies parsed from pypi json
+    """
+    content = parse_requires_txt(package, version)
+    if content is not None:
+        for line in content.split("\n"):
+            try:
+                pkg = re.search(r'^([a-zA-Z0-9_.-]+)', line).group(0)
+                dep = line.replace(pkg, '').strip()
+                if not dep:
+                    dep = None
+                if pkg in package_list:
+                    yield (pkg, dep)
+                else:
+                    for child in find_new_dependencies(
+                            pkg,
+                            get_newer_vers(find_available_vers(pkg, pyver), dep, None)[0],
+                            package_list,
+                            pyver
+                        ):
+                        yield child
+            except AttributeError:
+                pass
+
+def depless_vers(res):
+    """
+    If there is no dependencies or versionless dependencies, return the upgradeable version,
+    otherwise return None
+    """
+    depless = []
+    for ver, deps in res.iteritems():
+        if not deps:
+            depless.append(ver)
+        else:
+            if not [dep for dep in deps if dep[1] is not None]:
+                depless.append(ver)
+    if depless:
+        depless = sorted(depless, key=distutils.version.StrictVersion, reverse=True)[0]
+    else:
+        depless = None
+    return depless
+
+def collect_packages(package_list, jsonpipdeptree, pyver=None):
+    """
+    Collect data about packages as dictionary
+    """
+    result = {}
+    for package in package_list:
+        installed_version = "".join(list(set(
+            [_ for _ in json_search(jsonpipdeptree, package, 'installed_version')])))
+        required_version = []
+        for dep in list(set(
+                [_ for _ in json_search(jsonpipdeptree, package, 'required_version')]
+            )):
+            if 'Any' not in dep:
+                required_version.append(dep)
+        available_version = find_available_vers(package, pyver)
+        newer_version = get_newer_vers(available_version, required_version, installed_version)
+        rev = {'installed_version': installed_version,
+               'required_version': required_version,
+               'available_version': available_version}
+        if newer_version is not None:
+            res = {}
+            for version in newer_version:
+                res[version] = [
+                    _ for _ in find_new_dependencies(package, version, package_list, pyver)]
+            rev['newer_version'] = res
+
+            depless = depless_vers(res)
+            if depless:
+                rev['upgradeable_version'] = depless
+
+        result[package] = rev
+    return result
+
+def check_deps(deps, packages_data):
+    """
+    Return true, if all package dependencies conforms
+    """
+    ndeps = []
+    for item in deps:
+        if item[1] is not None:
+            ndeps.append(
+                version_conform_specifiers(
+                    get_highest_version(item[0], packages_data),
+                    packages_data[item[0]]['required_version']+[item[1]]
+                )
+            )
+    return all(ndeps)
+
+def select_pkgs(packages_data, rkey):
+    """
+    Return data packages having requested key
+    """
+    result = {}
+    for pkg, pkg_data in packages_data.iteritems():
+        if rkey in pkg_data.keys():
+            result[pkg] = pkg_data
+    return result
+
+def print_list(upgradeable_pkgs):
+    """
+    Provides list option
+    """
+    if upgradeable_pkgs:
+        data = []
+        for pkg, pkg_data in sorted(upgradeable_pkgs.iteritems(), key=lambda x: x[0].lower()):
+            data.append([pkg, pkg_data['installed_version'], pkg_data['upgradeable_version']])
+        print tabulate.tabulate(
+            data,
+            ['package', 'installed_version', 'upgradeable_version']
+        )
+        sys.exit(1)
+    else:
+        print "There is nothing to upgrade."
+        sys.exit(0)
+
+def main():
+    """
+    main function
+    """
+    os.environ["PYTHONWARNINGS"] = "ignore:DEPRECATION"
+    arguments = arg_parse()
+    pyver = get_pyver()
+    pkglist = get_pip_list()
+    jsonpipdeptree = get_jsonpipdeptree()
+    packages_data = collect_packages(pkglist, jsonpipdeptree, pyver=pyver)
+    for pkg, pkg_data in sorted(
+            select_pkgs(packages_data, 'newer_version').iteritems(), key=lambda x: x[0].lower()
+        ):
+        pkg_keys = pkg_data.keys()
+        if 'newer_version' in pkg_keys and 'upgradeable_version' not in pkg_keys:
+            for ver, deps in sorted(
+                    pkg_data['newer_version'].iteritems(),
+                    key=lambda x: distutils.version.StrictVersion(x[0]),
+                    reverse=True
+                ):
+                ndeps = check_deps(deps, packages_data)
+                if ndeps:
+                    packages_data[pkg]['upgradeable_version'] = ver
+                    break
+    upgradeable_pkgs = select_pkgs(packages_data, 'upgradeable_version')
+
+    if arguments.list:
+        print_list(upgradeable_pkgs)
+    if arguments.show:
+        for pkg in arguments.show:
+            pprint.pprint({pkg: packages_data[pkg]})
+        sys.exit(0)
+    if arguments.upgrade:
+        if 'pip' in upgradeable_pkgs.keys():
+            upgrade_package('pip', upgradeable_pkgs['pip']['upgradeable_version'])
+            del upgradeable_pkgs['pip']
+        for pkg, pkg_data in sorted(upgradeable_pkgs.iteritems(), key=lambda x: x[0].lower()):
+            upgrade_package(pkg, pkg_data['upgradeable_version'])
+        print "Done."
+
+if __name__ == "__main__":
+    main()
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..10457bd205c4ae45701f746c1cb4486a018d732a
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,38 @@
+"""
+pipdeps setup
+"""
+from setuptools import setup, find_packages
+
+setup(
+    name='pipdeps',
+
+    description='Pipdeps shows/upgrades outdated packages with respect to existing \
+                     dependencies.',
+    classifiers=[
+        'Operating System :: POSIX :: Linux',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 2.7',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+    ],
+
+    author='IT4Innovations',
+    author_email='support@it4i.cz',
+    url='https://code.it4i.cz/sccs/pip-deps',
+    license='GPLv3+',
+    packages=find_packages(),
+    namespace_packages=['pipdeps'],
+    zip_safe=False,
+    version_format='{tag}',
+    long_description_markdown_filename='README.md',
+    setup_requires=['mustache', 'pystache', 'setuptools-git-version', 'setuptools-markdown'],
+    install_requires=[
+        'packaging',
+        'pipdeptree',
+        'tabulate',
+    ],
+    entry_points={
+        'console_scripts': [
+            'pipdeps = pipdeps.pipdeps:main',
+        ]
+    }
+)