Skip to content

Commit 6a69b52

Browse files
Merge pull request #147 from cortexapps/146-add-support-for-evaluate-scorecard-entity-score
Add support for triggering scorecard entity evaluation
2 parents e9569e0 + 66d73d9 commit 6a69b52

File tree

4 files changed

+205
-2
lines changed

4 files changed

+205
-2
lines changed

CLAUDE.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is the **Cortex CLI** (`cortexapps-cli`), a command-line interface for interacting with the Cortex API (https://cortex.io). The CLI is built with Python 3.11+ using the Typer framework and provides commands for managing catalog entities, scorecards, teams, workflows, integrations, and other Cortex resources.
8+
9+
## Development Commands
10+
11+
### Setup
12+
```bash
13+
# Install dependencies
14+
poetry install
15+
16+
# Set required environment variables for testing
17+
export CORTEX_API_KEY=<your-api-key>
18+
export CORTEX_BASE_URL=https://api.getcortexapp.com # optional, defaults to this
19+
export CORTEX_API_KEY_VIEWER=<viewer-api-key> # for viewer permission tests
20+
```
21+
22+
### Testing
23+
The project uses [just](https://just.systems/) for task automation:
24+
25+
```bash
26+
# Run all available recipes
27+
just
28+
29+
# Run all tests (requires prior test-import)
30+
just test-all
31+
32+
# Run import test (prerequisite for other tests - loads test data)
33+
just test-import
34+
35+
# Run a single test file
36+
just test tests/test_catalog.py
37+
```
38+
39+
### Manual Testing
40+
```bash
41+
# Run the CLI locally
42+
poetry run cortex <command>
43+
44+
# Examples:
45+
poetry run cortex catalog list
46+
poetry run cortex -t my-tenant catalog list
47+
```
48+
49+
### Linting & Formatting
50+
Not currently configured in this project.
51+
52+
## Architecture
53+
54+
### Entry Point & CLI Structure
55+
- **Main entry point**: `cortexapps_cli/cli.py` - Defines the main Typer app and global options
56+
- **Command structure**: Each major resource has its own command module in `cortexapps_cli/commands/`
57+
- **Subcommands**: Some commands have nested subcommands in subdirectories (e.g., `backup_commands/`, `integrations_commands/`, `packages_commands/`, `scorecards_commands/`)
58+
59+
### Global Options
60+
All commands inherit global options defined in `cli.py:global_callback()`:
61+
- `-k, --api-key`: API key (or `CORTEX_API_KEY` env var)
62+
- `-u, --url`: Base URL (or `CORTEX_BASE_URL` env var)
63+
- `-c, --config`: Config file path (defaults to `~/.cortex/config`)
64+
- `-t, --tenant`: Tenant alias (defaults to "default")
65+
- `-l, --log-level`: Logging level (defaults to INFO)
66+
67+
### Configuration
68+
The CLI supports two authentication methods:
69+
1. **Config file** (`~/.cortex/config`): INI-style file with sections per tenant
70+
2. **Environment variables**: `CORTEX_API_KEY` and `CORTEX_BASE_URL`
71+
72+
Environment variables take precedence over config file values.
73+
74+
### Client Architecture
75+
- **`CortexClient`** (`cortexapps_cli/cortex_client.py`): Core HTTP client that handles all API requests
76+
- Provides `get()`, `post()`, `put()`, `patch()`, `delete()` methods
77+
- `fetch()`: Auto-paginated fetch for list endpoints
78+
- `fetch_or_get()`: Conditionally fetches all pages or single page based on parameters
79+
- Error handling with formatted error output using Rich
80+
81+
### Command Patterns
82+
Each command module follows a similar pattern:
83+
1. Creates a Typer app instance
84+
2. Defines command-specific option classes (following `CommandOptions` pattern in `command_options.py`)
85+
3. Implements command functions decorated with `@app.command()`
86+
4. Commands receive `ctx: typer.Context` to access the shared `CortexClient` via `ctx.obj["client"]`
87+
5. Uses utility functions from `utils.py` for output formatting
88+
89+
### Common Options Classes
90+
- **`ListCommandOptions`** (`command_options.py`): Standard options for list commands
91+
- `--table`, `--csv`: Output format options
92+
- `--columns, -C`: Select specific columns for table/csv output
93+
- `--filter, -F`: Filter rows using JSONPath and regex
94+
- `--sort, -S`: Sort rows by JSONPath fields
95+
- `--page, -p`, `--page-size, -z`: Pagination controls
96+
97+
- **`CommandOptions`**: Base options (e.g., `--print` for internal testing)
98+
99+
### Output Handling
100+
- Default: JSON output via Rich's `print_json()`
101+
- Table/CSV output: Configurable via `--table` or `--csv` flags with column selection
102+
- Output utilities in `utils.py`:
103+
- `print_output()`: JSON output
104+
- `print_output_with_context()`: Formatted output with table/csv support
105+
- `guess_data_key()`: Infers the data key in paginated responses
106+
107+
### Testing
108+
- **Test framework**: pytest with pytest-cov for coverage
109+
- **Test utilities**: `tests/helpers/utils.py` provides a `cli()` helper that wraps the Typer CLI runner
110+
- **Test data setup**: Tests depend on `test_import.py` running first to load test entities
111+
- Tests use the real Cortex API (not mocked) and require valid `CORTEX_API_KEY`
112+
- Parallel execution: Tests run with `pytest-xdist` (`-n auto`) for speed
113+
- Serial marker: Use `@pytest.mark.serial` for tests that must run sequentially
114+
115+
## Command Naming Style Guide
116+
117+
Follow the conventions in `STYLE.md`:
118+
- **Flags over arguments**: Use named flags for clarity and future compatibility
119+
- **Long and short versions**: All flags should have both (e.g., `--long-version, -l`)
120+
- **Consistent short flags**: Reuse short flags across commands where possible
121+
- **Kebab-case**: Multi-word flags use kebab-case (e.g., `--api-key`)
122+
123+
### Standard Verbs
124+
- **list**: Paginated list of resources (fetch all pages by default)
125+
- **get**: Retrieve full details of a single object
126+
- **create**: Create new object (fails if exists, unless `--replace-existing` or `--update-existing`)
127+
- **delete**: Delete object (interactive prompt unless `--force`)
128+
- **update**: Modify existing object (accepts full or partial definitions)
129+
- **archive/unarchive**: Archive operations
130+
- **add/remove**: Add/remove items from list attributes
131+
- **set/unset**: Set/unset single-value attributes
132+
- **open**: Open resource in browser
133+
134+
## Build & Release Process
135+
136+
### Release Workflow
137+
1. Create feature branch for changes
138+
2. Merge to `staging` branch for testing
139+
3. Merge `staging` to `main` to trigger release
140+
4. Version bumping:
141+
- Default: Patch version bump
142+
- `#minor` in commit message: Minor version bump
143+
- `#major` in commit message: Major version bump
144+
5. Release publishes to:
145+
- PyPI
146+
- Docker Hub (`cortexapp/cli:VERSION` and `cortexapp/cli:latest`)
147+
- Homebrew tap (`cortexapps/homebrew-tap`)
148+
149+
### Commit Message Format
150+
Commits should be prefixed with:
151+
- `add`: New features
152+
- `fix`: Bug fixes
153+
- `change`: Changes to existing features
154+
- `remove`: Removing features
155+
156+
Only commits with these prefixes appear in the auto-generated `HISTORY.md`.
157+
158+
### GitHub Actions
159+
- **`publish.yml`**: Triggered on push to `main`, handles versioning and multi-platform publishing
160+
- **`test-pr.yml`**: Runs tests on pull requests
161+
162+
## Key Files
163+
164+
- `cli.py`: Main CLI entry point and global callback
165+
- `cortex_client.py`: HTTP client for Cortex API
166+
- `command_options.py`: Reusable command option definitions
167+
- `utils.py`: Output formatting utilities
168+
- `commands/*.py`: Individual command implementations
169+
- `pyproject.toml`: Poetry configuration and dependencies
170+
- `Justfile`: Task automation recipes
171+
- `DEVELOPER.md`: Developer-specific testing and workflow notes
172+
- `STYLE.md`: Command design guidelines

HISTORY.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

88
<!-- insertion marker -->
9+
## [1.0.6](https://github.com/cortexapps/cli/releases/tag/1.0.6) - 2025-10-31
10+
11+
<small>[Compare with 1.0.5](https://github.com/cortexapps/cli/compare/1.0.5...1.0.6)</small>
12+
13+
### Added
14+
15+
- Add Stephanie to CODEOWNERS ([201daed](https://github.com/cortexapps/cli/commit/201daed2bf4b4652f846d4c2da4f849b7eccccd3) by Jeff Schnitter).
16+
17+
### Fixed
18+
19+
- fix: ensure base_url override is honored when parsing config file ([c9678e9](https://github.com/cortexapps/cli/commit/c9678e9e7203ba90822593688b772a57aea962dc) by Jeff Schnitter).
20+
921
## [1.0.5](https://github.com/cortexapps/cli/releases/tag/1.0.5) - 2025-08-25
1022

1123
<small>[Compare with 1.0.4](https://github.com/cortexapps/cli/compare/1.0.4...1.0.5)</small>

cortexapps_cli/commands/scorecards.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,25 @@ def scores(
183183
"entityTag": tag_or_id,
184184
"page": page,
185185
"pageSize": page_size
186-
}
186+
}
187187

188188
# remove any params that are None
189189
params = {k: v for k, v in params.items() if v is not None}
190-
190+
191191
client.fetch_or_get("api/v1/scorecards/" + scorecard_tag + "/scores", page, _print, params=params)
192192

193+
@app.command(name="trigger-evaluation")
194+
def trigger_evaluation(
195+
ctx: typer.Context,
196+
scorecard_tag: str = typer.Option(..., "--scorecard-tag", "-s", help="Unique tag for the scorecard"),
197+
entity_tag: str = typer.Option(..., "--entity-tag", "-e", help="The entity's unique tag (x-cortex-tag)"),
198+
):
199+
"""
200+
Trigger score evaluation for a specific entity in a scorecard
201+
"""
202+
203+
client = ctx.obj["client"]
204+
205+
client.post(f"api/v1/scorecards/{scorecard_tag}/entity/{entity_tag}/scores")
206+
print(f"Scorecard evaluation triggered successfully for entity '{entity_tag}' in scorecard '{scorecard_tag}'")
207+

tests/test_scorecards.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ def test_scorecards():
2727
# cannot rely on a scorecard evaluation being complete, so not performing any validation
2828
cli(["scorecards", "next-steps", "-s", "cli-test-scorecard", "-t", "cli-test-service"])
2929

30+
# Test trigger-evaluation command
31+
response = cli(["scorecards", "trigger-evaluation", "-s", "cli-test-scorecard", "-e", "cli-test-service"], return_type=ReturnType.STDOUT)
32+
assert "Scorecard evaluation triggered successfully" in response, "Should receive success message when triggering evaluation"
33+
3034
# cannot rely on a scorecard evaluation being complete, so not performing any validation
3135
#response = cli(["scorecards", "scores", "-s", "cli-test-scorecard", "-t", "cli-test-service"])
3236
#assert response['scorecardTag'] == "cli-test-scorecard", "Should get valid response that include cli-test-scorecard"

0 commit comments

Comments
 (0)