Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions scripts/ubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1238,19 +1238,26 @@ def pattern_static_prefix(pattern: str) -> Optional[str]:
return "/".join(segments) if segments else None


def preferred_output_roots(project: Project) -> List[Path]:
def preferred_output_roots(project: Project, artifacts: Sequence[Path]) -> List[Path]:
"""Derive common output roots per project type straight from ARTIFACT_PATTERNS.

Prefixes sharing the same top-level path segment are collapsed to their
common ancestor (e.g. flutter's four patterns all live under "build" and
become one root), while prefixes under different top-level segments stay
separate (e.g. tauri's "src-tauri/..." and "signing/build").
Only prefixes that actually produced an artifact this build are considered,
so a single-output build (e.g. just an .aab) opens its own specific folder
instead of the noisy shared "build" root. Prefixes sharing the same
top-level path segment are collapsed to their common ancestor only when
more than one of them has real output (e.g. flutter producing both an
.aab and build/web), while prefixes under different top-level segments
stay separate (e.g. tauri's "src-tauri/..." and "signing/build").
"""
groups: Dict[str, List[str]] = {}
for pattern in ARTIFACT_PATTERNS.get(project.type, []):
prefix = pattern_static_prefix(pattern)
if prefix:
groups.setdefault(prefix.split("/", 1)[0], []).append(prefix)
if not prefix:
continue
resolved = (project.path / prefix).resolve()
if not any(artifact == resolved or resolved in artifact.parents for artifact in artifacts):
continue
groups.setdefault(prefix.split("/", 1)[0], []).append(prefix)
return [project.path / os.path.commonpath(prefixes) for prefixes in groups.values()]


Expand All @@ -1263,7 +1270,7 @@ def artifact_output_directories(project: Project) -> List[Path]:

selected: Set[Path] = set()
covered: Set[Path] = set()
for root in preferred_output_roots(project):
for root in preferred_output_roots(project, artifacts):
resolved_root = root.resolve()
matches = {
artifact for artifact in artifacts
Expand Down
2 changes: 1 addition & 1 deletion scripts/update-manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version 3.7.0
file 84746b20a3a72f6ff85a629706e50507c1bd200907456b76dd808a2a5ca0efba VERSION
file 62d7189d00ccadbdeae9647a37b80db6ed9e179bfcf5b2f1d4d1c487dbc815bf build.sh
file d9672f7bb74551de59fb3d27e1cdf2a5c7c96d41f40135372552c2798a04d7c5 install.sh
file 76e4cfe7cd7301018a5cae450a6b6dcfbf18bc029759b1bc5da2eea046603b00 scripts/ubs.py
file 1c4d89ae31cf4ec50c4a908d8ef913525a3410a1017644ff91949d43713e50d7 scripts/ubs.py
file df0231e3cc0e584be7ae3ade3f1c0a7f92afb2124ec8a15bdb51c76737bbe1e0 scripts/ubs_mcp.py
file 0a3b9d28fbe62bc55c073f5337954ae92ffca7482fca5d1f681dc99333363416 scripts/bootstrap-update.sh
file 7b9a21a0854e0ca0476ae6ee38f2b7fc552446c97448ee655995f91e5c6f23d5 scripts/build-rust-helper.sh
Expand Down
15 changes: 15 additions & 0 deletions tests/test_python_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ def test_flutter_artifacts_open_the_common_build_root(self) -> None:
[root / "build"],
)

def test_flutter_single_output_opens_its_own_folder_not_whole_build(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
output = root / "build/app/outputs/bundle/release"
output.mkdir(parents=True)
(output / "app-release.aab").write_bytes(b"bundle")
# Unrelated plugin build caches that Flutter always leaves under
# build/ regardless of which output was actually produced — these
# must not cause the whole build/ root to be opened.
(root / "build/mobile_scanner").mkdir(parents=True)
self.assertEqual(
ubs.artifact_output_directories(ubs.Project("flutter", root)),
[output],
)

def test_output_directories_cover_tauri_gradle_and_xcode(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
Expand Down