-
-
Notifications
You must be signed in to change notification settings - Fork 52
feat: add package build from source (#101) #482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b931951
88b50fa
c7cea00
5ed2a4b
0bb1b85
23790fe
8e2439d
4ae5dbc
7e6687a
bac29c2
e75f2b2
882d4a9
38462e0
fdd96d9
a471a6a
7a6f6e8
8b23ce8
dbb3aea
cdd5e0f
bd4027b
84d8425
d3740b4
ac5cd85
c2581ab
7b6a20c
863cdf8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,10 +10,17 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from rich.markdown import Markdown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.api_key_detector import auto_detect_api_key, setup_api_key | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.ask import AskHandler | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.branding import VERSION, console, cx_header, cx_print, show_banner | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.coordinator import InstallationCoordinator, InstallationStep, StepStatus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.coordinator import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InstallationCoordinator, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InstallationResult, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InstallationStep, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| StepStatus, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.demo import run_demo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.dependency_importer import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DependencyImporter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -240,8 +247,9 @@ def notify(self, args): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif args.notify_action == "enable": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mgr.config["enabled"] = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Addressing CodeRabbit feedback: Ideally should use a public method instead of private _save_config, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # but keeping as is for a simple fix (or adding a save method to NotificationManager would be best). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Addressing CodeRabbit feedback: Ideally should use a public method | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # instead of private _save_config, but keeping as is for a simple fix | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # (or adding a save method to NotificationManager would be best). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mgr._save_config() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._print_success("Notifications enabled") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -817,6 +825,9 @@ def install( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| execute: bool = False, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dry_run: bool = False, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parallel: bool = False, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from_source: bool = False, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source_url: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Validate input first | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is_valid, error = validate_install_request(software) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -851,6 +862,10 @@ def install( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start_time = datetime.now() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Handle --from-source flag | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if from_source: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self._install_from_source(software, execute, dry_run, source_url, version) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._print_status("🧠", "Understanding request...") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interpreter = CommandInterpreter(api_key=api_key, provider=provider) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1516,7 +1531,8 @@ def history(self, limit: int = 20, status: str | None = None, show_id: str | Non | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| packages += f" +{len(r.packages) - 2}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"{r.id:<18} {date:<20} {r.operation_type.value:<12} {packages:<30} {r.status.value:<15}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"{r.id:<18} {date:<20} {r.operation_type.value:<12} " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"{packages:<30} {r.status.value:<15}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2105,7 +2121,8 @@ def _env_template(self, env_mgr: EnvironmentManager, args: argparse.Namespace) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self._env_template_apply(env_mgr, args) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._print_error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Please specify: template list, template show <name>, or template apply <name> <app>" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Please specify: template list, template show <name>, " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "or template apply <name> <app>" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2825,6 +2842,220 @@ def progress_callback(current: int, total: int, step: InstallationStep) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.print(f"Error: {result.error_message}", style="red") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _install_from_source( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package_name: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| execute: bool, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dry_run: bool, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source_url: str | None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version: str | None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2845
to
+2852
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Add comprehensive docstring for this method. The 📝 Suggested docstring def _install_from_source(
self,
package_name: str,
execute: bool,
dry_run: bool,
source_url: str | None,
version: str | None,
) -> int:
+ """Build and install a package from source.
+
+ Handles the complete source build workflow including dependency detection,
+ build system detection, compilation, and installation. Integrates with
+ InstallationHistory for audit logging.
+
+ Args:
+ package_name: Name of the package to build (supports package@version syntax)
+ execute: Execute installation commands after building
+ dry_run: Show commands without executing
+ source_url: Optional URL to source code (tarball, GitHub, etc.)
+ version: Optional version to build (can also be specified via package@version)
+
+ Returns:
+ int: Exit code (0 for success, 1 for failure)
+ """📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents✅ Addressed in commit bd4027b
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @aybanda Address this one.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Install a package from a source URL by building and optionally installing it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| This method handles the complete workflow for installing packages from source code: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsing version information, building the package, and optionally executing | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| installation commands. It supports dry-run mode for previewing operations and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| records all activities in the installation history for audit purposes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package_name: Name of the package to install. If version is specified | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using "@" syntax (e.g., "python@3.12"), it will be parsed automatically | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if version parameter is None. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| execute: If True, executes the installation commands after building. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| If False, only builds the package and displays commands without executing. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dry_run: If True, performs a dry run showing what commands would be executed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| without actually building or installing. Takes precedence over execute. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source_url: Optional URL to the source code repository or tarball. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| If None, the SourceBuilder will attempt to locate the source automatically. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version: Optional version string to build. If None and package_name contains | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@", the version will be extracted from package_name. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int: Exit status code. Returns 0 on success (build/install completed or | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dry-run completed), 1 on failure (build failed or installation failed). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Side Effects: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Invokes SourceBuilder.build_from_source() to build the package | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - May execute installation commands via InstallationCoordinator if execute=True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Records installation start, progress, and completion in InstallationHistory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Prints status messages and progress to console | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - May use cached builds if available | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Raises: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| No exceptions are raised directly, but underlying operations may fail: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - SourceBuilder.build_from_source() failures are caught and returned as status 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - InstallationCoordinator.execute() failures are caught and returned as status 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - InstallationHistory exceptions are caught and logged as warnings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Special Behavior: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - dry_run=True: Shows build/install commands without executing any operations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Returns 0 after displaying commands. Installation history is still recorded. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - execute=False, dry_run=False: Builds the package and displays install commands | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| but does not execute them. Returns 0. User is prompted to run with --execute. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - execute=True, dry_run=False: Builds the package and executes all installation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commands. Returns 0 on success, 1 on failure. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Version parsing: If package_name contains "@" (e.g., "python@3.12") and version | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is None, the version is automatically extracted and package_name is updated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Caching: Uses cached builds when available, printing a notification if cache | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is used. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.sandbox.sandbox_executor import SandboxExecutor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cortex.source_builder import SourceBuilder | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Initialize history for audit logging (same as install() method) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| history = InstallationHistory() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_id = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start_time = datetime.now() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder = SourceBuilder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Parse version from package name if specified (e.g., python@3.12) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if "@" in package_name and not version: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parts = package_name.split("@") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package_name = parts[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version = parts[1] if len(parts) > 1 and parts[1] else None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"Building {package_name} from source...", "info") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if version: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"Version: {version}", "info") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if source_url: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"Source URL: {source_url}", "info") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Do NOT record installation start yet - wait until after build completes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # so we can record the actual install commands | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_id = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = builder.build_from_source( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package_name=package_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version=version, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source_url=source_url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use_cache=True, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dry_run=dry_run, # Pass dry_run to skip full build in dry-run mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not result.success: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._print_error(f"Build failed: {result.error_message}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Record failed installation (only if we have real install commands) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if result.install_commands and result.build_dir != "<dry-run-no-dir>": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_id = history.record_installation( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InstallationType.INSTALL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [package_name], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.install_commands, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start_time, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| history.update_installation( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InstallationStatus.FAILED, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error_message=result.error_message or "Build failed", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.warning(f"Failed to record installation failure: {e}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"⚠️ Warning: Could not record installation failure: {e}", "warning") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2936
to
+2955
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Record failed builds in history even when install commands are empty. If the build fails before 🛠️ Suggested fix- if not result.success:
- self._print_error(f"Build failed: {result.error_message}")
- # Record failed installation (only if we have real install commands)
- if result.install_commands and result.build_dir != "<dry-run-no-dir>":
- try:
- install_id = history.record_installation(
- InstallationType.INSTALL,
- [package_name],
- result.install_commands,
- start_time,
- )
- history.update_installation(
- install_id,
- InstallationStatus.FAILED,
- error_message=result.error_message or "Build failed",
- )
- except Exception as e:
- logger.warning(f"Failed to record installation failure: {e}")
- cx_print(f"⚠️ Warning: Could not record installation failure: {e}", "warning")
- return 1
+ if not result.success:
+ self._print_error(f"Build failed: {result.error_message}")
+ try:
+ install_id = history.record_installation(
+ InstallationType.INSTALL,
+ [package_name],
+ result.install_commands or [],
+ start_time,
+ )
+ history.update_installation(
+ install_id,
+ InstallationStatus.FAILED,
+ error_message=result.error_message or "Build failed",
+ )
+ except Exception as e:
+ logger.warning(f"Failed to record installation failure: {e}")
+ cx_print(f"⚠️ Warning: Could not record installation failure: {e}", "warning")
+ return 1🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if result.cached: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"Using cached build for {package_name}", "info") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Record successful build/plan now that we have the actual install commands | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Record for all scenarios (dry-run, build-only, and execute) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_id = history.record_installation( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InstallationType.INSTALL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [package_name], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.install_commands, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start_time, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.warning(f"Failed to record installation: {e}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"⚠️ Warning: Could not record installation: {e}", "warning") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_id = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Use only actual install commands for history and execution | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commands = result.install_commands | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if dry_run: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print("\nBuild commands (dry run):", "info") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for cmd in result.install_commands: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.print(f" • {cmd}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Record successful dry run | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if install_id: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| history.update_installation(install_id, InstallationStatus.SUCCESS) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.warning(f"Failed to update installation record: {e}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"⚠️ Warning: Could not record installation success: {e}", "warning") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not execute: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print("\nBuild completed. Install commands:", "info") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for cmd in result.install_commands: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.print(f" • {cmd}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print("Run with --execute to install", "info") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Record successful build (but not installed) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if install_id: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| history.update_installation(install_id, InstallationStatus.SUCCESS) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.warning(f"Failed to update installation record: {e}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"⚠️ Warning: Could not record installation success: {e}", "warning") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Execute install commands | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def progress_callback(current: int, total: int, step: InstallationStep) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status_emoji = "⏳" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if step.status == StepStatus.SUCCESS: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status_emoji = "✅" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif step.status == StepStatus.FAILED: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status_emoji = "❌" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.print(f"[{current}/{total}] {status_emoji} {step.description}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Notify about sandbox availability for build/install commands | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sandbox_executor = SandboxExecutor() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if sandbox_executor.is_firejail_available(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print("🔒 Build/install commands will run in Firejail sandbox", "info") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print("⚠️ Firejail not available - running commands without sandboxing", "warning") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| coordinator = InstallationCoordinator( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commands=result.install_commands, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| descriptions=[f"Install {package_name}" for _ in result.install_commands], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout=600, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stop_on_error=True, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| progress_callback=progress_callback, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_result = coordinator.execute() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if install_result.success: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._print_success(f"{package_name} built and installed successfully!") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Record successful installation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if install_id: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| history.update_installation(install_id, InstallationStatus.SUCCESS) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.print(f"\n📝 Installation recorded (ID: {install_id})") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.print(f" To rollback: cortex rollback {install_id}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.warning(f"Failed to update installation record: {e}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"⚠️ Warning: Could not record installation success: {e}", "warning") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._print_error("Installation failed") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error_msg = install_result.error_message or "Installation failed" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if install_result.error_message: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.print(f"Error: {error_msg}", style="red") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Record failed installation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if install_id: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| history.update_installation( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_id, InstallationStatus.FAILED, error_message=error_msg | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.warning(f"Failed to update installation record: {e}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cx_print(f"⚠️ Warning: Could not record installation failure: {e}", "warning") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # -------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3026,6 +3257,21 @@ def main(): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action="store_true", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help="Enable parallel execution for multi-step installs", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_parser.add_argument( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--from-source", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action="store_true", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help=("Build and install from source code when binaries unavailable"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_parser.add_argument( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--source-url", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type=str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help="URL to source code (for --from-source)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| install_parser.add_argument( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--pkg-version", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type=str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help="Version to build (for --from-source)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Remove command - uninstall with impact analysis | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remove_parser = subparsers.add_parser( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3596,6 +3842,9 @@ def main(): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| execute=args.execute, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dry_run=args.dry_run, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parallel=args.parallel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from_source=getattr(args, "from_source", False), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source_url=getattr(args, "source_url", None), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version=getattr(args, "pkg_version", None), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif args.command == "remove": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Handle --execute flag to override default dry-run | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--from-sourceruns real builds without--execute.With
from_source=Trueandexecute=False, the flow still downloads, installs build deps, and compiles. This violates the “dry‑run by default” requirement for installation operations.✅ Suggested guard
Based on learnings, dry‑run should be the default for install operations.
🤖 Prompt for AI Agents