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
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/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)
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_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"
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"])