Files
pdf-reporting/report_generator/vendor/sync.py
T

104 lines
3.8 KiB
Python

"""Copies everything this tool borrows from the monorepo into ``vendor/``.
The package is meant to be liftable into a repository of its own, where none of ``cc_common`` or
``persistent_storage`` exists. The copies here are what it would need to take along.
They are produced by a script rather than by hand for one reason: a copy made once goes stale
without saying so. Re-run this after touching anything it lists and the diff shows what drifted.
python -m persistent_storage.report_generator.vendor.sync
Copied under their own import paths, so ``vendor/`` on ``sys.path`` makes the package import
unchanged in the new repository.
"""
from __future__ import annotations
import shutil
import sys
from pathlib import Path
# Where the monorepo's sources sit, relative to this file.
SOURCE_ROOT = Path(__file__).resolve().parents[4]
VENDOR_ROOT = Path(__file__).resolve().parent
# What the tool reaches for, and nothing beyond it. A whole package is copied where the tool uses
# enough of it that picking modules would only mean chasing imports on the next change.
COPIED = (
# The CLI framework, the logging context and the error type every command raises.
"cc_common",
# The scan and artifact rows, the storage configuration, and the readers over them.
"persistent_storage/__init__.py",
"persistent_storage/shared.py",
"persistent_storage/wrapper.py",
"persistent_storage/helper.py",
"persistent_storage/utils/__init__.py",
"persistent_storage/utils/db_utils.py",
"persistent_storage/utils/s3_utils.py",
# The CVSS reader the vulnerability summariser works through.
"persistent_storage/utils/cvss_utils.py",
# The two summarisers the report is built from. They live in the web application today and are
# written to be free of it -- no Django, no store -- which is what makes copying them honest
# rather than a workaround. Wherever this package ends up, they belong beside it.
"cipherverifier/__init__.py",
"cipherverifier/core/__init__.py",
"cipherverifier/core/services/__init__.py",
"cipherverifier/core/services/sarif.py",
"cipherverifier/core/services/vdr.py",
)
# Never copied: caches and the compiled leftovers of whoever ran a tool over the source tree last.
# They live inside the packages being copied, so leaving any of them out puts megabytes of someone
# else's machine into the vendor tree.
SKIPPED = shutil.ignore_patterns(
"__pycache__", "*.pyc", ".DS_Store", ".mypy_cache", ".ruff_cache", ".pytest_cache", "*.egg-info"
)
def sync() -> list[Path]:
"""Refresh every copy, replacing what is there.
The copied packages are removed whole before anything is written, so that dropping an entry
from :data:`COPIED` actually drops it from the tree. Replacing only what is listed leaves what
used to be listed behind, and the copy then carries files nothing refers to any more -- which is
the same staleness this script exists to prevent, one level up.
Returns:
What was written, relative to ``vendor/``.
"""
for root in {entry.split("/")[0] for entry in COPIED}:
stale = VENDOR_ROOT / root
if stale.is_dir():
shutil.rmtree(stale)
elif stale.exists():
stale.unlink()
written = []
for entry in COPIED:
source = SOURCE_ROOT / "src" / entry
target = VENDOR_ROOT / entry
if not source.exists():
message = f"{source} does not exist -- the monorepo moved it, and this list has not followed."
raise FileNotFoundError(message)
target.parent.mkdir(parents=True, exist_ok=True)
if source.is_dir():
shutil.copytree(source, target, ignore=SKIPPED)
else:
shutil.copy2(source, target)
written.append(Path(entry))
return written
if __name__ == "__main__":
for path in sync():
sys.stdout.write(f"{path}\n")