refactor(browseros): universal build as planner runs#1501
Conversation
|
Greptile (@greptileai) review |
|
Claude (@claude) review |
|
Claude finished Nikhil (@shadowfax92)'s task in 4m 21s —— View job Code ReviewTasks
SummaryThis is a clean, well-motivated refactor. Replacing the 350-line No critical bugs found. A few observations below, mostly minor. planner.py
Double-resolution of Error precedence in
steps/compile/universal.py (
|
Greptile SummaryThis PR replaces the 350-line
Confidence Score: 4/5Safe to merge; the refactoring is logically sound and well-tested, with the only gap being the absence of a CI lane for end-to-end universal validation (acknowledged in the PR). The 3-run planner design is clean and the step contracts hold: merge_architectures creates the output directory, the arm64/x64 .app bundles survive sign_macos/package_macos for run 3's validate check, and all rejection conditions (debug, non-macOS, --no-sign, mixed arches) are caught at planning time. Dead code removal is thorough and verified. The main residual risk is the universal path being exercised only on the next official release — no CI lane exists to catch a regression before then. The _plan_universal_runs function in core/planner.py and MergeUniversalModule in steps/compile/universal.py are the new load-bearing code paths that can only be fully validated by an actual universal release build. Important Files Changed
Reviews (1): Last reviewed commit: "fix(browseros): address review findings ..." | Re-trigger Greptile |
Greptile SummaryThis PR replaces the
Confidence Score: 4/5The three-run universal flow is well-structured and the new planner API is cleanly tested; the main untested surface is the live universal build end-to-end, which has no CI lane by design. The refactor correctly moves upload failures from silently-swallowed exceptions to loud pipeline failures, removes No files have logic issues; Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
CLI["--preset release --arch universal"] --> PR["plan_runs(switches)"]
PR --> R1["Run 1 arm64\nclean, git_setup, sparkle_setup, download_resources\nbundled_extensions, chromium_replace, string_replaces\nseries_patches, patches, resources, configure\ncompile, sign_macos, package_macos, upload"]
PR --> R2["Run 2 x64\nresources, configure, compile\nsign_macos, package_macos, upload"]
PR --> R3["Run 3 universal\nmerge_universal, sign_macos, package_macos, upload"]
R1 -->|sequential| R2
R2 -->|sequential| R3
R1 -->|arm64 app at deterministic out_dir| MU
R2 -->|x64 app at deterministic out_dir| MU
MU["merge_universal\n_arch_app_path x2 via universalizer_patched.py"]
MU -->|built_app registered| SA["sign_macos, package_macos, upload"]
style R1 fill:#d4edda,stroke:#28a745
style R2 fill:#d4edda,stroke:#28a745
style R3 fill:#cce5ff,stroke:#0d6efd
style MU fill:#fff3cd,stroke:#ffc107
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
CLI["--preset release --arch universal"] --> PR["plan_runs(switches)"]
PR --> R1["Run 1 arm64\nclean, git_setup, sparkle_setup, download_resources\nbundled_extensions, chromium_replace, string_replaces\nseries_patches, patches, resources, configure\ncompile, sign_macos, package_macos, upload"]
PR --> R2["Run 2 x64\nresources, configure, compile\nsign_macos, package_macos, upload"]
PR --> R3["Run 3 universal\nmerge_universal, sign_macos, package_macos, upload"]
R1 -->|sequential| R2
R2 -->|sequential| R3
R1 -->|arm64 app at deterministic out_dir| MU
R2 -->|x64 app at deterministic out_dir| MU
MU["merge_universal\n_arch_app_path x2 via universalizer_patched.py"]
MU -->|built_app registered| SA["sign_macos, package_macos, upload"]
style R1 fill:#d4edda,stroke:#28a745
style R2 fill:#d4edda,stroke:#28a745
style R3 fill:#cce5ff,stroke:#0d6efd
style MU fill:#fff3cd,stroke:#ffc107
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/browseros/bos_build/cli/build.py:274-277
The "multi-arch loop" label is slightly misleading for a universal build, where the three runs are sequential pipeline phases (arm64 build → x64 build → merge), not independent builds of the same product. An operator reading logs could reasonably expect all three entries to be arch-specific compilation runs. Consider "sequential runs" to match the PR's own terminology.
```suggestion
if len(runs) > 1:
log_info(
f"📍 Architectures: {[c.architecture for c, _ in runs]} (sequential runs)"
)
```
### Issue 2 of 2
packages/browseros/bos_build/steps/compile/universal.py:25-33
**Instantiating a full `Context` just to derive a sibling path**
`Context.__post_init__` reads version files from `chromium_src` (semantic version, Chromium version, build offset) and initialises `ArtifactRegistry` and `EnvConfig`. `_arch_app_path` is called twice per `validate()` invocation and twice in `execute()`, so each `merge_universal` run triggers four full `Context` initialisations at job-execution time.
For a build that typically takes hours this is negligible, but it's worth knowing the helper is not just a path computation — if version-file reads fail on a partially initialised tree (e.g., after `clean` wiped them), the error surface at merge time would be a confusing `Context` init failure rather than a clear merge-step error. The existing contract ("deterministic out dirs") means a thin standalone helper would be equally correct and cheaper. No change required unless the `Context` init ever becomes fallible.
Reviews (2): Last reviewed commit: "fix(browseros): address review findings ..." | Re-trigger Greptile |
Re-applies skip/slice/Profile on top of the plan_runs universal refactor (#1501): skip subtraction now covers the universal runs, --from resumes the sequential run timeline (slice_runs_from) so a failed universal merge restarts at merge_universal without recompiling, and the README documents the timeline semantics.
Summary
universal_buildmega-step (a 350-line pipeline-within-a-step that re-entered six steps per arch and bypassed the runner) with three planner-emitted runs: arm64 build → x64 build →merge_universal+ sign/package/upload, executed by the existingcli/build.pyruns loop.plan_runs(switches) -> [(arch, steps)]consumed for all preset builds; non-universal plans stay byte-identical (review verified 576 switch combinations against the old planner).merge_universalstep (macos-only, optional): merges the two deterministic per-arch out dirs via the vendoreduniversalizer_patched.pyintoctx(universal).get_app_path(), registersbuilt_appforsign_macos.Context._fixed_app_path(redundant now thatget_app_pathresolves strictly from the ctx's own out dir), the planner universal tail special-case, and dead universal helpers:sign_universal(macos/linux/windows) and merge.py'screate_minimal_context/merge_sign_package/handle_merge_command— all verified caller-less. Net −350 lines.Behavior changes (intentional)
try/exceptswallow is gone.--no-sign(the old flow always signed), mixing universal with other arches, and debug/off-macOS as before.clean(run 1) resets the tree and removes arm64's out dir; x64 rebuilds incrementally against the freshly reset+repatched tree instead of being force-rmtree'd;merge_architecturesstill removes a stale universal output app.Design
Universal is sequential runs with deterministic paths — the CLI already had the runs loop (multi-arch Linux). Run 1 carries the prep prefix exactly as the old universal YAML did (prep once, no outer
resources), runs 1–2 share the per-arch tail withresourcesstaged per arch, run 3 merges. Rejections are raised at planning time, beforechromium_srcresolution, so bad invocations die instantly. Universal has no CI lane; the safety net is the runs-shape golden asserting the three step lists verbatim plus per-runrequired_envparity with the deleted release YAMLs.Note:
.llm/0701-build-refactor/design.md(gitignored design log) says universal is a mega-step — superseded by this PR.Test plan
uv run python -m unittest discover -s bos_build -t . -p "*_test.py"— 300 tests green (287 → 300)uv run ruff check bos_buildclean;git diff --checkcleanbrowseros build --list(showsmerge_universal, nouniversal_build),browseros product doctor--preset release --arch universal --no-signand--preset debug --arch universalfail fast with clear planning errors🤖 Generated with Claude Code