Files
BrowserOS/build/modules/dev_cli/apply.py
2025-09-18 08:41:08 -07:00

211 lines
6.3 KiB
Python

"""
Apply module - Apply patches to Chromium source
Simple and straightforward patch application with minimal error handling.
"""
import click
import yaml
from pathlib import Path
from typing import List, Tuple
from context import BuildContext
from modules.dev_cli.utils import run_git_command, GitError
from utils import log_info, log_error, log_success, log_warning
@click.group(name='apply')
def apply_group():
"""Apply patches to Chromium source"""
pass
@apply_group.command(name='all')
@click.option('--commit-each', is_flag=True, help='Create git commit after each patch')
@click.option('--dry-run', is_flag=True, help='Test patches without applying')
@click.pass_context
def apply_all(ctx, commit_each, dry_run):
"""Apply all patches from chromium_src/
\b
Examples:
dev apply all
dev apply all --commit-each
dev apply all --dry-run
"""
chromium_src = ctx.parent.obj.get('chromium_src')
from dev import create_build_context
build_ctx = create_build_context(chromium_src)
if not build_ctx:
return
patches_dir = build_ctx.get_dev_patches_dir()
if not patches_dir.exists():
log_error(f"Patches directory does not exist: {patches_dir}")
ctx.exit(1)
# Find all patch files (excluding special markers)
patch_files = sorted([
p for p in patches_dir.rglob("*.patch")
if not p.name.endswith('.deleted') and not p.name.endswith('.binary')
])
if not patch_files:
log_warning("No patch files found")
return
log_info(f"Found {len(patch_files)} patches")
if dry_run:
log_info("DRY RUN - No changes will be made")
applied = 0
failed = []
for patch_path in patch_files:
rel_path = patch_path.relative_to(patches_dir)
if dry_run:
# Just check if patch would apply
result = run_git_command(
['git', 'apply', '--check', '-p1', str(patch_path)],
cwd=build_ctx.chromium_src
)
if result.returncode == 0:
log_success(f" ✓ Would apply: {rel_path}")
applied += 1
else:
log_error(f" ✗ Would fail: {rel_path}")
failed.append(str(rel_path))
else:
# Actually apply the patch
result = run_git_command(
['git', 'apply', '-p1', str(patch_path)],
cwd=build_ctx.chromium_src
)
if result.returncode == 0:
log_success(f" ✓ Applied: {rel_path}")
applied += 1
if commit_each:
# Create commit
run_git_command(['git', 'add', '-A'], cwd=build_ctx.chromium_src)
run_git_command(
['git', 'commit', '-m', f'Apply patch: {rel_path.stem}'],
cwd=build_ctx.chromium_src
)
else:
log_error(f" ✗ Failed: {rel_path}")
log_error(f" {result.stderr}")
failed.append(str(rel_path))
# Summary
log_info(f"\nSummary: {applied} applied, {len(failed)} failed")
if failed:
log_error("Failed patches:")
for p in failed:
log_error(f" - {p}")
ctx.exit(1)
@apply_group.command(name='feature')
@click.argument('feature_name')
@click.option('--commit-each', is_flag=True, help='Create git commit after each patch')
@click.option('--dry-run', is_flag=True, help='Test patches without applying')
@click.pass_context
def apply_feature(ctx, feature_name, commit_each, dry_run):
"""Apply patches for a specific feature
\b
Examples:
dev apply feature llm-chat
dev apply feature my-feature --commit-each
"""
chromium_src = ctx.parent.obj.get('chromium_src')
from dev import create_build_context
build_ctx = create_build_context(chromium_src)
if not build_ctx:
return
# Load features.yaml
features_path = build_ctx.get_features_yaml_path()
if not features_path.exists():
log_error(f"No features.yaml found")
ctx.exit(1)
with open(features_path) as f:
data = yaml.safe_load(f)
features = data.get('features', {})
if feature_name not in features:
log_error(f"Feature '{feature_name}' not found")
log_info("Available features:")
for name in features:
log_info(f" - {name}")
ctx.exit(1)
file_list = features[feature_name].get('files', [])
if not file_list:
log_warning(f"Feature '{feature_name}' has no files")
return
log_info(f"Applying patches for feature '{feature_name}' ({len(file_list)} files)")
if dry_run:
log_info("DRY RUN - No changes will be made")
applied = 0
failed = []
for file_path in file_list:
patch_path = build_ctx.get_patch_path_for_file(file_path)
if not patch_path.exists():
log_warning(f" Patch not found: {file_path}")
failed.append(file_path)
continue
if dry_run:
result = run_git_command(
['git', 'apply', '--check', '-p1', str(patch_path)],
cwd=build_ctx.chromium_src
)
if result.returncode == 0:
log_success(f" ✓ Would apply: {file_path}")
applied += 1
else:
log_error(f" ✗ Would fail: {file_path}")
failed.append(file_path)
else:
result = run_git_command(
['git', 'apply', '-p1', str(patch_path)],
cwd=build_ctx.chromium_src
)
if result.returncode == 0:
log_success(f" ✓ Applied: {file_path}")
applied += 1
if commit_each:
run_git_command(['git', 'add', '-A'], cwd=build_ctx.chromium_src)
run_git_command(
['git', 'commit', '-m', f'Apply {feature_name}: {Path(file_path).name}'],
cwd=build_ctx.chromium_src
)
else:
log_error(f" ✗ Failed: {file_path}")
failed.append(file_path)
# Summary
log_info(f"\nSummary: {applied} applied, {len(failed)} failed")
if failed:
log_error("Failed patches:")
for p in failed:
log_error(f" - {p}")
ctx.exit(1)