diff --git a/backend/src/hatchling/builders/sdist.py b/backend/src/hatchling/builders/sdist.py index 8081eb507..55fa5e4d8 100644 --- a/backend/src/hatchling/builders/sdist.py +++ b/backend/src/hatchling/builders/sdist.py @@ -15,6 +15,7 @@ from hatchling.builders.utils import ( get_reproducible_timestamp, normalize_archive_path, + normalize_artifact_permissions, normalize_file_permissions, normalize_relative_path, replace_file, @@ -36,10 +37,8 @@ def __init__(self, name: str, *, reproducible: bool) -> None: self.timestamp: int | None = get_reproducible_timestamp() if reproducible else None raw_fd, self.path = tempfile.mkstemp(suffix='.tar.gz') + normalize_artifact_permissions(self.path) self.fd = os.fdopen(raw_fd, 'w+b') - file_stat = os.stat(self.path) - new_mode = normalize_file_permissions(file_stat.st_mode) - os.chmod(self.path, new_mode) self.gz = gzip.GzipFile(fileobj=self.fd, mode='wb', mtime=self.timestamp) self.tf = tarfile.TarFile(fileobj=self.gz, mode='w', format=tarfile.PAX_FORMAT) self.gettarinfo = lambda *args, **kwargs: self.normalize_tar_metadata(self.tf.gettarinfo(*args, **kwargs)) diff --git a/backend/src/hatchling/builders/utils.py b/backend/src/hatchling/builders/utils.py index fceb06cef..6b5949125 100644 --- a/backend/src/hatchling/builders/utils.py +++ b/backend/src/hatchling/builders/utils.py @@ -110,6 +110,15 @@ def normalize_file_permissions(st_mode: int) -> int: return new_mode +def normalize_artifact_permissions(path: str) -> None: + """ + Normalize the permission bits for artifacts + """ + file_stat = os.stat(path) + new_mode = normalize_file_permissions(file_stat.st_mode) + os.chmod(path, new_mode) + + def set_zip_info_mode(zip_info: ZipInfo, mode: int = 0o644) -> None: """ https://github.com/python/cpython/blob/v3.12.3/Lib/zipfile/__init__.py#L574 diff --git a/backend/src/hatchling/builders/wheel.py b/backend/src/hatchling/builders/wheel.py index f7bf77049..7292b215f 100644 --- a/backend/src/hatchling/builders/wheel.py +++ b/backend/src/hatchling/builders/wheel.py @@ -20,6 +20,7 @@ get_known_python_major_versions, get_reproducible_timestamp, normalize_archive_path, + normalize_artifact_permissions, normalize_file_permissions, normalize_inclusion_map, replace_file, @@ -79,9 +80,7 @@ def __init__(self, project_id: str, *, reproducible: bool) -> None: self.time_tuple = None raw_fd, self.path = tempfile.mkstemp(suffix='.whl') - file_stat = os.stat(self.path) - new_mode = normalize_file_permissions(file_stat.st_mode) - os.chmod(self.path, new_mode) + normalize_artifact_permissions(self.path) self.fd = os.fdopen(raw_fd, 'w+b') self.zf = zipfile.ZipFile(self.fd, 'w', compression=zipfile.ZIP_DEFLATED) diff --git a/tests/backend/builders/test_sdist.py b/tests/backend/builders/test_sdist.py index 387d1f857..8341db1c8 100644 --- a/tests/backend/builders/test_sdist.py +++ b/tests/backend/builders/test_sdist.py @@ -1523,7 +1523,7 @@ def test_no_strict_naming(self, hatch, helpers, temp_dir, config_file): stat = os.stat(str(extraction_directory / builder.artifact_project_id / 'PKG-INFO')) assert stat.st_mtime == get_reproducible_timestamp() - def test_file_permissions_normalized(self, hatch, helpers, temp_dir, config_file): + def test_file_permissions_normalized(self, hatch, temp_dir, config_file): config_file.model.template.plugins['default']['src-layout'] = False config_file.save() diff --git a/tests/helpers/templates/wheel/utils.py b/tests/helpers/templates/wheel/utils.py index 42d5c04fc..35e355bc5 100644 --- a/tests/helpers/templates/wheel/utils.py +++ b/tests/helpers/templates/wheel/utils.py @@ -1,7 +1,8 @@ import hashlib import os +import tempfile -from hatchling.builders.utils import format_file_hash +from hatchling.builders.utils import format_file_hash, normalize_artifact_permissions def update_record_file_contents(record_file, files, generated_files=()): @@ -42,3 +43,18 @@ def update_record_file_contents(record_file, files, generated_files=()): record_file.contents += f'{template_file.path.as_posix()},sha256={hash_digest},{len(raw_contents)}\n' record_file.contents += f'{record_file.path.as_posix()},,\n' + + +def test_normalize_artifact_permissions(): + """ + assert that this func does what we expect on a tmpfile that that starts at 600 + """ + _, path = tempfile.mkstemp() + + file_stat = os.stat(path) + assert file_stat.st_mode == 0o100600 + + normalize_artifact_permissions(path) + + file_stat = os.stat(path) + assert file_stat.st_mode == 0o100644