fluidattacks_core_filesystem 12.0.0__tar.gz → 13.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/.gitignore +4 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/PKG-INFO +1 -1
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/fluidattacks_core/filesystem/defaults.py +13 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/fluidattacks_core/filesystem/detection.py +22 -2
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/fluidattacks_core/filesystem/partitioning.py +38 -2
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/import-linter.cfg +1 -10
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/pyproject.toml +1 -1
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_partitioning.py +99 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_projects.py +10 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/uv.lock +1 -1
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/.envrc +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/build/default.nix +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/build/filter.nix +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/flake.lock +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/flake.nix +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/fluidattacks_core/filesystem/__init__.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/fluidattacks_core/filesystem/language.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/fluidattacks_core/filesystem/parts.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/fluidattacks_core/filesystem/projects.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/fluidattacks_core/filesystem/py.typed +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/mypy.ini +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/ruff.toml +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/__init__.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/data/partitioning_snapshot.golden.json +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_defaults.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_extract_db.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_language_detection.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_partitioning_snapshot.py +0 -0
- {fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_parts.py +0 -0
|
@@ -23,6 +23,19 @@ class MarkerSet(TypedDict):
|
|
|
23
23
|
globs: list[str]
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
class WorkspaceSpec(TypedDict):
|
|
27
|
+
declares: frozenset[str]
|
|
28
|
+
claims: frozenset[str]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
WORKSPACE_MARKERS: dict[str, WorkspaceSpec] = {
|
|
32
|
+
"gradle": {
|
|
33
|
+
"declares": frozenset({"settings.gradle", "settings.gradle.kts"}),
|
|
34
|
+
"claims": frozenset({"build.gradle", "build.gradle.kts"}),
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
26
39
|
CONFIG_MARKERS: dict[Language, MarkerSet] = {
|
|
27
40
|
Language.Java: {
|
|
28
41
|
"names": {
|
|
@@ -3,11 +3,11 @@ from __future__ import annotations
|
|
|
3
3
|
from fnmatch import fnmatch
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
|
-
from .defaults import CONFIG_MARKERS, MANIFEST_MARKERS, MarkerSet
|
|
6
|
+
from .defaults import CONFIG_MARKERS, MANIFEST_MARKERS, WORKSPACE_MARKERS, MarkerSet
|
|
7
7
|
from .language import Language
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
|
-
from collections.abc import Iterable
|
|
10
|
+
from collections.abc import Collection, Iterable
|
|
11
11
|
|
|
12
12
|
__all__ = ["detect_anchor_language", "detect_languages_in_dir", "is_config_marker"]
|
|
13
13
|
|
|
@@ -17,6 +17,14 @@ _FOLDED_MARKER_NAMES = frozenset(
|
|
|
17
17
|
|
|
18
18
|
_MARKER_GLOBS = tuple(pat for markers in CONFIG_MARKERS.values() for pat in markers["globs"])
|
|
19
19
|
|
|
20
|
+
_FOLDED_WORKSPACES = {
|
|
21
|
+
kind: (
|
|
22
|
+
frozenset(name.casefold() for name in spec["declares"]),
|
|
23
|
+
frozenset(name.casefold() for name in spec["claims"]),
|
|
24
|
+
)
|
|
25
|
+
for kind, spec in WORKSPACE_MARKERS.items()
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
_FOLDED_MANIFESTS = {
|
|
21
29
|
language: frozenset(name.casefold() for name in names)
|
|
22
30
|
for language, names in MANIFEST_MARKERS.items()
|
|
@@ -112,3 +120,15 @@ def detect_anchor_language(files: Iterable[str]) -> Language | None:
|
|
|
112
120
|
sorted(anchors, key=lambda language: language.value)
|
|
113
121
|
)
|
|
114
122
|
return ranked[0] if len(ranked) == 1 else Language.Unknown
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def declared_workspaces(files: Iterable[str]) -> frozenset[str]:
|
|
126
|
+
folded = {name.casefold() for name in files}
|
|
127
|
+
return frozenset(
|
|
128
|
+
kind for kind, (declares, _) in _FOLDED_WORKSPACES.items() if declares & folded
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def is_claimed_manifest(name: str, kinds: Collection[str]) -> bool:
|
|
133
|
+
folded = name.casefold()
|
|
134
|
+
return any(folded in _FOLDED_WORKSPACES[kind][1] for kind in kinds)
|
|
@@ -10,7 +10,13 @@ from pathlib import Path
|
|
|
10
10
|
from typing import TYPE_CHECKING
|
|
11
11
|
|
|
12
12
|
from .defaults import EXTENDED_EXCLUDE_DIRS
|
|
13
|
-
from .detection import
|
|
13
|
+
from .detection import (
|
|
14
|
+
declared_workspaces,
|
|
15
|
+
detect_anchor_language,
|
|
16
|
+
detect_languages_in_dir,
|
|
17
|
+
is_claimed_manifest,
|
|
18
|
+
is_config_marker,
|
|
19
|
+
)
|
|
14
20
|
from .parts import Part, PartIdentity, PartTree
|
|
15
21
|
|
|
16
22
|
if TYPE_CHECKING:
|
|
@@ -63,11 +69,41 @@ def _root_filenames(listing: DirListing, root_path: Path) -> tuple[str, ...]:
|
|
|
63
69
|
return ()
|
|
64
70
|
|
|
65
71
|
|
|
72
|
+
def _workspace_kinds_by_dir(listing: DirListing) -> Mapping[Path, frozenset[str]]:
|
|
73
|
+
return {
|
|
74
|
+
dir_path: kinds
|
|
75
|
+
for dir_path, filenames in listing
|
|
76
|
+
if (kinds := declared_workspaces(filenames))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _claiming_kinds(dir_path: Path, workspaces: Mapping[Path, frozenset[str]]) -> frozenset[str]:
|
|
81
|
+
kinds: set[str] = set()
|
|
82
|
+
current = dir_path
|
|
83
|
+
while current != current.parent:
|
|
84
|
+
current = current.parent
|
|
85
|
+
kinds.update(workspaces.get(current, frozenset()))
|
|
86
|
+
return frozenset(kinds)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _anchor_names(
|
|
90
|
+
dir_path: Path,
|
|
91
|
+
filenames: Sequence[str],
|
|
92
|
+
workspaces: Mapping[Path, frozenset[str]],
|
|
93
|
+
) -> tuple[str, ...]:
|
|
94
|
+
kinds = _claiming_kinds(dir_path, workspaces)
|
|
95
|
+
if not kinds:
|
|
96
|
+
return tuple(filenames)
|
|
97
|
+
return tuple(name for name in filenames if not is_claimed_manifest(name, kinds))
|
|
98
|
+
|
|
99
|
+
|
|
66
100
|
def _discover_potential_parts(listing: DirListing) -> tuple[PotentialPart, ...]:
|
|
101
|
+
workspaces = _workspace_kinds_by_dir(listing)
|
|
67
102
|
return tuple(
|
|
68
103
|
(dir_path, _find_config_files(dir_path, filenames), language.value)
|
|
69
104
|
for dir_path, filenames in listing
|
|
70
|
-
if (language := detect_anchor_language(filenames))
|
|
105
|
+
if (language := detect_anchor_language(_anchor_names(dir_path, filenames, workspaces)))
|
|
106
|
+
is not None
|
|
71
107
|
)
|
|
72
108
|
|
|
73
109
|
|
{fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/import-linter.cfg
RENAMED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
[importlinter]
|
|
2
|
-
root_package = fluidattacks_core
|
|
2
|
+
root_package = fluidattacks_core.filesystem
|
|
3
3
|
include_external_packages = True
|
|
4
4
|
|
|
5
|
-
[importlinter:contract:portion]
|
|
6
|
-
name = Single Portion
|
|
7
|
-
type = layers
|
|
8
|
-
containers =
|
|
9
|
-
fluidattacks_core
|
|
10
|
-
exhaustive = true
|
|
11
|
-
layers =
|
|
12
|
-
filesystem
|
|
13
|
-
|
|
14
5
|
[importlinter:contract:dag]
|
|
15
6
|
name = Direct Acyclic Graph
|
|
16
7
|
type = layers
|
|
@@ -5,6 +5,7 @@ import pytest
|
|
|
5
5
|
|
|
6
6
|
from fluidattacks_core.filesystem import build_part_tree
|
|
7
7
|
from fluidattacks_core.filesystem.partitioning import (
|
|
8
|
+
DirListing,
|
|
8
9
|
PotentialPart,
|
|
9
10
|
_assign_files_to_parts,
|
|
10
11
|
_build_hierarchy,
|
|
@@ -28,6 +29,17 @@ _LISTING = (
|
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
_GRADLE_ROOT = Path("/service")
|
|
33
|
+
|
|
34
|
+
_GRADLE_MODULES = (
|
|
35
|
+
"applications/app-service",
|
|
36
|
+
"domain/model",
|
|
37
|
+
"domain/usecase",
|
|
38
|
+
"infrastructure/driven-adapters/rest-consumer",
|
|
39
|
+
"infrastructure/entry-points/reactive-web",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
31
43
|
def test_part_names_are_posix_relative_paths() -> None:
|
|
32
44
|
assert _generate_part_name(_ROOT, _ROOT) == "."
|
|
33
45
|
assert _generate_part_name(_ROOT / "web", _ROOT) == "web"
|
|
@@ -189,6 +201,93 @@ def test_part_names_stay_unique_for_nested_directories() -> None:
|
|
|
189
201
|
assert sorted(names) == [".", "a/svc", "b/svc"]
|
|
190
202
|
|
|
191
203
|
|
|
204
|
+
def _gradle_listing(suffix: str) -> DirListing:
|
|
205
|
+
return (
|
|
206
|
+
(_GRADLE_ROOT, (f"settings{suffix}", f"build{suffix}", "gradlew")),
|
|
207
|
+
*(
|
|
208
|
+
(_GRADLE_ROOT.joinpath(*module.split("/")), (f"build{suffix}",))
|
|
209
|
+
for module in _GRADLE_MODULES
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def test_a_settings_file_claims_every_subproject_manifest() -> None:
|
|
215
|
+
tree = decide_part_tree(_GRADLE_ROOT, _gradle_listing(".gradle"))
|
|
216
|
+
|
|
217
|
+
assert sorted(tree.parts) == ["."]
|
|
218
|
+
assert tree.children_map["."] == ()
|
|
219
|
+
root = tree.root
|
|
220
|
+
assert root is not None
|
|
221
|
+
assert root.detected_language == "Java"
|
|
222
|
+
assert Path("domain/model/build.gradle") in root.files
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_the_kotlin_gradle_dsl_collapses_the_same_way() -> None:
|
|
226
|
+
tree = decide_part_tree(_GRADLE_ROOT, _gradle_listing(".gradle.kts"))
|
|
227
|
+
|
|
228
|
+
assert sorted(tree.parts) == ["."]
|
|
229
|
+
root = tree.root
|
|
230
|
+
assert root is not None
|
|
231
|
+
assert root.detected_language == "Kotlin"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def test_a_settings_file_claims_the_other_gradle_dsl() -> None:
|
|
235
|
+
listing = (
|
|
236
|
+
(_GRADLE_ROOT, ("settings.gradle",)),
|
|
237
|
+
(_GRADLE_ROOT / "mod", ("build.gradle.kts",)),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
assert sorted(decide_part_tree(_GRADLE_ROOT, listing).parts) == ["."]
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def test_a_nested_workspace_anchors_but_its_modules_do_not() -> None:
|
|
244
|
+
listing = (
|
|
245
|
+
(_GRADLE_ROOT, ("settings.gradle",)),
|
|
246
|
+
(_GRADLE_ROOT / "sub", ("settings.gradle", "build.gradle")),
|
|
247
|
+
(_GRADLE_ROOT / "sub" / "mod", ("build.gradle",)),
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
assert sorted(decide_part_tree(_GRADLE_ROOT, listing).parts) == [".", "sub"]
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def test_a_claimed_directory_with_its_own_settings_still_anchors() -> None:
|
|
254
|
+
listing = (
|
|
255
|
+
(_GRADLE_ROOT, ("settings.gradle", "build.gradle")),
|
|
256
|
+
(_GRADLE_ROOT / "mod", ("build.gradle",)),
|
|
257
|
+
(_GRADLE_ROOT / "deployment" / "acceptance-test", ("settings.gradle", "build.gradle")),
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
assert sorted(decide_part_tree(_GRADLE_ROOT, listing).parts) == [
|
|
261
|
+
".",
|
|
262
|
+
"deployment/acceptance-test",
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def test_a_workspace_does_not_claim_a_foreign_manifest() -> None:
|
|
267
|
+
listing = (
|
|
268
|
+
(_GRADLE_ROOT, ("settings.gradle", "build.gradle")),
|
|
269
|
+
(_GRADLE_ROOT / "frontend", ("build.gradle", "package.json")),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
tree = decide_part_tree(_GRADLE_ROOT, listing)
|
|
273
|
+
|
|
274
|
+
assert sorted(tree.parts) == [".", "frontend"]
|
|
275
|
+
frontend = tree.get_part("frontend")
|
|
276
|
+
assert frontend is not None
|
|
277
|
+
assert frontend.detected_language == "JavaScript"
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def test_a_gradle_module_without_a_settings_file_anchors_alone() -> None:
|
|
281
|
+
listing = (
|
|
282
|
+
(_GRADLE_ROOT, ("README",)),
|
|
283
|
+
(_GRADLE_ROOT / "deployment" / "acceptance-test", ("build.gradle",)),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
tree = decide_part_tree(_GRADLE_ROOT, listing)
|
|
287
|
+
|
|
288
|
+
assert sorted(tree.parts) == [".", "deployment/acceptance-test"]
|
|
289
|
+
|
|
290
|
+
|
|
192
291
|
def _write(root: Path, files: list[str]) -> None:
|
|
193
292
|
for name in files:
|
|
194
293
|
path = root / name
|
{fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_projects.py
RENAMED
|
@@ -6,6 +6,7 @@ import pytest
|
|
|
6
6
|
|
|
7
7
|
from fluidattacks_core.filesystem import (
|
|
8
8
|
Language,
|
|
9
|
+
build_part_tree,
|
|
9
10
|
detect_root_language,
|
|
10
11
|
find_parent_project,
|
|
11
12
|
find_projects,
|
|
@@ -270,3 +271,12 @@ def test_no_marker_name_is_excluded_as_an_artifact() -> None:
|
|
|
270
271
|
if any(fnmatch(name, pat) for pat in EXCLUDE_FILES)
|
|
271
272
|
]
|
|
272
273
|
assert excluded == []
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def test_find_projects_still_splits_a_gradle_workspace(tmp_path: Path) -> None:
|
|
277
|
+
_make(tmp_path, ["settings.gradle", "build.gradle", "mod/build.gradle"])
|
|
278
|
+
root = tmp_path.resolve()
|
|
279
|
+
|
|
280
|
+
assert _by_path(find_projects(tmp_path), root) == {".": "Java", "mod": "Java"}
|
|
281
|
+
assert find_parent_project(root / "mod" / "build.gradle", root) == (root / "mod", "Java")
|
|
282
|
+
assert sorted(build_part_tree(tmp_path).parts) == ["."]
|
|
File without changes
|
{fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/build/default.nix
RENAMED
|
File without changes
|
{fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/build/filter.nix
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_defaults.py
RENAMED
|
File without changes
|
{fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_extract_db.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_core_filesystem-12.0.0 → fluidattacks_core_filesystem-13.0.0}/tests/test_parts.py
RENAMED
|
File without changes
|