|
|
@@ -7,11 +7,16 @@ SECRET_KEY and database credentials), any other local configuration*.py variant,
|
|
|
any ldap_config*.py (which holds LDAP bind credentials). The wheel guard alone is not
|
|
|
enough: a wheel rebuilt from the sdist re-applies the wheel excludes, so it can come
|
|
|
out clean even when the sdist itself leaks a file.
|
|
|
+
|
|
|
+The sdist must also declare the Core Metadata version pinned for the sdist target in
|
|
|
+pyproject.toml, so a backend default change cannot silently alter the artifact format.
|
|
|
"""
|
|
|
|
|
|
import sys
|
|
|
import tarfile
|
|
|
-from pathlib import PurePosixPath
|
|
|
+import tomllib
|
|
|
+from email.parser import Parser
|
|
|
+from pathlib import Path, PurePosixPath
|
|
|
|
|
|
# Allowed members, relative to the sdist's netbox-<version>/ root directory. The sdist
|
|
|
# keeps the full repository layout (no `sources` strip), unlike the wheel.
|
|
|
@@ -34,21 +39,46 @@ def configuration_members(sdist_path):
|
|
|
return members
|
|
|
|
|
|
|
|
|
+def expected_metadata_version():
|
|
|
+ """Return the core-metadata-version pinned for the sdist target, or None."""
|
|
|
+ pyproject = tomllib.loads((Path(__file__).resolve().parent.parent / 'pyproject.toml').read_text())
|
|
|
+ return pyproject['tool']['hatch']['build']['targets'].get('sdist', {}).get('core-metadata-version')
|
|
|
+
|
|
|
+
|
|
|
+def read_pkg_info(sdist_path):
|
|
|
+ """Return the sdist's parsed top-level PKG-INFO, or None when the file is absent."""
|
|
|
+ with tarfile.open(sdist_path) as archive:
|
|
|
+ for member in archive.getmembers():
|
|
|
+ if PurePosixPath(member.name).parts[1:] == ('PKG-INFO',):
|
|
|
+ return Parser().parsestr(archive.extractfile(member).read().decode())
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
def main(argv):
|
|
|
if len(argv) != 2:
|
|
|
print('usage: verify_sdist_contents.py <sdist>')
|
|
|
return 2
|
|
|
+ errors = []
|
|
|
found = configuration_members(argv[1])
|
|
|
- missing = sorted(ALLOWED - found)
|
|
|
- unexpected = sorted(found - ALLOWED)
|
|
|
- if missing or unexpected:
|
|
|
- print('Sdist configuration files are not as expected:')
|
|
|
- if missing:
|
|
|
- print(f' - missing templates: {missing}')
|
|
|
- if unexpected:
|
|
|
- print(f' - unexpected (possible secret leak): {unexpected}')
|
|
|
+ if missing := sorted(ALLOWED - found):
|
|
|
+ errors.append(f'missing templates: {missing}')
|
|
|
+ if unexpected := sorted(found - ALLOWED):
|
|
|
+ errors.append(f'unexpected (possible secret leak): {unexpected}')
|
|
|
+ expected = expected_metadata_version()
|
|
|
+ if expected is None:
|
|
|
+ errors.append('pyproject.toml does not pin core-metadata-version for the sdist target')
|
|
|
+ elif (pkg_info := read_pkg_info(argv[1])) is None:
|
|
|
+ errors.append('sdist is missing its top-level PKG-INFO')
|
|
|
+ elif (actual := pkg_info['Metadata-Version']) is None:
|
|
|
+ errors.append('sdist PKG-INFO does not declare Metadata-Version')
|
|
|
+ elif actual != expected:
|
|
|
+ errors.append(f'metadata version mismatch: sdist PKG-INFO has {actual}, pyproject.toml pins {expected}')
|
|
|
+ if errors:
|
|
|
+ print('Sdist contents are not as expected:')
|
|
|
+ for error in errors:
|
|
|
+ print(f' - {error}')
|
|
|
return 1
|
|
|
- print(f'OK: sdist ships only the {len(ALLOWED)} configuration templates')
|
|
|
+ print(f'OK: sdist ships only the {len(ALLOWED)} configuration templates and declares Metadata-Version {expected}')
|
|
|
return 0
|
|
|
|
|
|
|