diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a1cf00e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,20 @@ +name: test +on: + pull_request: + push: + branches: + - main + tags: + - "*" + +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: install dependencies + run: python3 -m pip install --user --upgrade poetry + - name: install + run: poetry install + - name: Run tests + run: make test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f832801 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +test: + @(cd pystarport/tests && poetry run pytest) diff --git a/poetry.lock b/poetry.lock index 22b57a6..e5e52c7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,11 @@ +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "attrs" version = "21.2.0" @@ -36,6 +44,28 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "deepdiff" +version = "5.6.0" +description = "Deep Difference and Search of any Python object/data." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +ordered-set = "4.0.2" + +[package.extras] +cli = ["click (==7.1.2)", "pyyaml (==5.4)", "toml (==0.10.2)", "clevercsv (==0.6.7)"] + [[package]] name = "docker" version = "4.4.4" @@ -82,6 +112,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "jsonmerge" version = "1.8.0" @@ -121,6 +159,56 @@ python-versions = "*" [package.dependencies] six = "*" +[[package]] +name = "ordered-set" +version = "4.0.2" +description = "A set that remembers its order, and allows looking up its items by their index in that order." +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyparsing" +version = "3.0.6" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pyrsistent" version = "0.17.3" @@ -129,6 +217,27 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "pytest" +version = "6.2.5" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + [[package]] name = "python-dateutil" version = "2.8.1" @@ -140,6 +249,17 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "0.19.2" +description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "pywin32" version = "227" @@ -201,6 +321,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + [[package]] name = "tomlkit" version = "0.7.0" @@ -236,9 +364,13 @@ six = "*" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "ed3b311461b920acc78d8c864c46bf78d78d3fc0edd798b07f43c812f8ea559e" +content-hash = "fc5b93eb2eeec32c14b45aaa60262ea743f678b1bfc4612dece9bdb434476072" [metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, @@ -255,6 +387,14 @@ chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +deepdiff = [ + {file = "deepdiff-5.6.0-py3-none-any.whl", hash = "sha256:ef3410ca31e059a9d10edfdff552245829835b3ecd03212dc5b533d45a6c3f57"}, + {file = "deepdiff-5.6.0.tar.gz", hash = "sha256:e3f1c3a375c7ea5ca69dba6f7920f9368658318ff1d8a496293c79481f48e649"}, +] docker = [ {file = "docker-4.4.4-py2.py3-none-any.whl", hash = "sha256:f3607d5695be025fa405a12aca2e5df702a57db63790c73b927eb6a94aac60af"}, {file = "docker-4.4.4.tar.gz", hash = "sha256:d3393c878f575d3a9ca3b94471a3c89a6d960b35feb92f033c0de36cc9d934db"}, @@ -269,6 +409,10 @@ idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, ] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] jsonmerge = [ {file = "jsonmerge-1.8.0.tar.gz", hash = "sha256:a86bfc44f32f6a28b749743df8960a4ce1930666b3b73882513825f845cb9558"}, ] @@ -279,13 +423,40 @@ jsonschema = [ multitail2 = [ {file = "multitail2-1.5.2.tar.gz", hash = "sha256:7086598c1cd1901ec79ce3c1eda9420299e3778f6c18464958c1f74ffd1950c9"}, ] +ordered-set = [ + {file = "ordered-set-4.0.2.tar.gz", hash = "sha256:ba93b2df055bca202116ec44b9bead3df33ea63a7d5827ff8e16738b97f33a95"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pyparsing = [ + {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, + {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, +] pyrsistent = [ {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, ] +pytest = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] python-dateutil = [ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] +python-dotenv = [ + {file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"}, + {file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"}, +] pywin32 = [ {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"}, {file = "pywin32-227-cp27-cp27m-win_amd64.whl", hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"}, @@ -346,6 +517,10 @@ supervisor = [ termcolor = [ {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, ] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] tomlkit = [ {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, diff --git a/pyproject.toml b/pyproject.toml index 571e797..ae538a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,8 +22,11 @@ supervisor = "^4.2.1" docker = "^4.3.1" bech32 = "^1.1.0" multitail2 = "^1.5.2" +python-dotenv = "^0.19.2" [tool.poetry.dev-dependencies] +pytest = "^6.2.5" +deepdiff = "^5.6.0" [tool.poetry.scripts] pystarport = "pystarport.cli:main" diff --git a/pystarport/cli.py b/pystarport/cli.py index adbf1c7..49722a3 100644 --- a/pystarport/cli.py +++ b/pystarport/cli.py @@ -15,6 +15,7 @@ def init( data, config, base_port, + dotenv, *args, **kwargs, ): @@ -22,7 +23,7 @@ def init( f"rm -r {data}; mkdir {data}", ignore_error=True, ) - return init_cluster(data, config, base_port, *args, **kwargs) + return init_cluster(data, config, base_port, dotenv, *args, **kwargs) def start(data, quiet): @@ -42,8 +43,8 @@ def start(data, quiet): tailer.join() -def serve(data, config, base_port, cmd, quiet): - init(data, config, base_port, cmd=cmd) +def serve(data, config, base_port, dotenv, cmd, quiet): + init(data, config, base_port, dotenv, cmd=cmd) start(data, quiet) @@ -59,6 +60,7 @@ def init( data: str = "./data", config: str = "./config.yaml", base_port: int = 26650, + dotenv: str = None, image: str = IMAGE, gen_compose_file: bool = False, cmd: str = CHAIN, @@ -70,11 +72,12 @@ def init( :param config: path to the configuration file :param base_port: the base port to use, the service ports of different nodes are calculated based on this + :param dotenv: path to .env file :param image: the image used in the generated docker-compose.yml :param gen_compose_file: generate a docker-compose.yml :param cmd: path to chain binary """ - init(Path(data), config, base_port, image, self.cmd, gen_compose_file) + init(Path(data), config, base_port, dotenv, image, self.cmd, gen_compose_file) def start(self, data: str = "./data", quiet: bool = False): """ @@ -99,6 +102,7 @@ def serve( data: str = "./data", config: str = "./config.yaml", base_port: int = 26650, + dotenv: str = None, quiet: bool = False, ): """ @@ -108,10 +112,11 @@ def serve( :param config: path to the configuration file :param base_port: the base port to use, the service ports of different nodes are calculated based on this + :param dotenv: path to .env file :param cmd: path to chain binary :param quiet: don't print logs of subprocesses """ - serve(Path(data), config, base_port, self.cmd, quiet) + serve(Path(data), config, base_port, dotenv, self.cmd, quiet) def supervisorctl(self, *args, data: str = "./data"): from supervisor.supervisorctl import main diff --git a/pystarport/cluster.py b/pystarport/cluster.py index 2b6e387..d9e25d1 100644 --- a/pystarport/cluster.py +++ b/pystarport/cluster.py @@ -24,6 +24,7 @@ from . import ports from .app import CHAIN, IMAGE, SUPERVISOR_CONFIG_FILE from .cosmoscli import ChainCommand, CosmosCLI, ModuleAccount, module_address +from .expansion import expand_yaml from .ledger import ZEMU_BUTTON_PORT, ZEMU_HOST from .utils import format_doc_string, interact, write_ini @@ -902,9 +903,9 @@ def relayer_chain_config(data_dir, chain, relayer_chains_config): def init_cluster( - data_dir, config_path, base_port, image=IMAGE, cmd=None, gen_compose_file=False + data_dir, config_path, base_port, dotenv=None, image=IMAGE, cmd=None, gen_compose_file=False ): - config = yaml.safe_load(open(config_path)) + config = expand_yaml(config_path, dotenv) relayer_config = config.pop("relayer", {}) for chain_id, cfg in config.items(): diff --git a/pystarport/expansion.py b/pystarport/expansion.py new file mode 100644 index 0000000..51bd71f --- /dev/null +++ b/pystarport/expansion.py @@ -0,0 +1,70 @@ +import os +from pathlib import Path +from typing import Any, Mapping, Optional, Text + +import yaml +from dotenv import dotenv_values, load_dotenv +from dotenv.variables import parse_variables + + +def expand_posix_vars(obj: Any, variables: Mapping[Text, Optional[Any]]) -> Any: + """expand_posix_vars recursively expands POSIX values in an object. + + Args: + obj (any): object in which to interpolate variables. + variables (dict): dictionary that maps variable names to their value + """ + if isinstance(obj, (dict,)): + for key, val in obj.items(): + obj[key] = expand_posix_vars(val, variables) + elif isinstance(obj, (list,)): + for index in range(len(obj)): + obj[index] = expand_posix_vars(obj[index], variables) + elif isinstance(obj, (str,)): + obj = _expand(obj, variables) + return obj + + +def _expand(value, variables): + """_expand does POSIX-style variable expansion + + This is adapted from python-dotenv, specifically here: + + https://github.com/theskumar/python-dotenv/commit/17dba65244c1d4d10f591fe37c924bd2c6fd1cfc + + We need this layer here so we can explicitly pass in variables; + python-dotenv assumes you want to use os.environ. + """ + + if not isinstance(value, (str,)): + return value + atoms = parse_variables(value) + return "".join([str(atom.resolve(variables)) for atom in atoms]) + + +def expand_yaml(config_path, dotenv): + config = yaml.safe_load(open(config_path)) + + def expand(dotenv): + if not isinstance(dotenv, str): + raise ValueError(f"Invalid value passed to dotenv: {dotenv}") + config_vars = dict(os.environ) # load system env + env_path = Path(config_path).parent.joinpath(dotenv) + if not env_path.is_file(): + raise ValueError( + f"Dotenv specified in config but not found at path: {env_path}" + ) + config_vars.update(dotenv_values(dotenv_path=env_path)) # type: ignore + load_dotenv(dotenv_path=env_path) + return expand_posix_vars(config, config_vars) + + if dotenv is not None: + if "dotenv" in config: + _ = config.pop("dotenv", {}) # remove dotenv field if exists + dotenv_path = dotenv + config = expand(dotenv_path) + elif "dotenv" in config: + dotenv_path = config.pop("dotenv", {}) # pop dotenv field if exists + config = expand(dotenv_path) + + return config diff --git a/pystarport/tests/test_expansion/cronos_has_dotenv.yaml b/pystarport/tests/test_expansion/cronos_has_dotenv.yaml new file mode 100644 index 0000000..a9e3d3f --- /dev/null +++ b/pystarport/tests/test_expansion/cronos_has_dotenv.yaml @@ -0,0 +1,53 @@ +dotenv: dotenv +cronos_777-1: + cmd: cronosd + start-flags: "--trace" + app-config: + minimum-gas-prices: 5000000000000basetcro + json-rpc: + address: "0.0.0.0:{EVMRPC_PORT}" + ws-address: "0.0.0.0:{EVMRPC_PORT_WS}" + validators: + - coins: 1000000000000000000stake,10000000000000000000000basetcro + staked: 1000000000000000000stake + mnemonic: ${VALIDATOR1_MNEMONIC} + - coins: 1000000000000000000stake,10000000000000000000000basetcro + staked: 1000000000000000000stake + mnemonic: ${VALIDATOR2_MNEMONIC} + accounts: + - name: community + coins: 10000000000000000000000basetcro + mnemonic: ${COMMUNITY_MNEMONIC} + - name: signer1 + coins: 20000000000000000000000basetcro + mnemonic: ${SIGNER1_MNEMONIC} + - name: signer2 + coins: 30000000000000000000000basetcro + mnemonic: ${SIGNER2_MNEMONIC} + + genesis: + consensus_params: + block: + max_bytes: "1048576" + max_gas: "81500000" + app_state: + evm: + params: + evm_denom: basetcro + cronos: + params: + cronos_admin: ${CRONOS_ADMIN} + enable_auto_deployment: true + ibc_cro_denom: ${IBC_CRO_DENOM} + gov: + voting_params: + voting_period: "10s" + deposit_params: + max_deposit_period: "10s" + min_deposit: + - denom: "basetcro" + amount: "1" + transfer: + params: + receive_enabled: true + send_enabled: true diff --git a/pystarport/tests/test_expansion/cronos_has_posix_no_dotenv.yaml b/pystarport/tests/test_expansion/cronos_has_posix_no_dotenv.yaml new file mode 100644 index 0000000..89925ef --- /dev/null +++ b/pystarport/tests/test_expansion/cronos_has_posix_no_dotenv.yaml @@ -0,0 +1,52 @@ +cronos_777-1: + cmd: cronosd + start-flags: "--trace" + app-config: + minimum-gas-prices: 5000000000000basetcro + json-rpc: + address: "0.0.0.0:{EVMRPC_PORT}" + ws-address: "0.0.0.0:{EVMRPC_PORT_WS}" + validators: + - coins: 1000000000000000000stake,10000000000000000000000basetcro + staked: 1000000000000000000stake + mnemonic: ${VALIDATOR1_MNEMONIC} + - coins: 1000000000000000000stake,10000000000000000000000basetcro + staked: 1000000000000000000stake + mnemonic: ${VALIDATOR2_MNEMONIC} + accounts: + - name: community + coins: 10000000000000000000000basetcro + mnemonic: ${COMMUNITY_MNEMONIC} + - name: signer1 + coins: 20000000000000000000000basetcro + mnemonic: ${SIGNER1_MNEMONIC} + - name: signer2 + coins: 30000000000000000000000basetcro + mnemonic: ${SIGNER2_MNEMONIC} + + genesis: + consensus_params: + block: + max_bytes: "1048576" + max_gas: "81500000" + app_state: + evm: + params: + evm_denom: basetcro + cronos: + params: + cronos_admin: ${CRONOS_ADMIN} + enable_auto_deployment: true + ibc_cro_denom: ${IBC_CRO_DENOM} + gov: + voting_params: + voting_period: "10s" + deposit_params: + max_deposit_period: "10s" + min_deposit: + - denom: "basetcro" + amount: "1" + transfer: + params: + receive_enabled: true + send_enabled: true diff --git a/pystarport/tests/test_expansion/cronos_no_dotenv.yaml b/pystarport/tests/test_expansion/cronos_no_dotenv.yaml new file mode 100644 index 0000000..84880f5 --- /dev/null +++ b/pystarport/tests/test_expansion/cronos_no_dotenv.yaml @@ -0,0 +1,52 @@ +cronos_777-1: + cmd: cronosd + start-flags: "--trace" + app-config: + minimum-gas-prices: 5000000000000basetcro + json-rpc: + address: "0.0.0.0:{EVMRPC_PORT}" + ws-address: "0.0.0.0:{EVMRPC_PORT_WS}" + validators: + - coins: 1000000000000000000stake,10000000000000000000000basetcro + staked: 1000000000000000000stake + mnemonic: visit craft resemble online window solution west chuckle music diesel vital settle comic tribe project blame bulb armed flower region sausage mercy arrive release + - coins: 1000000000000000000stake,10000000000000000000000basetcro + staked: 1000000000000000000stake + mnemonic: direct travel shrug hand twice agent sail sell jump phone velvet pilot mango charge usual multiply orient garment bleak virtual action mention panda vast + accounts: + - name: community + coins: 10000000000000000000000basetcro + mnemonic: "notable error gospel wave pair ugly measure elite toddler cost various fly make eye ketchup despair slab throw tribe swarm word fruit into inmate" + - name: signer1 + coins: 20000000000000000000000basetcro + mnemonic: shed crumble dismiss loyal latin million oblige gesture shrug still oxygen custom remove ribbon disorder palace addict again blanket sad flock consider obey popular + - name: signer2 + coins: 30000000000000000000000basetcro + mnemonic: night renew tonight dinner shaft scheme domain oppose echo summer broccoli agent face guitar surface belt veteran siren poem alcohol menu custom crunch index + + genesis: + consensus_params: + block: + max_bytes: "1048576" + max_gas: "81500000" + app_state: + evm: + params: + evm_denom: basetcro + cronos: + params: + cronos_admin: crc12luku6uxehhak02py4rcz65zu0swh7wjsrw0pp + enable_auto_deployment: true + ibc_cro_denom: ibc/6411AE2ADA1E73DB59DB151A8988F9B7D5E7E233D8414DB6817F8F1A01611F86 + gov: + voting_params: + voting_period: "10s" + deposit_params: + max_deposit_period: "10s" + min_deposit: + - denom: "basetcro" + amount: "1" + transfer: + params: + receive_enabled: true + send_enabled: true diff --git a/pystarport/tests/test_expansion/dotenv b/pystarport/tests/test_expansion/dotenv new file mode 100644 index 0000000..6ccf82e --- /dev/null +++ b/pystarport/tests/test_expansion/dotenv @@ -0,0 +1,10 @@ +export VALIDATOR_KEY='826E479F5385C8C32CD96B0C0ACCDB8CC4FA5CACCC1BE54C1E3AA4D676A6EFF5' +export COMMUNITY_KEY='5D665FBD2FB40CB8E9849263B04457BA46D5F948972D0FE4C1F19B6B0F243574' +export PASSWORD='123456' +export VALIDATOR2_MNEMONIC="visit craft resemble online window solution west chuckle music diesel vital settle comic tribe project blame bulb armed flower region sausage mercy arrive release" +export VALIDATOR1_MNEMONIC="direct travel shrug hand twice agent sail sell jump phone velvet pilot mango charge usual multiply orient garment bleak virtual action mention panda vast" +export COMMUNITY_MNEMONIC="notable error gospel wave pair ugly measure elite toddler cost various fly make eye ketchup despair slab throw tribe swarm word fruit into inmate" +export SIGNER1_MNEMONIC="shed crumble dismiss loyal latin million oblige gesture shrug still oxygen custom remove ribbon disorder palace addict again blanket sad flock consider obey popular" +export SIGNER2_MNEMONIC="night renew tonight dinner shaft scheme domain oppose echo summer broccoli agent face guitar surface belt veteran siren poem alcohol menu custom crunch index" +export CRONOS_ADMIN="crc12luku6uxehhak02py4rcz65zu0swh7wjsrw0pp" +export IBC_CRO_DENOM="ibc/6411AE2ADA1E73DB59DB151A8988F9B7D5E7E233D8414DB6817F8F1A01611F86" diff --git a/pystarport/tests/test_expansion/dotenv1 b/pystarport/tests/test_expansion/dotenv1 new file mode 100644 index 0000000..95f0dc5 --- /dev/null +++ b/pystarport/tests/test_expansion/dotenv1 @@ -0,0 +1,10 @@ +export VALIDATOR_KEY='826E479F5385C8C32CD96B0C0ACCDB8CC4FA5CACCC1BE54C1E3AA4D676A6EFF5' +export COMMUNITY_KEY='5D665FBD2FB40CB8E9849263B04457BA46D5F948972D0FE4C1F19B6B0F243574' +export PASSWORD='123456' +export VALIDATOR1_MNEMONIC="good" +export VALIDATOR2_MNEMONIC="direct travel shrug hand twice agent sail sell jump phone velvet pilot mango charge usual multiply orient garment bleak virtual action mention panda vast" +export COMMUNITY_MNEMONIC="notable error gospel wave pair ugly measure elite toddler cost various fly make eye ketchup despair slab throw tribe swarm word fruit into inmate" +export SIGNER1_MNEMONIC="shed crumble dismiss loyal latin million oblige gesture shrug still oxygen custom remove ribbon disorder palace addict again blanket sad flock consider obey popular" +export SIGNER2_MNEMONIC="night renew tonight dinner shaft scheme domain oppose echo summer broccoli agent face guitar surface belt veteran siren poem alcohol menu custom crunch index" +export CRONOS_ADMIN="crc12luku6uxehhak02py4rcz65zu0swh7wjsrw0pp" +export IBC_CRO_DENOM="ibc/6411AE2ADA1E73DB59DB151A8988F9B7D5E7E233D8414DB6817F8F1A01611F86" diff --git a/pystarport/tests/test_expansion/test_expansion.py b/pystarport/tests/test_expansion/test_expansion.py new file mode 100644 index 0000000..b89fd46 --- /dev/null +++ b/pystarport/tests/test_expansion/test_expansion.py @@ -0,0 +1,63 @@ +import os +from pathlib import Path + +import yaml +from deepdiff import DeepDiff +from pystarport.expansion import expand_yaml + + +def test_expansion(): + cronos_has_dotenv = Path(__file__).parent / "cronos_has_dotenv.yaml" + cronos_no_dotenv = Path(__file__).parent / "cronos_no_dotenv.yaml" + cronos_has_posix_no_dotenv = ( + Path(__file__).parent / "cronos_has_posix_no_dotenv.yaml" + ) + + # `expand_yaml` is backward compatible, not expanded, and no diff + assert yaml.safe_load(open(cronos_no_dotenv)) == expand_yaml(cronos_no_dotenv, None) + + # `expand_yaml` is expanded but no diff + assert not DeepDiff( + yaml.safe_load(open(cronos_no_dotenv)), + expand_yaml(cronos_has_dotenv, None), + ignore_order=True, + ) + + # overriding dotenv with relative path is expanded and has diff) + assert DeepDiff( + yaml.safe_load(open(cronos_no_dotenv)), + expand_yaml(cronos_has_dotenv, "dotenv1"), + ignore_order=True, + ) == { + "values_changed": { + "root['cronos_777-1']['validators'][0]['mnemonic']": { + "new_value": "good", + "old_value": "visit craft resemble online window solution west chuckle " + "music diesel vital settle comic tribe project blame bulb armed flower " + "region sausage mercy arrive release", + } + } + } + + # overriding dotenv with absolute path is expanded and has diff + assert DeepDiff( + yaml.safe_load(open(cronos_no_dotenv)), + expand_yaml(cronos_has_dotenv, os.path.abspath("test_expansion/dotenv1")), + ignore_order=True, + ) == { + "values_changed": { + "root['cronos_777-1']['validators'][0]['mnemonic']": { + "new_value": "good", + "old_value": "visit craft resemble online window solution west chuckle " + "music diesel vital settle comic tribe project blame bulb armed flower " + "region sausage mercy arrive release", + } + } + } + + # overriding dotenv with absolute path is expanded and no diff + assert not DeepDiff( + yaml.safe_load(open(cronos_no_dotenv)), + expand_yaml(cronos_has_posix_no_dotenv, os.path.abspath("test_expansion/dotenv")), + ignore_order=True, + )