Skip to content

Commit

Permalink
Use deterministic time for generated sdist files
Browse files Browse the repository at this point in the history
When generating setup.py and PKG-INFO files, ensure that generated
files use a deterministic timestamp to enhance reproducibility of
source distributions.
  • Loading branch information
abn authored and kasteph committed Apr 19, 2021
1 parent 015e05d commit f478ec3
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 3 deletions.
7 changes: 4 additions & 3 deletions poetry/core/masonry/builders/sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import os
import re
import tarfile
import time

from collections import defaultdict
from contextlib import contextmanager
Expand Down Expand Up @@ -97,14 +96,16 @@ def build(self, target_dir: Optional[Path] = None) -> Path:
setup = self.build_setup()
tar_info = tarfile.TarInfo(pjoin(tar_dir, "setup.py"))
tar_info.size = len(setup)
tar_info.mtime = time.time()
tar_info.mtime = 0
tar_info = self.clean_tarinfo(tar_info)
tar.addfile(tar_info, BytesIO(setup))

pkg_info = self.build_pkg_info()

tar_info = tarfile.TarInfo(pjoin(tar_dir, "PKG-INFO"))
tar_info.size = len(pkg_info)
tar_info.mtime = time.time()
tar_info.mtime = 0
tar_info = self.clean_tarinfo(tar_info)
tar.addfile(tar_info, BytesIO(pkg_info))
finally:
tar.close()
Expand Down
26 changes: 26 additions & 0 deletions tests/masonry/builders/test_sdist.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ast
import gzip
import hashlib
import shutil
import tarfile

Expand Down Expand Up @@ -250,6 +251,24 @@ def test_package():
assert "my-package-1.2.3/LICENSE" in tar.getnames()


def test_sdist_reproducibility():
poetry = Factory().create_poetry(project("complete"))

hashes = set()

for _ in range(2):
builder = SdistBuilder(poetry)
builder.build()

sdist = fixtures_dir / "complete" / "dist" / "my-package-1.2.3.tar.gz"

assert sdist.exists()

hashes.add(hashlib.sha256(sdist.read_bytes()).hexdigest())

assert len(hashes) == 1


def test_setup_py_context():
poetry = Factory().create_poetry(project("complete"))

Expand Down Expand Up @@ -438,6 +457,13 @@ def test_default_with_excluded_data(mocker):
assert "my-package-1.2.3/PKG-INFO" in names
# all last modified times should be set to a valid timestamp
for tarinfo in tar.getmembers():
if tarinfo.name in [
"my-package-1.2.3/setup.py",
"my-package-1.2.3/PKG-INFO",
]:
# generated files have timestamp set to 0
assert 0 == tarinfo.mtime
continue
assert 0 < tarinfo.mtime


Expand Down

0 comments on commit f478ec3

Please sign in to comment.