Files
Stirling-PDF/scripts/pre-commit/sort_locale_toml.py
T
05eb74022a chore(ci): migrate Python tooling to uv and standardize workflow execution (#7386)
# Description of Changes

This PR modernizes the project's Python tooling across GitHub Actions by
migrating CI workflows from pip-based dependency management to `uv` and
aligning Python execution with the engine project's managed environment.

### What was changed

- Replaced `actions/setup-python` and ad-hoc `pip install` steps with
`astral-sh/setup-uv` across CI workflows.
- Configured shared `uv` dependency caching using
`engine/pyproject.toml` and `engine/uv.lock`.
- Updated Python script execution to use `uv run --project engine
--locked` for a consistent runtime environment.
- Replaced package installation steps with `uv sync` for the required
dependency groups (e.g. `tools` and `cucumber`).
- Added Docker image build validation for both production and
development AI engine images.
- Updated workflow cache configuration and Docker build context where
required.
- Removed obsolete Python requirements files that are no longer needed
after the migration.
- Applied minor Python code modernizations, including import cleanup,
modern built-in generic type annotations (`list[...]`, `tuple[...]`,
`float | None`), and small style improvements.
- Removed unnecessary Python formatter/linter extensions from the
development container configuration.

### Why the change was made

- Standardize Python dependency management across the repository.
- Reduce duplicated dependency installation logic in CI.
- Improve workflow performance through shared dependency caching.
- Ensure all Python utilities execute against the same locked dependency
set managed by the engine project.
- Simplify long-term maintenance by eliminating legacy requirements
files and pip-specific workflow steps.


---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have run `task check` to verify linters, typechecks, and tests
pass
- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing)
for more details.

---------

Signed-off-by: Carsten Drewes <c.drewes@stud.uni-hannover.de>
Co-authored-by: albanobattistella <34811668+albanobattistella@users.noreply.github.com>
Co-authored-by: kastenherri <116314318+kastenherri@users.noreply.github.com>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: James Brunton <jbrunton96@gmail.com>
2026-08-11 08:09:21 +00:00

93 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Key-sort the locale translation.toml files.
python sort_locale_toml.py <pathspec>... # check: report, exit 1 if unsorted
python sort_locale_toml.py --fix <pathspec>... # fix: rewrite in place
"""
from __future__ import annotations
import subprocess
import sys
import tomllib
from pathlib import Path
import tomli_w
class SortError(Exception):
"""A file could not be sorted without risking its contents."""
def ordered(table: dict[str, object]) -> dict[str, object]:
"""Rebuild a table with its keys sorted, and sub-tables after its own keys."""
keys = {key: value for key, value in table.items() if not isinstance(value, dict)}
subtables = {key: value for key, value in table.items() if isinstance(value, dict)}
result: dict[str, object] = {key: keys[key] for key in sorted(keys, key=str.lower)}
for key in sorted(subtables, key=str.lower):
result[key] = ordered(subtables[key])
return result
def tracked_files(path_specs: list[str]) -> list[str]:
result = subprocess.run(
["git", "ls-files", "-z", *path_specs],
check=True,
capture_output=True,
text=True,
)
return [path for path in result.stdout.split("\0") if path]
def sort_file(path: str, fix: bool) -> bool:
"""Rewrite one file if `fix`; return whether it was not already sorted."""
text = Path(path).read_text(encoding="utf-8")
try:
original = tomllib.loads(text)
except tomllib.TOMLDecodeError as exc:
raise SortError(f"{path}: invalid TOML: {exc}") from exc
expected = tomli_w.dumps(ordered(original))
if expected == text:
return False
try:
reordered = tomllib.loads(expected)
except tomllib.TOMLDecodeError as exc:
raise SortError(f"{path}: refusing to sort, the sorted output is not valid TOML: {exc}") from exc
if reordered != original:
raise SortError(f"{path}: refusing to sort, sorting would change the file's contents")
if fix:
Path(path).write_text(expected, encoding="utf-8")
return True
def main() -> int:
args = sys.argv[1:]
fix = "--fix" in args
pathspecs = [a for a in args if a != "--fix"]
offenders: list[str] = []
errors: list[str] = []
for path in tracked_files(pathspecs):
try:
if sort_file(path, fix):
offenders.append(path)
except SortError as exc:
errors.append(str(exc))
for error in errors:
print(error, file=sys.stderr)
if offenders and not fix:
print(f"{len(offenders)} file(s) need TOML sorting:")
for path in offenders:
print(f" {path}")
if offenders and fix:
print(f"Sorted TOML in {len(offenders)} file(s).")
return 1 if errors or (offenders and not fix) else 0
if __name__ == "__main__":
sys.exit(main())