Skip to content

Conversation

@dhvll
Copy link
Collaborator

@dhvll dhvll commented Dec 19, 2025

Closes #250

Features:

  • OS detection (Ubuntu/Debian only)
  • Python version check (3.10+)
  • Virtual environment setup in ~/.cortex/venv
  • Installs cortex-linux via pip
  • Creates symlink in ~/.local/bin/cortex
  • PATH configuration (with automatic shell rc update)
  • API key detection and configuration
  • Installation verification (cortex --help)
  • Non-interactive mode support (for CI/CD)
  • Colored output with emojis

Usage:

curl -fsSL https://cortexlinux.com/install.sh | bash

Example Output: 🧠 Cortex Linux Installer

ℹ️ Detected: Ubuntu 24.04
ℹ️ Python version: 3.10.12
ℹ️ Installing to ~/.cortex...
✅ Virtual environment created
✅ cortex-linux installed successfully
✅ Created symlink: ~/.local/bin/cortex
✅ Installed! Run: cortex install nginx

Summary by CodeRabbit

Release Notes

  • New Features

    • Install multiple packages simultaneously with parallel execution
    • Automatic dependency resolution and optimization
    • Rollback failed installations to previous state
    • New Linux installer script for Cortex setup
    • Real-time progress tracking during batch operations
  • Bug Fixes

    • Improved installation history robustness to database connectivity issues
  • Tests

    • Added comprehensive test suite for batch installation workflows

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 19, 2025

Warning

Rate limit exceeded

@dhvll has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 11 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c90151c and 06a8459.

📒 Files selected for processing (1)
  • install.sh (1 hunks)

Walkthrough

The PR introduces comprehensive batch package installation orchestration with dependency graph analysis and parallel execution, extends CLI to support multi-package workflows, hardens the installation history module against database failures, and adds a one-liner Linux installer script.

Changes

Cohort / File(s) Summary
Batch Installer Core
cortex/batch_installer.py
New module introducing PackageStatus enum, PackageInstallation and BatchInstallationResult dataclasses, and BatchInstaller class with methods for concurrent dependency analysis, graph optimization, topological sorting, batch installation orchestration, and rollback management.
CLI Integration
cortex/cli.py
Adds _install_batch method and multi-package routing logic; integrates BatchInstaller with progress callbacks for parallel installation; extends install flow to handle multiple packages with status reporting and history recording.
Installation History Robustness
cortex/installation_history.py
Adds fallback-tolerant DB initialization with graceful degradation; guards read/write operations with availability flag; updates record_installation return type to str | None; implements directory fallback and timeout handling for non-fatal DB failures.
Installer Script
install.sh
New end-to-end Bash installer for Linux (Ubuntu/Debian) supporting non-interactive and interactive modes; covers OS detection, Python 3.10+ validation, venv setup, cortex installation from PyPI/GitHub/local source, PATH configuration, and API key management.
Batch Installer Tests
tests/test_batch_installer.py
Comprehensive test suite covering PackageInstallation, BatchInstallationResult, dependency analysis and resolution, topological sorting, graph optimization, dry-run and executed workflows, rollback behavior, and integration scenarios with shared dependencies.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI (install command)
    participant BI as BatchInstaller
    participant DR as DependencyResolver
    participant IC as InstallationCoordinator
    
    CLI->>BI: install_batch(packages)
    activate BI
    
    BI->>BI: analyze_packages() [concurrent]
    activate BI
    BI->>DR: resolve_dependencies(package)
    DR-->>BI: DependencyGraph
    BI->>BI: record PackageInstallation
    deactivate BI
    
    BI->>BI: optimize_dependency_graph()
    activate BI
    BI->>BI: identify shared dependencies
    BI->>BI: topological_sort()
    BI->>BI: generate per-package commands
    deactivate BI
    
    BI->>BI: prepare batch [dry-run or execute]
    alt execute=true
        BI->>IC: execute_installations() [parallel]
        activate IC
        IC->>IC: install shared dependencies
        IC->>IC: install packages in parallel
        IC-->>BI: results & statuses
        deactivate IC
    else dry_run=true
        BI->>BI: mark packages SKIPPED
    end
    
    BI->>BI: compute statistics
    BI-->>CLI: BatchInstallationResult
    deactivate BI
    
    CLI->>CLI: record history & report status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Key areas requiring attention:
    • BatchInstaller orchestration logic, especially _execute_installations parallel execution and error handling paths
    • Installation history DB fallback logic and graceful degradation (multiple fallback directories, timeout handling)
    • CLI routing logic ensuring correct dispatch between single-package and batch flows
    • Test coverage completeness for edge cases (shared dependencies, rollback failures, DB unavailability)

Possibly related issues

Possibly related PRs

Suggested reviewers

  • mikejmorgan-ai

Poem

🐰 Packages grouped and sorted bright,
Dependencies resolved just right,
Parallel installs take their flight,
With shared deps and rollback tight—
One command to install with might! ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description covers features and includes usage, but does not follow the required template structure (Related Issue link format, formatted checklist with checkboxes, etc.). Reformat the description to match the template: use "Closes #250" as a link, add a Summary section, and include the checklist with proper markdown formatting.
Out of Scope Changes check ⚠️ Warning The PR includes four files (install.sh, cortex/batch_installer.py, cortex/cli.py, cortex/installation_history.py, tests/test_batch_installer.py) but only install.sh directly addresses issue #250; batch installer and related changes appear out of scope. Remove or separate batch installer changes (batch_installer.py, cli.py modifications, installation_history.py, and test_batch_installer.py) into a dedicated PR focused solely on that feature.
Title check ❓ Inconclusive The PR title "install script" is vague and generic, providing minimal insight into the specific changes beyond acknowledging a script addition. Use a more descriptive title like "Add one-liner install script for Cortex Linux" or "[#250] Add one-liner install script (curl | bash)" to better convey the PR's purpose.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed All core coding requirements from issue #250 are met: OS detection, Python version check, venv creation, pip install, PATH setup, API key handling, and verification via cortex --help.
Docstring Coverage ✅ Passed Docstring coverage is 80.39% which is sufficient. The required threshold is 80.00%.

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: 5

Caution

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

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

406-408: Inconsistent error handling breaks the "history is optional" pattern.

update_installation re-raises exceptions on line 408, while other methods like record_installation gracefully return None. This could cause unexpected crashes in batch installation flows when history DB becomes unavailable mid-operation.

🔎 Proposed fix
         except Exception as e:
-            logger.error(f"Failed to update installation: {e}")
-            raise
+            logger.warning(f"Failed to update installation (non-fatal): {e}")
+            self._db_available = False
🧹 Nitpick comments (4)
install.sh (2)

286-289: Consider sourcing .env instead of duplicating the API key in shell rc.

Writing the API key to both ~/.cortex/.env and the shell rc file creates duplication and potential sync issues. A more maintainable approach would be to source the .env file from the shell rc.

🔎 Proposed alternative
                     if ! grep -q "ANTHROPIC_API_KEY" "$HOME/.${SHELL_NAME}rc" 2>/dev/null; then
                         echo "" >> "$HOME/.${SHELL_NAME}rc"
                         echo "# Added by Cortex Linux installer" >> "$HOME/.${SHELL_NAME}rc"
-                        echo "export ANTHROPIC_API_KEY=\"$API_KEY\"" >> "$HOME/.${SHELL_NAME}rc"
+                        echo "[ -f \"\$HOME/.cortex/.env\" ] && export \$(grep -v '^#' \"\$HOME/.cortex/.env\" | xargs)" >> "$HOME/.${SHELL_NAME}rc"
                     fi

This sources all variables from .env and avoids duplicating secrets in multiple files.


148-159: Error output is suppressed, making failures hard to diagnose.

Redirecting stderr to /dev/null on lines 149 and 156 hides pip errors. When installation fails, users won't see why. Consider showing errors on failure.

🔎 Proposed fix
     # Try to install from PyPI first
-    if "$PIP_CMD" install --quiet cortex-linux 2>/dev/null; then
+    if "$PIP_CMD" install --quiet cortex-linux; then
         print_success "cortex-linux installed successfully from PyPI"
         return 0
     fi
     
     # Fallback: Try installing from GitHub
     print_info "PyPI package not found, installing from GitHub..."
-    if "$PIP_CMD" install --quiet "git+https://github.com/cortexlinux/cortex.git" 2>/dev/null; then
+    if "$PIP_CMD" install --quiet "git+https://github.com/cortexlinux/cortex.git"; then
         print_success "cortex-linux installed successfully from GitHub"
         return 0
     fi

The --quiet flag already suppresses normal output; removing 2>/dev/null lets errors through.

tests/test_batch_installer.py (1)

43-73: Tests cover core happy paths well.

The test_analyze_packages and related tests properly mock DependencyResolver and verify the expected status transitions. Consider adding a test for when resolve_dependencies raises an exception to verify error handling.

def test_analyze_packages_with_failure(self, mock_resolver_class):
    """Test package analysis when dependency resolution fails"""
    mock_resolver = Mock()
    mock_resolver_class.return_value = mock_resolver
    mock_resolver.resolve_dependencies.side_effect = Exception("Network error")

    installer = BatchInstaller()
    packages = installer.analyze_packages(["nginx"])

    self.assertEqual(packages["nginx"].status, PackageStatus.FAILED)
    self.assertIn("Network error", packages["nginx"].error_message)
cortex/batch_installer.py (1)

296-304: Dry-run duplicates shared dependency commands across all packages.

In dry-run mode, _shared_deps commands are added to every package's command list (lines 301-303). This could confuse users as each package appears to independently install shared dependencies. Consider showing shared deps separately in the output.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c482df9 and c90151c.

📒 Files selected for processing (5)
  • cortex/batch_installer.py (1 hunks)
  • cortex/cli.py (7 hunks)
  • cortex/installation_history.py (6 hunks)
  • install.sh (1 hunks)
  • tests/test_batch_installer.py (1 hunks)
🧰 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/cli.py
  • tests/test_batch_installer.py
  • cortex/installation_history.py
  • cortex/batch_installer.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_batch_installer.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:

  • tests/test_batch_installer.py
  • cortex/installation_history.py
  • cortex/batch_installer.py
🧠 Learnings (2)
📚 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/cli.py
  • cortex/installation_history.py
  • cortex/batch_installer.py
📚 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 : Dry-run by default for all installations in command execution

Applied to files:

  • cortex/cli.py
🧬 Code graph analysis (2)
tests/test_batch_installer.py (1)
cortex/dependency_resolver.py (4)
  • DependencyGraph (29-36)
  • Dependency (18-25)
  • resolve_dependencies (202-260)
  • is_package_installed (95-97)
cortex/batch_installer.py (2)
cortex/dependency_resolver.py (4)
  • DependencyResolver (39-377)
  • DependencyGraph (29-36)
  • resolve_dependencies (202-260)
  • is_package_installed (95-97)
cortex/coordinator.py (1)
  • InstallationCoordinator (65-338)
🔇 Additional comments (13)
cortex/installation_history.py (1)

76-83: Resilient DB initialization looks good.

The try/except wrapper around DB initialization with the _db_available flag ensures history functionality is optional and won't break core installation flows. Based on learnings, this aligns with the audit logging requirement while gracefully handling environments where ~/.cortex/history.db isn't writable.

install.sh (3)

6-6: Good use of strict mode.

set -euo pipefail ensures the script fails fast on errors, unset variables, and pipe failures. This is essential for a curl | bash installer where silent failures could leave the system in an inconsistent state.


114-117: Automatic venv removal in non-interactive mode is appropriate for CI.

The warning message is printed before removal, which is the right balance for CI/CD environments where clean installations are expected.


358-372: Clean sequential installation flow.

The main function orchestrates all steps in a logical order. The set -e ensures early exit on failures. This aligns with the PR objectives for a zero-friction installer.

tests/test_batch_installer.py (2)

1-19: Good test structure and imports.

The test file follows Python testing conventions with unittest, proper mocking, and clear test class organization. The sys.path manipulation (line 11) ensures imports work from the tests directory.


254-258: Rollback test correctly verifies subprocess execution.

The test patches subprocess.run and verifies it's called when rollback is enabled. This ensures the rollback commands are actually executed.

cortex/cli.py (3)

185-193: Good routing logic for single vs batch installation.

The install method cleanly splits input and routes to _install_batch for multiple packages while preserving the existing single-package flow. This maintains backward compatibility.


751-754: Default behavior correctly requires explicit --execute flag.

Per coding guidelines, installations are dry-run by default. The CLI requires the --execute flag to actually run commands, which aligns with the "no silent execution" principle. Based on learnings from AGENTS.md.


346-362: Progress callback covers key statuses with clear emoji indicators.

The callback handles the main states (SUCCESS, FAILED, INSTALLING, SKIPPED) with distinct emojis. Transitional states fall through to "⏳" which is appropriate.

cortex/batch_installer.py (4)

38-56: PackageInstallation dataclass well-structured with helper method.

The dataclass cleanly captures installation state with appropriate defaults. The duration() helper is a good pattern for computed properties.


423-453: Parallel execution with ThreadPoolExecutor is well-implemented.

The parallel execution correctly uses as_completed for progress tracking and handles the shared dependencies flag to avoid duplicate installations. The apt package manager's built-in locking will prevent race conditions at the system level.


224-254: Simplified topological sort is adequate for current use case.

The implementation places all dependencies before packages. Since apt-get handles transitive dependency ordering internally, this approach is sufficient. The docstring accurately describes the behavior.


79-100: BatchInstaller class is well-documented with proper docstrings and type hints.

All public methods include comprehensive docstrings with Args and Returns sections. Type hints are present throughout, meeting coding guidelines requirements.

Comment on lines 476 to 480
for cmd in reversed(pkg.rollback_commands):
logger.info(f"Rolling back {pkg.name}: {cmd}")
# Execute rollback command
import subprocess
subprocess.run(cmd, shell=True, capture_output=True, timeout=60)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Security: shell=True enables shell injection; import should be at module level.

Using shell=True with rollback commands is a security risk. Additionally, importing subprocess inside the loop is inefficient.

🔎 Proposed fix

Add import at module level:

import subprocess

Then refactor the rollback execution:

                 for cmd in reversed(pkg.rollback_commands):
                     logger.info(f"Rolling back {pkg.name}: {cmd}")
                     # Execute rollback command
-                    import subprocess
-                    subprocess.run(cmd, shell=True, capture_output=True, timeout=60)
+                    subprocess.run(cmd.split(), capture_output=True, timeout=60, check=False)

Note: If commands contain quoted arguments, use shlex.split(cmd) instead of cmd.split().

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for cmd in reversed(pkg.rollback_commands):
logger.info(f"Rolling back {pkg.name}: {cmd}")
# Execute rollback command
import subprocess
subprocess.run(cmd, shell=True, capture_output=True, timeout=60)
for cmd in reversed(pkg.rollback_commands):
logger.info(f"Rolling back {pkg.name}: {cmd}")
# Execute rollback command
subprocess.run(cmd.split(), capture_output=True, timeout=60, check=False)
🤖 Prompt for AI Agents
In cortex/batch_installer.py around lines 476 to 480, the code imports
subprocess inside the loop and calls subprocess.run(..., shell=True) which is a
security risk and inefficient; move "import subprocess" to module level, remove
shell=True and pass a list of argv instead (use shlex.split(cmd) if
pkg.rollback_commands are strings with quoted args, or ensure commands are
already lists), call subprocess.run(args_list, capture_output=True, timeout=60)
and catch subprocess.SubprocessError (or TimeoutExpired) to log stdout/stderr
and the error; ensure you preserve the logger.info line and log failures with
logger.error including command output for debugging.

cortex/cli.py Outdated
Comment on lines 413 to 418
install_id = history.record_installation(
InstallationType.INSTALL,
package_names,
all_commands,
datetime.now(),
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

History records incorrect start time for batch installations.

datetime.now() on line 417 captures the current time (after execution), not the actual installation start time. This differs from the single-package flow which captures start_time = datetime.now() before installation begins.

🔎 Proposed fix
     def _install_batch(
         self, package_names: list[str], execute: bool = False, dry_run: bool = False
     ) -> int:
+        start_time = datetime.now()  # Capture before installation
         self._print_status("🧠", f"Analyzing {len(package_names)} packages...")
         
         # ... rest of method ...
         
         if execute or dry_run:
             try:
                 history = InstallationHistory()
                 # ...
                 install_id = history.record_installation(
                     InstallationType.INSTALL,
                     package_names,
                     all_commands,
-                    datetime.now(),
+                    start_time,
                 )

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In cortex/cli.py around lines 413 to 418, the batch-install branch records
installation start time using datetime.now() at the point of logging (after
installs complete) instead of the actual start time; capture start_time =
datetime.now() before the batch installation begins (same spot pattern as
single-package flow) and pass that start_time into history.record_installation
instead of calling datetime.now() inline so the recorded start time reflects
when the installation started.

Comment on lines 158 to 161
except sqlite3.OperationalError as e:
# Database might be locked or corrupted
logger.warning(f"Database operational error (non-fatal): {e}")
# Don't raise - allow operations to continue without history
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

OperationalError handler doesn't mark DB as unavailable.

When sqlite3.OperationalError is caught (e.g., locked or corrupted DB), _db_available is not set to False, but the method returns early in the generic except block. This inconsistency could lead to subsequent operations attempting DB access and failing repeatedly.

🔎 Proposed fix
         except sqlite3.OperationalError as e:
             # Database might be locked or corrupted
             logger.warning(f"Database operational error (non-fatal): {e}")
-            # Don't raise - allow operations to continue without history
+            self._db_available = False
+            return
         except Exception as e:
🤖 Prompt for AI Agents
In cortex/installation_history.py around lines 158 to 161, the
sqlite3.OperationalError handler logs the error but does not mark the database
as unavailable; update the except block to set self._db_available = False (and
optionally clear/close any active connection handles if applicable) before
returning so subsequent operations know the DB is not usable and will avoid
further DB access attempts.

Comment on lines +185 to +190
if [ -L "$CORTEX_SYMLINK" ] || [ -f "$CORTEX_SYMLINK" ]; then
rm -f "$CORTEX_SYMLINK"
fi

ln -s "$CORTEX_BIN/cortex" "$CORTEX_SYMLINK"
print_success "Created symlink: $CORTEX_SYMLINK"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Symlink created without verifying target binary exists.

If the pip installation silently fails or the binary name differs, this creates a broken symlink. Add a verification step.

🔎 Proposed fix
     # Create symlink
     if [ -L "$CORTEX_SYMLINK" ] || [ -f "$CORTEX_SYMLINK" ]; then
         rm -f "$CORTEX_SYMLINK"
     fi
     
+    if [ ! -f "$CORTEX_BIN/cortex" ]; then
+        print_error "Cortex binary not found at $CORTEX_BIN/cortex"
+        exit 1
+    fi
+    
     ln -s "$CORTEX_BIN/cortex" "$CORTEX_SYMLINK"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ -L "$CORTEX_SYMLINK" ] || [ -f "$CORTEX_SYMLINK" ]; then
rm -f "$CORTEX_SYMLINK"
fi
ln -s "$CORTEX_BIN/cortex" "$CORTEX_SYMLINK"
print_success "Created symlink: $CORTEX_SYMLINK"
if [ -L "$CORTEX_SYMLINK" ] || [ -f "$CORTEX_SYMLINK" ]; then
rm -f "$CORTEX_SYMLINK"
fi
if [ ! -f "$CORTEX_BIN/cortex" ]; then
print_error "Cortex binary not found at $CORTEX_BIN/cortex"
exit 1
fi
ln -s "$CORTEX_BIN/cortex" "$CORTEX_SYMLINK"
print_success "Created symlink: $CORTEX_SYMLINK"
🤖 Prompt for AI Agents
In install.sh around lines 185 to 190, the script creates the CORTEX_SYMLINK
without verifying that the target binary "$CORTEX_BIN/cortex" actually exists or
is executable; update the logic to first check that "$CORTEX_BIN/cortex" exists
and is executable (e.g. [ -x "$CORTEX_BIN/cortex" ]) and if not, print an error
and exit non-zero, only proceeding to remove any existing symlink and create the
new symlink when the target check passes; ensure the failure path does not leave
a broken symlink and returns a non-zero status for CI/automation to catch.

Comment on lines +277 to +293
# Save to .env file
mkdir -p "$HOME/.cortex"
echo "ANTHROPIC_API_KEY=$API_KEY" >> "$HOME/.cortex/.env"
export ANTHROPIC_API_KEY="$API_KEY"

# Also add to shell rc for persistence
SHELL_NAME=$(basename "$SHELL")
if [ -f "$HOME/.${SHELL_NAME}rc" ]; then
if ! grep -q "ANTHROPIC_API_KEY" "$HOME/.${SHELL_NAME}rc" 2>/dev/null; then
echo "" >> "$HOME/.${SHELL_NAME}rc"
echo "# Added by Cortex Linux installer" >> "$HOME/.${SHELL_NAME}rc"
echo "export ANTHROPIC_API_KEY=\"$API_KEY\"" >> "$HOME/.${SHELL_NAME}rc"
fi
fi

print_success "API key saved to ~/.cortex/.env and ~/.${SHELL_NAME}rc"
fi
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

API key may be duplicated in .env file on repeated runs.

Line 279 appends the API key to ~/.cortex/.env without checking if it already exists, unlike the shell rc handling on lines 285-289. Repeated installer runs could create duplicate entries.

🔎 Proposed fix
                 if [ -n "$API_KEY" ]; then
                     # Save to .env file
                     mkdir -p "$HOME/.cortex"
+                    # Remove existing key if present, then add new one
+                    if [ -f "$HOME/.cortex/.env" ]; then
+                        sed -i '/^ANTHROPIC_API_KEY=/d' "$HOME/.cortex/.env"
+                    fi
-                    echo "ANTHROPIC_API_KEY=$API_KEY" >> "$HOME/.cortex/.env"
+                    echo "ANTHROPIC_API_KEY=$API_KEY" >> "$HOME/.cortex/.env"
                     export ANTHROPIC_API_KEY="$API_KEY"

Apply the same pattern to the OPENAI_API_KEY block (lines 298-315).

🤖 Prompt for AI Agents
In install.sh around lines 277 to 293, the installer unconditionally appends
ANTHROPIC_API_KEY to ~/.cortex/.env which can create duplicate entries on
repeated runs; change the logic to check ~/.cortex/.env for an existing
ANTHROPIC_API_KEY (e.g., use grep -q) and if present either replace the existing
line (e.g., with sed) or skip appending, otherwise append the key; keep the
export ANTHROPIC_API_KEY="$API_KEY" behavior and mirror this same
non-duplicating pattern for the OPENAI_API_KEY block as noted in the review.

- Introduced install.sh for a streamlined installation process of Cortex Linux.
- The script includes OS detection, Python version checks, virtual environment setup, and installation of the cortex-linux package from PyPI or GitHub.
- Added functionality for API key configuration and PATH adjustments for user convenience.
- Enhanced error handling and user feedback throughout the installation process.
@sonarqubecloud
Copy link

@mikejmorgan-ai
Copy link
Member

Closing as duplicate - #315 was merged for the one-liner installer. Thanks for contributing!

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.

[MVP] One-liner install script (curl | bash)

2 participants