mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-21 21:05:09 +00:00
218 lines
6.8 KiB
Python
Executable File
218 lines
6.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Dev CLI - Chromium patch management tool
|
|
|
|
A git-like patch management system for maintaining patches against Chromium.
|
|
Enables extracting, applying, and managing patches across Chromium upgrades.
|
|
"""
|
|
|
|
import click
|
|
import os
|
|
import sys
|
|
import yaml
|
|
from pathlib import Path
|
|
from typing import Optional, Dict, Any
|
|
from dataclasses import dataclass
|
|
|
|
# Add build directory to path for imports
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
|
|
from context import BuildContext
|
|
from utils import log_info, log_error, log_success, log_warning, join_paths
|
|
|
|
|
|
@dataclass
|
|
class DevCliConfig:
|
|
"""Configuration for Dev CLI from various sources"""
|
|
|
|
chromium_src: Optional[Path] = None
|
|
auto_commit: bool = False
|
|
interactive: bool = True
|
|
|
|
@classmethod
|
|
def load(cls, cli_chromium_src: Optional[Path] = None) -> "DevCliConfig":
|
|
"""Load configuration from various sources with precedence:
|
|
1. CLI arguments (highest priority)
|
|
2. Environment variables
|
|
3. Config file
|
|
4. Defaults (lowest priority)
|
|
"""
|
|
config = cls()
|
|
|
|
# Load from config file if exists
|
|
config_file = Path.cwd() / ".dev-cli.yaml"
|
|
if config_file.exists():
|
|
try:
|
|
with open(config_file, "r") as f:
|
|
file_config = yaml.safe_load(f)
|
|
if file_config and "defaults" in file_config:
|
|
defaults = file_config["defaults"]
|
|
if "chromium_src" in defaults:
|
|
config.chromium_src = Path(defaults["chromium_src"])
|
|
config.auto_commit = defaults.get("auto_commit", False)
|
|
config.interactive = defaults.get("interactive", True)
|
|
except Exception as e:
|
|
log_warning(f"Failed to load config file: {e}")
|
|
|
|
# Override with environment variables
|
|
if "DEV_CLI_CHROMIUM_SRC" in os.environ:
|
|
config.chromium_src = Path(os.environ["DEV_CLI_CHROMIUM_SRC"])
|
|
if "DEV_CLI_AUTO_COMMIT" in os.environ:
|
|
config.auto_commit = os.environ["DEV_CLI_AUTO_COMMIT"].lower() in (
|
|
"true",
|
|
"1",
|
|
"yes",
|
|
)
|
|
if "DEV_CLI_INTERACTIVE" in os.environ:
|
|
config.interactive = os.environ["DEV_CLI_INTERACTIVE"].lower() in (
|
|
"true",
|
|
"1",
|
|
"yes",
|
|
)
|
|
|
|
# Override with CLI arguments (highest priority)
|
|
if cli_chromium_src:
|
|
config.chromium_src = cli_chromium_src
|
|
|
|
return config
|
|
|
|
|
|
def create_build_context(chromium_src: Optional[Path] = None) -> Optional[BuildContext]:
|
|
"""Create BuildContext with dev CLI extensions"""
|
|
config = DevCliConfig.load(chromium_src)
|
|
|
|
if not config.chromium_src:
|
|
log_error("Chromium source directory not specified")
|
|
log_info("Use --chromium-src or set DEV_CLI_CHROMIUM_SRC environment variable")
|
|
return None
|
|
|
|
if not config.chromium_src.exists():
|
|
log_error(f"Chromium source directory does not exist: {config.chromium_src}")
|
|
return None
|
|
|
|
# For dev CLI, we just need it to be a git repository
|
|
# Don't enforce strict Chromium structure
|
|
if not (config.chromium_src / ".git").exists():
|
|
log_warning(f"Warning: Not a git repository: {config.chromium_src}")
|
|
# Continue anyway - patches might still work
|
|
|
|
try:
|
|
ctx = BuildContext(
|
|
root_dir=Path.cwd(),
|
|
chromium_src=config.chromium_src,
|
|
architecture="", # Not needed for patch operations
|
|
build_type="debug", # Not needed for patch operations
|
|
)
|
|
|
|
# Store config in context for access by commands
|
|
ctx.dev_config = config
|
|
|
|
return ctx
|
|
except Exception as e:
|
|
log_error(f"Failed to create build context: {e}")
|
|
return None
|
|
|
|
|
|
@click.group()
|
|
@click.option(
|
|
"--chromium-src",
|
|
"-S",
|
|
type=click.Path(exists=True, path_type=Path),
|
|
help="Path to Chromium source directory",
|
|
)
|
|
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
|
@click.option("--quiet", "-q", is_flag=True, help="Suppress non-essential output")
|
|
@click.pass_context
|
|
def cli(ctx, chromium_src, verbose, quiet):
|
|
"""Dev CLI - Chromium patch management tool
|
|
|
|
This tool provides git-like commands for managing patches against Chromium:
|
|
|
|
\b
|
|
Extract patches from commits:
|
|
dev extract commit HEAD
|
|
dev extract range HEAD~5 HEAD
|
|
|
|
\b
|
|
Apply patches:
|
|
dev apply all
|
|
dev apply feature llm-chat
|
|
|
|
\b
|
|
Manage features:
|
|
dev feature list
|
|
dev feature add my-feature HEAD
|
|
dev feature show my-feature
|
|
"""
|
|
# Store options in context for subcommands
|
|
ctx.ensure_object(dict)
|
|
ctx.obj["chromium_src"] = chromium_src
|
|
ctx.obj["verbose"] = verbose
|
|
ctx.obj["quiet"] = quiet
|
|
|
|
|
|
# Import and register subcommand groups
|
|
# These will be created in the next step
|
|
try:
|
|
from modules.dev_cli import extract, apply, feature
|
|
|
|
cli.add_command(extract.extract_group)
|
|
cli.add_command(apply.apply_group)
|
|
cli.add_command(feature.feature_group)
|
|
except ImportError as e:
|
|
# During initial setup, modules might not exist yet
|
|
log_warning(f"Some modules not yet available: {e}")
|
|
|
|
# Add placeholder commands for testing
|
|
@cli.command()
|
|
@click.pass_context
|
|
def status(ctx):
|
|
"""Show dev CLI status"""
|
|
log_info("Dev CLI Status")
|
|
log_info("-" * 40)
|
|
|
|
build_ctx = create_build_context(ctx.obj.get("chromium_src"))
|
|
if build_ctx:
|
|
log_success(f"Chromium source: {build_ctx.chromium_src}")
|
|
|
|
# Check for patches directory
|
|
patches_dir = build_ctx.root_dir / "chromium_src"
|
|
if patches_dir.exists():
|
|
patch_count = len(list(patches_dir.rglob("*.patch")))
|
|
log_info(f"Individual patches: {patch_count}")
|
|
else:
|
|
log_warning("No patches directory found")
|
|
|
|
# Check for features.yaml
|
|
features_file = build_ctx.root_dir / "features.yaml"
|
|
if features_file.exists():
|
|
with open(features_file) as f:
|
|
features = yaml.safe_load(f)
|
|
feature_count = len(features.get("features", {}))
|
|
log_info(f"Features defined: {feature_count}")
|
|
else:
|
|
log_warning("No features.yaml found")
|
|
else:
|
|
log_error("Failed to create build context")
|
|
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
try:
|
|
cli()
|
|
except KeyboardInterrupt:
|
|
log_warning("\nInterrupted by user")
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
if "--verbose" in sys.argv or "-v" in sys.argv:
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
else:
|
|
log_error(f"Error: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|