diff --git a/.gitignore b/.gitignore index ccf4f0f3..393a7859 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ */__pycache__/ docs/build/ venv/ +env/ */__TEMP_DATA__/ # File extensions diff --git a/readme.md b/readme.md index 12be7902..a6acd02f 100644 --- a/readme.md +++ b/readme.md @@ -37,7 +37,7 @@ The [terms of use](https://api.tzkt.io/#section/Terms-of-Use) of TzKT API allow ## Requirements and Setup -Python 3 is required. You can use the following commands to install it. +The setup instructions are Linux specific. Python 3 is required. You can use the following commands to install it. ```bash sudo apt-get update @@ -50,21 +50,25 @@ Download the application repository using git clone: git clone https://github.com/tezos-reward-distributor-organization/tezos-reward-distributor ``` -To install required modules, use pip with `requirements.txt` provided. +To install required modules, use pip with `requirements.txt` provided. Follow the instructions to create a virtual environment for your project specific python installation: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ + +```bash +python3 -m venv env +source env/bin/activate +``` ```bash cd tezos-reward-distributor -pip install -r requirements.txt +python3 -m pip install -r requirements.txt ``` To install the required modules for developers, use pip with `requirements_developer.txt` provided. ```bash cd tezos-reward-distributor -pip install -r requirements_developer.txt +python3 -m pip install -r requirements_developer.txt ``` - Regularly check and upgrade to the latest available version: ```bash diff --git a/src/launch_common.py b/src/launch_common.py index bc195a86..d8c12e70 100644 --- a/src/launch_common.py +++ b/src/launch_common.py @@ -1,17 +1,51 @@ +import os +import sys +import pkg_resources +import subprocess + from time import sleep +from datetime import date -from log_config import main_logger from Constants import ( + PYTHON_MAJOR, + PYTHON_MINOR, LINER, + NEW_PROTOCOL_DATE, + NEW_PROTOCOL_NAME, + REQUIREMENTS_FILE_PATH, ) -from util.parser import build_parser -from util.args_validator import validate -logger = main_logger +def python_version_ok(args=None): + print("Checking python version ...\n") + major = sys.version_info.major + minor = sys.version_info.minor + if not ( + major >= PYTHON_MAJOR + and minor >= PYTHON_MINOR + ): + raise Exception( + "... must be using Python {}.{} or later but it installed is {}.{}. Please upgrade!\n".format( + PYTHON_MAJOR, + PYTHON_MINOR, + major, + minor, + ) + ) + else: + print( + "... installed Python version {}.{} is greater then minimum required version {}.{}. OK!\n".format( + major, + minor, + PYTHON_MAJOR, + PYTHON_MINOR, + ) + ) + return True def print_banner(args, script_name): + print(LINER, flush=True) with open("./banner.txt", "rt") as file: print(file.read()) print(LINER, flush=True) @@ -25,8 +59,115 @@ def print_banner(args, script_name): print("Tezos Reward Distributor (TRD)" + script_name + " is Starting") -def parse_arguments(args=None): - parser = build_parser() - # Basic validations - args = validate(parser) - return args +def renamed_fee_ini(args=None): + if os.path.isfile("fee.ini"): + print( + "File fee.ini is deprecated. You can change the values at src/pay/batch_payer.py." + ) + print("File fee.ini is renamed to fee.ini.old?") + try: + os.rename("fee.ini", "fee.ini.old") + print("File fee.ini has been renamed to fee.ini.old") + except: + print("Failed: File fee.ini needs to be manually deleted or renamed") + return False + return True + + +def new_protocol_not_live(args=None): + print("Checking ...\n") + today = date.today() + print(("... current date: {}\n").format(today)) + print(("... new protocol date: {}\n").format(NEW_PROTOCOL_DATE)) + if today >= NEW_PROTOCOL_DATE: + print( + ( + "Protocol {} could be live now. If it is live there are risks using this branch.\n" + "It is suggested to reach out to the community to confirm, and switch to the new test branch \n" + "or accept of the risks of using this branch".format(NEW_PROTOCOL_NAME) + ) + ) + print("Do you want to continue using this branch? (y/N)") + value = input().lower() + if not value or value == "n": + return False + else: + print(("... protocol {} not live yet. OK!").format(NEW_PROTOCOL_NAME)) + return True + + +def in_venv(): + return sys.prefix != sys.base_prefix + + +def installed(package): + """ + The error status is 0. (bool(0) == False) + The success status is 1. (bool(1) == True) + """ + try: + subprocess.check_call([sys.executable, "-m", "pip", "install", package]) + return True + except: + return False + + # if hasattr(pip, "main"): + # status_code = pip.main(["install", package]) + # else: + # status_code = pip._internal.main(["install", package]) + # return not bool(status_code) + + +def requirements_installed(requirement_path=REQUIREMENTS_FILE_PATH): + if not in_venv(): + print( + "Please make sure to activate a virtual environment for python due to breaking changes in Ubutu >= 23.XX:\n" + "https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ \n" + ) + return False + + print("Checking installed packages ...\n") + missing_requirements = [] + try: + with open(requirement_path, "r") as requirements: + for requirement in requirements: + try: + pkg_resources.require(requirement) + except Exception as e: + requirement = requirement.replace("\n", "") + missing_requirements.append(requirement) + print( + "... requirement {} was not found: {}\n".format(requirement, e) + ) + if len(missing_requirements) > 0: + print("Would you like to install missing requirements? (Y/n)") + value = input().lower() + if not value or value == "y": + success = True + for r in missing_requirements: + success = success and installed(r) + if not success: + print("Could not install missing packages: {}\n".format(r)) + break + + if value == "n" or not success: + print( + "Please make sure to install all the required packages from 'requirements.txt' before using the TRD:\n" + "https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ \n" + ) + return False + else: + print("Requirements successfully installed!\n") + return True + else: + print("... all dependencies available. OK!\n") + return True + except (OSError, IOError) as e: + print( + "Error opening requirements.txt: {}\n" + "Please make sure to install all the required packages from 'requirements.txt' before using the TRD:\n" + "https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ \n".format( + e + ) + ) + return False diff --git a/src/main.py b/src/main.py index 3efd4cc8..fbeeebc7 100644 --- a/src/main.py +++ b/src/main.py @@ -1,147 +1,31 @@ -import os -import sys -import pip -import pkg_resources -from datetime import date -from Constants import ( - PYTHON_MAJOR, - PYTHON_MINOR, - REQUIREMENTS_FILE_PATH, - NEW_PROTOCOL_DATE, - NEW_PROTOCOL_NAME, +from launch_common import ( + new_protocol_not_live, + requirements_installed, + renamed_fee_ini, + python_version_ok, + in_venv, ) -def installed(package): - """ - The error status is 0. (bool(0) == False) - The success status is 1. (bool(1) == True) - """ - if hasattr(pip, "main"): - status_code = pip.main(["install", package]) - else: - status_code = pip._internal.main(["install", package]) - return not bool(status_code) - - -def requirements_installed(requirement_path=REQUIREMENTS_FILE_PATH): - print("Checking installed packages ...\n") - missing_requirements = [] - try: - with open(requirement_path, "r") as requirements: - for requirement in requirements: - try: - pkg_resources.require(requirement) - except Exception as e: - requirement = requirement.replace("\n", "") - missing_requirements.append(requirement) - print( - "... requirement {} was not found: {}\n".format(requirement, e) - ) - if len(missing_requirements) > 0: - print("Would you like to install missing requirements? (y/n)") - value = input().lower() - if value == "y": - success = True - for r in missing_requirements: - success = success and installed(r) - if success: - print("Success: Please restart TRD!\n") - else: - print("Error: Could not install missing packages!\n") - - if value != "y" or not success: - print( - "Please make sure to install all the required packages before using the TRD.\n" - "To install the requirements: 'pip3 install -r requirements.txt'\n" - ) - return False - else: - print("... all dependencies available!\n") - return True - except (OSError, IOError) as e: - print( - "Error opening requirements.txt: {}\n" - "Please make sure to install all the required packages before using the TRD.\n" - "To install the requirements: 'pip3 install -r requirements.txt'\n".format( - e - ) - ) - return False - - -def check_fee_ini(args=None): - # Check if the fee.ini configuration file is still present and, if so, - # warn the user that the file has to be removed or renamed - if os.path.isfile("fee.ini"): - print( - "File fee.ini is deprecated. You can change the values at src/pay/batch_payer.py." - ) - print("Would you like to rename fee.ini to fee.ini.old? (y/n)") - value = input().lower() - if value == "yes" or value == "y": - os.rename("fee.ini", "fee.ini.old") - print("File fee.ini has been renamed to fee.ini.old") - else: - print("File fee.ini needs to be manually deleted or renamed") - return 1 - - -def new_protocol_live(args=None): - today = date.today() - print(("Current date: {}").format(today)) - print(("TRD sunset date: {}").format(NEW_PROTOCOL_DATE)) - if today >= NEW_PROTOCOL_DATE: - print( - ( - "Protocol {} could be live now. If it is live there are risks using this branch.\n" - "It is suggested to reach out to the community to confirm, and switch to the new test branch \n" - "or be accept of the risks of using this branch".format( - NEW_PROTOCOL_NAME - ) - ) - ) - print("Do you want to continue using this branch? (y/n) Default is n") - value = input().lower() - if not value: - value = "n" - if value == "y" or value == "Yes": - return False - if value == "n" or value == "No": - return True - else: - new_protocol_live() - return False - - def start_application(args=None): - if new_protocol_live(): - return 1 - check_fee_ini() - # Requirements need to be checked outside of the state machine # because the library transitions could not be present - if requirements_installed(): + ready = ( + python_version_ok() + and requirements_installed() + and renamed_fee_ini() + and new_protocol_not_live() + ) + + if ready: from util.process_life_cycle import ProcessLifeCycle life_cycle = ProcessLifeCycle(args) life_cycle.start() return 0 - return 1 + else: + return 1 if __name__ == "__main__": - # Check the python version - if not ( - sys.version_info.major >= PYTHON_MAJOR - and sys.version_info.minor >= PYTHON_MINOR - ): - raise Exception( - "Must be using Python {}.{} or later but it is {}.{}".format( - PYTHON_MAJOR, - PYTHON_MINOR, - sys.version_info.major, - sys.version_info.minor, - ) - ) start_application() diff --git a/src/util/parser.py b/src/util/parser.py index 4d44535a..a8ed6550 100644 --- a/src/util/parser.py +++ b/src/util/parser.py @@ -1,5 +1,6 @@ -import argparse import os +import argparse +from util.args_validator import validate from Constants import ( BASE_DIR, @@ -268,3 +269,10 @@ def add_argument_log_file(argparser): ), default=default_log_file, ) + + +def parse_arguments(args=None): + parser = build_parser() + # Basic validations + args = validate(parser) + return args diff --git a/src/util/process_life_cycle.py b/src/util/process_life_cycle.py index 05da2cdd..7b0efa09 100644 --- a/src/util/process_life_cycle.py +++ b/src/util/process_life_cycle.py @@ -11,7 +11,8 @@ from calc.service_fee_calculator import ServiceFeeCalculator from cli.client_manager import ClientManager from fsm.TransitionsFsmBuilder import TransitionsFsmBuilder -from launch_common import print_banner, parse_arguments +from launch_common import print_banner +from util.parser import parse_arguments from model.baking_dirs import BakingDirs from pay.payment_consumer import PaymentConsumer from pay.payment_producer import PaymentProducer diff --git a/tests/unit/test_launch_common.py b/tests/unit/test_launch_common.py index 22969153..48eb6c1e 100644 --- a/tests/unit/test_launch_common.py +++ b/tests/unit/test_launch_common.py @@ -1,5 +1,3 @@ -import argparse -import pytest import io import sys