From c70f06779472151c3f0d4c6373526c602986d7ea Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 25 Aug 2025 22:49:40 +0000 Subject: [PATCH 1/4] chore: update HISTORY.md for main --- HISTORY.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 47c499a..75e8a6c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.5](https://github.com/cortexapps/cli/releases/tag/1.0.5) - 2025-08-25 + +[Compare with 1.0.4](https://github.com/cortexapps/cli/compare/1.0.4...1.0.5) + +### Fixed + +- fix: correct end endpoint for adding multiple configurations ([8e325bb](https://github.com/cortexapps/cli/commit/8e325bbfd71a38f9d6ac4439276ad7eef8e34fff) by Jeff Schnitter). + ## [1.0.4](https://github.com/cortexapps/cli/releases/tag/1.0.4) - 2025-08-01 [Compare with 1.0.3](https://github.com/cortexapps/cli/compare/1.0.3...1.0.4) From 2007ed5448c46f9cfb6870987874eec74db970f9 Mon Sep 17 00:00:00 2001 From: Jeff Schnitter Date: Tue, 26 Aug 2025 14:00:34 -0700 Subject: [PATCH 2/4] chore: test uses incorrect entity tag --- tests/test_catalog_archive_entity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_catalog_archive_entity.py b/tests/test_catalog_archive_entity.py index c6aa7da..8e2f7fa 100644 --- a/tests/test_catalog_archive_entity.py +++ b/tests/test_catalog_archive_entity.py @@ -1,7 +1,7 @@ from tests.helpers.utils import * def test(): - cli(["catalog", "archive", "-t", "archive-entity"]) + cli(["catalog", "archive", "-t", "cli-test-archive-entity"]) - response = cli(["catalog", "details", "-t", "archive-entity"]) + response = cli(["catalog", "details", "-t", "cli-test-archive-entity"]) assert response['isArchived'] == True, "isArchived attribute should be true" From 201daed2bf4b4652f846d4c2da4f849b7eccccd3 Mon Sep 17 00:00:00 2001 From: Jeff Schnitter Date: Mon, 6 Oct 2025 12:38:23 -0700 Subject: [PATCH 3/4] Add Stephanie to CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 865712d..918ad85 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,4 +5,4 @@ # the repo. Unless a later match takes precedence, # @global-owner1 and @global-owner2 will be requested for # review when someone opens a pull request. -* @jeff-schnitter +* @jeff-schnitter @Cantaley From c9678e9e7203ba90822593688b772a57aea962dc Mon Sep 17 00:00:00 2001 From: Jeff Schnitter Date: Wed, 29 Oct 2025 12:39:06 -0700 Subject: [PATCH 4/4] fix: ensure base_url override is honored when parsing config file --- DEVELOPER.md | 9 ------ Justfile | 15 +++------- cortexapps_cli/cli.py | 17 ++++++------ tests/test_config_file.py | 58 +++++++++++++++++++++++++-------------- 4 files changed, 50 insertions(+), 49 deletions(-) diff --git a/DEVELOPER.md b/DEVELOPER.md index 37fbe45..03d8e7e 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -18,17 +18,8 @@ Available recipes: test testname # Run a single test, ie: just test tests/test_catalog.py test-all # Run all tests test-import # Run import test, a pre-requisite for any tests that rely on test data. - test-parallel # Run tests that can run in parallel - test-serial # Run tests that have to run sequentially ``` -## Sequential and parallel tests -Most tests can run in a parallel but a handful have dependencies that can impact other tests, -for example changing the scope of CORTEX_API_KEY, so they have to run sequentially. - -The `test-all` target runs all the tests that can be run in parallel, followed by those that -have to run sequentially. - # Commit messages The CLI uses [git-changelog](https://pypi.org/project/git-changelog/) to dynamically generate the [HISTORY.md](./HISTORY.md) file. diff --git a/Justfile b/Justfile index 5c78318..2dca61a 100644 --- a/Justfile +++ b/Justfile @@ -12,24 +12,17 @@ _setup: @if [ -f .coverage ]; then rm .coverage; fi # Run all tests -test-all: _setup test-parallel test-serial - -# Run tests that can run in parallel -test-parallel: test-import - {{pytest}} -n auto -m "not setup and not serial" --html=report-parallel.html --self-contained-html --cov=cortexapps_cli --cov-append --cov-report term-missing tests +test-all: _setup test-import + {{pytest}} -n auto -m "not setup" --html=report.html --self-contained-html --cov=cortexapps_cli --cov-append --cov-report term-missing tests # Run all tests serially - helpful to see if any tests seem to be hanging _test-all-individual: test-import {{pytest}} --html=report-all-invidual.html --self-contained-html --cov=cortexapps_cli --cov-append --cov-report term-missing tests -# Run tests that have to run sequentially -test-serial: test-import - {{pytest}} -n auto -m "serial" --html=report-serial.html --self-contained-html --cov=cortexapps_cli --cov-append --cov-report term-missing tests - # Run import test, a pre-requisite for any tests that rely on test data. test-import: - {{pytest}} tests/test_import.py --cov=cortexapps_cli --cov-report= + {{pytest}} tests/test_import.py --cov=cortexapps_cli --cov-report= # Run a single test, ie: just test tests/test_catalog.py test testname: - {{pytest}} {{testname}} + {{pytest}} -n auto {{testname}} diff --git a/cortexapps_cli/cli.py b/cortexapps_cli/cli.py index 4d3e0be..8998fa5 100755 --- a/cortexapps_cli/cli.py +++ b/cortexapps_cli/cli.py @@ -78,7 +78,7 @@ def global_callback( ctx: typer.Context, api_key: str = typer.Option(None, "--api-key", "-k", help="API key", envvar="CORTEX_API_KEY"), - url: str = typer.Option("https://api.getcortexapp.com", "--url", "-u", help="Base URL for the API", envvar="CORTEX_BASE_URL"), + url: str = typer.Option(None, "--url", "-u", help="Base URL for the API", envvar="CORTEX_BASE_URL"), config_file: str = typer.Option(os.path.join(os.path.expanduser('~'), '.cortex', 'config'), "--config", "-c", help="Config file path", envvar="CORTEX_CONFIG"), tenant: str = typer.Option("default", "--tenant", "-t", help="Tenant alias", envvar="CORTEX_TENANT_ALIAS"), log_level: Annotated[str, typer.Option("--log-level", "-l", help="Set the logging level")] = "INFO" @@ -109,16 +109,17 @@ def global_callback( else: # config file found # if api_key is provided, use that in preference to the config file + config = configparser.ConfigParser() + config.read(config_file) + if tenant not in config: + raise typer.BadParameter(f"Tenant {tenant} not found in config file") if not api_key: - config = configparser.ConfigParser() - config.read(config_file) - if tenant not in config: - raise typer.BadParameter(f"Tenant {tenant} not found in config file") api_key = config[tenant]["api_key"] - if url not in config[tenant]: - url = url + if not url: + if 'base_url' in config[tenant].keys(): + url = config[tenant]['base_url'] else: - url = config[tenant]["base_url"] + url = "https://api.getcortexapp.com" # strip any quotes or spaces from the api_key and url api_key = api_key.strip('"\' ') diff --git a/tests/test_config_file.py b/tests/test_config_file.py index 6455f58..1c356dc 100644 --- a/tests/test_config_file.py +++ b/tests/test_config_file.py @@ -3,27 +3,12 @@ Tests for the cortex CLI config file """ -# These tests are all marked to run in serial order because they make modifications to the -# cortex config file and/or CORTEX_API_KEY value and would potentially impact other tests -# that are running in parallel (with poetry run pytest -n auto), so they are run separately. - -# Additionally, order is VERY IMPORTANT in this file because of the way CORTEX_API key is -# deleted, set to invalid values, etc. Moving test order could impact the overall success -# of pytest. Tread carefully here. - import io import os import pytest import sys from string import Template -# Requires user input, so use monkeypatch to set it. -@pytest.fixture(scope="session") -def delete_cortex_api_key(): - if "CORTEX_API_KEY" in os.environ: - del os.environ['CORTEX_API_KEY'] - -@pytest.mark.serial def test_config_file_api_key_quotes(tmp_path): cortex_api_key = os.getenv('CORTEX_API_KEY') f = tmp_path / "cortex_config_api_key_quotes" @@ -35,22 +20,53 @@ def test_config_file_api_key_quotes(tmp_path): f.write_text(content) cli(["-c", str(f), "entity-types", "list"]) -@pytest.mark.serial def test_config_file_create(monkeypatch, tmp_path): monkeypatch.setattr('sys.stdin', io.StringIO('y')) f = tmp_path / "test-config.txt" response = cli(["-c", str(f), "-k", os.getenv('CORTEX_API_KEY'), "scorecards", "list"]) assert any(scorecard['tag'] == 'cli-test-scorecard' for scorecard in response['scorecards']), "Should find scorecard with tag cli-test-scorecard" -@pytest.mark.serial -def test_config_file_bad_api_key(monkeypatch, tmp_path, delete_cortex_api_key): +def test_config_file_bad_api_key(monkeypatch, tmp_path): + monkeypatch.delenv("CORTEX_API_KEY") monkeypatch.setattr('sys.stdin', io.StringIO('y')) f = tmp_path / "test-config-bad-api-key.txt" response = cli(["-c", str(f), "-k", "invalidApiKey", "scorecards", "list"], return_type=ReturnType.RAW) assert "401 Client Error: Unauthorized" in str(response), "should get Unauthorized error" -@pytest.mark.serial -def test_environment_variable_invalid_key(): - os.environ["CORTEX_API_KEY"] = "invalidKey" +def test_environment_variable_invalid_key(monkeypatch): + monkeypatch.setenv("CORTEX_API_KEY", "invalidKey") response = cli(["scorecards", "list"], return_type=ReturnType.RAW) assert "401 Client Error: Unauthorized" in str(response), "should get Unauthorized error" + +def test_config_file_bad_url(monkeypatch, tmp_path): + monkeypatch.delenv("CORTEX_BASE_URL") + cortex_api_key = os.getenv('CORTEX_API_KEY') + f = tmp_path / "cortex_config_api_key_quotes" + template = Template(""" + [default] + api_key = "${cortex_api_key}" + + [mySection] + api_key = "${cortex_api_key}" + base_url = https://bogus.url + """) + content = template.substitute(cortex_api_key=cortex_api_key) + f.write_text(content) + response = cli(["-c", str(f), "-l", "DEBUG", "-t", "mySection", "entity-types", "list"], return_type=ReturnType.RAW) + assert "Max retries exceeded with url" in str(response), "should get max retries error" + +def test_config_file_base_url_env_var(monkeypatch, tmp_path): + cortex_api_key = os.getenv('CORTEX_API_KEY') + f = tmp_path / "cortex_config_api_key_quotes" + template = Template(""" + [default] + api_key = "${cortex_api_key}" + + [mySection] + api_key = "${cortex_api_key}" + base_url = https://bogus.url + """) + content = template.substitute(cortex_api_key=cortex_api_key) + f.write_text(content) + monkeypatch.setenv("CORTEX_BASE_URL", "https://api.getcortexapp.com") + cli(["-c", str(f), "-t", "mySection", "entity-types", "list"])