From 0251970ec31bc370c326c4c3c3b93a5513bdc028 Mon Sep 17 00:00:00 2001 From: Peter Urda Date: Sat, 7 Nov 2020 23:13:44 -0800 Subject: [PATCH] Migrate from 'Travis CI' to 'GitHub Actions' (...) Made corrections for pylint upgrade: pylint --rcfile=./.pylintrc --reports=y --output-format=text nistbeacon ************* Module nistbeacon.nistbeacon nistbeacon/nistbeacon.py:23:0: R0205: Class 'NistBeacon' inherits from object, can be safely removed from bases in python3 (useless-object-inheritance) nistbeacon/nistbeacon.py:69:12: R1705: Unnecessary "else" after "return" (no-else-return) nistbeacon/nistbeacon.py:80:4: R1710: Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements) nistbeacon/nistbeacon.py:160:8: R1705: Unnecessary "else" after "return" (no-else-return) ************* Module nistbeacon.nistbeaconvalue nistbeacon/nistbeaconvalue.py:26:0: R0205: Class 'NistBeaconValue' inherits from object, can be safely removed from bases in python3 (useless-object-inheritance) ************* Module nistbeacon.nistbeaconcrypto nistbeacon/nistbeaconcrypto.py:26:0: R0205: Class 'NistBeaconCrypto' inherits from object, can be safely removed from bases in python3 (useless-object-inheritance) nistbeacon/nistbeaconcrypto.py:186:21: R1719: The if expression can be replaced with 'test' (simplifiable-if-expression) --- .codecov.yml | 0 .github/codecov.yml | 6 + .github/workflows/linting.yaml | 60 ++++++++++ .github/workflows/package-building.yaml | 53 +++++++++ .github/workflows/pull-request-test.yaml | 45 ++++++++ .github/workflows/testing.yaml | 47 ++++++++ .gitignore | 9 +- .travis.yml | 57 ---------- Makefile | 123 +++++++++++++-------- README.rst | 24 ++-- nistbeacon/nistbeacon.py | 45 ++++---- nistbeacon/nistbeaconcrypto.py | 14 ++- nistbeacon/nistbeaconvalue.py | 8 +- requirements-test.txt | 2 +- scripts/version_manager.py | 133 ++++++++++++++++++++--- setup.py | 23 ++-- 16 files changed, 470 insertions(+), 179 deletions(-) delete mode 100644 .codecov.yml create mode 100644 .github/codecov.yml create mode 100644 .github/workflows/linting.yaml create mode 100644 .github/workflows/package-building.yaml create mode 100644 .github/workflows/pull-request-test.yaml create mode 100644 .github/workflows/testing.yaml delete mode 100644 .travis.yml diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index e69de29..0000000 diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 0000000..b250ada --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,6 @@ +coverage: + status: + patch: + default: + enabled: yes + target: 100% diff --git a/.github/workflows/linting.yaml b/.github/workflows/linting.yaml new file mode 100644 index 0000000..b63659c --- /dev/null +++ b/.github/workflows/linting.yaml @@ -0,0 +1,60 @@ +name: Linting Checks + + +on: + pull_request: + branches: + - master + + +jobs: + linting-checks: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: + - 3.8 + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install -r requirements-test.txt + + - name: Pep8 Check + run: make pep8 + + - name: Pylint Check + run: make pylint + + version-check: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: + - 3.8 + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install -r requirements-test.txt + + - name: Version Check + run: make version-check diff --git a/.github/workflows/package-building.yaml b/.github/workflows/package-building.yaml new file mode 100644 index 0000000..a01a572 --- /dev/null +++ b/.github/workflows/package-building.yaml @@ -0,0 +1,53 @@ +name: Package Building + + +on: + push: + branches: + - master + + +jobs: + build-beta: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: + - 3.8 + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install -r requirements-test.txt + + - name: Build beta packages + run: make build-beta + + - name: Archive beta package + uses: actions/upload-artifact@v2 + with: + name: python-package-nistbeacon-tar-ball.tar.gz + path: beta_dist/nistbeacon-*.tar.gz + if-no-files-found: error + retention-days: 90 + + - name: Archive beta python wheel + uses: actions/upload-artifact@v2 + with: + name: python-package-nistbeacon-wheel.whl + path: beta_dist/nistbeacon-*.whl + if-no-files-found: error + retention-days: 90 + + - name: Show generated beta package names + shell: bash + run: ls -1a beta_dist/ diff --git a/.github/workflows/pull-request-test.yaml b/.github/workflows/pull-request-test.yaml new file mode 100644 index 0000000..ed80bd5 --- /dev/null +++ b/.github/workflows/pull-request-test.yaml @@ -0,0 +1,45 @@ +name: Pull Request Test + + +on: + pull_request: + branches: + - master + + +jobs: + pull-request-test: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: + - 3.8 + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Environment Report + run: | + echo Python Version ... $(python --version) + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install -r requirements-test.txt + + - name: Unit Testing + run: make unittest + + - name: Local Integration Testing + run: make integration + + - name: Upload Coverage Report + uses: codecov/codecov-action@v1 + with: + fail_ci_if_error: true diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml new file mode 100644 index 0000000..b3299ee --- /dev/null +++ b/.github/workflows/testing.yaml @@ -0,0 +1,47 @@ +name: Comprehensive Testing + + +on: + pull_request: + branches: + - master + + +jobs: + testing: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: + - 3.8 + - 3.7 + - 3.6 + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Environment Report + run: | + echo Python Version ... $(python --version) + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install -r requirements-test.txt + + - name: Unit Testing + run: make unittest + + - name: Local Integration Testing + run: make integration + + - name: Upload Coverage Report + uses: codecov/codecov-action@v1 + with: + fail_ci_if_error: true diff --git a/.gitignore b/.gitignore index 5575142..3b38cf5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ ### Python ### __pycache__/ +*.py[cod] # Packaging -dist/ MANIFEST +*.egg-info/ +build/ +dist/ +beta_dist/ + # Sphinx sphinx/_build @@ -12,5 +17,5 @@ sphinx/_build .coverage .cache/ +.tox/ htmlcov/ - diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 36c96fe..0000000 --- a/.travis.yml +++ /dev/null @@ -1,57 +0,0 @@ -language: python - -matrix: - fast_finish: true - include: - - python: "3.3" - - python: "3.4" - - python: "3.5" - - python: "3.6" - - python: "3.7-dev" - allow_failures: - - os: osx - - python: "3.7-dev" - -# `travis lint` is marking this section as a false positive! -# References: -# - https://github.com/travis-ci/travis-ci/issues/3663 -# - https://github.com/travis-ci/travis-ci/issues/5396 -addons: - apt: - packages: - - python3-dev - -branches: - only: - - master - - develop - -before_install: - - echo Travis OS Name ... ${TRAVIS_OS_NAME} - - echo Travis Python .... ${TRAVIS_PYTHON_VERSION} - - echo Python Version ... $(python --version) - -install: - - pip install --upgrade pip - - pip install --upgrade --requirement requirements-test.txt - -script: - - make test - -after_success: - - bash <(curl -s https://codecov.io/bash) - -notifications: - email: false - slack: - secure: > - VCS7dHeluFzEE+1vuHi/H3CEvDrknqIpxDANxor8tyB2Iu2ZrLHH/s6n7hwnaU6uxiq7oOo1s/ - cSiXw0UfSlY9CkjxKdP8U/RhYF200ngEBpm3vvfFynEZ5otnJNznQs3Vga9AxZKezTT8btkbO0 - pUj4YoswTl2OEl+i1esOk3iWPXA9vZdPFmO1XchmnFC+IzyMeA3V7uxfNwh8d0cvlTsFKDB3M+ - Wx5GpsSaVJbH09oRzDXN3Lsoe9Q+PYQ2dkIP0JkeMMMBi8/D0Zqrjy9m1i/VhW45hA9+h2qKsO - C2TPjp3Oi6coeCyNaZWPjA8Wf/I6PiGs5LM8PoT7u7+irtRqIRSI9/yerPOzultl/Di/kHDF0D - r73Zn7tisBGEeAQuqsFYBiwaRJzWoWuzd1tXNARkeF6x9t3lRSf/UMGG3rJSyqWsxvzgzPHtEh - DhcKo4ZBCeliBlwwJlNpCGhOII687HqI859VOAAfpXyuQsAaAD+gBgFr11SijIiMv6wCgyFyJ0 - MNYFr2jCs+ziqtPunKT85hI4S/hCNiE+SN5IcbjCjFawmxXMHE2tdrOQJHyQbmEIeIfrFJUYWc - LbSf8avF1iidrrBtQlQ0LqwnvdwzlOfdjaXJoPNeeXa1DwHcGlHQ+g1JENbqXYnFgCSp4LHzXN - qZdaWaEQbXHRjHqX0= diff --git a/Makefile b/Makefile index ffee383..d1be49f 100644 --- a/Makefile +++ b/Makefile @@ -1,47 +1,54 @@ +######################################################################################################################## +# Variables +######################################################################################################################## + + +BETA_DIST = ./beta_dist +CLEAN_TARGETS = ./.cache ./*.egg-info $(BETA_DIST) $(DIST) ./build ./htmlcov .coverage coverage.xml +DIST = ./dist +GPG_ID = CA0B97334F9449EB5AFFCB93240BD54D194E3161 + + +######################################################################################################################## +# `make help` Needs to be first so it is ran when just `make` is called +######################################################################################################################## + + .PHONY: help help: # Show this help screen @ack '^[a-zA-Z_-]+:.*?# .*$$' $(MAKEFILE_LIST) |\ - sort |\ - awk 'BEGIN {FS = ":.*?# "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' + sort -k1,1 |\ + awk 'BEGIN {FS = ":.*?# "}; {printf "\033[1m%-30s\033[0m %s\n", $$1, $$2}' -.PHONY: build -build: test clean build-package # Clean, Test, and Build the package +######################################################################################################################## +# Linting +######################################################################################################################## -.PHONY: build-package -build-package: # Build 'sdist' for this package - python setup.py sdist +.PHONY: pep8 +pep8: # Run pep8 against project files + pep8 --verbose ./nistbeacon/* ./scripts/* ./tests/* -.PHONY: test -test: pep8 pylint unittest integration version-check # Run the full Travis CI testing suite +.PHONY: pylint +pylint: # Run pylint against project files + pylint --rcfile=./.pylintrc --reports=y --output-format=text nistbeacon -.PHONY: clean -clean: # Clean up build, test, and other project artifacts - rm -rf \ - ./.cache \ - ./*.egg-info \ - ./build \ - ./dist \ - ./htmlcov \ - .coverage \ - coverage.xml \ - && \ - find . | grep -E "(__pycache__|\.pyc|\.pyo$$)" | xargs rm -rf \ - && : - - -.PHONY: docs -docs: # Build the documentation - pandoc --from markdown_github --to rst ./CHANGELOG.md > ./sphinx/changelog.rst && \ - pushd ./sphinx && \ - make clean && \ - make html && \ - popd && \ - rsync -av --delete sphinx/_build/html/ docs/ - : +.PHONY: version-check +version-check: # Verify the project version string is correct across the project + ./scripts/version_manager.py check + + +######################################################################################################################## +# Testing +######################################################################################################################## + + +.PHONY: unittest +unittest: # Run only unit tests + py.test --cov nistbeacon --cov-report html ./tests/unit/ .PHONY: integration @@ -49,26 +56,46 @@ integration: # Run only integration tests py.test ./tests/integration/ -.PHONY: pep8 -pep8: # Run pep8 against project files - pep8 --verbose ./nistbeacon/* ./scripts/* ./tests/* +######################################################################################################################## +# Project Building +######################################################################################################################## -.PHONY: publish -publish: build # Build, sign, and publish the package - twine upload dist/* --sign -r pypi +.PHONY: build +build: build-pre build-package # Build the release package -.PHONY: pylint -pylint: # Run pylint against project files - pylint --rcfile=./.pylintrc --reports=y --output-format=text nistbeacon +.PHONY: build-beta +build-beta: build-pre build-beta-package # Build the beta package -.PHONY: unittest -unittest: # Run only unit tests - py.test --cov nistbeacon --cov-report html ./tests/unit/ +.PHONY: clean +clean: # Clean up build, test, and other project artifacts + rm -rf $(CLEAN_TARGETS) && \ + find . | grep -E "(__pycache__|\.pyc|\.pyo$$)" | xargs rm -rf && \ + : +#--------------------------------------------------------------------------------------------------- +# Build Subcommands +#--------------------------------------------------------------------------------------------------- -.PHONY: version-check -version-check: # Verify the project version string is correct across the project - ./scripts/version_manager.py check + +# Perform required pre-build steps for all build types +.PHONY: build-pre +build-pre: clean pep8 pylint unittest integration version-check + + +# Build 'sdist' and 'bdist_wheel' for this package +.PHONY: build-package +build-package: + python setup.py sdist --dist-dir $(DIST) bdist_wheel --dist-dir $(DIST) + + +# Build 'sdist' and 'bdist_wheel' for the beta package +.PHONY: build-beta-package +build-beta-package: + ./scripts/version_manager.py set-beta-build && \ + ./scripts/version_manager.py check && \ + python setup.py sdist --dist-dir $(BETA_DIST) bdist_wheel --dist-dir $(BETA_DIST) && \ + ./scripts/version_manager.py unset-beta-build && \ + : diff --git a/README.rst b/README.rst index cc2b098..a35529c 100644 --- a/README.rst +++ b/README.rst @@ -83,13 +83,11 @@ document on GitHub Project Health ============== -+---------+-----------------+--------------------+ -| Branch | Build Status | Coverage Status | -+=========+=================+====================+ -| Master | |MasterBuild|_ | |MasterCoverage|_ | -+---------+-----------------+--------------------+ -| Develop | |DevelopBuild|_ | |DevelopCoverage|_ | -+---------+-----------------+--------------------+ ++---------+-----------------+--------------------+----------------------+ +| Branch | Linting Checks | Testing Status | Code Coverage Status | ++=========+=================+====================+======================+ +| Master | |MasterLint| | |MasterTesting| | |MasterCoverage| | ++---------+-----------------+--------------------+----------------------+ References ========== @@ -97,12 +95,6 @@ References - `NIST Randomness Beacon Homepage `_ - `NIST Beacon REST API `_ -.. |MasterBuild| image:: https://travis-ci.org/urda/nistbeacon.svg?branch=master -.. _MasterBuild: https://travis-ci.org/urda/nistbeacon -.. |MasterCoverage| image:: https://codecov.io/gh/urda/nistbeacon/branch/master/graph/badge.svg -.. _MasterCoverage: https://codecov.io/gh/urda/nistbeacon/branch/master - -.. |DevelopBuild| image:: https://travis-ci.org/urda/nistbeacon.svg?branch=develop -.. _DevelopBuild: https://travis-ci.org/urda/nistbeacon -.. |DevelopCoverage| image:: https://codecov.io/gh/urda/nistbeacon/branch/develop/graph/badge.svg -.. _DevelopCoverage: https://codecov.io/gh/urda/nistbeacon/branch/develop +.. |MasterCoverage| image:: https://codecov.io/gh/urda/nistbeacon/branch/master/graph/badge.svg?branch=master +.. |MasterLint| image:: https://github.com/urda/nistbeacon/workflows/Linting%20Checks/badge.svg?branch=master +.. |MasterTesting| image:: https://github.com/urda/nistbeacon/workflows/Comprehensive%20Testing/badge.svg?branch=master diff --git a/nistbeacon/nistbeacon.py b/nistbeacon/nistbeacon.py index 2917fec..0f3150e 100644 --- a/nistbeacon/nistbeacon.py +++ b/nistbeacon/nistbeacon.py @@ -1,5 +1,5 @@ """ -Copyright 2015-2016 Peter Urda +Copyright 2015-2020 Peter Urda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. """ +from typing import Optional import requests from requests.exceptions import RequestException @@ -20,7 +21,7 @@ from nistbeacon.nistbeaconvalue import NistBeaconValue -class NistBeacon(object): +class NistBeacon: """ The NistBeacon object is used to query the NIST Randomness Beacon. For the most part, it returns actual NistBeaconValue objects for the @@ -57,7 +58,7 @@ class NistBeacon(object): ) @classmethod - def _query_nist(cls, url_data: str) -> NistBeaconValue: + def _query_nist(cls, url_data: str) -> Optional[NistBeaconValue]: try: nist_response = requests.get( "{0}/{1}".format( @@ -71,8 +72,8 @@ def _query_nist(cls, url_data: str) -> NistBeaconValue: nist_response.status_code is requests.codes.OK ): return NistBeaconValue.from_xml(nist_response.text) - else: - return None + + return None except RequestException: return None @@ -103,23 +104,6 @@ def chain_check(cls, timestamp: int) -> bool: # I'm not even mad, that's amazing. return False - if ( - isinstance(prev_record, NistBeaconValue) and - isinstance(next_record, NistBeaconValue) - ): - # Majority case, somewhere in the middle of the chain - # True if: - # - All three records have proper signatures - # - The requested record's previous output equals previous - # - The next possible record's previous output equals the record - return ( - record.valid_signature and - prev_record.valid_signature and - next_record.valid_signature and - record.previous_output_value == prev_record.output_value and - next_record.previous_output_value == record.output_value - ) - if ( prev_record is None and isinstance(next_record, NistBeaconValue) @@ -143,6 +127,19 @@ def chain_check(cls, timestamp: int) -> bool: record.previous_output_value == prev_record.output_value ) + # Majority case, somewhere in the middle of the chain + # True if: + # - All three records have proper signatures + # - The requested record's previous output equals previous + # - The next possible record's previous output equals the record + return ( + record.valid_signature and + prev_record.valid_signature and + next_record.valid_signature and + record.previous_output_value == prev_record.output_value and + next_record.previous_output_value == record.output_value + ) + @classmethod def get_first_record( cls, @@ -159,8 +156,8 @@ def get_first_record( if download: return NistBeacon.get_record(cls._INIT_RECORD.timestamp) - else: - return NistBeaconValue.from_json(cls._INIT_RECORD.json) + + return NistBeaconValue.from_json(cls._INIT_RECORD.json) @classmethod def get_last_record(cls) -> NistBeaconValue: diff --git a/nistbeacon/nistbeaconcrypto.py b/nistbeacon/nistbeaconcrypto.py index bf6c054..b79708a 100644 --- a/nistbeacon/nistbeaconcrypto.py +++ b/nistbeacon/nistbeaconcrypto.py @@ -1,5 +1,5 @@ """ -Copyright 2015-2017 Peter Urda +Copyright 2015-2020 Peter Urda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,13 +17,14 @@ import binascii import struct +from Crypto import Hash from Crypto.Hash import SHA512 from Crypto.Hash.SHA512 import SHA512Hash from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 -class NistBeaconCrypto(object): +class NistBeaconCrypto: """ Helper class to handle beacon value signature and crypto checks. """ @@ -149,7 +150,7 @@ def get_hash( def verify( cls, timestamp: int, - message_hash: SHA512Hash, + message_hash: Hash, signature: bytes, ) -> bool: """ @@ -174,6 +175,11 @@ def verify( # If a verifier exists to handle this problem, use it directly. # Else, we cannot verify the record and must mark it invalid. if verifier: + # PyCharm is wrong, see: + # site-packages/Crypto/Signature/PKCS1_v1_5.py + # Crypto.Signature.PKCS1_v1_5._pycrypto_verify + # + # noinspection PyNoneFunctionAssignment result = verifier.verify( message_hash, signature, @@ -183,6 +189,6 @@ def verify( # Convert 1 to 'True', 'False' otherwise if isinstance(result, int): - result = True if result == 1 else False + result = bool(result == 1) return result diff --git a/nistbeacon/nistbeaconvalue.py b/nistbeacon/nistbeaconvalue.py index 9914b7f..8834c2a 100644 --- a/nistbeacon/nistbeaconvalue.py +++ b/nistbeacon/nistbeaconvalue.py @@ -1,5 +1,5 @@ """ -Copyright 2015-2017 Peter Urda +Copyright 2015-2020 Peter Urda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,6 +14,8 @@ limitations under the License. """ +from typing import Optional + import binascii import hashlib import json @@ -23,7 +25,7 @@ from nistbeacon.nistbeaconcrypto import NistBeaconCrypto -class NistBeaconValue(object): +class NistBeaconValue: """ A single NIST Beacon Value object represents one beacon value. It has all the normal properties of a NIST beacon API call, @@ -368,7 +370,7 @@ def from_json(cls, input_json: str) -> 'NistBeaconValue': ) @classmethod - def from_xml(cls, input_xml: str) -> 'NistBeaconValue': + def from_xml(cls, input_xml: str) -> Optional['NistBeaconValue']: """ Convert a string of XML which represents a NIST Randomness Beacon value into a 'NistBeaconValue' object. diff --git a/requirements-test.txt b/requirements-test.txt index 9ec88bc..1aa8e40 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -r requirements.txt pep8 -pylint==1.6.5 +pylint pytest pytest-cov diff --git a/scripts/version_manager.py b/scripts/version_manager.py index e104967..7533d43 100755 --- a/scripts/version_manager.py +++ b/scripts/version_manager.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright 2015-2016 Peter Urda +Copyright 2015-2020 Peter Urda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,9 +16,13 @@ limitations under the License. """ +import datetime import sys from argparse import ArgumentParser -from collections import Counter +from collections import ( + Counter, + OrderedDict, +) from os.path import ( dirname, join, @@ -174,7 +178,7 @@ def get_versions() -> FileVersionResult: version_counter = Counter() versions_match = False version_str = None - versions_discovered = {} + versions_discovered = OrderedDict() for version_obj in version_objects: discovered = version_obj.get_version() @@ -203,14 +207,79 @@ def set_versions(new_version: str): version_obj.set_version(new_version) +def get_version_without_beta(version_info: FileVersionResult) -> str: + """ + Get the project's version string *without* any test or beta build labels. + + :param version_info: The current version_info of the project. + :return: The current version string, without any beta build string values. + """ + + if not version_info: + raise TypeError("version_info cannot be 'None'!") + + if not version_info.uniform: + raise ValueError("version_info is not uniform!") + + beta_flag = ".123." + current_version = version_info.version_result + + # We can just split and take the first value since: + # + # - If the flag *is not there*, we get the entire string back. + # + # OR + # + # - If the flag *is there*, we want everything before it, + # AKA the first value. + # + return current_version.split(beta_flag)[0] + + +def get_version_with_beta(version_info: FileVersionResult) -> str: + """ + Get the project's version string *with* a beta build label based on UTC. + + :param version_info: The current version_info of the project. + :return: The project's version string, with a beta build number. + """ + + if not version_info: + raise TypeError("version_info cannot be 'None'!") + + if not version_info.uniform: + raise ValueError("version_info is not uniform!") + + # Capture datetime as UTC for build timestamp + utc_datetime = datetime.datetime.utcnow() + + # Setup version string information + beta_flag = ".123." + build_number = utc_datetime.strftime("%Y%m%d.%H%M%S") + cleaned_version = get_version_without_beta(version_info) + + # Return the new version string for the build. + return "{base}{flag}{build}".format( + base=cleaned_version, + flag=beta_flag, + build=build_number, + ) + + if __name__ == '__main__': parser = ArgumentParser() understood_commands = [ 'check', + 'get-version-only', + 'set-beta-build', + 'unset-beta-build', 'update', ] + warning_banner = "/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\" + warning_version_mismatch = "Versions DO NOT MATCH across the project!" + sp = parser.add_subparsers(dest="command") for command in understood_commands: sp.add_parser(command) @@ -220,26 +289,60 @@ def set_versions(new_version: str): if args.command == "check" or args.command is None: version_data = get_versions() + for key, version_val in version_data.version_details.items(): + print("{0:.<20} {1}".format(key + " ", version_val)) + + print("") + if version_data.uniform: print("Versions look OK across the project") - print("Version: '{}'".format(version_data.version_result)) + print("Version is '{}'".format(version_data.version_result)) else: - print("Versions DO NOT MATCH across the project!") - print() - for key, version_val in version_data.version_details.items(): - print("{0: <10}: {1}".format(key, version_val)) + print(warning_banner) + print(warning_version_mismatch) + print(warning_banner) + sys.exit(1) + + elif args.command == "get-version-only": + version_data = get_versions() + + if not version_data.uniform: + sys.exit(1) + + print(version_data.version_result) + elif args.command == "set-beta-build": + version_data = get_versions() + + # Halt operations if versions do not match across project + if not version_data.uniform: + print(warning_banner) + print("Unable to set beta build on project version!") + print(warning_version_mismatch) + print(warning_banner) + sys.exit(1) + + build_version = get_version_with_beta(version_data) + print("Setting beta build version as: '{}'".format(build_version)) + set_versions(build_version) + + elif args.command == "unset-beta-build": + version_data = get_versions() + + # Halt operations if versions do not match across project + if not version_data.uniform: + print(warning_banner) + print("Unable to unset beta build on project version!") + print(warning_version_mismatch) + print(warning_banner) sys.exit(1) + clean_version = get_version_without_beta(version_data) + print("Resetting version as: '{}'".format(clean_version)) + set_versions(clean_version) + elif args.command == "update": new_version_str = input('New version string > ') set_versions(new_version_str) - else: - print("You need to specific a command for the manager!") - print("Available commands are:") - - for command in understood_commands: - print(" {}".format(command)) - sys.exit(0) diff --git a/setup.py b/setup.py index ef483b1..8530441 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ """ -Copyright 2015-2017 Peter Urda +Copyright 2015-2020 Peter Urda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,20 +14,23 @@ limitations under the License. """ -from distutils.core import setup +from setuptools import setup setup( name='nistbeacon', - packages=['nistbeacon'], version='0.9.4', + python_requires=">=3.6, <4", + packages=['nistbeacon'], + include_package_data=True, + license='Apache License, Version 2.0', description='Python 3 Library to access the NIST Randomness Beacon', long_description=open('README.rst').read(), - license='Apache License, Version 2.0', + long_description_content_type='text/x-rst', + url='https://github.com/urda/nistbeacon', author='Peter Urda', - author_email='noreply@urda.cc', - url='https://github.com/urda/nistbeacon', + author_email='noreply@urda.com', install_requires=[ 'pycryptodome>=3.4.7,<4', @@ -39,11 +42,13 @@ 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3 :: Only', + 'Topic :: Internet', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Utilities', ], )