Skip to content

Conversation

@jaysurse
Copy link
Collaborator

@jaysurse jaysurse commented Dec 22, 2025

Closes #95

Overview

This pull request introduces a complete environment variable management system for Cortex.
It enables applications and services to securely store, validate, and manage configuration values using a unified command-line interface.

The solution is designed to be safe for production use and supports encryption, validation, templates, and runtime loading.
It includes full documentation, extensive tests, and example workflows to ensure reliability and ease of adoption.

What This PR Adds

Environment Variable Management
• Per-application isolated environments
• Persistent storage of environment variables
• Optional encryption for sensitive values
• Safe loading into process runtime

CLI Support (cortex env)
• Create, read, update, delete environment variables
• Import and export .env files
• Clear environments safely
• List applications with stored environments

Environment Templates
• Built-in templates for common stacks (Node.js, Python, Django, Docker)
• Required variables and default values enforced
• Automatic validation when applying templates

Validation
• URL, port, and required-field validation
• Prevents invalid configuration from being saved

Security
• Encrypted secrets using Fernet symmetric encryption
• Encryption keys stored locally with restricted permissions
• Encrypted values are hidden unless explicitly decrypted

CLI Commands and Usage

Set an environment variable
cortex env set

Set an encrypted variable
cortex env set --encrypt

Get a variable value
cortex env get

List all variables for an app
cortex env list

Delete a variable
cortex env delete

Export environment variables
cortex env export
cortex env export --output

Import variables from a file
cortex env import
cortex env import --encrypt-keys KEY1,KEY2

Load variables into the current process
cortex env load

List applications with stored environments
cortex env apps

Clear an application environment
cortex env clear
cortex env clear --force

Environment templates
cortex env template list
cortex env template show
cortex env template apply

Summary by CodeRabbit

  • New Features

    • Added environment variable management system with secure storage and optional encryption for sensitive values
    • Support for environment templates (Node.js, Python, Django, Flask, Docker, AWS, etc.)
    • Variable validation by type (URL, port, boolean, integer, path)
    • Import/export functionality for .env files
  • Documentation

    • New ENV_MANAGEMENT.md guide with usage examples and best practices
  • Tests

    • Added comprehensive test coverage for environment management system

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 22, 2025 15:47
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive environment variable management subsystem for Cortex, adding encrypted storage, per-app isolation, template-based validation, import/export capabilities, and a new CLI command group with 13+ subcommands. New EnvironmentManager, EncryptionManager, EnvironmentStorage, and EnvironmentValidator classes orchestrate functionality. Also includes documentation, demo script, and extensive test coverage, plus numerous cosmetic formatting adjustments across multiple files.

Changes

Cohort / File(s) Summary
Core Environment Manager
cortex/env_manager.py
New 600+ line module providing EnvironmentVariable, EnvironmentTemplate, TemplateVariable, ValidationResult data models; VariableType enum; EncryptionManager (Fernet-based encryption with secure key file creation); EnvironmentValidator (type-based validation: string, url, port, boolean, integer, path, custom regex); EnvironmentStorage (atomic JSON per-app file operations); and EnvironmentManager API orchestrating set/get/list/delete, encrypt/decrypt, export/import (.env format), template application, and load-to-environ functionality. Includes 10+ built-in templates (nodejs, python, django, flask, docker, database, aws, etc.).
CLI Integration & Commands
cortex/cli.py
Adds env command entry point (CortexCLI.env) dispatching to 13 helper methods: _env_set, _env_get, _env_list, _env_delete, _env_export, _env_import, _env_clear, _env_template, _env_template_list, _env_template_show, _env_template_apply, _env_list_apps, _env_load. Implements encryption/decryption toggles, type/description metadata, error propagation, and confirmation prompts. Updates main() dispatcher and help table. Imports EnvironmentManager and get_env_manager.
Documentation & Examples
docs/ENV_MANAGEMENT.md, examples/env_demo.py
New documentation covering overview, quick start, storage layout, command reference, templates, encryption, validation types, integration patterns, best practices. Demo script showcases basic operations, encryption, validation, templates, export/import, app isolation, and os.environ loading.
Tests
tests/test_env_manager.py
Comprehensive test suite (500+ lines) covering: EnvironmentVariable serialization, EnvironmentValidator across all types and custom patterns, EncryptionManager key creation/persistence/permissions, EnvironmentStorage per-app isolation and atomic operations, EnvironmentManager CRUD and template workflows, end-to-end integration tests, edge cases (Unicode, multiline, special characters, concurrent writes).
Dependencies
requirements.txt
Adds cryptography>=42.0.0 for Fernet encryption support.
Formatting & Whitespace
cortex/coordinator.py, cortex/hardware_detection.py, cortex/installation_history.py, cortex/installation_verifier.py, cortex/kernel_features/accelerator_limits.py, cortex/kernel_features/kv_cache_manager.py, cortex/kernel_features/model_lifecycle.py, cortex/logging_system.py, cortex/progress_indicators.py, cortex/progress_tracker.py, cortex/transaction_history.py, examples/parallel_llm_demo.py, examples/progress_demo.py, examples/standalone_demo.py, src/intent/llm_agent.py, src/intent/planner.py, tests/test_cli.py, tests/test_coordinator.py, tests/test_interpreter.py, tests/test_llm_router.py, tests/test_logging_system.py, tests/unit/test_progress_tracker.py
Minor cosmetic changes: spacing around operators (e.g., i + 1 vs i+1, a / b vs a/b), multi-line reformatting, blank line removal, no functional or control-flow impact.

Sequence Diagrams

sequenceDiagram
    participant User
    participant CLI as cortex/cli.py
    participant Manager as EnvironmentManager
    participant Validator as EnvironmentValidator
    participant Storage as EnvironmentStorage
    participant Crypto as EncryptionManager
    participant FS as Filesystem

    User->>CLI: cortex env set app KEY=value --encrypt
    CLI->>Manager: set_variable(app, KEY, value, encrypt=true)
    Manager->>Validator: validate(value, var_type)
    Validator-->>Manager: valid=True
    Manager->>Crypto: encrypt(value)
    Crypto->>Crypto: _get_fernet()
    Crypto-->>Manager: encrypted_value
    Manager->>Storage: save(app, {KEY: EnvironmentVariable(...)})
    Storage->>FS: write ~/.cortex/environments/app.json
    FS-->>Storage: success
    Storage-->>Manager: success
    Manager-->>CLI: EnvironmentVariable object
    CLI-->>User: ✓ Variable encrypted and stored
Loading
sequenceDiagram
    participant User
    participant CLI as cortex/cli.py
    participant Manager as EnvironmentManager
    participant Storage as EnvironmentStorage
    participant Crypto as EncryptionManager
    participant FS as Filesystem

    User->>CLI: cortex env get app KEY
    CLI->>Manager: get_variable(app, KEY, decrypt=true)
    Manager->>Storage: load(app)
    Storage->>FS: read ~/.cortex/environments/app.json
    FS-->>Storage: JSON content
    Storage-->>Manager: {KEY: EnvironmentVariable(encrypted=true)}
    
    alt Variable is encrypted
        Manager->>Crypto: decrypt(encrypted_value)
        Crypto-->>Manager: plaintext_value
    else Variable is plaintext
        Manager-->>Manager: use plaintext_value
    end
    
    Manager-->>CLI: plaintext_value (or [encrypted] if no decrypt)
    CLI-->>User: Variable value displayed
Loading
sequenceDiagram
    participant User
    participant CLI as cortex/cli.py
    participant Manager as EnvironmentManager
    participant Template as EnvironmentTemplate
    participant Storage as EnvironmentStorage
    participant Validator as EnvironmentValidator
    participant Crypto as EncryptionManager

    User->>CLI: cortex env template apply django app --encrypt API_KEY
    CLI->>Manager: apply_template(django, app, values, encrypt_keys=[API_KEY])
    Manager->>Manager: get_template(django)
    Manager-->>Manager: retrieve django EnvironmentTemplate
    
    loop For each TemplateVariable
        Manager->>Validator: validate(value, var_type)
        Validator-->>Manager: valid or error
    end
    
    loop For each variable in encrypt_keys
        Manager->>Crypto: encrypt(value)
        Crypto-->>Manager: encrypted_value
    end
    
    Manager->>Storage: save(app, all_variables)
    Storage-->>Manager: success
    Manager-->>CLI: ValidationResult(valid=True, errors=[])
    CLI-->>User: ✓ Template applied with X variables set
Loading
sequenceDiagram
    participant User
    participant CLI as cortex/cli.py
    participant Manager as EnvironmentManager
    participant Storage as EnvironmentStorage
    participant FS as Filesystem
    participant OS as os.environ

    User->>CLI: cortex env import app < .env.local --encrypt SECRET_KEY
    CLI->>Manager: import_env(app, content, encrypt_keys=[SECRET_KEY])
    Manager->>Manager: parse .env-like content
    
    loop For each KEY=VALUE pair
        alt KEY in encrypt_keys
            Manager->>Crypto: encrypt(VALUE)
            Crypto-->>Manager: encrypted_value
        else plaintext
            Manager-->>Manager: keep plaintext
        end
    end
    
    Manager->>Storage: save(app, parsed_variables)
    Storage->>FS: write ~/.cortex/environments/app.json
    FS-->>Storage: success
    Storage-->>Manager: success
    Manager-->>CLI: (count, errors)
    CLI-->>User: ✓ Imported 8 variables, encrypted 2
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • mikejmorgan-ai

Poem

🐰 A warren of secrets, now tucked safe and sound,
Templates and templates, where riches are found,
Encrypt, validate, and migrate with grace,
Each app gets its own little environment space!
🔐 ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive The PR includes minor formatting changes across multiple files (spacing around operators, blank line removal) that are not directly related to the environment management feature but represent code quality improvements. Clarify whether formatting-only changes in unrelated files (coordinator.py, hardware_detection.py, logging_system.py, etc.) are intentional or should be reverted to keep the PR focused on environment management.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add environment variable manager with encryption and templates' clearly and concisely describes the main feature addition, matching the primary changeset of introducing a complete environment management subsystem.
Description check ✅ Passed The PR description provides comprehensive coverage of the feature, including overview, detailed feature list, CLI commands, security approach, and examples. It addresses the template structure by including related issue and summary, though missing explicit checkbox completion status.
Linked Issues check ✅ Passed The PR implements all core coding requirements from issue #95: per-app isolation, encryption with Fernet, templates for common stacks, validation rules, import/export, CLI operations, comprehensive tests, and documentation.
Docstring Coverage ✅ Passed Docstring coverage is 95.65% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d035c66 and ec78840.

📒 Files selected for processing (7)
  • cortex/cli.py
  • cortex/hardware_detection.py
  • cortex/installation_history.py
  • cortex/kernel_features/accelerator_limits.py
  • cortex/kernel_features/kv_cache_manager.py
  • cortex/progress_indicators.py
  • cortex/transaction_history.py
✅ Files skipped from review due to trivial changes (1)
  • cortex/installation_history.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • cortex/kernel_features/kv_cache_manager.py
  • cortex/progress_indicators.py
  • cortex/transaction_history.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/hardware_detection.py
  • cortex/cli.py
  • cortex/kernel_features/accelerator_limits.py
🧬 Code graph analysis (1)
cortex/cli.py (1)
cortex/env_manager.py (16)
  • EnvironmentManager (665-1105)
  • get_env_manager (1108-1110)
  • encrypt (502-514)
  • set_variable (689-738)
  • get_variable (740-767)
  • decrypt (516-534)
  • get_variable_info (769-781)
  • list_variables (783-794)
  • export_env (837-870)
  • import_env (872-932)
  • list_templates (960-967)
  • get_template (969-979)
  • apply_template (981-1061)
  • list_apps (650-662)
  • list_apps (828-835)
  • load_to_environ (934-956)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Package
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.10)
🔇 Additional comments (6)
cortex/kernel_features/accelerator_limits.py (1)

104-104: PEP 8 formatting improvement.

The added spaces around the division operators (/ 100 and / 1e9) improve readability and align with PEP 8 style guidelines. This is a cosmetic change unrelated to the PR's main objectives but contributes to consistent code formatting across the repository.

cortex/hardware_detection.py (1)

699-699: LGTM! Formatting improvements align with PEP 8.

The added spaces around the multiplication operator improve readability and conform to PEP 8 style guidelines.

Also applies to: 709-709

cortex/cli.py (4)

1184-1195: LGTM! The load command correctly handles encrypted variables.

The _env_load method delegates to env_mgr.load_to_environ(), which automatically decrypts encrypted values per the EnvironmentManager implementation (see relevant_code_snippets). No --decrypt flag is needed here, addressing the concern raised in PR objectives.


1392-1399: Flag naming is semantically appropriate.

The export command uses --include-encrypted while get/list use --decrypt. While the PR reviewer suggested standardization, the current naming reflects different semantics:

  • get/list with --decrypt: decrypt and display values
  • export with --include-encrypted: include decrypted encrypted values in the output

Both approaches are clear and contextually appropriate for their respective use cases.

Also applies to: 1372-1377, 1380-1384


1489-1490: LGTM! Environment command routing is correct.

The env command is properly routed to the cli.env(args) method, following the established pattern for other commands.


1222-1222: LGTM! Help documentation updated correctly.

The new env command is properly documented in the help table with a clear description.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (9)
examples/env_demo.py (1)

32-37: Unused import: get_env_manager.

The get_env_manager function is imported but never used in the demo script. The demo explicitly creates its own EnvironmentManager instance with temporary storage.

🔎 Proposed fix
 from cortex.env_manager import (
     EnvironmentManager,
     EnvironmentStorage,
     EncryptionManager,
-    get_env_manager,
 )
docs/ENV_MANAGEMENT.md (1)

37-39: Add language specifier to fenced code block.

Per static analysis, fenced code blocks should have a language specified for proper syntax highlighting.

🔎 Proposed fix
-```
+```text
 ~/.cortex/environments/<app>.json
</details>

</blockquote></details>
<details>
<summary>tests/test_env_manager.py (1)</summary><blockquote>

`919-927`: **Test name is misleading - these are sequential writes, not concurrent.**

The test `test_concurrent_writes_same_app` performs sequential writes in a loop, which doesn't actually test concurrent access. To properly test concurrency, you'd need threads or async operations.



Consider renaming to `test_rapid_sequential_writes_same_app` or implementing actual concurrent testing with threads if race condition testing is intended.

</blockquote></details>
<details>
<summary>cortex/cli.py (3)</summary><blockquote>

`748-751`: **Redundant import of `sys` module.**

The `sys` module is already imported at line 4 of this file. This local import is unnecessary.


<details>
<summary>🔎 Proposed fix</summary>

```diff
     def env(self, args: argparse.Namespace) -> int:
         """Handle environment variable management commands."""
-        import sys
-
         env_mgr = get_env_manager()

914-917: Redundant import of sys module.

Same issue as in the env() method - sys is already imported at the module level (line 4).

🔎 Proposed fix
     def _env_import(self, env_mgr: EnvironmentManager, args: argparse.Namespace) -> int:
         """Import environment variables from .env format."""
-        import sys
-
         app = args.app

856-868: Consider using the public API for decryption.

The code directly accesses env_mgr.encryption.decrypt() instead of using env_mgr.get_variable(app, var.key, decrypt=True). While this works, using the public API would be more maintainable.

🔎 Proposed fix
         for var in sorted(variables, key=lambda v: v.key):
             if var.encrypted:
                 if show_encrypted:
                     try:
-                        value = env_mgr.encryption.decrypt(var.value)
+                        value = env_mgr.get_variable(app, var.key, decrypt=True)
                         console.print(f"  {var.key}: {value} [dim](decrypted)[/dim]")
                     except Exception:
                         console.print(f"  {var.key}: [red][decryption failed][/red]")
cortex/env_manager.py (3)

507-519: Consider handling decryption failures gracefully.

fernet.decrypt() raises cryptography.fernet.InvalidToken if the encrypted value is corrupted or the key has changed. Currently, this exception will propagate uncaught, potentially causing confusing error messages for users.

🔎 Proposed fix
     def decrypt(self, encrypted_value: str) -> str:
         """
         Decrypt a value.
         
         Args:
             encrypted_value: Base64-encoded encrypted value
             
         Returns:
             Decrypted plaintext value
+            
+        Raises:
+            ValueError: If decryption fails (corrupted data or wrong key)
         """
         fernet = self._get_fernet()
-        decrypted = fernet.decrypt(encrypted_value.encode("utf-8"))
-        return decrypted.decode("utf-8")
+        try:
+            decrypted = fernet.decrypt(encrypted_value.encode("utf-8"))
+            return decrypted.decode("utf-8")
+        except Exception as e:
+            raise ValueError(f"Failed to decrypt value: {e}") from e

893-896: Handle escaped characters when parsing quoted values.

The import logic strips quotes but doesn't unescape special characters (e.g., \"", \\\, \n → newline). This creates an asymmetry with any proper export implementation.

🔎 Proposed fix
             # Handle quoted values
             if (value.startswith('"') and value.endswith('"')) or \
                (value.startswith("'") and value.endswith("'")):
                 value = value[1:-1]
+                # Unescape common escape sequences for double-quoted values
+                if value:
+                    value = value.replace("\\n", "\n")
+                    value = value.replace('\\"', '"')
+                    value = value.replace("\\\\", "\\")

1065-1071: Clarify validation logic for encrypted variables.

The current approach validates encrypted values using a placeholder string, then ignores the result. This is confusing and inefficient. Consider skipping validation for encrypted values explicitly.

🔎 Proposed fix
         # Validate all variable types
         for var in variables.values():
-            is_valid, error = EnvironmentValidator.validate(
-                var.value if not var.encrypted else "placeholder",
-                var.var_type,
-            )
-            if not is_valid and not var.encrypted:
-                errors.append(f"{var.key}: {error}")
+            # Skip validation for encrypted values (would require decryption)
+            if var.encrypted:
+                continue
+            is_valid, error = EnvironmentValidator.validate(var.value, var.var_type)
+            if not is_valid:
+                errors.append(f"{var.key}: {error}")
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f6bfa49 and 428e9ca.

📒 Files selected for processing (6)
  • cortex/cli.py
  • cortex/env_manager.py
  • docs/ENV_MANAGEMENT.md
  • examples/env_demo.py
  • requirements.txt
  • tests/test_env_manager.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • tests/test_env_manager.py
  • examples/env_demo.py
  • cortex/env_manager.py
  • cortex/cli.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_env_manager.py
🧬 Code graph analysis (4)
tests/test_env_manager.py (1)
cortex/env_manager.py (36)
  • EnvironmentManager (650-1087)
  • EnvironmentStorage (530-647)
  • EnvironmentVariable (56-83)
  • EnvironmentValidator (368-442)
  • EncryptionManager (445-527)
  • EnvironmentTemplate (98-122)
  • TemplateVariable (87-94)
  • ValidationResult (126-130)
  • VariableType (45-52)
  • get_env_manager (1090-1092)
  • to_dict (64-72)
  • to_dict (104-110)
  • from_dict (75-83)
  • from_dict (113-122)
  • validate (386-442)
  • encrypt (493-505)
  • decrypt (507-519)
  • is_key_available (521-527)
  • save (582-616)
  • load (556-580)
  • delete_app (618-633)
  • list_apps (635-647)
  • list_apps (813-820)
  • set_variable (674-723)
  • get_variable (725-752)
  • get_variable_info (754-766)
  • list_variables (768-779)
  • delete_variable (781-799)
  • clear_app (801-811)
  • export_env (822-853)
  • import_env (855-914)
  • load_to_environ (916-938)
  • list_templates (942-949)
  • get_template (951-961)
  • apply_template (963-1043)
  • validate_app (1045-1087)
examples/env_demo.py (2)
tests/test_env_manager.py (2)
  • env_manager (64-68)
  • storage (52-54)
cortex/env_manager.py (16)
  • EnvironmentManager (650-1087)
  • EnvironmentStorage (530-647)
  • EncryptionManager (445-527)
  • get_env_manager (1090-1092)
  • set_variable (674-723)
  • get_variable (725-752)
  • list_variables (768-779)
  • delete_variable (781-799)
  • encrypt (493-505)
  • decrypt (507-519)
  • list_templates (942-949)
  • get_template (951-961)
  • apply_template (963-1043)
  • export_env (822-853)
  • import_env (855-914)
  • load_to_environ (916-938)
cortex/env_manager.py (1)
tests/test_env_manager.py (1)
  • storage (52-54)
cortex/cli.py (1)
cortex/env_manager.py (18)
  • EnvironmentManager (650-1087)
  • get_env_manager (1090-1092)
  • encrypt (493-505)
  • set_variable (674-723)
  • get_variable (725-752)
  • decrypt (507-519)
  • get_variable_info (754-766)
  • list_variables (768-779)
  • delete_variable (781-799)
  • export_env (822-853)
  • import_env (855-914)
  • clear_app (801-811)
  • list_templates (942-949)
  • get_template (951-961)
  • apply_template (963-1043)
  • list_apps (635-647)
  • list_apps (813-820)
  • load_to_environ (916-938)
🪛 GitHub Actions: CI
cortex/cli.py

[error] 905-905: ruff check failed: UP024 Replace aliased errors with OSError.

🪛 GitHub Check: Lint
cortex/env_manager.py

[failure] 378-378: Ruff (W293)
cortex/env_manager.py:378:1: W293 Blank line contains whitespace


[failure] 370-370: Ruff (W293)
cortex/env_manager.py:370:1: W293 Blank line contains whitespace


[failure] 111-111: Ruff (W293)
cortex/env_manager.py:111:1: W293 Blank line contains whitespace


[failure] 103-103: Ruff (W293)
cortex/env_manager.py:103:1: W293 Blank line contains whitespace


[failure] 73-73: Ruff (W293)
cortex/env_manager.py:73:1: W293 Blank line contains whitespace


[failure] 63-63: Ruff (W293)
cortex/env_manager.py:63:1: W293 Blank line contains whitespace


[failure] 24-24: Ruff (UP035)
cortex/env_manager.py:24:1: UP035 Import from collections.abc instead: Callable

cortex/cli.py

[failure] 955-955: Ruff (UP024)
cortex/cli.py:955:16: UP024 Replace aliased errors with OSError


[failure] 924-924: Ruff (UP015)
cortex/cli.py:924:39: UP015 Unnecessary mode argument


[failure] 905-905: Ruff (UP024)
cortex/cli.py:905:20: UP024 Replace aliased errors with OSError

🪛 markdownlint-cli2 (0.18.1)
docs/ENV_MANAGEMENT.md

37-37: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (12)
examples/env_demo.py (1)

379-427: Well-structured demo implementation.

Good use of tempfile.TemporaryDirectory context manager for clean isolation and automatic cleanup. The demo effectively showcases all environment management features.

docs/ENV_MANAGEMENT.md (1)

293-334: Good security documentation with appropriate warnings.

The encryption section properly documents key protection, backup considerations, and provides a practical key rotation procedure. Using shred -u for secure deletion of temporary files is a good practice.

tests/test_env_manager.py (3)

44-68: Well-designed test fixtures.

The fixtures properly use temporary directories for isolation, ensuring tests don't interfere with each other or the real filesystem. Good separation between storage-only, encryption-only, and full manager fixtures.


597-618: Good test hygiene for os.environ manipulation.

The test properly cleans environment variables both before and after the test, preventing test pollution.


855-870: Comprehensive factory function testing.

Good coverage of the get_env_manager factory, verifying both instance type and default path configuration.

cortex/cli.py (2)

1245-1339: Well-structured CLI argument parsing.

The argparse configuration is comprehensive, with clear help text and proper option definitions. The nested subparser structure for env template commands follows good CLI design patterns.


959-976: Good UX pattern for destructive operations.

The confirmation prompt with --force flag bypass is a good pattern for protecting against accidental data loss.

cortex/env_manager.py (5)

30-42: LGTM!

The lazy loading pattern for the optional cryptography dependency is well implemented with a clear error message guiding users to install the package.


133-365: LGTM!

The built-in templates are well-designed with appropriate variable types, sensible defaults, and helpful descriptions. Good coverage of common application environments.


1090-1092: LGTM!

The factory function is straightforward. Creating a new instance per call is appropriate for CLI usage.


86-94: LGTM!

The TemplateVariable dataclass is well-structured with appropriate defaults and type hints.


916-938: LGTM!

The load_to_environ method correctly decrypts encrypted values before loading them into os.environ.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive environment variable management capabilities to Cortex, including encrypted storage for secrets, template-based configuration, validation, and import/export functionality. The implementation provides both programmatic API access and CLI commands for managing per-application environment configurations.

Key changes:

  • Environment variable storage with Fernet encryption for secrets
  • Built-in templates for common frameworks (Node.js, Python, Django, Flask, Docker, AWS, Database)
  • Validation system supporting URLs, ports, booleans, integers, and paths
  • Import/export functionality for .env file format

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 22 comments.

Show a summary per file
File Description
cortex/env_manager.py Core implementation with storage, encryption, validation, and template management
tests/test_env_manager.py Comprehensive test suite covering all major functionality with >80% coverage target
cortex/cli.py CLI integration adding env command group with subcommands for all operations
docs/ENV_MANAGEMENT.md Detailed user documentation with examples, security considerations, and API reference
examples/env_demo.py Interactive demonstration script showcasing all features
requirements.txt Added cryptography>=41.0.0 dependency for encryption support

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (13)
tests/test_env_manager.py (4)

15-15: Remove unused import json.

The json module is imported but not used anywhere in this test file.

Proposed fix
-import json

20-20: Remove unused mock imports.

MagicMock, mock_open, and patch are imported but not used in this test file.

Proposed fix
-from unittest.mock import MagicMock, mock_open, patch

34-34: Remove unused import VariableType.

VariableType is imported but not used in the tests.

Proposed fix
-    VariableType,

338-343: Consider making the intentional side-effect explicit.

The storage variable is created but not used directly - the test relies on the constructor's side effect of creating directories. Consider using _ prefix or a comment to make the intent clear.

Proposed fix
     def test_create_storage(self, temp_dir):
         """Test creating storage creates directories."""
         base_path = temp_dir / "environments"
-        storage = EnvironmentStorage(base_path=base_path)
+        EnvironmentStorage(base_path=base_path)  # Constructor creates directory
         assert base_path.exists()
cortex/env_manager.py (9)

628-631: Handle temp file cleanup more safely.

If the os.replace call at line 626 fails, the cleanup at lines 629-630 assumes temp_path exists. If mkstemp or os.fdopen failed before assignment, or the file was already removed, os.unlink could raise. Wrap the cleanup in a try-except to avoid masking the original exception. (Previously flagged by Copilot.)

🔎 Proposed fix
         except Exception:
             # Clean up temp file on failure
-            if os.path.exists(temp_path):
+            try:
                 os.unlink(temp_path)
+            except (FileNotFoundError, NameError):
+                # Temp file never created or already removed
+                pass
             raise

894-914: Support dots in variable names and unescape quoted values.

Two issues in import_env:

  1. Line 902 regex ^([A-Za-z_][A-Za-z0-9_]*)= does not allow dots in variable names (e.g., my.var.name=value). Many systems (Java/Spring) use dots. Consider ^([A-Za-z_][A-Za-z0-9_.]*)=.
  2. Lines 911-914 strip quotes but don't unescape internal escaped quotes. For example, "hello\"world" becomes hello\"world instead of hello"world. Properly unescape after stripping quotes.

(Previously flagged by Copilot.)

🔎 Proposed fix
             # Parse KEY=value
-            match = re.match(r"^([A-Za-z_][A-Za-z0-9_]*)=(.*)$", line)
+            match = re.match(r"^([A-Za-z_][A-Za-z0-9_.]*)=(.*)$", line)
             if not match:
                 errors.append(f"Line {line_num}: Invalid format")
                 continue

             key = match.group(1)
             value = match.group(2)

             # Handle quoted values
-            if (value.startswith('"') and value.endswith('"')) or (
-                value.startswith("'") and value.endswith("'")
-            ):
-                value = value[1:-1]
+            if (value.startswith('"') and value.endswith('"')):
+                inner = value[1:-1]
+                # Unescape backslash and double-quote sequences
+                inner = inner.replace('\\"', '"').replace('\\\\', '\\')
+                value = inner
+            elif (value.startswith("'") and value.endswith("'")):
+                inner = value[1:-1]
+                # Unescape backslash and single-quote sequences
+                inner = inner.replace("\\'", "'").replace('\\\\', '\\')
+                value = inner

24-24: Remove unused import Callable.

Static analysis (Ruff UP035) flags that Callable is imported but never used. Per past review comments, remove it from the import statement.

🔎 Proposed fix
-from typing import Any, Callable
+from typing import Any

57-86: Fix trailing whitespace on blank lines.

Static analysis (Ruff W293) flags trailing whitespace on blank lines 63 and 73. Per PEP 8 and coding guidelines, blank lines should contain no trailing spaces.

🔎 Proposed fix
     var_type: str = "string"
-    
+
     def to_dict(self) -> dict[str, Any]:
         }
-    
+
     @classmethod

101-125: Fix trailing whitespace on blank lines.

Static analysis (Ruff W293) flags trailing whitespace on lines 103 and 111.

🔎 Proposed fix
     variables: list[TemplateVariable] = field(default_factory=list)
-    
+
     def to_dict(self) -> dict[str, Any]:
             "variables": [asdict(v) for v in self.variables],
         }
-    
+
     @classmethod

372-381: Fix trailing whitespace and URL host pattern.

Two issues:

  1. Static analysis (Ruff W293) flags trailing whitespace on lines 370 and 378.
  2. The URL_PATTERN host part [^\s/$.?#] requires exactly one character, rejecting valid single-char hostnames like "http://a". Use [^\s/$.?#]+ to allow one or more characters. (Previously flagged by Copilot.)
🔎 Proposed fix
 class EnvironmentValidator:
     """Validates environment variable values based on type."""
-    
+
     # URL pattern: protocol://host[:port][/path]
     URL_PATTERN = re.compile(
         r"^[a-zA-Z][a-zA-Z0-9+.-]*://"  # scheme
-        r"[^\s/$.?#]"  # at least one character for host
+        r"[^\s/$.?#]+"  # one or more characters for host
         r"[^\s]*$",  # rest of URL
         re.IGNORECASE,
     )
-    
+
     # Port: 1-65535

469-492: Fix race condition in encryption key creation.

Multiple processes could simultaneously call _ensure_key_exists(), leading to a race between checking if the key exists (line 471) and creating it (lines 482-490). Use os.O_EXCL with os.O_CREAT to atomically create the file, failing if it already exists, then retry reading if creation fails due to the file existing. (Previously flagged by Copilot.)

🔎 Proposed fix
     def _ensure_key_exists(self) -> bytes:
         """Ensure encryption key exists, create if needed."""
         if self.key_path.exists():
             return self.key_path.read_bytes()

         # Create new key
         Fernet = _get_fernet()
         key = Fernet.generate_key()

         # Ensure parent directory exists
         self.key_path.parent.mkdir(parents=True, exist_ok=True)

-        # Write key with secure permissions (atomic write)
-        fd = os.open(
-            str(self.key_path),
-            os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
-            stat.S_IRUSR | stat.S_IWUSR,  # 0600
-        )
-        try:
+        try:
+            # Try to create the key file atomically; fail if it already exists
+            fd = os.open(
+                str(self.key_path),
+                os.O_WRONLY | os.O_CREAT | os.O_EXCL,
+                stat.S_IRUSR | stat.S_IWUSR,  # 0600
+            )
+        except FileExistsError:
+            # Another process created the key; use that key
+            return self.key_path.read_bytes()
+
+        try:
             os.write(fd, key)
         finally:
             os.close(fd)

         return key

650-662: App name mismatch: list_apps() returns sanitized filenames.

list_apps() returns path.stem, which is the sanitized filename. If an app name contained special characters (e.g., "my/app:name" → "my_app_name.json"), the original name cannot be recovered from the filename alone. This breaks the ability to work with apps that have special characters. Consider reading the JSON file to retrieve the original app field stored at line 608. (Previously flagged by CodeRabbit.)

🔎 Proposed fix
     def list_apps(self) -> list[str]:
         """
         List all applications with stored environments.
         
         Returns:
             List of application names
         """
         apps = []
         for path in self.base_path.glob("*.json"):
-            # Extract app name from filename
-            app_name = path.stem
-            apps.append(app_name)
+            # Load the file to get the original app name
+            try:
+                with open(path, "r", encoding="utf-8") as f:
+                    data = json.load(f)
+                apps.append(data.get("app", path.stem))
+            except (json.JSONDecodeError, KeyError, OSError):
+                # Fall back to filename if file is corrupted
+                apps.append(path.stem)
         return sorted(apps)

864-868: Escape special characters in quoted values.

When exporting values containing double quotes, backslashes, or newlines, the code wraps them in quotes (line 866) but does not escape internal special characters. This produces invalid .env syntax. For example, KEY="hello "world"" or KEY="line1\nline2" (with literal backslash-n) instead of escaped sequences. Escape backslashes first, then quotes, and replace actual newlines with \n. (Previously flagged by CodeRabbit and Copilot.)

🔎 Proposed fix

Add an escape helper method:

+    def _escape_value(self, value: str) -> str:
+        """Escape special characters for .env format."""
+        # Escape backslashes first, then quotes and newlines
+        value = value.replace("\\", "\\\\")
+        value = value.replace('"', '\\"')
+        value = value.replace("\n", "\\n")
+        return value

Then update the export logic:

                 if var.description:
                     lines.append(f"# {var.description}")
                 # Quote values with special characters
                 if any(c in var.value for c in " \t\n'\"$`\\"):
-                    lines.append(f'{var.key}="{var.value}"')
+                    lines.append(f'{var.key}="{self._escape_value(var.value)}"')
                 else:
                     lines.append(f"{var.key}={var.value}")
🧹 Nitpick comments (4)
cortex/installation_history.py (2)

458-458: Consider: Coding guidelines require dry-run by default for install operations.

The rollback method defaults to dry_run=False, which means it will execute destructive package operations by default. As per the coding guidelines for **/*install*.py files, dry-run should be the default.

🔎 Suggested change
-    def rollback(self, install_id: str, dry_run: bool = False) -> tuple[bool, str]:
+    def rollback(self, install_id: str, dry_run: bool = True) -> tuple[bool, str]:

This would require callers to explicitly pass dry_run=False to execute real operations, preventing accidental package modifications.

Based on coding guidelines for **/*install*.py.


534-541: Consider: Coding guidelines require explicit user confirmation before sudo execution.

The rollback executes sudo commands (e.g., sudo apt-get remove -y) without prompting the user for confirmation. The -y flag auto-confirms apt prompts, and there's no interactive confirmation step in the code.

Consider adding an explicit confirmation prompt before executing privileged operations:

if not dry_run:
    print(f"The following {len(actions)} action(s) will be executed:")
    for action in actions:
        print(f"  {action}")
    confirm = input("Proceed? [y/N]: ").strip().lower()
    if confirm != 'y':
        return (False, "Rollback cancelled by user")

Based on coding guidelines for **/*install*.py.

cortex/cli.py (2)

748-750: Remove redundant import.

sys is already imported at line 4; the local import at line 750 is unnecessary.

🔎 Proposed fix
     def env(self, args: argparse.Namespace) -> int:
         """Handle environment variable management commands."""
-        import sys
-
         env_mgr = get_env_manager()

787-789: Narrow exception handling to specific types.

Catching all exceptions can hide unexpected errors. Catch specific exception types (ValueError, ImportError, OSError) separately to provide clearer error messages, as suggested in past review comments.

🔎 Proposed fix
-        except Exception as e:
+        except (ValueError, ImportError, OSError) as e:
             self._print_error(f"Environment operation failed: {e}")
             return 1
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 428e9ca and 2d6f42c.

📒 Files selected for processing (31)
  • cortex/cli.py
  • cortex/coordinator.py
  • cortex/doctor.py
  • cortex/env_manager.py
  • cortex/hardware_detection.py
  • cortex/installation_history.py
  • cortex/installation_verifier.py
  • cortex/kernel_features/accelerator_limits.py
  • cortex/kernel_features/kv_cache_manager.py
  • cortex/kernel_features/model_lifecycle.py
  • cortex/llm_router.py
  • cortex/logging_system.py
  • cortex/progress_indicators.py
  • cortex/progress_tracker.py
  • cortex/transaction_history.py
  • docs/ENV_MANAGEMENT.md
  • examples/env_demo.py
  • examples/parallel_llm_demo.py
  • examples/progress_demo.py
  • examples/standalone_demo.py
  • requirements.txt
  • src/intent/llm_agent.py
  • src/intent/planner.py
  • test_parallel_llm.py
  • tests/test_cli.py
  • tests/test_coordinator.py
  • tests/test_env_manager.py
  • tests/test_interpreter.py
  • tests/test_llm_router.py
  • tests/test_logging_system.py
  • tests/unit/test_progress_tracker.py
💤 Files with no reviewable changes (5)
  • tests/test_coordinator.py
  • tests/test_interpreter.py
  • tests/test_cli.py
  • src/intent/llm_agent.py
  • src/intent/planner.py
✅ Files skipped from review due to trivial changes (15)
  • cortex/hardware_detection.py
  • examples/standalone_demo.py
  • tests/test_llm_router.py
  • examples/parallel_llm_demo.py
  • tests/unit/test_progress_tracker.py
  • cortex/kernel_features/kv_cache_manager.py
  • cortex/kernel_features/accelerator_limits.py
  • cortex/progress_indicators.py
  • cortex/llm_router.py
  • cortex/transaction_history.py
  • tests/test_logging_system.py
  • examples/progress_demo.py
  • cortex/installation_verifier.py
  • cortex/logging_system.py
  • cortex/doctor.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • requirements.txt
  • docs/ENV_MANAGEMENT.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/coordinator.py
  • cortex/kernel_features/model_lifecycle.py
  • cortex/progress_tracker.py
  • tests/test_env_manager.py
  • cortex/cli.py
  • examples/env_demo.py
  • test_parallel_llm.py
  • cortex/installation_history.py
  • cortex/env_manager.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_env_manager.py
**/*install*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*install*.py: Dry-run by default for all installations in command execution
No silent sudo execution - require explicit user confirmation
Implement audit logging to ~/.cortex/history.db for all package operations

Files:

  • cortex/installation_history.py
🧠 Learnings (1)
📚 Learning: 2025-12-11T12:03:24.071Z
Learnt from: CR
Repo: cortexlinux/cortex PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-11T12:03:24.071Z
Learning: Applies to **/*install*.py : Implement audit logging to ~/.cortex/history.db for all package operations

Applied to files:

  • cortex/installation_history.py
🧬 Code graph analysis (1)
test_parallel_llm.py (1)
cortex/llm_router.py (4)
  • query_multiple_packages (725-772)
  • diagnose_errors_parallel (775-821)
  • check_hardware_configs_parallel (824-872)
  • acomplete (441-505)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build Package
🔇 Additional comments (35)
cortex/kernel_features/model_lifecycle.py (1)

97-97: Minor consistency improvement; unrelated to PR scope.

The change from single quotes to double quotes for the comma separator is purely stylistic and improves consistency with the rest of the file, which predominantly uses double quotes. Functionally, both produce identical output. However, this change appears unrelated to the PR's stated objectives (environment variable management system with encryption and templates).

cortex/installation_history.py (1)

693-693: LGTM!

The formatting change adding spaces around the minus operator (len(r.packages) - 2) follows PEP 8 style for arithmetic operators.

cortex/progress_tracker.py (1)

584-588: LGTM!

The multi-line formatting of the add_task call arguments improves readability. No functional change.

cortex/coordinator.py (2)

74-79: LGTM!

Minor formatting change in the f-string expression ({i + 1} with spaces). No functional impact.


253-253: LGTM!

Consistent formatting update in the log message.

test_parallel_llm.py (8)

51-56: LGTM!

Correctly using a plain string instead of an f-string when no interpolation is needed.


98-100: LGTM!

Formatting improvements: plain string and consistent spacing in arithmetic expression.


111-114: LGTM!

Adding a blank line before traceback.print_exc() improves readability.


173-173: LGTM!

Consolidating function call arguments to a single line is cleaner.


184-184: LGTM!


195-195: LGTM!


231-233: LGTM!

The multi-line formatting of the acomplete call with dictionary unpacking is readable.


243-246: LGTM!

Consistent use of plain string where no interpolation is needed.

examples/env_demo.py (9)

1-46: LGTM!

Well-structured demo file with proper module docstring, imports, and helper function. The Rich console integration provides clear visual output.


48-102: LGTM!

Basic operations demo covers set/get/list/delete with clear console output. Good use of table formatting for variable listing.


104-155: LGTM!

Encryption demo clearly shows the encrypt/decrypt workflow and how encrypted values appear in storage vs when decrypted.


157-209: LGTM!

Validation demo effectively shows both successful validation and proper error handling for invalid values.


211-256: LGTM!

Template demo shows listing, inspection, and application of templates with proper null checking for template retrieval.


258-313: LGTM!

Export/import demo covers both encrypted and non-encrypted scenarios, including the import workflow with error reporting.


315-338: LGTM!

App isolation demo effectively shows that the same key can hold different values across different applications.


340-376: LGTM!

Load-to-environ demo properly handles cleanup of test environment variables both before and after the demonstration, preventing test pollution.


378-430: LGTM!

Main function properly uses temporary directory for demo isolation, ensuring no persistent state is left behind. The usage examples at the end provide helpful guidance for users.

tests/test_env_manager.py (12)

43-68: LGTM!

Well-designed fixtures providing proper test isolation with temporary directories.


75-140: LGTM!

Comprehensive tests for EnvironmentVariable dataclass covering creation, serialization, and deserialization.


147-256: LGTM!

Thorough validation tests covering all variable types, edge cases, and custom pattern validation with proper error handling.


263-328: LGTM!

Excellent encryption tests including security-sensitive permission checks (0o600) and edge cases like empty strings and unicode characters.


344-418: LGTM!

Storage tests thoroughly cover save/load, app isolation, deletion, listing, and special character handling in app names.


579-591: Line numbering appears correct.

The invalid line "invalid line without equals" is on line 3 of the content (counting the empty first line as line 1), so the assertion "Line 3" in errors[0] is correct.


425-614: LGTM!

Comprehensive EnvironmentManager tests covering CRUD operations, encryption, validation, export/import workflows, and loading to os.environ. Good cleanup of test environment variables.


621-756: LGTM!

Template tests comprehensively cover listing, retrieval, application with defaults/custom values, validation of required fields, and encryption support.


763-840: LGTM!

Template dataclass and ValidationResult tests properly verify serialization/deserialization and result construction.


847-866: LGTM!

Factory function tests verify proper path defaults for storage and encryption key locations.


873-939: LGTM!

Edge case tests provide excellent coverage for special characters, unicode, multiline values, long strings, and concurrent access patterns.


946-1020: LGTM!

Integration tests validate complete workflows including export/import cycles, template customization, and multi-app isolation with proper cleanup verification.

cortex/cli.py (1)

947-952: Reconsider exit code for partial imports.

Currently, if any variables are imported successfully but warnings/errors exist, the method returns 1 (failure). This means a partial import with invalid lines is marked as failed even though some variables succeeded. Consider returning 0 when count > 0 to indicate partial success, or clarify the intended behavior. (Previously flagged by Copilot.)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
tests/test_env_manager.py (2)

579-591: Verify line numbering in error messages.

The test expects the error message to contain "Line 3" for the invalid line. However, the actual line numbering depends on how the implementation counts lines:

  • If all lines (including empty ones) are counted: line 3 is correct ✓
  • If only non-empty lines are counted: the invalid line would be line 2 ✗

Please verify that the implementation in env_manager.import_env() counts lines consistently with this expectation.

#!/bin/bash
# Verify how import_env counts lines in error messages
rg -n -A5 -B5 "import_env.*def|Line \d+" cortex/env_manager.py

15-15: Remove unused imports.

The following imports are not used anywhere in the test file:

  • Line 15: json
  • Line 20: MagicMock, mock_open, patch
  • Line 34: VariableType

Removing unused imports improves code clarity and complies with PEP 8.

🔎 Proposed fix
-import json
 import os
-from unittest.mock import MagicMock, mock_open, patch
-
 import pytest
     EnvironmentValidator,
     EnvironmentVariable,
     TemplateVariable,
     ValidationResult,
-    VariableType,
     get_env_manager,

Also applies to: 20-20, 34-34

🧹 Nitpick comments (2)
tests/test_env_manager.py (2)

410-418: Consider testing additional failure scenarios.

The test suite is comprehensive, but consider adding tests for these additional edge cases:

  1. Corrupted storage files: Test behavior when JSON files are malformed
  2. Corrupted encryption key: Test behavior when the key file is corrupted
  3. File permission errors: Test behavior when storage directory is read-only
  4. get_variable_info() with non-existent variable: Test return value/behavior

These would improve robustness and error-handling coverage.


906-912: Consider documenting the rationale for test boundaries.

The test uses a 100KB value as a "very long" boundary. While this is reasonable, consider adding a comment explaining the choice (e.g., "Tests large values typical of certificates or API responses" or "Tests values exceeding typical environment variable sizes").

This helps future maintainers understand test intent.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d6f42c and 6f39fa8.

📒 Files selected for processing (1)
  • tests/test_env_manager.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • tests/test_env_manager.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_env_manager.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build Package
🔇 Additional comments (5)
tests/test_env_manager.py (5)

338-343: Test correctly verifies directory creation side effect.

The storage variable on line 341 is intentionally created to trigger directory creation as a side effect. The assertion on line 342 verifies this side effect occurred. This is a valid test pattern for initialization behavior.

The past review comment suggesting the variable is unused is incorrect.


38-48: Excellent test organization and structure.

The test suite demonstrates strong organization:

  • Clear sectioning with comment markers
  • Logical grouping by component (EnvironmentVariable, Validator, Storage, Manager, Templates)
  • Descriptive test names following pytest conventions
  • Comprehensive docstrings for all test methods
  • Proper fixture usage for test isolation
  • Dedicated integration test suite

This structure makes the test suite maintainable and easy to navigate.

Also applies to: 75-76, 147-148, 263-264, 335-336, 425-426, 621-622, 763-764, 820-821, 847-848, 873-874, 946-947


621-756: Comprehensive template test coverage.

The template tests thoroughly cover:

  • Built-in template validation (nodejs, python, django, flask, docker, database, aws)
  • Template lifecycle (list, get, apply)
  • Default and custom value handling
  • Required variable validation
  • Type validation during application
  • Encryption integration
  • Error cases (missing templates, invalid values)
  • Template-based validation

This aligns well with the PR objectives for template support.


285-296: Good security practice: verify key file permissions.

The test correctly verifies that encryption keys are created with 0600 permissions, which is essential for security. This ensures only the file owner can read the key.


946-1020: Integration tests validate PR objectives effectively.

The integration test suite validates key requirements from issue #95:

  • Per-application environment isolation (lines 996-1020)
  • Template-driven setup workflow (lines 977-994)
  • Complete variable lifecycle with encryption (lines 949-976)

These tests ensure the system works cohesively beyond unit-level validation.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 31 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
cortex/dependency_check.py (5)

15-22: Critical: Missing cryptography dependency.

The PR introduces encryption features for environment variable management using Fernet encryption, but cryptography is not included in the REQUIRED_DEPENDENCIES list. This will cause runtime failures when users attempt to use encryption features.

🔎 Add cryptography to the dependencies list
 REQUIRED_DEPENDENCIES = [
     ("pyyaml", "yaml"),
     ("rich", "rich"),
     ("anthropic", "anthropic"),
     ("openai", "openai"),
     ("python-dotenv", "dotenv"),
     ("requests", "requests"),
+    ("cryptography", "cryptography"),
 ]

24-29: Add docstring for public function.

The function lacks a docstring explaining its purpose, parameters, and return value.

🔎 Add docstring
 def check_dependency(package_name: str, import_name: str) -> DependencyStatus:
+    """
+    Check if a dependency is installed by attempting to import it.
+    
+    Args:
+        package_name: The package name as used by pip (e.g., 'pyyaml').
+        import_name: The name to use for importing (e.g., 'yaml').
+    
+    Returns:
+        DependencyStatus with package name, installation status, and import name.
+    """
     try:
         __import__(import_name)
         return DependencyStatus(package_name, True, import_name)
     except ImportError:
         return DependencyStatus(package_name, False, import_name)

As per coding guidelines, docstrings are required for all public APIs.


31-32: Add docstring for public function.

The function lacks a docstring explaining its purpose and return value.

🔎 Add docstring
 def get_missing_dependencies() -> list[str]:
+    """
+    Get a list of package names for dependencies that are not installed.
+    
+    Returns:
+        List of package names (for pip installation) that are missing.
+    """
     return [pkg for pkg, imp in REQUIRED_DEPENDENCIES if not check_dependency(pkg, imp).installed]

As per coding guidelines, docstrings are required for all public APIs.


34-56: Add docstring for public function.

The function lacks a docstring explaining its purpose, parameters, and return value.

🔎 Add docstring
 def format_installation_instructions(missing: list[str]) -> str:
+    """
+    Format user-friendly installation instructions for missing dependencies.
+    
+    Args:
+        missing: List of missing package names.
+    
+    Returns:
+        Formatted multi-line string with installation instructions.
+    """
     packages = " ".join(missing)
     return f"""

As per coding guidelines, docstrings are required for all public APIs.


58-62: Add docstring for public function.

The function lacks a docstring explaining its purpose and side effects (exits the program).

🔎 Add docstring
 def verify_dependencies_or_exit() -> None:
+    """
+    Verify all required dependencies are installed.
+    
+    Exits the program with status code 1 and prints installation
+    instructions if any dependencies are missing.
+    """
     missing = get_missing_dependencies()
     if missing:
         print(format_installation_instructions(missing), file=sys.stderr)
         sys.exit(1)

As per coding guidelines, docstrings are required for all public APIs.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f39fa8 and 3a61150.

📒 Files selected for processing (1)
  • cortex/dependency_check.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/dependency_check.py
🔇 Additional comments (1)
cortex/dependency_check.py (1)

10-13: LGTM! Good separation of concerns.

Adding the import_name field correctly distinguishes between the package name (for pip installation) and the import name (for runtime checks). This is essential for packages like pyyaml where the package name differs from the import name.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
cortex/cli.py (3)

751-792: Consider more specific exception handling.

The broad Exception catch at line 790 can hide important error details from users. Based on the operations being performed and past review feedback, consider catching specific exception types to provide more helpful error messages.

🔎 Proposed refinement
-        except Exception as e:
-            self._print_error(f"Environment operation failed: {e}")
-            return 1
+        except (ValueError, ImportError, OSError) as e:
+            self._print_error(f"Environment operation failed: {e}")
+            return 1
+        except Exception as e:
+            self._print_error(f"Unexpected error: {e}")
+            if self.verbose:
+                import traceback
+                traceback.print_exc()
+            return 1

794-826: LGTM with minor suggestion: Verify ImportError source.

The method properly handles errors and provides good user feedback. However, line 824 assumes all ImportError exceptions are related to the cryptography library. Consider verifying the error message or catching this more specifically.

🔎 Optional improvement
         except ImportError as e:
-            self._print_error(str(e))
-            cx_print("Install with: pip install cryptography", "info")
+            error_msg = str(e)
+            self._print_error(error_msg)
+            if "cryptography" in error_msg.lower():
+                cx_print("Install with: pip install cryptography", "info")
             return 1

919-962: Consider return value consistency for partial imports.

The method now correctly uses OSError and omits the unnecessary "r" mode (addressing previous review feedback). However, there's a potential inconsistency: when count > 0 and errors exist, the method prints a success message (line 951) but returns exit code 1 (line 955). This treats partial imports as failures despite successfully importing some variables.

Based on previous review feedback, consider treating partial imports with warnings as successful:

🔎 Proposed refinement
             if count > 0:
                 cx_print(f"✓ Imported {count} variable(s) to '{app}'", "success")
+                # Treat partial imports with warnings as success
+                return 0
             else:
                 cx_print("No variables imported", "info")
-
-            return 0 if not errors else 1
+                # Only return failure if nothing was imported and there were errors
+                return 1 if errors else 0
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a61150 and d77375f.

📒 Files selected for processing (1)
  • cortex/cli.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/cli.py
🧬 Code graph analysis (1)
cortex/cli.py (1)
cortex/env_manager.py (12)
  • EnvironmentManager (665-1105)
  • get_env_manager (1108-1110)
  • encrypt (502-514)
  • set_variable (689-738)
  • get_variable (740-767)
  • decrypt (516-534)
  • export_env (837-870)
  • import_env (872-932)
  • list_templates (960-967)
  • get_template (969-979)
  • apply_template (981-1061)
  • load_to_environ (934-956)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Package
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
🔇 Additional comments (17)
cortex/cli.py (17)

12-12: LGTM: Import is correct and necessary.

The import of EnvironmentManager and get_env_manager is properly scoped and aligns with the new environment management functionality.


827-846: LGTM: Method implementation is clean and correct.

The _env_get method properly retrieves and displays environment variables, with appropriate handling of encrypted values and clear user feedback.


848-879: LGTM: Well-structured list display with proper error handling.

The method handles both encrypted and unencrypted variables appropriately, includes proper error handling for decryption failures, and provides a clean user experience.


881-891: LGTM: Clean and straightforward implementation.

The delete operation is simple and correct, with appropriate user feedback.


893-917: LGTM: Export functionality is well-implemented.

The method correctly handles both file and stdout output, with proper error handling using OSError (addressing previous review feedback).


964-981: LGTM: Clear operation includes appropriate safety measures.

The confirmation prompt (unless --force is used) is a good safeguard against accidental data loss.


983-997: LGTM: Template command routing is clean.

Simple and effective delegation to template-specific handlers.


999-1012: LGTM: Template listing is well-formatted.

Clear presentation of available templates with helpful usage guidance.


1014-1037: LGTM: Template details display is user-friendly.

The method provides clear visualization of template structure, including required fields and defaults.


1039-1072: LGTM: Template application is well-implemented.

The method properly parses user input, handles validation, and provides clear feedback on success or failure.


1074-1087: LGTM: App listing is straightforward and effective.

Clear display of applications with environment variable counts.


1089-1100: LGTM: Load operation is simple and correct.

Direct delegation to the environment manager with appropriate user feedback.


1252-1342: LGTM: Comprehensive argument parsing for env commands.

The argument parser configuration is well-structured, with appropriate constraints and helpful descriptions for all environment management commands and subcommands.


1387-1388: LGTM: Command dispatch is properly integrated.

The env command is correctly wired into the main command dispatcher, following the established pattern.


469-469: LGTM: Minor formatting consistency improvement.


598-598: LGTM: Consistent string formatting.


1125-1126: LGTM: Help table updated correctly.

The notify and env commands are appropriately added to the rich help display.

Jay Surse added 2 commits December 23, 2025 12:00
- Replace broad Exception blocks with specific exceptions (ValueError, OSError, ImportError)
- Add verbose traceback printing for unexpected exceptions
- Only suggest pip install cryptography when ImportError mentions it
- Return success (0) for partial imports with warnings
- Remove unused sys import in env() method
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cortex/cli.py (1)

1-1462: Run Black to fix formatting violations and unblock CI pipeline.

The file contains 71 lines exceeding Black's 88-character limit. Run python -m black cortex/cli.py to automatically reformat the file according to the project's Black configuration.

🧹 Nitpick comments (1)
cortex/cli.py (1)

560-568: Consider extracting the exception handling pattern.

The try-except pattern with verbose traceback support is repeated ~8 times. While correct, you could reduce duplication with a decorator or helper method.

Example refactoring approach
def _handle_errors(self, func):
    """Decorator to standardize exception handling."""
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except (ValueError, ImportError, OSError) as e:
            self._print_error(str(e))
            return 1
        except Exception as e:
            self._print_error(f"Unexpected error: {e}")
            if self.verbose:
                import traceback
                traceback.print_exc()
            return 1
    return wrapper

Then apply as: @_handle_errors above methods like cache_stats, history, etc.

Also applies to: 629-637, 656-664, 691-699, 746-754, 836-844

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d77375f and d035c66.

📒 Files selected for processing (1)
  • cortex/cli.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/cli.py
🧬 Code graph analysis (1)
cortex/cli.py (1)
cortex/env_manager.py (12)
  • EnvironmentManager (665-1105)
  • get_env_manager (1108-1110)
  • encrypt (502-514)
  • set_variable (689-738)
  • get_variable (740-767)
  • decrypt (516-534)
  • export_env (837-870)
  • import_env (872-932)
  • list_templates (960-967)
  • get_template (969-979)
  • apply_template (981-1061)
  • load_to_environ (934-956)
🪛 GitHub Actions: CI
cortex/cli.py

[error] 1-1: Black formatting check failed. 1 file would be reformatted. Run 'black' to format.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.10)
🔇 Additional comments (7)
cortex/cli.py (7)

12-12: LGTM: Environment manager imports.

The imports are correctly structured and align with the external cortex/env_manager.py module API.


459-475: LGTM: Exception handling improvements.

The exception handling correctly uses OSError (addressing past review feedback), and the verbose traceback support is a helpful debugging feature.

Also applies to: 532-544


799-844: LGTM: Well-structured env command dispatcher.

The main env() method provides clear routing to all subcommands with appropriate error handling. The implementation covers all requirements from the PR objectives.


846-1154: LGTM: Comprehensive env helper implementations.

All 13 helper methods are well-implemented with:

  • Clear parameter handling
  • Appropriate use of the EnvironmentManager API
  • Good user feedback (success/error messages)
  • Proper encryption handling
  • Confirmation prompts for destructive operations

The implementation aligns well with the acceptance criteria from issue #95.


1306-1396: LGTM: Comprehensive argument parsing for env commands.

The argparse configuration is thorough and well-structured:

  • All subcommands properly defined
  • Appropriate argument types and choices
  • Clear help text for users
  • Template subcommand nesting handled correctly

1441-1442: LGTM: Env command routing.

The routing correctly dispatches the env command to cli.env(args), completing the integration.


1449-1458: LGTM: Enhanced exception handling in main().

The updated exception handling in main() properly catches specific exception types and provides verbose tracebacks when requested, improving debuggability.

@ShreeJejurikar
Copy link
Collaborator

PR Review - Environment Variable Manager ✅

Tested locally and the core functionality works well. Here are my findings:


✅ What Works

Core Functionality

  • Set/get/list/delete operations work correctly for non-encrypted variables
  • Per-application isolation is excellent - apps maintain separate environments
  • CLI is intuitive with clean output formatting

Templates System

  • 7 built-in templates (aws, database, django, docker, flask, nodejs, python)
  • Template details include variable types, descriptions, and sensible defaults
  • cortex env template apply creates variables with helpful comments
  • Very polished feature!

Import/Export

  • Import from .env files works correctly
  • Export to .env files works correctly
  • Selective encryption with --encrypt-keys flag works

Security

  • Encryption works and stores values securely (Fernet format)
  • Values are properly encrypted in storage files
  • Security-by-default design (encrypted values hidden without explicit flags)

🔧 How to Use Encrypted Variables

The encryption feature works, but requires understanding the flag system:

For get and list:

# Hidden by default (secure)
cortex env list myapp
# → API_KEY:    (empty/hidden)

# Show decrypted value
cortex env get myapp API_KEY --decrypt
# → API_KEY: secret123

cortex env list myapp --decrypt
# → API_KEY: secret123 (decrypted)

For export:

# Safe export (excludes encrypted values)
cortex env export myapp > .env
# → # API_KEY=[encrypted - use --include-encrypted to export]

# Export with secrets (when needed)
cortex env export myapp --include-encrypted > .env.production
# → API_KEY="secret123"

⚠️ Minor Issues (Non-Blocking)

1. Inconsistent Flag Naming

  • get and list use --decrypt
  • export uses --include-encrypted

Recommendation: Standardize to one flag name across all commands for consistency.

2. Display of Encrypted Values
When listing without --decrypt, encrypted values show as blank instead of a placeholder:

cortex env list myapp
# Current:  API_KEY:    (just blank)
# Expected: API_KEY: [encrypted]

3. Load Command
The load command doesn't support --decrypt flag:

cortex env load myapp --decrypt
# → error: unrecognized arguments: --decrypt

Unclear how users should load encrypted variables into their shell environment.


📋 Acceptance Criteria Status

From Issue #95:

  • Store environment variables ✅
  • Per-application isolation ✅
  • Encrypt sensitive values ✅ (works with --decrypt flag)
  • Environment templates ✅ (excellent implementation!)
  • Import/Export .env files ✅
  • Auto-load on service start ⚠️ (load command unclear)
  • Validation rules ❓ (not tested)
  • Unit tests >80% coverage ❓ (not verified)

💡 Recommendations

For Merge:
The core functionality works well and is production-ready. The issues are minor UX inconsistencies that can be addressed in follow-up PRs.

Suggested Follow-ups:

  1. Standardize flag naming (--decrypt or --include-encrypted everywhere)
  2. Show [encrypted] placeholder in list output
  3. Add --decrypt support to load command (or document the intended workflow)
  4. Verify test coverage meets 80% requirement

✅ Recommendation

APPROVE - Core features work correctly. Encryption/decryption is functional and secure. Minor UX issues can be improved in future PRs but don't block merging.

Great work on the templates system - that's a standout feature! 🎉


Test Environment:

  • OS: Ubuntu 22.04
  • Python: 3.x
  • Branch: pr-344

Reviewer: @ShreeJejurikar

@sonarqubecloud
Copy link

@Sahilbhatane Sahilbhatane merged commit 295e6eb into cortexlinux:main Dec 25, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Environment Variable Manager

4 participants