From 2c6ca44a0b2ccebcdbb78200747704b260bfe037 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 13 Apr 2026 19:54:16 +0100 Subject: [PATCH 1/5] Generate an SBOM --- templates/sbom.cdx.json.in | 38 ++++++++++++++++++++++++++++++++++++++ update.py | 9 +++++++++ 2 files changed, 47 insertions(+) create mode 100644 templates/sbom.cdx.json.in diff --git a/templates/sbom.cdx.json.in b/templates/sbom.cdx.json.in new file mode 100644 index 0000000..1648d8b --- /dev/null +++ b/templates/sbom.cdx.json.in @@ -0,0 +1,38 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "version": 1, + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "metadata": { + "component": { + "bom-ref": "pkg:pypi/tzdata@%%PACKAGE_VERSION%%", + "name": "tzdata", + "version": "%%PACKAGE_VERSION%%", + "purl": "pkg:pypi/tzdata@%%PACKAGE_VERSION%%", + "type": "library", + "components": [ + { + "bom-ref": "https://www.iana.org/time-zones", + "name": "tz", + "version": "%%IANA_VERSION%%", + "type": "data", + "data": [ + { + "type": "dataset", + "name": "IANA Time Zone Database", + "description": "zic-compiled TZif timezone files" + } + ], + "licenses": [ + { + "license": { + "name": "tz database license", + "url": "https://data.iana.org/time-zones/tz-link.html" + } + } + ] + } + ] + } + } +} diff --git a/update.py b/update.py index 5090cdc..c90fbce 100644 --- a/update.py +++ b/update.py @@ -196,6 +196,15 @@ def create_package(version: str, zonenames: Sequence[str], zoneinfo_dir: pathlib with open(target_dir / "__init__.py", "w") as f_out: f_out.write(contents) + # Generate the SBOM from a template. + with open(TEMPLATES_DIR / "sbom.cdx.json.in", "r") as f_in: + contents = f_in.read() + contents = contents.replace("%%PACKAGE_VERSION%%", package_version) + contents = contents.replace("%%IANA_VERSION%%", version) + + with open(REPO_ROOT / "sbom.cdx.json", "w") as f_out: + f_out.write(contents) + with open(REPO_ROOT / "VERSION", "w") as f: f.write(package_version) From 191791a6d17ab965c0d0a760845e84e91d75230f Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 16 Apr 2026 18:12:49 +0100 Subject: [PATCH 2/5] Use an override in `setup.py` instead --- setup.py | 28 ++++++++++++++++++++++++++++ update.py | 9 --------- 2 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 setup.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c1ed4d7 --- /dev/null +++ b/setup.py @@ -0,0 +1,28 @@ +import re +from pathlib import Path + +from setuptools import setup +from wheel.bdist_wheel import bdist_wheel as _bdist_wheel + +ROOT = Path(__file__).parent + + +def generate_sbom(): + version = (ROOT / "VERSION").read_text().strip() + init_text = (ROOT / "src" / "tzdata" / "__init__.py").read_text() + iana_version = re.search(r'IANA_VERSION\s*=\s*"([^"]+)"', init_text).group(1) + template = (ROOT / "templates" / "sbom.cdx.json.in").read_text() + return template.replace("%%PACKAGE_VERSION%%", version).replace( + "%%IANA_VERSION%%", iana_version + ) + + +class bdist_wheel(_bdist_wheel): + def write_wheelfile(self, wheelfile_base, *args, **kwargs): + super().write_wheelfile(wheelfile_base, *args, **kwargs) + (Path(wheelfile_base) / "sboms").mkdir(exist_ok=True) + (Path(wheelfile_base) / "sboms" / "sbom.cdx.json").write_text(generate_sbom()) + + +cmdclass = {"bdist_wheel": bdist_wheel} +setup(cmdclass=cmdclass) diff --git a/update.py b/update.py index c90fbce..5090cdc 100644 --- a/update.py +++ b/update.py @@ -196,15 +196,6 @@ def create_package(version: str, zonenames: Sequence[str], zoneinfo_dir: pathlib with open(target_dir / "__init__.py", "w") as f_out: f_out.write(contents) - # Generate the SBOM from a template. - with open(TEMPLATES_DIR / "sbom.cdx.json.in", "r") as f_in: - contents = f_in.read() - contents = contents.replace("%%PACKAGE_VERSION%%", package_version) - contents = contents.replace("%%IANA_VERSION%%", version) - - with open(REPO_ROOT / "sbom.cdx.json", "w") as f_out: - f_out.write(contents) - with open(REPO_ROOT / "VERSION", "w") as f: f.write(package_version) From 6fa1e33ec6f0b4840f566d1f57be906abdcc91b8 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 16 Apr 2026 18:19:40 +0100 Subject: [PATCH 3/5] Use `os` instead :-( --- setup.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index c1ed4d7..611c2f9 100644 --- a/setup.py +++ b/setup.py @@ -1,17 +1,20 @@ +import os import re -from pathlib import Path from setuptools import setup from wheel.bdist_wheel import bdist_wheel as _bdist_wheel -ROOT = Path(__file__).parent +ROOT = os.path.dirname(os.path.abspath(__file__)) def generate_sbom(): - version = (ROOT / "VERSION").read_text().strip() - init_text = (ROOT / "src" / "tzdata" / "__init__.py").read_text() + with open(os.path.join(ROOT, "VERSION")) as f: + version = f.read().strip() + with open(os.path.join(ROOT, "src", "tzdata", "__init__.py")) as f: + init_text = f.read() iana_version = re.search(r'IANA_VERSION\s*=\s*"([^"]+)"', init_text).group(1) - template = (ROOT / "templates" / "sbom.cdx.json.in").read_text() + with open(os.path.join(ROOT, "templates", "sbom.cdx.json.in")) as f: + template = f.read() return template.replace("%%PACKAGE_VERSION%%", version).replace( "%%IANA_VERSION%%", iana_version ) @@ -19,9 +22,12 @@ def generate_sbom(): class bdist_wheel(_bdist_wheel): def write_wheelfile(self, wheelfile_base, *args, **kwargs): - super().write_wheelfile(wheelfile_base, *args, **kwargs) - (Path(wheelfile_base) / "sboms").mkdir(exist_ok=True) - (Path(wheelfile_base) / "sboms" / "sbom.cdx.json").write_text(generate_sbom()) + super(bdist_wheel, self).write_wheelfile(wheelfile_base, *args, **kwargs) + sboms_dir = os.path.join(wheelfile_base, "sboms") + if not os.path.isdir(sboms_dir): + os.makedirs(sboms_dir) + with open(os.path.join(sboms_dir, "sbom.cdx.json"), "w") as f: + f.write(generate_sbom()) cmdclass = {"bdist_wheel": bdist_wheel} From 0900609e0be499f3baa8439226b64ee24a51c273 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 16 Apr 2026 18:22:53 +0100 Subject: [PATCH 4/5] Can't have anything, there goes my `super()`... --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 611c2f9..10c14e3 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def generate_sbom(): class bdist_wheel(_bdist_wheel): def write_wheelfile(self, wheelfile_base, *args, **kwargs): - super(bdist_wheel, self).write_wheelfile(wheelfile_base, *args, **kwargs) + _bdist_wheel.write_wheelfile(self, wheelfile_base, *args, **kwargs) sboms_dir = os.path.join(wheelfile_base, "sboms") if not os.path.isdir(sboms_dir): os.makedirs(sboms_dir) From c6f72d779dc3355995cf04a097d5b54d8a111a2b Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 25 Apr 2026 19:12:55 +0100 Subject: [PATCH 5/5] Inject it instead --- .github/workflows/tests.yml | 31 +++++++++++++++++++++++++++--- sbom.cdx.json | 38 +++++++++++++++++++++++++++++++++++++ setup.py | 34 --------------------------------- tox.ini | 3 +++ update.py | 9 +++++++++ 5 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 sbom.cdx.json delete mode 100644 setup.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20eb70c..0b73e82 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,8 +10,28 @@ on: permissions: {} jobs: - tests: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.14" + - name: Install tox + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox + - name: Build wheel and sdist + run: tox -e build + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: dist + path: dist/ + tests: + needs: build runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -38,19 +58,24 @@ jobs: with: python-version: ${{ matrix.python-version }} allow-prereleases: true + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: dist + path: dist/ - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade tox - name: Run tests + shell: bash run: | - tox + tox --installpkg dist/tzdata-*.whl other: runs-on: "ubuntu-latest" strategy: matrix: - toxenv: ["build", "precommit", "typing", "docs"] + toxenv: ["precommit", "typing", "docs"] env: TOXENV: ${{ matrix.toxenv }} diff --git a/sbom.cdx.json b/sbom.cdx.json new file mode 100644 index 0000000..7591f86 --- /dev/null +++ b/sbom.cdx.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "version": 1, + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "metadata": { + "component": { + "bom-ref": "pkg:pypi/tzdata@2026.1", + "name": "tzdata", + "version": "2026.1", + "purl": "pkg:pypi/tzdata@2026.1", + "type": "library", + "components": [ + { + "bom-ref": "https://www.iana.org/time-zones", + "name": "tz", + "version": "2026a", + "type": "data", + "data": [ + { + "type": "dataset", + "name": "IANA Time Zone Database", + "description": "zic-compiled TZif timezone files" + } + ], + "licenses": [ + { + "license": { + "name": "tz database license", + "url": "https://data.iana.org/time-zones/tz-link.html" + } + } + ] + } + ] + } + } +} diff --git a/setup.py b/setup.py deleted file mode 100644 index 10c14e3..0000000 --- a/setup.py +++ /dev/null @@ -1,34 +0,0 @@ -import os -import re - -from setuptools import setup -from wheel.bdist_wheel import bdist_wheel as _bdist_wheel - -ROOT = os.path.dirname(os.path.abspath(__file__)) - - -def generate_sbom(): - with open(os.path.join(ROOT, "VERSION")) as f: - version = f.read().strip() - with open(os.path.join(ROOT, "src", "tzdata", "__init__.py")) as f: - init_text = f.read() - iana_version = re.search(r'IANA_VERSION\s*=\s*"([^"]+)"', init_text).group(1) - with open(os.path.join(ROOT, "templates", "sbom.cdx.json.in")) as f: - template = f.read() - return template.replace("%%PACKAGE_VERSION%%", version).replace( - "%%IANA_VERSION%%", iana_version - ) - - -class bdist_wheel(_bdist_wheel): - def write_wheelfile(self, wheelfile_base, *args, **kwargs): - _bdist_wheel.write_wheelfile(self, wheelfile_base, *args, **kwargs) - sboms_dir = os.path.join(wheelfile_base, "sboms") - if not os.path.isdir(sboms_dir): - os.makedirs(sboms_dir) - with open(os.path.join(sboms_dir, "sbom.cdx.json"), "w") as f: - f.write(generate_sbom()) - - -cmdclass = {"bdist_wheel": bdist_wheel} -setup(cmdclass=cmdclass) diff --git a/tox.ini b/tox.ini index 83e7708..1a9d172 100644 --- a/tox.ini +++ b/tox.ini @@ -81,4 +81,7 @@ commands = python -c "from pathlib import Path; \ [x.unlink(missing_ok=True) for x in Path('{toxinidir}/dist').glob('*')]" python -m build -o {toxinidir}/dist {toxinidir} + python -c "import re, zipfile; from pathlib import Path; \ + d = Path('{toxinidir}/sbom.cdx.json').read_bytes(); \ + [((z := zipfile.ZipFile(w, 'a')).writestr(re.match(r'(.+?-.+?)-', w.name)[1] + '.dist-info/sboms/sbom.cdx.json', d), z.close()) for w in Path('{toxinidir}/dist').glob('*.whl')]" twine check {toxinidir}/dist/* diff --git a/update.py b/update.py index d11773a..da361f8 100644 --- a/update.py +++ b/update.py @@ -196,6 +196,15 @@ def create_package(version: str, zonenames: Sequence[str], zoneinfo_dir: pathlib with open(target_dir / "__init__.py", "w") as f_out: f_out.write(contents) + # Generate the SBOM from a template. + with open(TEMPLATES_DIR / "sbom.cdx.json.in", "r") as f_in: + contents = f_in.read() + contents = contents.replace("%%PACKAGE_VERSION%%", package_version) + contents = contents.replace("%%IANA_VERSION%%", version) + + with open(REPO_ROOT / "sbom.cdx.json", "w") as f_out: + f_out.write(contents) + with open(REPO_ROOT / "VERSION", "w") as f: f.write(package_version)