Files
BrowserOS/packages/browseros/build/modules/apply/apply_force.py
Nikhil 478a35e4ef feat: chromium 145 upgrade (#362)
* feat: new apply --force

* chore: update chromium version

* feat: chromium 145 updated patches

* fix: disable series patches for nwo

* chore: bump offset + version

---------

Co-authored-by: Nikhil <shadowfax@mac.local.meter>
2026-02-11 14:03:02 -08:00

197 lines
6.1 KiB
Python
Generated

"""
Apply Force - Non-interactive apply that uses --reject for conflicts.
When a patch fails to apply cleanly, falls back to `git apply --reject`
which applies what it can and writes .rej files for failed hunks.
Always continues to the next patch regardless of failures.
"""
from pathlib import Path
from typing import List, Tuple, Optional
from ...common.context import Context
from ...common.module import CommandModule, ValidationError
from ...common.utils import log_info, log_error, log_warning, log_success
from .common import find_patch_files
from .utils import run_git_command, file_exists_in_commit, reset_file_to_commit
def apply_patch_with_reject(
patch_path: Path,
chromium_src: Path,
relative_to: Optional[Path] = None,
reset_to: Optional[str] = None,
) -> Tuple[str, Optional[str]]:
"""Apply a single patch, falling back to --reject on conflict.
Args:
patch_path: Path to the patch file
chromium_src: Chromium source directory
relative_to: Base path for displaying relative paths
reset_to: Commit to reset file to before applying
Returns:
Tuple of (status, rej_files) where status is one of:
- "applied": patch applied cleanly (rej_files is [])
- "rejected": patch had conflicts, .rej files written (rej_files has full paths)
- "failed": patch could not be applied at all
"""
display_path = patch_path.relative_to(relative_to) if relative_to else patch_path
# Reset file to base commit if requested
if reset_to:
file_path = str(display_path)
if file_exists_in_commit(file_path, reset_to, chromium_src):
reset_file_to_commit(file_path, reset_to, chromium_src)
else:
target_file = chromium_src / file_path
if target_file.exists():
target_file.unlink()
# Try clean apply first (same strategy as common.apply_single_patch)
result = run_git_command(
[
"git", "apply",
"--ignore-whitespace", "--whitespace=nowarn",
"-p1", str(patch_path),
],
cwd=chromium_src,
)
if result.returncode == 0:
log_success(f" Applied: {display_path}")
return "applied", []
# Try 3-way merge
result = run_git_command(
[
"git", "apply",
"--ignore-whitespace", "--whitespace=nowarn",
"-p1", "--3way",
str(patch_path),
],
cwd=chromium_src,
)
if result.returncode == 0:
log_success(f" Applied (3way): {display_path}")
return "applied", []
# Fall back to --reject: applies what it can, writes .rej for the rest
result = run_git_command(
[
"git", "apply",
"--ignore-whitespace", "--whitespace=nowarn",
"-p1", "--reject",
str(patch_path),
],
cwd=chromium_src,
)
if result.returncode == 0:
# --reject returned 0 means it applied (possibly with warnings)
log_success(f" Applied (reject): {display_path}")
return "applied", []
else:
# Some hunks failed - .rej files were written
log_warning(f" Conflict: {display_path}")
# Find .rej files created for this patch
rej_path = chromium_src / f"{display_path}.rej"
rej_files = []
if rej_path.exists():
rej_files.append(str(rej_path))
log_warning(f" .rej: {rej_path}")
return "rejected", rej_files
def apply_all_force(
build_ctx: Context,
reset_to: Optional[str] = None,
) -> Tuple[int, int, List[str]]:
"""Apply all patches non-interactively, using --reject for conflicts.
Args:
build_ctx: Build context
reset_to: Commit to reset files to before applying
Returns:
Tuple of (applied_count, rejected_count, failed_list)
"""
patches_dir = build_ctx.get_patches_dir()
if not patches_dir.exists():
log_warning(f"Patches directory does not exist: {patches_dir}")
return 0, 0, []
patch_files = find_patch_files(patches_dir)
if not patch_files:
log_warning("No patch files found")
return 0, 0, []
log_info(f"Found {len(patch_files)} patches (non-interactive, --reject on conflict)")
applied = 0
rejected = 0
failed = []
all_rej_files: List[str] = []
for patch_path in patch_files:
display_name = str(patch_path.relative_to(patches_dir))
if not patch_path.exists():
log_warning(f" Patch not found: {display_name}")
failed.append(display_name)
continue
status, rej_files = apply_patch_with_reject(
patch_path, build_ctx.chromium_src, patches_dir, reset_to
)
if status == "applied":
applied += 1
elif status == "rejected":
rejected += 1
failed.append(display_name)
all_rej_files.extend(rej_files)
else:
failed.append(display_name)
# Summary
log_info(f"\nSummary: {applied} applied, {rejected} rejected (.rej), {len(failed)} total failed")
if all_rej_files:
log_warning("Reject files:")
for rej in all_rej_files:
log_warning(f" {rej}")
return applied, rejected, failed
class ApplyForceModule(CommandModule):
"""Non-interactive apply with --reject for conflicts"""
produces = []
requires = []
description = "Apply all patches non-interactively, writing .rej files for conflicts"
def validate(self, ctx: Context) -> None:
import shutil
if not shutil.which("git"):
raise ValidationError("Git is not available in PATH")
if not ctx.chromium_src.exists():
raise ValidationError(f"Chromium source not found: {ctx.chromium_src}")
def execute(
self,
ctx: Context,
reset_to: Optional[str] = None,
**kwargs,
) -> None:
applied, rejected, failed = apply_all_force(ctx, reset_to=reset_to)
if rejected > 0:
log_warning(
f"{rejected} patch(es) had conflicts. Review .rej files in chromium source."
)