diff --git a/.github/workflows/find-vulnerabilities.yml b/.github/workflows/find-vulnerabilities.yml index 48b46989..0f22c117 100644 --- a/.github/workflows/find-vulnerabilities.yml +++ b/.github/workflows/find-vulnerabilities.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 with: path: scancode-inputs - sparse-checkout: setup.cfg + sparse-checkout: pyproject.toml sparse-checkout-cone-mode: false - uses: aboutcode-org/scancode-action@main diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ac0c6c6a..a636e840 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,9 @@ Release notes https://github.com/aboutcode-org/dejacode/pull/315 https://github.com/aboutcode-org/dejacode/pull/312 +- Replace the setup.py/setup.cfg by pyproject.toml file. + https://github.com/aboutcode-org/dejacode/pull/329 + - Replace the hardcoded ``/var/www/html`` by a ``webroot`` named volume in ``docker-compose.yml``. In the Docker compose ``nginx`` service, the hardcoded ``/var/www/html`` was declared diff --git a/Dockerfile b/Dockerfile index deb6254d..28593553 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,7 +62,7 @@ RUN python -m venv $VENV_LOCATION ENV PATH=$VENV_LOCATION/bin:$PATH # Install the dependencies before the codebase COPY for proper Docker layer caching -COPY --chown=$APP_USER:$APP_USER setup.cfg setup.py $APP_DIR/ +COPY --chown=$APP_USER:$APP_USER pyproject.toml $APP_DIR/ COPY --chown=$APP_USER:$APP_USER ./thirdparty/dist/ $APP_DIR/thirdparty/dist/ RUN pip install --find-links=$APP_DIR/thirdparty/dist/ --no-index --no-cache-dir . diff --git a/RELEASE.md b/RELEASE.md index 32d53c83..140b1850 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -4,7 +4,7 @@ - Create a new `release-x.x.x` branch - Update the version in: - - `setup.cfg` + - `pyproject.toml` - `dejacode/__init__.py` - `CHANGELOG.rst` (set date) - Commit and push this branch diff --git a/dejacode_toolkit/download.py b/dejacode_toolkit/download.py index 8aaf47ef..4f9ad251 100644 --- a/dejacode_toolkit/download.py +++ b/dejacode_toolkit/download.py @@ -6,7 +6,6 @@ # See https://aboutcode.org for more information about AboutCode FOSS projects. # -import socket from pathlib import Path from urllib.parse import unquote from urllib.parse import urlparse @@ -31,7 +30,7 @@ class DataCollectionException(Exception): def collect_package_data(url): try: response = requests.get(url, timeout=5, stream=True) - except (requests.RequestException, socket.timeout) as e: + except (TimeoutError, requests.RequestException) as e: raise DataCollectionException(e) if response.status_code != 200: diff --git a/dje/outputs.py b/dje/outputs.py index 77da4360..2b07bc24 100644 --- a/dje/outputs.py +++ b/dje/outputs.py @@ -8,8 +8,8 @@ import json import re +from datetime import UTC from datetime import datetime -from datetime import timezone from django.http import FileResponse from django.http import Http404 @@ -223,7 +223,7 @@ def get_cyclonedx_filename(instance, extension="cdx"): def get_csaf_document(product): """Return a csaf.Document object using the provided `product` context.""" - now = datetime.now(timezone.utc).isoformat(timespec="seconds") + now = datetime.now(UTC).isoformat(timespec="seconds") publisher = csaf.Publisher( category="vendor", name=product.dataspace.name, diff --git a/dje/tests/test_outputs.py b/dje/tests/test_outputs.py index d6e1ea4c..0ef5fd24 100644 --- a/dje/tests/test_outputs.py +++ b/dje/tests/test_outputs.py @@ -7,8 +7,8 @@ # import json +from datetime import UTC from datetime import datetime -from datetime import timezone from pathlib import Path from unittest import mock @@ -187,7 +187,7 @@ def test_outputs_get_csaf_security_advisory(self): detail="Need an update", ) - mock_now = datetime(2024, 12, 19, 12, 0, 0, tzinfo=timezone.utc) + mock_now = datetime(2024, 12, 19, 12, 0, 0, tzinfo=UTC) with mock.patch("dje.outputs.datetime") as mock_datetime: mock_datetime.now.return_value = mock_now mock_datetime.side_effect = lambda *args, **kwargs: datetime(*args, **kwargs) diff --git a/license_library/tests/test_admin.py b/license_library/tests/test_admin.py index 28d5cad5..ef9b14b0 100644 --- a/license_library/tests/test_admin.py +++ b/license_library/tests/test_admin.py @@ -1350,7 +1350,7 @@ def test_short_and_long_descriptions(self): def test_history_list_filter(self): with patch("dje.filters.timezone.now") as mock_timezone: fake_now = datetime.datetime(year=2012, month=8, day=1) - fake_now = fake_now.astimezone(datetime.timezone.utc) + fake_now = fake_now.astimezone(datetime.UTC) # patch timezone.now() so that it Return a consistent date mock_timezone.return_value = fake_now diff --git a/organization/tests/test_filters.py b/organization/tests/test_filters.py index 5eefb4d0..0be40c2e 100644 --- a/organization/tests/test_filters.py +++ b/organization/tests/test_filters.py @@ -64,7 +64,7 @@ def setUp(self): # time *anytime* the model is saved, so to set a custom value for # action_time we use QuerySet.update() fake_now = datetime.datetime(year=2012, month=8, day=1) - self.fake_now = fake_now.astimezone(datetime.timezone.utc) + self.fake_now = fake_now.astimezone(datetime.UTC) # create LogEntry objects for the owners diff --git a/pyproject.toml b/pyproject.toml index 966a8ea9..221670e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,200 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "dejacode" +version = "5.3.1-dev" +description = "Automate open source license compliance and ensure supply chain integrity" +readme = "README.rst" +requires-python = ">=3.13,<3.14" +license = "AGPL-3.0-only" +license-files = ["LICENSE", "NOTICE"] +authors = [ + { name = "nexB Inc.", email = "info@aboutcode.org" } +] +keywords = [ + "open source", "scan", "license", "package", "dependency", + "copyright", "filetype", "author", "extract", "licensing", + "scancode", "scanpipe", "docker", "rootfs", "vm", + "virtual machine", "pipeline", "code analysis", "container" +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: Legal Industry", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.13", + "Topic :: Utilities" +] +dependencies = [ + # Base configuration tools + "setuptools==80.9.0", + "wheel==0.45.1", + "pip==25.1.1", + # Django + "Django==5.2.3", + "asgiref==3.8.1", + "typing_extensions==4.14.0", + "sqlparse==0.5.3", + # Django apps + "django-crispy-forms==2.4", + "crispy_bootstrap5==2025.6", + "django-grappelli==4.0.2", + "django-filter==25.1", + "django-registration==3.4", + "confusable_homoglyphs==3.3.1", + "django-guardian==3.0.0", + "django-environ==0.12.0", + "django-debug-toolbar==5.2.0", + # CAPTCHA + "altcha==0.2.0", + "django_altcha==0.2.0", + # REST API + "djangorestframework==3.16.0", + # API documentation + "drf-yasg==1.21.10", + "uritemplate==4.1.1", + "inflection==0.5.1", + "pytz==2025.2", + # Track failed login attempts + "django-axes==8.0.0", + # Multi-factor authentication + "django-otp==1.6.0", + "qrcode==8.2", + "pypng==0.20220715.0", + # Database + "psycopg==3.2.9", + # Cache + "redis==6.2.0", + # redis dependencies: + "packaging==25.0", + "pyparsing==3.2.3", + "async-timeout==5.0.1", + "Deprecated==1.2.18", + "wrapt==1.17.2", + # Antivirus + "clamd==1.0.2", + # Testing + "model_bakery==1.10.1", + # Task queue + "rq==2.3.3", + "django-rq==3.0.1", + "fakeredis==2.29.0", + # Scheduler + "rq-scheduler==0.14.0", + "crontab==1.0.4", + "freezegun==1.5.2", + # Libs + "certifi==2025.4.26", + "urllib3==2.4.0", + "python-dateutil==2.9.0.post0", + "python-mimeparse==2.0.0", + "PyJWT==2.10.1", + "natsort==8.4.0", + "six==1.17.0", + "requests==2.32.4", + "idna==3.10", + "charset-normalizer==3.4.2", + "PyYAML==6.0.2", + "cython==3.1.1", + "zipp==3.22.0", + "XlsxWriter==3.2.3", + # Markdown + "markdown==3.8", + "bleach==6.2.0", + "bleach_allowlist==1.0.3", + "webencodings==0.5.1", + # Authentication + "oauthlib==3.2.2", + "python3-openid==3.2.0", + "requests-oauthlib==2.0.0", + "defusedxml==0.7.1", + # LDAP Auth + "python-ldap==3.4.4", + "pyasn1==0.6.1", + "pyasn1-modules==0.4.2", + "django-auth-ldap==5.2.0", + # LDAP Testing + "mockldap==0.3.0.post1", + "funcparserlib==0.3.6", + # license expressions + "boolean.py==5.0", + "license-expression==30.4.1", + # Webhooks + "django-rest-hooks==1.6.1", + # django-notifications + "django_notifications_patched==2.0.0", + "jsonfield==3.1.0", + "swapper==1.4.0", + # AboutCode Toolkit + "aboutcode_toolkit==11.1.1", + "click==8.2.1", + "Jinja2==3.1.6", + "MarkupSafe==3.0.2", + "saneyaml==0.6.1", + "openpyxl==3.1.5", + "et-xmlfile==2.0.0", + # PackageURL + "packageurl-python==0.17.1", + # Gunicorn + "gunicorn==23.0.0", + # SPDX validation + "jsonschema==4.24.0", + "jsonschema-specifications==2025.4.1", + "referencing==0.36.2", + "rpds-py==0.25.1", + "attrs==25.3.0", + "pyrsistent==0.20.0", + # CycloneDX + "cyclonedx-python-lib==10.2.0", + "sortedcontainers==2.4.0", + "toml==0.10.2", + "py-serializable==2.0.0", + # Git + "GitPython==3.1.44", + "gitdb==4.0.12", + "smmap==5.0.2", + # CSAF + "pydantic==2.11.5", + "pydantic-core==2.33.2", + "typing-inspection==0.4.1", + "maturin==1.8.6", + "setuptools-rust==1.11.1", + "annotated-types==0.7.0", + "semantic-version==2.10.0" +] + +[project.optional-dependencies] +dev = [ + # Linter and Validation + "ruff==0.11.12", + # Documentation + "doc8==1.1.2", + "stevedore==5.4.1", + "Pygments==2.19.1", + "docutils==0.21.2", + "restructuredtext-lint==1.4.0", + "pbr==6.1.1", + # Parallel testing + "tblib==3.1.0" +] + +[project.urls] +Homepage = "https://github.com/aboutcode-org/dejacode" +Documentation = "https://dejacode.readthedocs.io/" +Repository = "https://github.com/aboutcode-org/dejacode.git" +Issues = "https://github.com/aboutcode-org/dejacode/issues" +Changelog = "https://github.com/aboutcode-org/dejacode/blob/main/CHANGELOG.rst" + +[project.scripts] +dejacode = "dejacode:command_line" + +[tool.setuptools.packages.find] +where = ["."] + [tool.ruff] line-length = 100 exclude = [ @@ -25,7 +222,7 @@ select = [ "I", # isort "C9", # McCabe complexity ] -ignore = ["UP032", "D1", "D203", "D205", "D212", "D400", "D415", "S308"] +ignore = ["UP032", "UP038", "D1", "D203", "D205", "D212", "D400", "D415", "S308"] [tool.ruff.lint.isort] force-single-line = true @@ -40,10 +237,11 @@ section-order = [ ] [tool.ruff.lint.mccabe] -max-complexity = 17 +max-complexity = 16 [tool.ruff.lint.per-file-ignores] # Do not run bandit on test files. "**/tests/*" = ["S"] "dejacode_toolkit/csaf/*" = ["D", "UP", "E501", "F401"] "dejacode_toolkit/spdx.py" = ["UP"] +"component_catalog/models.py" = ["C901"] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index ed6b5ade..00000000 --- a/setup.cfg +++ /dev/null @@ -1,201 +0,0 @@ -[metadata] -name = dejacode -version = 5.3.1-dev -license = AGPL-3.0-only -description = Automate open source license compliance and ensure supply chain integrity -long_description = file:README.rst -author = nexB Inc. -author_email = info@aboutcode.org -url = https://github.com/aboutcode-org/dejacode -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Developers - Intended Audience :: Information Technology - Intended Audience :: Legal Industry - Programming Language :: Python - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.13 - Topic :: Utilities -keywords = - open source - scan - license - package - dependency - copyright - filetype - author - extract - licensing - scancode - scanpipe - docker - rootfs - vm - virtual machine - pipeline - code analysis - container -license_files = - LICENSE - NOTICE - -[options] -python_requires = >=3.13, <3.14 -packages=find: -include_package_data = true -zip_safe = false -install_requires = - # Base configuration tools - setuptools==80.9.0 - wheel==0.45.1 - pip==25.1.1 - # Django - Django==5.2.3 - asgiref==3.8.1 - typing_extensions==4.14.0 - sqlparse==0.5.3 - # Django apps - django-crispy-forms==2.4 - crispy_bootstrap5==2025.6 - django-grappelli==4.0.2 - django-filter==25.1 - django-registration==3.4 - confusable_homoglyphs==3.3.1 - django-guardian==3.0.0 - django-environ==0.12.0 - django-debug-toolbar==5.2.0 - # CAPTCHA - altcha==0.2.0 - django_altcha==0.2.0 - # REST API - djangorestframework==3.16.0 - # API documentation - drf-yasg==1.21.10 - uritemplate==4.1.1 - inflection==0.5.1 - pytz==2025.2 - # Track failed login attempts - django-axes==8.0.0 - # Multi-factor authentication - django-otp==1.6.0 - qrcode==8.2 - pypng==0.20220715.0 - # Database - psycopg==3.2.9 - # Cache - redis==6.2.0 - # redis dependencies: - packaging==25.0 - pyparsing==3.2.3 - async-timeout==5.0.1 - Deprecated==1.2.18 - wrapt==1.17.2 - # Antivirus - clamd==1.0.2 - # Testing - model_bakery==1.10.1 - # Task queue - rq==2.3.3 - django-rq==3.0.1 - fakeredis==2.29.0 - # Scheduler - rq-scheduler==0.14.0 - crontab==1.0.4 - freezegun==1.5.2 - # Libs - certifi==2025.4.26 - urllib3==2.4.0 - python-dateutil==2.9.0.post0 - python-mimeparse==2.0.0 - PyJWT==2.10.1 - natsort==8.4.0 - six==1.17.0 - requests==2.32.4 - idna==3.10 - charset-normalizer==3.4.2 - PyYAML==6.0.2 - cython==3.1.1 - zipp==3.22.0 - XlsxWriter==3.2.3 - # Markdown - markdown==3.8 - bleach==6.2.0 - bleach_allowlist==1.0.3 - webencodings==0.5.1 - # Authentication - oauthlib==3.2.2 - python3-openid==3.2.0 - requests-oauthlib==2.0.0 - defusedxml==0.7.1 - # LDAP Auth - python-ldap==3.4.4 - pyasn1==0.6.1 - pyasn1-modules==0.4.2 - django-auth-ldap==5.2.0 - # LDAP Testing - mockldap==0.3.0.post1 - funcparserlib==0.3.6 - # license expressions - boolean.py==5.0 - license-expression==30.4.1 - # Webhooks - django-rest-hooks==1.6.1 - # django-notifications - django_notifications_patched==2.0.0 - jsonfield==3.1.0 - swapper==1.4.0 - # AboutCode Toolkit - aboutcode_toolkit==11.1.1 - click==8.2.1 - Jinja2==3.1.6 - MarkupSafe==3.0.2 - saneyaml==0.6.1 - openpyxl==3.1.5 - et-xmlfile==2.0.0 - # PackageURL - packageurl-python==0.17.1 - # Gunicorn - gunicorn==23.0.0 - # SPDX validation - jsonschema==4.24.0 - jsonschema-specifications==2025.4.1 - referencing==0.36.2 - rpds-py==0.25.1 - attrs==25.3.0 - pyrsistent==0.20.0 - # CycloneDX - cyclonedx-python-lib==10.2.0 - sortedcontainers==2.4.0 - toml==0.10.2 - py-serializable==2.0.0 - # Git - GitPython==3.1.44 - gitdb==4.0.12 - smmap==5.0.2 - # CSAF - pydantic==2.11.5 - pydantic-core==2.33.2 - typing-inspection==0.4.1 - maturin==1.8.6 - setuptools-rust==1.11.1 - annotated-types==0.7.0 - semantic-version==2.10.0 - -[options.extras_require] -dev = - # Linter and Validation - ruff==0.11.12 - # Documentation - doc8==1.1.2 - stevedore==5.4.1 - Pygments==2.19.1 - docutils==0.21.2 - restructuredtext-lint==1.4.0 - pbr==6.1.1 - # Parallel testing - tblib==3.1.0 - -[options.entry_points] -console_scripts = - dejacode = dejacode:command_line diff --git a/setup.py b/setup.py deleted file mode 100755 index 914880b8..00000000 --- a/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python - -# -# Copyright (c) nexB Inc. and others. All rights reserved. -# DejaCode is a trademark of nexB Inc. -# SPDX-License-Identifier: AGPL-3.0-only -# See https://github.com/aboutcode-org/dejacode for support or download. -# See https://aboutcode.org for more information about AboutCode FOSS projects. -# - -import setuptools - -if __name__ == "__main__": - setuptools.setup()