diff --git a/.travis.yml b/.travis.yml index c4bdb859..9cca7663 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,15 @@ --- # Add additional stages in the order of execution here, and then under the job:include: key stages: - - "lint" - - "test" + - name: "lint" + - name: "test" + - name: "verify_pypi_release" + if: "tag IS present" + - name: "deploy_github" + if: "tag IS present" + - name: "deploy_pypi" + if: "tag IS present" -if: "type IN (pull_request)" # Add in "branch" as an option if desired for branch testing as well language: "python" env: @@ -40,3 +45,40 @@ jobs: - "invoke flake8" - "invoke yamllint" - "invoke pylint" + - stage: "verify_pypi_release" + before_script: + - "pip install --upgrade pip" + - "pip install invoke poetry toml" + - "poetry config virtualenvs.create false" + - "poetry install --no-interaction" + script: + - "invoke check-pypi-version" + - stage: "deploy_github" + before_script: + - "pip install --upgrade pip" + - "pip install poetry" + script: + - "echo Deploying the release to GitHub" + - "poetry version $TRAVIS_TAG" + - "poetry build" + deploy: + provider: "releases" + api_key: "$GITHUB_AUTH_TOKEN" + file_glob: true + file: "dist/*" + skip_cleanup: true + "on": + all_branches: true + - stage: "deploy_pypi" + before_script: + - "pip install --upgrade pip" + - "pip install poetry" + script: + - "echo Deploying the release to PyPI" + - "poetry version $TRAVIS_TAG" + deploy: + provider: "script" + skip_cleanup: true + script: "poetry publish --build -u __token__ -p $PYPI_TOKEN" + "on": + all_branches: true diff --git a/netutils/__init__.py b/netutils/__init__.py index ebdef7b8..98f63d71 100644 --- a/netutils/__init__.py +++ b/netutils/__init__.py @@ -29,4 +29,4 @@ "variables", "vlan", ] -__version__ = "0.1.1" +__version__ = "0.1.2" diff --git a/poetry.lock b/poetry.lock index 15ab2026..c599673b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -79,6 +79,22 @@ typing-extensions = ">=3.7.4" colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] +[[package]] +name = "certifi" +version = "2020.12.5" +description = "Python package for providing Mozilla's CA Bundle." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "chardet" +version = "4.0.0" +description = "Universal encoding detector for Python 2 and 3" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "click" version = "7.1.2" @@ -150,6 +166,14 @@ python-versions = ">=3.4" [package.dependencies] gitdb = ">=4.0.1,<5" +[[package]] +name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "importlib-metadata" version = "3.7.3" @@ -359,6 +383,24 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "requests" +version = "2.25.1" +description = "Python HTTP for Humans." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<5" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] + [[package]] name = "six" version = "1.15.0" @@ -419,6 +461,19 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "urllib3" +version = "1.26.4" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotlipy (>=0.6.0)"] + [[package]] name = "wrapt" version = "1.12.1" @@ -454,7 +509,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "d36512e0b9fa4875a8d04ea47d48ac17171233c2931d7e7e1e56a6de81cefffd" +content-hash = "d065c94805beabbcd40ef67a9df7b4c4e60a06c9645e575696c0b7ad0acad77e" [metadata.files] appdirs = [ @@ -480,6 +535,14 @@ bandit = [ black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] +certifi = [ + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, +] +chardet = [ + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, @@ -558,6 +621,10 @@ gitpython = [ {file = "GitPython-3.1.14-py3-none-any.whl", hash = "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b"}, {file = "GitPython-3.1.14.tar.gz", hash = "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"}, ] +idna = [ + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] importlib-metadata = [ {file = "importlib_metadata-3.7.3-py3-none-any.whl", hash = "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4"}, {file = "importlib_metadata-3.7.3.tar.gz", hash = "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71"}, @@ -727,6 +794,10 @@ regex = [ {file = "regex-2021.3.17-cp39-cp39-win_amd64.whl", hash = "sha256:a0d04128e005142260de3733591ddf476e4902c0c23c1af237d9acf3c96e1b38"}, {file = "regex-2021.3.17.tar.gz", hash = "sha256:4b8a1fb724904139149a43e172850f35aa6ea97fb0545244dc0b805e0154ed68"}, ] +requests = [ + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, +] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, @@ -784,6 +855,10 @@ typing-extensions = [ {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] +urllib3 = [ + {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, + {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, +] wrapt = [ {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, ] diff --git a/pyproject.toml b/pyproject.toml index 0ab53b5e..ef58ad74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netutils" -version = "0.1.1" +version = "0.1.2" description = "Common helper functions useful in network automation." authors = ["Network to Code, LLC "] @@ -19,6 +19,7 @@ invoke = "^1.4.1" toml = "0.10.1" flake8 = "^3.8.3" coverage = "^5.5" +requests = "^2.25.1" [tool.black] line-length = 120 diff --git a/tasks.py b/tasks.py index 46734c37..e223a906 100644 --- a/tasks.py +++ b/tasks.py @@ -2,6 +2,7 @@ import os import sys from distutils.util import strtobool +import requests from invoke import task try: @@ -39,6 +40,10 @@ def is_truthy(arg): PWD = os.getcwd() # Local or Docker execution provide "local" to run locally without docker execution INVOKE_LOCAL = is_truthy(os.getenv("INVOKE_LOCAL", False)) # pylint: disable=W1508 +# Get project name from the toml file +PROJECT_NAME = PYPROJECT_CONFIG["tool"]["poetry"]["name"] +# Get current project version from the toml file +PROJECT_VERSION = PYPROJECT_CONFIG["tool"]["poetry"]["version"] def run_cmd(context, exec_cmd, local=INVOKE_LOCAL): @@ -234,3 +239,32 @@ def tests(context, local=INVOKE_LOCAL): coverage(context, local) print("All tests have passed!") + + +@task +def check_pypi_version(context, name=PROJECT_NAME, version=PROJECT_VERSION): + """Verify if the version specified already exists on PyPI. + + Used mostly in CI/CD to make sure that the new version is merged to main. + If version already exists, then function exits with non-zero return code, + else the function exits with zero return code. + + Args: + context (obj): Used to run specific commands + name (str): The name of the project + version (str): The version of the project + """ + # Running the following from context to pass pylint: + # context must be the first argument in invoke + context.run(f"echo Verifying the version {version} on PyPI.") + + url = f"https://pypi.org/pypi/{name}/json" + response = requests.get(url) + data = response.json() + if version in data.get("releases", {}).keys(): + print(f"The version {version} already exists.") + print("Bump the version. Run the command: poetry version.") + sys.exit(1) + print(f"The version {version} does not exist on PyPI.") + print("The version can be released.") + sys.exit(0) diff --git a/tests/__init__.py b/tests/__init__.py index 8b137891..e69de29b 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +0,0 @@ - diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 8b137891..e69de29b 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1 +0,0 @@ -