wuf 0.1.0a7__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.
- wuf-0.1.0a7/.github/scripts/sync_lock_version.py +123 -0
- wuf-0.1.0a7/LICENSE +21 -0
- wuf-0.1.0a7/MANIFEST.in +4 -0
- wuf-0.1.0a7/PKG-INFO +214 -0
- wuf-0.1.0a7/README.md +174 -0
- wuf-0.1.0a7/THIRD_PARTY_NOTICES.md +19 -0
- wuf-0.1.0a7/benchmarks/README.md +87 -0
- wuf-0.1.0a7/benchmarks/__init__.py +1 -0
- wuf-0.1.0a7/benchmarks/cases.py +96 -0
- wuf-0.1.0a7/benchmarks/fixture.py +389 -0
- wuf-0.1.0a7/benchmarks/metrics.py +243 -0
- wuf-0.1.0a7/benchmarks/run.py +517 -0
- wuf-0.1.0a7/pyproject.toml +115 -0
- wuf-0.1.0a7/setup.cfg +4 -0
- wuf-0.1.0a7/tests/architecture/test_dependencies.py +214 -0
- wuf-0.1.0a7/tests/end_to_end/test_wuf.py +691 -0
- wuf-0.1.0a7/tests/integration/application/test_derived_scope.py +429 -0
- wuf-0.1.0a7/tests/integration/application/test_execution.py +676 -0
- wuf-0.1.0a7/tests/integration/application/test_git_selection.py +263 -0
- wuf-0.1.0a7/tests/integration/benchmarks/test_fixture.py +132 -0
- wuf-0.1.0a7/tests/integration/filesystem/test_collector.py +1878 -0
- wuf-0.1.0a7/tests/integration/filesystem/test_ignore.py +692 -0
- wuf-0.1.0a7/tests/integration/git/test_changes.py +693 -0
- wuf-0.1.0a7/tests/integration/git/test_commits.py +1183 -0
- wuf-0.1.0a7/tests/integration/git/test_reflogs.py +692 -0
- wuf-0.1.0a7/tests/integration/git/test_tags.py +524 -0
- wuf-0.1.0a7/tests/integration/package/test_distribution.py +39 -0
- wuf-0.1.0a7/tests/integration/package/test_release_scripts.py +77 -0
- wuf-0.1.0a7/tests/support/__init__.py +1 -0
- wuf-0.1.0a7/tests/support/git_repo.py +130 -0
- wuf-0.1.0a7/tests/support/reports.py +154 -0
- wuf-0.1.0a7/tests/unit/application/test_report.py +9 -0
- wuf-0.1.0a7/tests/unit/benchmarks/test_benchmarks.py +70 -0
- wuf-0.1.0a7/tests/unit/cli/test_cli.py +354 -0
- wuf-0.1.0a7/tests/unit/cli/test_progress.py +32 -0
- wuf-0.1.0a7/tests/unit/collection/filesystem/test_inventory_metadata.py +167 -0
- wuf-0.1.0a7/tests/unit/collection/filesystem/test_linux.py +322 -0
- wuf-0.1.0a7/tests/unit/collection/filesystem/test_metadata.py +275 -0
- wuf-0.1.0a7/tests/unit/collection/filesystem/test_publication.py +67 -0
- wuf-0.1.0a7/tests/unit/collection/filesystem/test_timestamps.py +76 -0
- wuf-0.1.0a7/tests/unit/collection/git/test_change_spool.py +95 -0
- wuf-0.1.0a7/tests/unit/collection/git/test_objects.py +318 -0
- wuf-0.1.0a7/tests/unit/collection/test_diagnostics.py +152 -0
- wuf-0.1.0a7/tests/unit/collection/test_process.py +37 -0
- wuf-0.1.0a7/tests/unit/configuration/test_local_timezone.py +94 -0
- wuf-0.1.0a7/tests/unit/configuration/test_options.py +655 -0
- wuf-0.1.0a7/tests/unit/configuration/test_settings.py +970 -0
- wuf-0.1.0a7/tests/unit/configuration/test_styles.py +94 -0
- wuf-0.1.0a7/tests/unit/domain/test_coverage.py +330 -0
- wuf-0.1.0a7/tests/unit/domain/test_evidence.py +118 -0
- wuf-0.1.0a7/tests/unit/domain/test_observations.py +319 -0
- wuf-0.1.0a7/tests/unit/domain/test_schedule.py +202 -0
- wuf-0.1.0a7/tests/unit/domain/test_scope.py +130 -0
- wuf-0.1.0a7/tests/unit/domain/test_time.py +184 -0
- wuf-0.1.0a7/tests/unit/folding/test_aggregation.py +1092 -0
- wuf-0.1.0a7/tests/unit/folding/test_pipeline.py +31 -0
- wuf-0.1.0a7/tests/unit/folding/test_spill_failures.py +124 -0
- wuf-0.1.0a7/tests/unit/reporting/test_coverage_rendering.py +78 -0
- wuf-0.1.0a7/tests/unit/reporting/test_identity.py +70 -0
- wuf-0.1.0a7/tests/unit/reporting/test_summary.py +240 -0
- wuf-0.1.0a7/tests/unit/reporting/test_terminal.py +1071 -0
- wuf-0.1.0a7/wuf/__init__.py +10 -0
- wuf-0.1.0a7/wuf/__main__.py +5 -0
- wuf-0.1.0a7/wuf/application/__init__.py +1 -0
- wuf-0.1.0a7/wuf/application/collection.py +132 -0
- wuf-0.1.0a7/wuf/application/collection_plan.py +56 -0
- wuf-0.1.0a7/wuf/application/coverage/__init__.py +5 -0
- wuf-0.1.0a7/wuf/application/coverage/reconciliation.py +23 -0
- wuf-0.1.0a7/wuf/application/errors.py +10 -0
- wuf-0.1.0a7/wuf/application/execution.py +128 -0
- wuf-0.1.0a7/wuf/application/report.py +381 -0
- wuf-0.1.0a7/wuf/application/report_context.py +156 -0
- wuf-0.1.0a7/wuf/application/resolution.py +93 -0
- wuf-0.1.0a7/wuf/cli/__init__.py +21 -0
- wuf-0.1.0a7/wuf/cli/config_display.py +208 -0
- wuf-0.1.0a7/wuf/cli/main.py +131 -0
- wuf-0.1.0a7/wuf/cli/parser.py +333 -0
- wuf-0.1.0a7/wuf/cli/runner.py +154 -0
- wuf-0.1.0a7/wuf/collection/__init__.py +1 -0
- wuf-0.1.0a7/wuf/collection/diagnostics.py +259 -0
- wuf-0.1.0a7/wuf/collection/filesystem/__init__.py +24 -0
- wuf-0.1.0a7/wuf/collection/filesystem/accounting.py +170 -0
- wuf-0.1.0a7/wuf/collection/filesystem/collector.py +276 -0
- wuf-0.1.0a7/wuf/collection/filesystem/entries.py +216 -0
- wuf-0.1.0a7/wuf/collection/filesystem/git_inventory.py +277 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/__init__.py +50 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/exclusions.py +79 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/inventory.py +149 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/inventory_format.py +190 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/inventory_spool.py +280 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/models.py +146 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/paths.py +192 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/runner.py +256 -0
- wuf-0.1.0a7/wuf/collection/filesystem/ignore/service.py +381 -0
- wuf-0.1.0a7/wuf/collection/filesystem/inventory_metadata.py +262 -0
- wuf-0.1.0a7/wuf/collection/filesystem/linux.py +257 -0
- wuf-0.1.0a7/wuf/collection/filesystem/metadata.py +472 -0
- wuf-0.1.0a7/wuf/collection/filesystem/models.py +254 -0
- wuf-0.1.0a7/wuf/collection/filesystem/root.py +358 -0
- wuf-0.1.0a7/wuf/collection/filesystem/root_schedule.py +116 -0
- wuf-0.1.0a7/wuf/collection/filesystem/scan.py +192 -0
- wuf-0.1.0a7/wuf/collection/filesystem/timestamps.py +81 -0
- wuf-0.1.0a7/wuf/collection/filesystem/traversal.py +345 -0
- wuf-0.1.0a7/wuf/collection/git/__init__.py +46 -0
- wuf-0.1.0a7/wuf/collection/git/batches.py +22 -0
- wuf-0.1.0a7/wuf/collection/git/changes/__init__.py +23 -0
- wuf-0.1.0a7/wuf/collection/git/changes/collector.py +365 -0
- wuf-0.1.0a7/wuf/collection/git/changes/diff.py +244 -0
- wuf-0.1.0a7/wuf/collection/git/changes/models.py +184 -0
- wuf-0.1.0a7/wuf/collection/git/changes/spool.py +166 -0
- wuf-0.1.0a7/wuf/collection/git/command_error.py +44 -0
- wuf-0.1.0a7/wuf/collection/git/commits/__init__.py +21 -0
- wuf-0.1.0a7/wuf/collection/git/commits/collector.py +105 -0
- wuf-0.1.0a7/wuf/collection/git/commits/models.py +240 -0
- wuf-0.1.0a7/wuf/collection/git/commits/repository.py +386 -0
- wuf-0.1.0a7/wuf/collection/git/coverage.py +246 -0
- wuf-0.1.0a7/wuf/collection/git/evidence/__init__.py +30 -0
- wuf-0.1.0a7/wuf/collection/git/evidence/collector.py +249 -0
- wuf-0.1.0a7/wuf/collection/git/evidence/models.py +148 -0
- wuf-0.1.0a7/wuf/collection/git/evidence/summary.py +127 -0
- wuf-0.1.0a7/wuf/collection/git/objects/__init__.py +56 -0
- wuf-0.1.0a7/wuf/collection/git/objects/cat_file.py +241 -0
- wuf-0.1.0a7/wuf/collection/git/objects/commit_parser.py +110 -0
- wuf-0.1.0a7/wuf/collection/git/objects/compact.py +234 -0
- wuf-0.1.0a7/wuf/collection/git/objects/models.py +193 -0
- wuf-0.1.0a7/wuf/collection/git/objects/reader.py +219 -0
- wuf-0.1.0a7/wuf/collection/git/objects/revision_scan.py +57 -0
- wuf-0.1.0a7/wuf/collection/git/objects/signatures.py +131 -0
- wuf-0.1.0a7/wuf/collection/git/process.py +242 -0
- wuf-0.1.0a7/wuf/collection/git/reflogs/__init__.py +41 -0
- wuf-0.1.0a7/wuf/collection/git/reflogs/collector.py +326 -0
- wuf-0.1.0a7/wuf/collection/git/reflogs/models.py +174 -0
- wuf-0.1.0a7/wuf/collection/git/reflogs/parser.py +248 -0
- wuf-0.1.0a7/wuf/collection/git/reflogs/reader.py +106 -0
- wuf-0.1.0a7/wuf/collection/git/reflogs/spill.py +188 -0
- wuf-0.1.0a7/wuf/collection/git/repository.py +198 -0
- wuf-0.1.0a7/wuf/collection/git/revisions.py +181 -0
- wuf-0.1.0a7/wuf/collection/git/runner.py +277 -0
- wuf-0.1.0a7/wuf/collection/git/selection.py +78 -0
- wuf-0.1.0a7/wuf/collection/git/tags/__init__.py +23 -0
- wuf-0.1.0a7/wuf/collection/git/tags/collector.py +242 -0
- wuf-0.1.0a7/wuf/collection/git/tags/models.py +188 -0
- wuf-0.1.0a7/wuf/collection/git/tags/parser.py +129 -0
- wuf-0.1.0a7/wuf/collection/git/tags/reader.py +271 -0
- wuf-0.1.0a7/wuf/collection/process.py +49 -0
- wuf-0.1.0a7/wuf/configuration/__init__.py +100 -0
- wuf-0.1.0a7/wuf/configuration/effective.py +330 -0
- wuf-0.1.0a7/wuf/configuration/files.py +362 -0
- wuf-0.1.0a7/wuf/configuration/layers.py +78 -0
- wuf-0.1.0a7/wuf/configuration/local_timezone.py +74 -0
- wuf-0.1.0a7/wuf/configuration/options.py +236 -0
- wuf-0.1.0a7/wuf/configuration/parsing.py +304 -0
- wuf-0.1.0a7/wuf/configuration/profiles.py +50 -0
- wuf-0.1.0a7/wuf/configuration/resolution.py +286 -0
- wuf-0.1.0a7/wuf/configuration/schema.py +106 -0
- wuf-0.1.0a7/wuf/configuration/styles.py +243 -0
- wuf-0.1.0a7/wuf/domain/__init__.py +5 -0
- wuf-0.1.0a7/wuf/domain/coverage/__init__.py +41 -0
- wuf-0.1.0a7/wuf/domain/coverage/builder.py +229 -0
- wuf-0.1.0a7/wuf/domain/coverage/models.py +468 -0
- wuf-0.1.0a7/wuf/domain/evidence.py +318 -0
- wuf-0.1.0a7/wuf/domain/identity.py +65 -0
- wuf-0.1.0a7/wuf/domain/observations.py +388 -0
- wuf-0.1.0a7/wuf/domain/provenance.py +259 -0
- wuf-0.1.0a7/wuf/domain/schedule.py +270 -0
- wuf-0.1.0a7/wuf/domain/scope.py +103 -0
- wuf-0.1.0a7/wuf/domain/time.py +268 -0
- wuf-0.1.0a7/wuf/folding/__init__.py +30 -0
- wuf-0.1.0a7/wuf/folding/bands.py +42 -0
- wuf-0.1.0a7/wuf/folding/builder.py +488 -0
- wuf-0.1.0a7/wuf/folding/layout.py +239 -0
- wuf-0.1.0a7/wuf/folding/markers.py +177 -0
- wuf-0.1.0a7/wuf/folding/models.py +284 -0
- wuf-0.1.0a7/wuf/folding/pipeline.py +140 -0
- wuf-0.1.0a7/wuf/folding/spill.py +340 -0
- wuf-0.1.0a7/wuf/py.typed +1 -0
- wuf-0.1.0a7/wuf/reporting/__init__.py +1 -0
- wuf-0.1.0a7/wuf/reporting/sanitization.py +131 -0
- wuf-0.1.0a7/wuf/reporting/terminal/__init__.py +6 -0
- wuf-0.1.0a7/wuf/reporting/terminal/chart.py +396 -0
- wuf-0.1.0a7/wuf/reporting/terminal/chart_time.py +138 -0
- wuf-0.1.0a7/wuf/reporting/terminal/coverage_details.py +206 -0
- wuf-0.1.0a7/wuf/reporting/terminal/coverage_scope.py +45 -0
- wuf-0.1.0a7/wuf/reporting/terminal/coverage_status.py +82 -0
- wuf-0.1.0a7/wuf/reporting/terminal/event_list.py +150 -0
- wuf-0.1.0a7/wuf/reporting/terminal/identity.py +92 -0
- wuf-0.1.0a7/wuf/reporting/terminal/layout.py +144 -0
- wuf-0.1.0a7/wuf/reporting/terminal/legend.py +283 -0
- wuf-0.1.0a7/wuf/reporting/terminal/markers.py +74 -0
- wuf-0.1.0a7/wuf/reporting/terminal/options.py +56 -0
- wuf-0.1.0a7/wuf/reporting/terminal/renderer.py +78 -0
- wuf-0.1.0a7/wuf/reporting/terminal/scope_details.py +74 -0
- wuf-0.1.0a7/wuf/reporting/terminal/summary.py +150 -0
- wuf-0.1.0a7/wuf.egg-info/PKG-INFO +214 -0
- wuf-0.1.0a7/wuf.egg-info/SOURCES.txt +197 -0
- wuf-0.1.0a7/wuf.egg-info/dependency_links.txt +1 -0
- wuf-0.1.0a7/wuf.egg-info/entry_points.txt +3 -0
- wuf-0.1.0a7/wuf.egg-info/requires.txt +21 -0
- wuf-0.1.0a7/wuf.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Synchronize Wuf's release version into committed release metadata."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
import tempfile
|
|
9
|
+
import tomllib
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from packaging.version import InvalidVersion, Version
|
|
13
|
+
|
|
14
|
+
_VERSION_LINE = re.compile(r'version = "[^"]+"(?P<newline>\r?\n)?\Z')
|
|
15
|
+
_COMPATIBILITY_DEPENDENCIES_LINE = re.compile(
|
|
16
|
+
r'^dependencies = \["wuf[^"]*"\](?P<newline>\r?\n)?$',
|
|
17
|
+
re.MULTILINE,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def synchronize_version(repository: Path, requested_version: str) -> None:
|
|
22
|
+
"""Update the editable lock entry and Workfold compatibility requirement."""
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
normalized = str(Version(requested_version))
|
|
26
|
+
except InvalidVersion as error:
|
|
27
|
+
raise ValueError(f"invalid release version: {requested_version!r}") from error
|
|
28
|
+
|
|
29
|
+
pyproject_path = repository / "pyproject.toml"
|
|
30
|
+
lock_path = repository / "uv.lock"
|
|
31
|
+
project = tomllib.loads(pyproject_path.read_text(encoding="utf-8"))
|
|
32
|
+
configured = str(project["project"]["version"])
|
|
33
|
+
if Version(configured) != Version(normalized):
|
|
34
|
+
raise ValueError(f"release version {normalized!r} does not match pyproject.toml version {configured!r}")
|
|
35
|
+
|
|
36
|
+
_synchronize_compatibility_requirement(repository, normalized)
|
|
37
|
+
|
|
38
|
+
lines = lock_path.read_text(encoding="utf-8").splitlines(keepends=True)
|
|
39
|
+
matching_blocks = _editable_project_blocks(lines)
|
|
40
|
+
if len(matching_blocks) != 1:
|
|
41
|
+
raise ValueError(f"expected one editable wuf package in uv.lock, found {len(matching_blocks)}")
|
|
42
|
+
start, end = matching_blocks[0]
|
|
43
|
+
version_indexes = tuple(index for index in range(start, end) if _VERSION_LINE.fullmatch(lines[index]) is not None)
|
|
44
|
+
if len(version_indexes) != 1:
|
|
45
|
+
raise ValueError("editable wuf package must contain exactly one version in uv.lock")
|
|
46
|
+
|
|
47
|
+
index = version_indexes[0]
|
|
48
|
+
newline = "\r\n" if lines[index].endswith("\r\n") else "\n"
|
|
49
|
+
replacement = f'version = "{normalized}"{newline}'
|
|
50
|
+
if lines[index] != replacement:
|
|
51
|
+
lines[index] = replacement
|
|
52
|
+
_atomic_write(lock_path, "".join(lines))
|
|
53
|
+
|
|
54
|
+
locked = tomllib.loads(lock_path.read_text(encoding="utf-8"))
|
|
55
|
+
packages = tuple(
|
|
56
|
+
package
|
|
57
|
+
for package in locked.get("package", ())
|
|
58
|
+
if package.get("name") == "wuf" and package.get("source") == {"editable": "."}
|
|
59
|
+
)
|
|
60
|
+
if len(packages) != 1 or Version(str(packages[0].get("version"))) != Version(normalized):
|
|
61
|
+
raise RuntimeError("uv.lock version synchronization did not validate")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _synchronize_compatibility_requirement(repository: Path, normalized: str) -> None:
|
|
65
|
+
compatibility_path = repository / "compat" / "workfold" / "pyproject.toml"
|
|
66
|
+
content = compatibility_path.read_text(encoding="utf-8")
|
|
67
|
+
project = tomllib.loads(content)
|
|
68
|
+
configured = str(project["project"]["version"])
|
|
69
|
+
if Version(configured) != Version(normalized):
|
|
70
|
+
raise ValueError(f"release version {normalized!r} does not match compatibility package version {configured!r}")
|
|
71
|
+
|
|
72
|
+
matches = tuple(_COMPATIBILITY_DEPENDENCIES_LINE.finditer(content))
|
|
73
|
+
if len(matches) != 1:
|
|
74
|
+
raise ValueError(f"expected one Wuf compatibility dependency, found {len(matches)}")
|
|
75
|
+
match = matches[0]
|
|
76
|
+
newline = match.group("newline") or ""
|
|
77
|
+
replacement = f'dependencies = ["wuf=={normalized}"]{newline}'
|
|
78
|
+
if match.group(0) != replacement:
|
|
79
|
+
content = content[: match.start()] + replacement + content[match.end() :]
|
|
80
|
+
_atomic_write(compatibility_path, content)
|
|
81
|
+
|
|
82
|
+
synchronized = tomllib.loads(compatibility_path.read_text(encoding="utf-8"))
|
|
83
|
+
dependencies = synchronized["project"].get("dependencies", ())
|
|
84
|
+
if dependencies != [f"wuf=={normalized}"]:
|
|
85
|
+
raise RuntimeError("Workfold compatibility dependency synchronization did not validate")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _editable_project_blocks(lines: list[str]) -> tuple[tuple[int, int], ...]:
|
|
89
|
+
starts = [index for index, line in enumerate(lines) if line.rstrip("\r\n") == "[[package]]"]
|
|
90
|
+
blocks: list[tuple[int, int]] = []
|
|
91
|
+
for ordinal, start in enumerate(starts):
|
|
92
|
+
end = starts[ordinal + 1] if ordinal + 1 < len(starts) else len(lines)
|
|
93
|
+
content = {line.rstrip("\r\n") for line in lines[start:end]}
|
|
94
|
+
if 'name = "wuf"' in content and 'source = { editable = "." }' in content:
|
|
95
|
+
blocks.append((start, end))
|
|
96
|
+
return tuple(blocks)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _atomic_write(path: Path, content: str) -> None:
|
|
100
|
+
mode = path.stat().st_mode
|
|
101
|
+
descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
|
|
102
|
+
temporary = Path(temporary_name)
|
|
103
|
+
try:
|
|
104
|
+
with os.fdopen(descriptor, "w", encoding="utf-8", newline="") as stream:
|
|
105
|
+
stream.write(content)
|
|
106
|
+
stream.flush()
|
|
107
|
+
os.fsync(stream.fileno())
|
|
108
|
+
os.chmod(temporary, mode)
|
|
109
|
+
os.replace(temporary, path)
|
|
110
|
+
finally:
|
|
111
|
+
temporary.unlink(missing_ok=True)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def main(argv: list[str] | None = None) -> int:
|
|
115
|
+
arguments = sys.argv[1:] if argv is None else argv
|
|
116
|
+
if len(arguments) != 1:
|
|
117
|
+
raise SystemExit("usage: sync_lock_version.py VERSION")
|
|
118
|
+
synchronize_version(Path(__file__).resolve().parents[2], arguments[0])
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
raise SystemExit(main())
|
wuf-0.1.0a7/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jan T. Müller
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
wuf-0.1.0a7/MANIFEST.in
ADDED
wuf-0.1.0a7/PKG-INFO
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wuf
|
|
3
|
+
Version: 0.1.0a7
|
|
4
|
+
Summary: Unify local Git and filesystem timestamp footprints in a representative weekly activity view.
|
|
5
|
+
Author-email: "Jan T. Müller" <mail@jantmueller.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: homepage, https://github.com/janthmueller/wuf
|
|
8
|
+
Project-URL: documentation, https://janthmueller.github.io/wuf/
|
|
9
|
+
Project-URL: source, https://github.com/janthmueller/wuf
|
|
10
|
+
Project-URL: tracker, https://github.com/janthmueller/wuf/issues
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: pathspec<2,>=1
|
|
24
|
+
Requires-Dist: rich<15,>=14.3
|
|
25
|
+
Requires-Dist: tzdata>=2025.2; sys_platform == "win32"
|
|
26
|
+
Requires-Dist: tzlocal<6,>=5
|
|
27
|
+
Provides-Extra: audit
|
|
28
|
+
Requires-Dist: pip-audit<3,>=2.9; extra == "audit"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pyright>=1.1; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=6; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.8; extra == "dev"
|
|
34
|
+
Provides-Extra: release
|
|
35
|
+
Requires-Dist: build>=1; extra == "release"
|
|
36
|
+
Requires-Dist: pyinstaller>=6; extra == "release"
|
|
37
|
+
Requires-Dist: python-semantic-release<11,>=10; extra == "release"
|
|
38
|
+
Requires-Dist: twine>=5; extra == "release"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# Wuf
|
|
42
|
+
|
|
43
|
+
[](https://pypi.org/project/wuf/)
|
|
44
|
+
[](https://pepy.tech/project/wuf)
|
|
45
|
+
[](https://github.com/janthmueller/wuf/blob/main/LICENSE)
|
|
46
|
+
|
|
47
|
+
**WUF Unifies Footprints.**
|
|
48
|
+
|
|
49
|
+
Wuf folds local Git and filesystem timestamp footprints onto one representative
|
|
50
|
+
Monday-to-Sunday week. It highlights activity outside your intended working
|
|
51
|
+
hours without pretending that timestamped events are hours worked.
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
|
|
55
|
+
Wuf is local, private by design, terminal-only, and currently alpha.
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install wuf
|
|
61
|
+
wuf --help
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For an isolated installation, use `uv tool install wuf` or
|
|
65
|
+
`pipx install wuf`. Standalone binaries are available from
|
|
66
|
+
[GitHub releases](https://github.com/janthmueller/wuf/releases).
|
|
67
|
+
|
|
68
|
+
### Renamed from Workfold
|
|
69
|
+
|
|
70
|
+
Wuf is the continuation of Workfold under its shorter, permanent name. Wuf
|
|
71
|
+
packages also install `workfold` as a compatibility command, but `wuf` is the
|
|
72
|
+
canonical command going forward. Existing configuration should be renamed from
|
|
73
|
+
`workfold.toml` to `wuf.toml`, and `[tool.workfold]` tables should become
|
|
74
|
+
`[tool.wuf]`.
|
|
75
|
+
|
|
76
|
+
## Quick start
|
|
77
|
+
|
|
78
|
+
Run `wuf` inside a Git repository to see the current ISO week:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
wuf
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The selectors you will use most are:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
wuf . -t 2w3d # rolling elapsed window
|
|
88
|
+
wuf . -t 2026-W31 # one ISO week
|
|
89
|
+
wuf . -p fs # current filesystem metadata
|
|
90
|
+
wuf . -p both # low-noise Git + filesystem view
|
|
91
|
+
wuf . -p portable -t all # evidence stored in Git objects
|
|
92
|
+
wuf . -p full -t all --git-commits-from all-refs --include-ignored
|
|
93
|
+
wuf . -e git:tag:tagger fs:file:modified
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
They control separate parts of the request:
|
|
97
|
+
|
|
98
|
+
| Selector | Purpose | Values |
|
|
99
|
+
| --- | --- | --- |
|
|
100
|
+
| `-t`, `--time` | Date scope | `this-week`, `2w3d`, `YYYY-Www`, `DATE..DATE`, `all` |
|
|
101
|
+
| `-p`, `--profile` | Named event set | `git`, `fs`, `both`, `portable`, `full` |
|
|
102
|
+
| `-e`, `--events` | Exact event set (alternative to a profile) | IDs and wildcards |
|
|
103
|
+
|
|
104
|
+
The profiles answer different questions:
|
|
105
|
+
|
|
106
|
+
- `git`: What does ordinary commit activity look like? This is the default.
|
|
107
|
+
- `fs`: What birth and modification metadata exists for current regular files?
|
|
108
|
+
- `both`: What does the combined low-noise Git and filesystem pattern look like?
|
|
109
|
+
- `portable`: What dated evidence is stored inside Git objects?
|
|
110
|
+
- `full`: Which timestamps exist across every supported Git and filesystem event kind?
|
|
111
|
+
|
|
112
|
+
Profiles expand only to event sets. Time, Git reachability, ignored files, and
|
|
113
|
+
explicit exclusions remain independently configurable.
|
|
114
|
+
|
|
115
|
+
For exact control, `-e/--events` accepts identifiers such as
|
|
116
|
+
`git:commit:author`, `git:tag:tagger`, and `fs:file:modified`; quote wildcards
|
|
117
|
+
such as `'git:*'`. `-l/--list` appends bounded event details. Paths must appear
|
|
118
|
+
before either space-separated selector, or after an option-terminating `--`.
|
|
119
|
+
|
|
120
|
+
Schedules support daily intervals, breaks, overnight shifts, and `all`:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
wuf . --hours 'Mo-Thu 08:00-16:30; Fr 08:00-14:00'
|
|
124
|
+
wuf . --hours 'Mo-Fr 22:00-06:00'
|
|
125
|
+
wuf . --hours all
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
See the [usage guide](https://janthmueller.github.io/wuf/guides/usage/)
|
|
129
|
+
for clustering, fixed bands, identity markers, day hiding, grids, exact event
|
|
130
|
+
selection, configuration, and every CLI option.
|
|
131
|
+
|
|
132
|
+
## Configuration
|
|
133
|
+
|
|
134
|
+
Put personal defaults in the platform configuration directory, or project
|
|
135
|
+
defaults in `wuf.toml`:
|
|
136
|
+
|
|
137
|
+
```toml
|
|
138
|
+
timezone = "Europe/Berlin"
|
|
139
|
+
hours = "Mo-Thu 08:00-16:30; Fr 08:00-14:00"
|
|
140
|
+
profile = "portable"
|
|
141
|
+
cluster-anchor = "midnight"
|
|
142
|
+
band-label = "start"
|
|
143
|
+
show-empty-bands = true
|
|
144
|
+
count-grouping = "visual"
|
|
145
|
+
grid = "vertical"
|
|
146
|
+
hide-empty-days = ["weekend"]
|
|
147
|
+
|
|
148
|
+
[styles."git:tag:*"]
|
|
149
|
+
symbol = "◆"
|
|
150
|
+
color = "magenta"
|
|
151
|
+
outside-symbol = "◇"
|
|
152
|
+
outside-color = "bright_red"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Python projects may use `[tool.wuf]` in `pyproject.toml` instead. Values
|
|
156
|
+
resolve as built-in → global → nearest project → CLI. Inspect the result and
|
|
157
|
+
each value's origin without collecting timestamps:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
wuf . --show-config
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Use `--config FILE` for one exact file or `--no-config` for built-ins plus CLI
|
|
164
|
+
only. The [usage guide](https://janthmueller.github.io/wuf/guides/usage/#configuration-files)
|
|
165
|
+
documents locations, discovery, merging, and every supported key.
|
|
166
|
+
|
|
167
|
+
## Reading the chart
|
|
168
|
+
|
|
169
|
+
- By default, circles are Git events and squares are filesystem events.
|
|
170
|
+
- Default green/blue filled markers are inside the schedule; red hollow markers
|
|
171
|
+
are outside. Colorless output preserves the shape distinction, and event
|
|
172
|
+
style rules may replace both symbol pairs and their colors.
|
|
173
|
+
- One symbol is one event; busy cells use exact `×N` counts. Counts stay
|
|
174
|
+
separate per event kind by default; `--count-grouping visual` merges kinds
|
|
175
|
+
only when their resolved symbol and configured color match.
|
|
176
|
+
- Empty time is compressed, and `⋮` reports a meaningful gap.
|
|
177
|
+
- Identity-marker mode replaces Git circles with codes mapped in the key.
|
|
178
|
+
- Event-selector style rules can replace source-marker symbols and colors while
|
|
179
|
+
keeping collection and coverage unchanged.
|
|
180
|
+
|
|
181
|
+
The summary independently splits all events by schedule and by calendar day.
|
|
182
|
+
Weekend events can therefore also be outside working hours.
|
|
183
|
+
|
|
184
|
+
## Accuracy and privacy
|
|
185
|
+
|
|
186
|
+
- Events are discrete timestamp observations, not work sessions or duration.
|
|
187
|
+
- Collection is local: Wuf does not contact a Git host or telemetry service.
|
|
188
|
+
- Git history can be rewritten; reflogs can expire; filesystem metadata is a
|
|
189
|
+
mutable snapshot and birth time depends on platform and filesystem support.
|
|
190
|
+
- Coverage reports unavailable, unsupported, and unreadable evidence that can
|
|
191
|
+
prevent a complete answer. Known timestamps outside the requested time or
|
|
192
|
+
identity scope are not counted as coverage outcomes.
|
|
193
|
+
|
|
194
|
+
See the [documentation](https://janthmueller.github.io/wuf/) for every CLI
|
|
195
|
+
option, collector semantics, coverage guarantees, and platform notes.
|
|
196
|
+
|
|
197
|
+
## Development
|
|
198
|
+
|
|
199
|
+
The Python package lives directly in `wuf/`; there is no `src/` wrapper.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
nix develop
|
|
203
|
+
uv sync --locked --extra dev
|
|
204
|
+
uv run --locked pytest
|
|
205
|
+
uv run --locked ruff check .
|
|
206
|
+
uv run --locked ruff format --check .
|
|
207
|
+
uv run --locked pyright
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Use `nix run .#docs-dev` for the documentation site and
|
|
211
|
+
`nix run .#docs-check` to validate it. The
|
|
212
|
+
[architecture guide](https://janthmueller.github.io/wuf/reference/architecture/)
|
|
213
|
+
documents the package boundaries, dependency rules, data pipeline, and test
|
|
214
|
+
layout.
|
wuf-0.1.0a7/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Wuf
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/wuf/)
|
|
4
|
+
[](https://pepy.tech/project/wuf)
|
|
5
|
+
[](https://github.com/janthmueller/wuf/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
**WUF Unifies Footprints.**
|
|
8
|
+
|
|
9
|
+
Wuf folds local Git and filesystem timestamp footprints onto one representative
|
|
10
|
+
Monday-to-Sunday week. It highlights activity outside your intended working
|
|
11
|
+
hours without pretending that timestamped events are hours worked.
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
Wuf is local, private by design, terminal-only, and currently alpha.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install wuf
|
|
21
|
+
wuf --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For an isolated installation, use `uv tool install wuf` or
|
|
25
|
+
`pipx install wuf`. Standalone binaries are available from
|
|
26
|
+
[GitHub releases](https://github.com/janthmueller/wuf/releases).
|
|
27
|
+
|
|
28
|
+
### Renamed from Workfold
|
|
29
|
+
|
|
30
|
+
Wuf is the continuation of Workfold under its shorter, permanent name. Wuf
|
|
31
|
+
packages also install `workfold` as a compatibility command, but `wuf` is the
|
|
32
|
+
canonical command going forward. Existing configuration should be renamed from
|
|
33
|
+
`workfold.toml` to `wuf.toml`, and `[tool.workfold]` tables should become
|
|
34
|
+
`[tool.wuf]`.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
Run `wuf` inside a Git repository to see the current ISO week:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
wuf
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The selectors you will use most are:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
wuf . -t 2w3d # rolling elapsed window
|
|
48
|
+
wuf . -t 2026-W31 # one ISO week
|
|
49
|
+
wuf . -p fs # current filesystem metadata
|
|
50
|
+
wuf . -p both # low-noise Git + filesystem view
|
|
51
|
+
wuf . -p portable -t all # evidence stored in Git objects
|
|
52
|
+
wuf . -p full -t all --git-commits-from all-refs --include-ignored
|
|
53
|
+
wuf . -e git:tag:tagger fs:file:modified
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
They control separate parts of the request:
|
|
57
|
+
|
|
58
|
+
| Selector | Purpose | Values |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `-t`, `--time` | Date scope | `this-week`, `2w3d`, `YYYY-Www`, `DATE..DATE`, `all` |
|
|
61
|
+
| `-p`, `--profile` | Named event set | `git`, `fs`, `both`, `portable`, `full` |
|
|
62
|
+
| `-e`, `--events` | Exact event set (alternative to a profile) | IDs and wildcards |
|
|
63
|
+
|
|
64
|
+
The profiles answer different questions:
|
|
65
|
+
|
|
66
|
+
- `git`: What does ordinary commit activity look like? This is the default.
|
|
67
|
+
- `fs`: What birth and modification metadata exists for current regular files?
|
|
68
|
+
- `both`: What does the combined low-noise Git and filesystem pattern look like?
|
|
69
|
+
- `portable`: What dated evidence is stored inside Git objects?
|
|
70
|
+
- `full`: Which timestamps exist across every supported Git and filesystem event kind?
|
|
71
|
+
|
|
72
|
+
Profiles expand only to event sets. Time, Git reachability, ignored files, and
|
|
73
|
+
explicit exclusions remain independently configurable.
|
|
74
|
+
|
|
75
|
+
For exact control, `-e/--events` accepts identifiers such as
|
|
76
|
+
`git:commit:author`, `git:tag:tagger`, and `fs:file:modified`; quote wildcards
|
|
77
|
+
such as `'git:*'`. `-l/--list` appends bounded event details. Paths must appear
|
|
78
|
+
before either space-separated selector, or after an option-terminating `--`.
|
|
79
|
+
|
|
80
|
+
Schedules support daily intervals, breaks, overnight shifts, and `all`:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
wuf . --hours 'Mo-Thu 08:00-16:30; Fr 08:00-14:00'
|
|
84
|
+
wuf . --hours 'Mo-Fr 22:00-06:00'
|
|
85
|
+
wuf . --hours all
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
See the [usage guide](https://janthmueller.github.io/wuf/guides/usage/)
|
|
89
|
+
for clustering, fixed bands, identity markers, day hiding, grids, exact event
|
|
90
|
+
selection, configuration, and every CLI option.
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
Put personal defaults in the platform configuration directory, or project
|
|
95
|
+
defaults in `wuf.toml`:
|
|
96
|
+
|
|
97
|
+
```toml
|
|
98
|
+
timezone = "Europe/Berlin"
|
|
99
|
+
hours = "Mo-Thu 08:00-16:30; Fr 08:00-14:00"
|
|
100
|
+
profile = "portable"
|
|
101
|
+
cluster-anchor = "midnight"
|
|
102
|
+
band-label = "start"
|
|
103
|
+
show-empty-bands = true
|
|
104
|
+
count-grouping = "visual"
|
|
105
|
+
grid = "vertical"
|
|
106
|
+
hide-empty-days = ["weekend"]
|
|
107
|
+
|
|
108
|
+
[styles."git:tag:*"]
|
|
109
|
+
symbol = "◆"
|
|
110
|
+
color = "magenta"
|
|
111
|
+
outside-symbol = "◇"
|
|
112
|
+
outside-color = "bright_red"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Python projects may use `[tool.wuf]` in `pyproject.toml` instead. Values
|
|
116
|
+
resolve as built-in → global → nearest project → CLI. Inspect the result and
|
|
117
|
+
each value's origin without collecting timestamps:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
wuf . --show-config
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Use `--config FILE` for one exact file or `--no-config` for built-ins plus CLI
|
|
124
|
+
only. The [usage guide](https://janthmueller.github.io/wuf/guides/usage/#configuration-files)
|
|
125
|
+
documents locations, discovery, merging, and every supported key.
|
|
126
|
+
|
|
127
|
+
## Reading the chart
|
|
128
|
+
|
|
129
|
+
- By default, circles are Git events and squares are filesystem events.
|
|
130
|
+
- Default green/blue filled markers are inside the schedule; red hollow markers
|
|
131
|
+
are outside. Colorless output preserves the shape distinction, and event
|
|
132
|
+
style rules may replace both symbol pairs and their colors.
|
|
133
|
+
- One symbol is one event; busy cells use exact `×N` counts. Counts stay
|
|
134
|
+
separate per event kind by default; `--count-grouping visual` merges kinds
|
|
135
|
+
only when their resolved symbol and configured color match.
|
|
136
|
+
- Empty time is compressed, and `⋮` reports a meaningful gap.
|
|
137
|
+
- Identity-marker mode replaces Git circles with codes mapped in the key.
|
|
138
|
+
- Event-selector style rules can replace source-marker symbols and colors while
|
|
139
|
+
keeping collection and coverage unchanged.
|
|
140
|
+
|
|
141
|
+
The summary independently splits all events by schedule and by calendar day.
|
|
142
|
+
Weekend events can therefore also be outside working hours.
|
|
143
|
+
|
|
144
|
+
## Accuracy and privacy
|
|
145
|
+
|
|
146
|
+
- Events are discrete timestamp observations, not work sessions or duration.
|
|
147
|
+
- Collection is local: Wuf does not contact a Git host or telemetry service.
|
|
148
|
+
- Git history can be rewritten; reflogs can expire; filesystem metadata is a
|
|
149
|
+
mutable snapshot and birth time depends on platform and filesystem support.
|
|
150
|
+
- Coverage reports unavailable, unsupported, and unreadable evidence that can
|
|
151
|
+
prevent a complete answer. Known timestamps outside the requested time or
|
|
152
|
+
identity scope are not counted as coverage outcomes.
|
|
153
|
+
|
|
154
|
+
See the [documentation](https://janthmueller.github.io/wuf/) for every CLI
|
|
155
|
+
option, collector semantics, coverage guarantees, and platform notes.
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
The Python package lives directly in `wuf/`; there is no `src/` wrapper.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
nix develop
|
|
163
|
+
uv sync --locked --extra dev
|
|
164
|
+
uv run --locked pytest
|
|
165
|
+
uv run --locked ruff check .
|
|
166
|
+
uv run --locked ruff format --check .
|
|
167
|
+
uv run --locked pyright
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Use `nix run .#docs-dev` for the documentation site and
|
|
171
|
+
`nix run .#docs-check` to validate it. The
|
|
172
|
+
[architecture guide](https://janthmueller.github.io/wuf/reference/architecture/)
|
|
173
|
+
documents the package boundaries, dependency rules, data pipeline, and test
|
|
174
|
+
layout.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Third-party notices for standalone Wuf bundles
|
|
2
|
+
|
|
3
|
+
The standalone archives are produced with PyInstaller and include portions of
|
|
4
|
+
the Python runtime plus Wuf's runtime dependencies. Their exact license
|
|
5
|
+
files are copied into the archive's `licenses/` directory at build time.
|
|
6
|
+
|
|
7
|
+
- Python and its standard library: Python Software Foundation License.
|
|
8
|
+
- pathspec: Mozilla Public License 2.0.
|
|
9
|
+
- Rich: MIT License.
|
|
10
|
+
- markdown-it-py and mdurl: MIT License.
|
|
11
|
+
- Pygments: BSD 2-Clause License.
|
|
12
|
+
- tzlocal: MIT License.
|
|
13
|
+
- tzdata, when bundled on Windows: licenses shipped by the Python tzdata
|
|
14
|
+
distribution and the underlying IANA time-zone database.
|
|
15
|
+
- PyInstaller bootloader: GNU General Public License with PyInstaller's
|
|
16
|
+
bootloader exception; its shipped notices are included.
|
|
17
|
+
|
|
18
|
+
This notice is informational. The complete license texts included beside the
|
|
19
|
+
binary control the corresponding components.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Wuf benchmarks
|
|
2
|
+
|
|
3
|
+
These are opt-in end-to-end measurements, not ordinary correctness tests. A
|
|
4
|
+
benchmark starts a complete Wuf process and includes configuration,
|
|
5
|
+
collection, folding, terminal rendering, and the local Git subprocesses it
|
|
6
|
+
uses.
|
|
7
|
+
|
|
8
|
+
## Run a suite
|
|
9
|
+
|
|
10
|
+
From the repository root:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uv run python -m benchmarks.run ~/linux --suite quick
|
|
14
|
+
uv run python -m benchmarks.run ~/linux --suite scope --json benchmarks/results/linux.json
|
|
15
|
+
uv run python -m benchmarks.run --fixture medium --suite complete
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The built-in suites are:
|
|
19
|
+
|
|
20
|
+
- `quick`: low-noise Git, filesystem, and combined current-week profiles;
|
|
21
|
+
- `scope`: those three profiles for both current-week and all-date selection;
|
|
22
|
+
- `profiles`: deliberately broad portable and full workloads with bounded and
|
|
23
|
+
all-date selections; Git cases use all locally stored refs and filesystem
|
|
24
|
+
cases include ignored entries where applicable;
|
|
25
|
+
- `complete`: every named case.
|
|
26
|
+
|
|
27
|
+
Repeat `--case NAME` to select an exact subset instead. Run `--help` for the
|
|
28
|
+
case names and process controls. `--wuf-executable PATH` measures an
|
|
29
|
+
installed script or release binary instead of `python -m wuf` from the
|
|
30
|
+
checkout.
|
|
31
|
+
|
|
32
|
+
## Measurements
|
|
33
|
+
|
|
34
|
+
Each recorded sample includes:
|
|
35
|
+
|
|
36
|
+
- monotonic wall time;
|
|
37
|
+
- child user and system CPU time where the platform exposes it;
|
|
38
|
+
- the Wuf process's kernel-recorded high-water RSS on Linux;
|
|
39
|
+
- sampled peak RSS summed across the Wuf process tree on Linux;
|
|
40
|
+
- minor and major page faults and voluntary/involuntary context switches;
|
|
41
|
+
- exit status, timeout state, stdout/stderr sizes, and a stdout digest;
|
|
42
|
+
- the exact `Events` summary count.
|
|
43
|
+
|
|
44
|
+
Every workload adds `--strict`, so incomplete collection fails instead of
|
|
45
|
+
becoming a successful timing sample. The runner also rejects missing summaries,
|
|
46
|
+
changed event counts, or changed stdout across repetitions. Raw JSON records Python, Wuf, Git,
|
|
47
|
+
OS, CPU, load, total memory, Wuf checkout revision/dirty state, target
|
|
48
|
+
HEAD/tracked dirty state, all-ref commit count, Git object-store statistics,
|
|
49
|
+
commands, cache policy, and every individual sample.
|
|
50
|
+
|
|
51
|
+
Main RSS is the stable high-water mark for Wuf itself. Summed tree RSS also
|
|
52
|
+
captures concurrently running Git children, but it is not proportional-set
|
|
53
|
+
size: shared pages can appear in more than one process. Sampling defaults to 5
|
|
54
|
+
ms, so extremely short-lived child peaks can be missed. Non-Linux hosts report
|
|
55
|
+
memory as `n/a`; timing and output validation still work.
|
|
56
|
+
|
|
57
|
+
Warmups are explicit and the runner never drops the operating system's page
|
|
58
|
+
cache. Compare results only under the same host, target, command, sample
|
|
59
|
+
interval, and warmup policy. Do not use fixed cross-machine timing thresholds
|
|
60
|
+
in the normal test workflow. Global and system Git configuration are disabled
|
|
61
|
+
for repeatability; repository-local configuration still applies.
|
|
62
|
+
|
|
63
|
+
## Synthetic fixtures
|
|
64
|
+
|
|
65
|
+
`--fixture small|medium|directory-heavy|large` creates an automatically removed repository with
|
|
66
|
+
historical and current-week commits, distinct author/committer identities,
|
|
67
|
+
annotated tags, reflogs, tracked files, untracked files, ignored files,
|
|
68
|
+
directories, and symlinks where supported.
|
|
69
|
+
|
|
70
|
+
The `directory-heavy` fixture places one tracked, untracked, or ignored file in
|
|
71
|
+
each directory. It isolates directory traversal and parent-handle validation
|
|
72
|
+
costs that file-dense fixtures can hide:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv run python -m benchmarks.run --fixture directory-heavy --case fs-all
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
To keep a fixture for profiling tools, create one explicitly in a new or empty
|
|
79
|
+
directory:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv run python -m benchmarks.fixture /tmp/wuf-medium --size medium
|
|
83
|
+
uv run python -m benchmarks.run /tmp/wuf-medium --suite complete
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Fixture generation refuses non-empty targets and never deletes an existing
|
|
87
|
+
repository.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Opt-in end-to-end performance benchmarks for Wuf."""
|