Compare commits

...

No commits in common. 'c9' and 'cs10' have entirely different histories.
c9 ... cs10

2
.gitignore vendored

@ -1 +1 @@
SOURCES/setuptools-53.0.0.tar.gz SOURCES/setuptools-69.0.3.tar.gz

@ -1 +1 @@
878b8c351cac940e0b9fd1ba3ad49665dfc2889c SOURCES/setuptools-53.0.0.tar.gz 4b11f92850b237a9bd3d563160c8a927cbcdcf01 SOURCES/setuptools-69.0.3.tar.gz

@ -1,170 +0,0 @@
From 21b122e06969a9d85c65ce8276519d34da7dc747 Mon Sep 17 00:00:00 2001
From: Melissa Li <li.melissa.kun@gmail.com>
Date: Tue, 23 Feb 2021 21:23:35 -0500
Subject: [PATCH 1/6] Preserve case-sensitive keys in setup.cfg
---
setuptools/dist.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 050388de16..c31020f0c4 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -583,6 +583,7 @@ def _parse_config_files(self, filenames=None): # noqa: C901
self.announce("Distribution.parse_config_files():")
parser = ConfigParser()
+ parser.optionxform = str
for filename in filenames:
with io.open(filename, encoding='utf-8') as reader:
if DEBUG:
From 90d8740c353ddf20c1c76d8c06cd923c19b8cc84 Mon Sep 17 00:00:00 2001
From: Melissa Li <li.melissa.kun@gmail.com>
Date: Tue, 23 Feb 2021 21:06:55 -0500
Subject: [PATCH 2/6] Add case-sensitive entry point name test
---
setuptools/tests/test_config.py | 34 +++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py
index 1dee12718f..6cc1d0a46b 100644
--- a/setuptools/tests/test_config.py
+++ b/setuptools/tests/test_config.py
@@ -802,6 +802,40 @@ def test_entry_points(self, tmpdir):
with get_dist(tmpdir) as dist:
assert dist.entry_points == expected
+ def test_case_sensitive_entry_points(self, tmpdir):
+ _, config = fake_env(
+ tmpdir,
+ '[options.entry_points]\n'
+ 'GROUP1 = point1 = pack.module:func, '
+ '.point2 = pack.module2:func_rest [rest]\n'
+ 'group2 = point3 = pack.module:func2\n'
+ )
+
+ with get_dist(tmpdir) as dist:
+ assert dist.entry_points == {
+ 'GROUP1': [
+ 'point1 = pack.module:func',
+ '.point2 = pack.module2:func_rest [rest]',
+ ],
+ 'group2': ['point3 = pack.module:func2']
+ }
+
+ expected = (
+ '[blogtool.parsers]\n'
+ '.rst = some.nested.module:SomeClass.some_classmethod[reST]\n'
+ )
+
+ tmpdir.join('entry_points').write(expected)
+
+ # From file.
+ config.write(
+ '[options]\n'
+ 'entry_points = file: entry_points\n'
+ )
+
+ with get_dist(tmpdir) as dist:
+ assert dist.entry_points == expected
+
def test_data_files(self, tmpdir):
fake_env(
tmpdir,
From 39659040bda0664ee08588ecd2faa41b4ea406a1 Mon Sep 17 00:00:00 2001
From: Melissa Li <li.melissa.kun@gmail.com>
Date: Wed, 24 Feb 2021 00:31:16 -0500
Subject: [PATCH 3/6] Add change note
---
changelog.d/1937.breaking.rst | 1 +
1 file changed, 1 insertion(+)
create mode 100644 changelog.d/1937.breaking.rst
diff --git a/changelog.d/1937.breaking.rst b/changelog.d/1937.breaking.rst
new file mode 100644
index 0000000000..94dc739ab6
--- /dev/null
+++ b/changelog.d/1937.breaking.rst
@@ -0,0 +1 @@
+Preserved case-sensitivity of keys in setup.cfg so that entry point names are case-sensitive. Changed sensitivity of configparser -- by :user:`melissa-kun-li`
\ No newline at end of file
From 7f3e6d688e5ff080ee6bd7ccc6bd81a87c05cfd7 Mon Sep 17 00:00:00 2001
From: Melissa Li <li.melissa.kun@gmail.com>
Date: Wed, 24 Feb 2021 23:57:59 -0500
Subject: [PATCH 4/6] Update test for case-sensitive entry point names
---
setuptools/tests/test_config.py | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py
index 6cc1d0a46b..649075609a 100644
--- a/setuptools/tests/test_config.py
+++ b/setuptools/tests/test_config.py
@@ -820,22 +820,6 @@ def test_case_sensitive_entry_points(self, tmpdir):
'group2': ['point3 = pack.module:func2']
}
- expected = (
- '[blogtool.parsers]\n'
- '.rst = some.nested.module:SomeClass.some_classmethod[reST]\n'
- )
-
- tmpdir.join('entry_points').write(expected)
-
- # From file.
- config.write(
- '[options]\n'
- 'entry_points = file: entry_points\n'
- )
-
- with get_dist(tmpdir) as dist:
- assert dist.entry_points == expected
-
def test_data_files(self, tmpdir):
fake_env(
tmpdir,
From 11529db0de4081404b37fab17711660faa85abb8 Mon Sep 17 00:00:00 2001
From: Melissa Li <li.melissa.kun@gmail.com>
Date: Thu, 25 Feb 2021 00:00:23 -0500
Subject: [PATCH 5/6] Update change log
---
changelog.d/1937.change.rst | 1 +
1 file changed, 1 insertion(+)
create mode 100644 changelog.d/1937.change.rst
diff --git a/changelog.d/1937.change.rst b/changelog.d/1937.change.rst
new file mode 100644
index 0000000000..acd4305968
--- /dev/null
+++ b/changelog.d/1937.change.rst
@@ -0,0 +1 @@
+Preserved case-sensitivity of keys in setup.cfg so that entry point names are case-sensitive. Changed sensitivity of configparser. NOTE: Any projects relying on case-insensitivity will need to adapt to accept the original case as published. -- by :user:`melissa-kun-li`
\ No newline at end of file
From 898a0b59427f143efe0bcc0cabf69007fb3ee439 Mon Sep 17 00:00:00 2001
From: "Jason R. Coombs" <jaraco@jaraco.com>
Date: Thu, 25 Feb 2021 08:57:04 -0500
Subject: [PATCH 6/6] Remove 'breaking' changelog, superseded by 'change'.
---
changelog.d/1937.breaking.rst | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 changelog.d/1937.breaking.rst
diff --git a/changelog.d/1937.breaking.rst b/changelog.d/1937.breaking.rst
deleted file mode 100644
index 94dc739ab6..0000000000
--- a/changelog.d/1937.breaking.rst
+++ /dev/null
@@ -1 +0,0 @@
-Preserved case-sensitivity of keys in setup.cfg so that entry point names are case-sensitive. Changed sensitivity of configparser -- by :user:`melissa-kun-li`
\ No newline at end of file

@ -0,0 +1,41 @@
From 58f33f0aef5b137287e6f425b922a03123735a77 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Wed, 20 Sep 2023 17:18:47 +0200
Subject: [PATCH] Adjust the setup.py install deprecation message and URL
But only when building RPM packages.
---
setuptools/command/install.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
index 606cce9..0af1631 100644
--- a/setuptools/command/install.py
+++ b/setuptools/command/install.py
@@ -1,6 +1,7 @@
from distutils.errors import DistutilsArgError
import inspect
import glob
+import os
import platform
import distutils.command.install as orig
@@ -40,8 +41,13 @@ class install(orig.install):
Please avoid running ``setup.py`` directly.
Instead, use pypa/build, pypa/installer or other
standards-based tools.
- """,
- see_url="https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html",
+ """
+ + ("""
+ Follow the current Python packaging guidelines when building
+ Python RPM packages.
+ """ if "RPM_BUILD_ROOT" in os.environ else ""),
+ see_url=("https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html" +
+ ("\nand https://docs.fedoraproject.org/en-US/packaging-guidelines/Python/" if "RPM_BUILD_ROOT" in os.environ else "")),
# TODO: Document how to bootstrap setuptools without install
# (e.g. by unziping the wheel file)
# and then add a due_date to this warning.
--
2.41.0

@ -1,28 +0,0 @@
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index 123e958..a90b810 100644
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -215,7 +215,7 @@ def unique_values(func):
return wrapper
-REL = re.compile(r"""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
+REL = re.compile(r"""<([^>]*\srel\s{0,10}=\s{0,10}['"]?([^'" >]+)[^>]*)>""", re.I)
# this line is here to fix emacs' cruddy broken syntax highlighting
diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py
index 8e9435e..bc1e373 100644
--- a/setuptools/tests/test_packageindex.py
+++ b/setuptools/tests/test_packageindex.py
@@ -308,3 +308,10 @@ class TestPyPIConfig:
cred = cfg.creds_by_repository['https://pypi.org']
assert cred.username == 'jaraco'
assert cred.password == 'pity%'
+
+@pytest.mark.timeout(1)
+def test_REL_DoS():
+ """
+ REL should not hang on a contrived attack string.
+ """
+ setuptools.package_index.REL.search('< rel=' + ' ' * 2**12)

@ -1,51 +1,25 @@
From 39a1aa65fb4163d917131b4814d4c2dd2bf19677 Mon Sep 17 00:00:00 2001 From 472528deea4063f20c5d9525f0faf64ae0cd0a90 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com> From: Lumir Balhar <lbalhar@redhat.com>
Date: Wed, 24 Jul 2024 12:43:20 +0200 Date: Wed, 24 Jul 2024 14:26:09 +0200
Subject: [PATCH] CVE-2024-6345 Subject: [PATCH] CVE-2024-6345
--- ---
setuptools/package_index.py | 23 +++++++++------------- setuptools/package_index.py | 21 +++++----------------
setuptools/tests/test_packageindex.py | 28 +++++++++++++-------------- setuptools/tests/test_packageindex.py | 20 ++++++++++----------
2 files changed, 23 insertions(+), 28 deletions(-) 2 files changed, 15 insertions(+), 26 deletions(-)
diff --git a/setuptools/package_index.py b/setuptools/package_index.py diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index 123e9582b..07cc8924b 100644 index 7095585..1368bde 100644
--- a/setuptools/package_index.py --- a/setuptools/package_index.py
+++ b/setuptools/package_index.py +++ b/setuptools/package_index.py
@@ -1,4 +1,5 @@ @@ -1,5 +1,6 @@
"""PyPI and direct package downloading""" """PyPI and direct package downloading."""
+import subprocess +import subprocess
import sys import sys
import os import os
import re import re
@@ -860,7 +861,7 @@ class PackageIndex(Environment): @@ -881,17 +882,11 @@ class PackageIndex(Environment):
def _download_svn(self, url, filename):
warnings.warn("SVN download support is deprecated", UserWarning)
url = url.split('#', 1)[0] # remove any fragment for svn's sake
- creds = ''
+ creds = []
if url.lower().startswith('svn:') and '@' in url:
scheme, netloc, path, p, q, f = urllib.parse.urlparse(url)
if not netloc and path.startswith('//') and '/' in path[2:]:
@@ -869,14 +870,14 @@ class PackageIndex(Environment):
if auth:
if ':' in auth:
user, pw = auth.split(':', 1)
- creds = " --username=%s --password=%s" % (user, pw)
+ creds = [f"--username={user}", f"--password={pw}"]
else:
- creds = " --username=" + auth
+ creds = [f"--username={auth}"]
netloc = host
parts = scheme, netloc, url, p, q, f
url = urllib.parse.urlunparse(parts)
self.info("Doing subversion checkout from %s to %s", url, filename)
- os.system("svn checkout%s -q %s %s" % (creds, url, filename))
+ subprocess.check_call(["svn", "checkout"] + creds + ["-q", url, filename])
return filename
@staticmethod
@@ -902,14 +903,11 @@ class PackageIndex(Environment):
url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
self.info("Doing git clone from %s to %s", url, filename) self.info("Doing git clone from %s to %s", url, filename)
@ -54,15 +28,18 @@ index 123e9582b..07cc8924b 100644
if rev is not None: if rev is not None:
self.info("Checking out %s", rev) self.info("Checking out %s", rev)
- os.system("git -C %s checkout --quiet %s" % ( - os.system(
- filename, - "git -C %s checkout --quiet %s"
- rev, - % (
- )) - filename,
- rev,
- )
- )
+ subprocess.check_call(["git", "-C", filename, "checkout", "--quiet", rev]) + subprocess.check_call(["git", "-C", filename, "checkout", "--quiet", rev])
return filename return filename
@@ -918,14 +916,11 @@ class PackageIndex(Environment): @@ -900,17 +895,11 @@ class PackageIndex(Environment):
url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
self.info("Doing hg clone from %s to %s", url, filename) self.info("Doing hg clone from %s to %s", url, filename)
@ -71,19 +48,22 @@ index 123e9582b..07cc8924b 100644
if rev is not None: if rev is not None:
self.info("Updating to %s", rev) self.info("Updating to %s", rev)
- os.system("hg --cwd %s up -C -r %s -q" % ( - os.system(
- filename, - "hg --cwd %s up -C -r %s -q"
- rev, - % (
- )) - filename,
- rev,
- )
- )
+ subprocess.check_call(["hg", "--cwd", filename, "up", "-C", "-r", rev, "-q"]) + subprocess.check_call(["hg", "--cwd", filename, "up", "-C", "-r", rev, "-q"])
return filename return filename
diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py
index 8e9435efe..9289b9032 100644 index 0287063..c136e8d 100644
--- a/setuptools/tests/test_packageindex.py --- a/setuptools/tests/test_packageindex.py
+++ b/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py
@@ -197,56 +197,56 @@ class TestPackageIndex: @@ -190,37 +190,37 @@ class TestPackageIndex:
url = 'git+https://github.example/group/project@master#egg=foo' url = 'git+https://github.example/group/project@master#egg=foo'
index = setuptools.package_index.PackageIndex() index = setuptools.package_index.PackageIndex()
@ -96,8 +76,7 @@ index 8e9435efe..9289b9032 100644
expected_dir = str(tmpdir / 'project@master') expected_dir = str(tmpdir / 'project@master')
expected = ( expected = (
'git clone --quiet ' 'git clone --quiet ' 'https://github.example/group/project {expected_dir}'
'https://github.example/group/project {expected_dir}'
- ).format(**locals()) - ).format(**locals())
- first_call_args = os_system_mock.call_args_list[0][0] - first_call_args = os_system_mock.call_args_list[0][0]
+ ).format(**locals()).split() + ).format(**locals()).split()
@ -124,8 +103,7 @@ index 8e9435efe..9289b9032 100644
expected_dir = str(tmpdir / 'project') expected_dir = str(tmpdir / 'project')
expected = ( expected = (
'git clone --quiet ' 'git clone --quiet ' 'https://github.example/group/project {expected_dir}'
'https://github.example/group/project {expected_dir}'
- ).format(**locals()) - ).format(**locals())
- os_system_mock.assert_called_once_with(expected) - os_system_mock.assert_called_once_with(expected)
+ ).format(**locals()).split() + ).format(**locals()).split()
@ -133,27 +111,6 @@ index 8e9435efe..9289b9032 100644
def test_download_svn(self, tmpdir): def test_download_svn(self, tmpdir):
url = 'svn+https://svn.example/project#egg=foo' url = 'svn+https://svn.example/project#egg=foo'
index = setuptools.package_index.PackageIndex()
with pytest.warns(UserWarning):
- with mock.patch("os.system") as os_system_mock:
+ with mock.patch("subprocess.check_call") as subprocess_check_call_mock:
result = index.download(url, str(tmpdir))
- os_system_mock.assert_called()
+ subprocess_check_call_mock.assert_called()
expected_dir = str(tmpdir / 'project')
expected = (
'svn checkout -q '
'svn+https://svn.example/project {expected_dir}'
- ).format(**locals())
- os_system_mock.assert_called_once_with(expected)
+ ).format(**locals()).split()
+ subprocess_check_call_mock.assert_called_once_with(expected)
class TestContentCheckers:
-- --
2.45.2 2.45.2

@ -0,0 +1,48 @@
From 850efbc4dfe5b8c64dd21617bc856ba0d4e3a082 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Wed, 20 Sep 2023 19:47:05 +0200
Subject: [PATCH] Remove optional or unpackaged test deps
---
setup.cfg | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/setup.cfg b/setup.cfg
index c7aaf14..33b9cd7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -42,17 +42,7 @@ exclude =
[options.extras_require]
testing =
pytest >= 6
- pytest-checkdocs >= 2.4
- pytest-black >= 0.3.7; \
- python_implementation != "PyPy"
- pytest-cov; \
- python_implementation != "PyPy"
- pytest-mypy >= 0.9.1; \
- python_implementation != "PyPy"
- pytest-enabler >= 2.2
- pytest-ruff; sys_platform != "cygwin"
- flake8-2020
virtualenv>=13.0.0
wheel
pip>=19.1 # For proper file:// URLs support.
@@ -60,13 +50,9 @@ testing =
pytest-xdist
jaraco.path>=3.2.0
build[virtualenv]
- filelock>=3.4.0
ini2toml[lite]>=0.9
tomli-w>=1.0.0
pytest-timeout
- pytest-perf; \
- sys_platform != "cygwin"
- jaraco.develop >= 7.21; python_version >= "3.9" and sys_platform != "cygwin"
testing-integration =
pytest
pytest-xdist
--
2.41.0

@ -1,650 +0,0 @@
From 490a2b28fa2325f9929261aa2ee398fbb4c715dd Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sat, 3 Apr 2021 16:12:19 -0400
Subject: [PATCH 1/2] license_files - Add support for glob patterns + add
default patterns
https://github.com/pypa/setuptools/pull/2620
---
changelog.d/2620.breaking.rst | 4 ++
changelog.d/2620.change.rst | 1 +
changelog.d/2620.deprecation.rst | 2 +
changelog.d/2620.doc.rst | 1 +
docs/references/keywords.rst | 11 +++++
docs/userguide/declarative_config.rst | 2 +-
setuptools/command/sdist.py | 55 +++++++++++++---------
setuptools/tests/test_egg_info.py | 68 +++++++++++++++++++++++++--
setuptools/tests/test_manifest.py | 1 +
9 files changed, 118 insertions(+), 27 deletions(-)
create mode 100644 changelog.d/2620.breaking.rst
create mode 100644 changelog.d/2620.change.rst
create mode 100644 changelog.d/2620.deprecation.rst
create mode 100644 changelog.d/2620.doc.rst
diff --git a/changelog.d/2620.breaking.rst b/changelog.d/2620.breaking.rst
new file mode 100644
index 00000000..431e7105
--- /dev/null
+++ b/changelog.d/2620.breaking.rst
@@ -0,0 +1,4 @@
+If neither ``license_file`` nor ``license_files`` is specified, the ``sdist``
+option will now auto-include files that match the following patterns:
+``LICEN[CS]E*``, ``COPYING*``, ``NOTICE*``, ``AUTHORS*``.
+This matches the behavior of ``bdist_wheel``. -- by :user:`cdce8p`
diff --git a/changelog.d/2620.change.rst b/changelog.d/2620.change.rst
new file mode 100644
index 00000000..5470592d
--- /dev/null
+++ b/changelog.d/2620.change.rst
@@ -0,0 +1 @@
+The ``license_file`` and ``license_files`` options now support glob patterns. -- by :user:`cdce8p`
diff --git a/changelog.d/2620.deprecation.rst b/changelog.d/2620.deprecation.rst
new file mode 100644
index 00000000..1af5f246
--- /dev/null
+++ b/changelog.d/2620.deprecation.rst
@@ -0,0 +1,2 @@
+The ``license_file`` option is now marked as deprecated.
+Use ``license_files`` instead. -- by :user:`cdce8p`
diff --git a/changelog.d/2620.doc.rst b/changelog.d/2620.doc.rst
new file mode 100644
index 00000000..7564adac
--- /dev/null
+++ b/changelog.d/2620.doc.rst
@@ -0,0 +1 @@
+Added documentation for the ``license_files`` option. -- by :user:`cdce8p`
diff --git a/docs/references/keywords.rst b/docs/references/keywords.rst
index 03ce9fa2..619b2d14 100644
--- a/docs/references/keywords.rst
+++ b/docs/references/keywords.rst
@@ -76,6 +76,17 @@ Keywords
``license``
A string specifying the license of the package.
+``license_file``
+
+ .. warning::
+ ``license_file`` is deprecated. Use ``license_files`` instead.
+
+``license_files``
+
+ A list of glob patterns for license related files that should be included.
+ If neither ``license_file`` nor ``license_files`` is specified, this option
+ defaults to ``LICEN[CS]E*``, ``COPYING*``, ``NOTICE*``, and ``AUTHORS*``.
+
``keywords``
A list of strings or a comma-separated string providing descriptive
meta-data. See: `PEP 0314`_.
diff --git a/docs/userguide/declarative_config.rst b/docs/userguide/declarative_config.rst
index bc66869b..1d2d66e2 100644
--- a/docs/userguide/declarative_config.rst
+++ b/docs/userguide/declarative_config.rst
@@ -184,7 +184,7 @@ maintainer_email maintainer-email str
classifiers classifier file:, list-comma
license str
license_file str
-license_files list-comma
+license_files list-comma 42.0.0
description summary file:, str
long_description long-description file:, str
long_description_content_type str 38.6.0
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index 887b7efa..a6ea814a 100644
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -4,6 +4,7 @@ import os
import sys
import io
import contextlib
+from glob import iglob
from setuptools.extern import ordered_set
@@ -194,29 +195,41 @@ class sdist(sdist_add_defaults, orig.sdist):
"""Checks if license_file' or 'license_files' is configured and adds any
valid paths to 'self.filelist'.
"""
-
- files = ordered_set.OrderedSet()
-
opts = self.distribution.get_option_dict('metadata')
- # ignore the source of the value
- _, license_file = opts.get('license_file', (None, None))
-
- if license_file is None:
- log.debug("'license_file' option was not specified")
- else:
- files.add(license_file)
-
+ files = ordered_set.OrderedSet()
try:
- files.update(self.distribution.metadata.license_files)
+ license_files = self.distribution.metadata.license_files
except TypeError:
log.warn("warning: 'license_files' option is malformed")
-
- for f in files:
- if not os.path.exists(f):
- log.warn(
- "warning: Failed to find the configured license file '%s'",
- f)
- files.remove(f)
-
- self.filelist.extend(files)
+ license_files = ordered_set.OrderedSet()
+ patterns = license_files if isinstance(license_files, ordered_set.OrderedSet) \
+ else ordered_set.OrderedSet(license_files)
+
+ if 'license_file' in opts:
+ log.warn(
+ "warning: the 'license_file' option is deprecated, "
+ "use 'license_files' instead")
+ patterns.append(opts['license_file'][1])
+
+ if 'license_file' not in opts and 'license_files' not in opts:
+ # Default patterns match the ones wheel uses
+ # See https://wheel.readthedocs.io/en/stable/user_guide.html
+ # -> 'Including license files in the generated wheel file'
+ patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*')
+
+ for pattern in patterns:
+ for path in iglob(pattern):
+ if path.endswith('~'):
+ log.debug(
+ "ignoring license file '%s' as it looks like a backup",
+ path)
+ continue
+
+ if path not in files and os.path.isfile(path):
+ log.info(
+ "adding license file '%s' (matched pattern '%s')",
+ path, pattern)
+ files.add(path)
+
+ self.filelist.extend(sorted(files))
diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py
index 1047468b..c93ed020 100644
--- a/setuptools/tests/test_egg_info.py
+++ b/setuptools/tests/test_egg_info.py
@@ -533,7 +533,7 @@ class TestEggInfo:
'setup.cfg': DALS("""
"""),
'LICENSE': "Test license"
- }, False), # no license_file attribute
+ }, True), # no license_file attribute, LICENSE auto-included
({
'setup.cfg': DALS("""
[metadata]
@@ -541,7 +541,15 @@ class TestEggInfo:
"""),
'MANIFEST.in': "exclude LICENSE",
'LICENSE': "Test license"
- }, False) # license file is manually excluded
+ }, False), # license file is manually excluded
+ pytest.param({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file = LICEN[CS]E*
+ """),
+ 'LICENSE': "Test license",
+ }, True,
+ id="glob_pattern"),
])
def test_setup_cfg_license_file(
self, tmpdir_cwd, env, files, license_in_sources):
@@ -621,7 +629,7 @@ class TestEggInfo:
'setup.cfg': DALS("""
"""),
'LICENSE': "Test license"
- }, [], ['LICENSE']), # no license_files attribute
+ }, ['LICENSE'], []), # no license_files attribute, LICENSE auto-included
({
'setup.cfg': DALS("""
[metadata]
@@ -640,7 +648,36 @@ class TestEggInfo:
'MANIFEST.in': "exclude LICENSE-XYZ",
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
- }, ['LICENSE-ABC'], ['LICENSE-XYZ']) # subset is manually excluded
+ }, ['LICENSE-ABC'], ['LICENSE-XYZ']), # subset is manually excluded
+ pytest.param({
+ 'setup.cfg': "",
+ 'LICENSE-ABC': "ABC license",
+ 'COPYING-ABC': "ABC copying",
+ 'NOTICE-ABC': "ABC notice",
+ 'AUTHORS-ABC': "ABC authors",
+ 'LICENCE-XYZ': "XYZ license",
+ 'LICENSE': "License",
+ 'INVALID-LICENSE': "Invalid license",
+ }, [
+ 'LICENSE-ABC',
+ 'COPYING-ABC',
+ 'NOTICE-ABC',
+ 'AUTHORS-ABC',
+ 'LICENCE-XYZ',
+ 'LICENSE',
+ ], ['INVALID-LICENSE'],
+ # ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*')
+ id="default_glob_patterns"),
+ pytest.param({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files =
+ LICENSE*
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'NOTICE-XYZ': "XYZ notice",
+ }, ['LICENSE-ABC'], ['NOTICE-XYZ'],
+ id="no_default_glob_patterns"),
])
def test_setup_cfg_license_files(
self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
@@ -745,7 +782,28 @@ class TestEggInfo:
'LICENSE-PQR': "PQR license",
'LICENSE-XYZ': "XYZ license"
# manually excluded
- }, ['LICENSE-XYZ'], ['LICENSE-ABC', 'LICENSE-PQR'])
+ }, ['LICENSE-XYZ'], ['LICENSE-ABC', 'LICENSE-PQR']),
+ pytest.param({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file = LICENSE*
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'NOTICE-XYZ': "XYZ notice",
+ }, ['LICENSE-ABC'], ['NOTICE-XYZ'],
+ id="no_default_glob_patterns"),
+ pytest.param({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file = LICENSE*
+ license_files =
+ NOTICE*
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'NOTICE-ABC': "ABC notice",
+ 'AUTHORS-ABC': "ABC authors",
+ }, ['LICENSE-ABC', 'NOTICE-ABC'], ['AUTHORS-ABC'],
+ id="combined_glob_patterrns"),
])
def test_setup_cfg_license_file_license_files(
self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py
index 82bdb9c6..589cefb2 100644
--- a/setuptools/tests/test_manifest.py
+++ b/setuptools/tests/test_manifest.py
@@ -55,6 +55,7 @@ def touch(filename):
default_files = frozenset(map(make_local_path, [
'README.rst',
'MANIFEST.in',
+ 'LICENSE',
'setup.py',
'app.egg-info/PKG-INFO',
'app.egg-info/SOURCES.txt',
From e1aa3949d2b0d610f6d83bc3c85d96c5c4cabd3a Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sat, 22 May 2021 20:00:24 -0400
Subject: [PATCH 2/2] Add License-File field to package metadata
https://github.com/pypa/setuptools/pull/2645
---
changelog.d/2645.breaking.rst | 3 ++
changelog.d/2645.change.rst | 4 +++
setuptools/command/egg_info.py | 9 +++++-
setuptools/command/sdist.py | 46 --------------------------
setuptools/config.py | 5 +++
setuptools/dist.py | 54 ++++++++++++++++++++++++++++++-
setuptools/tests/test_egg_info.py | 54 ++++++++++++++++++++++++++++---
setuptools/tests/test_manifest.py | 1 -
8 files changed, 122 insertions(+), 54 deletions(-)
create mode 100644 changelog.d/2645.breaking.rst
create mode 100644 changelog.d/2645.change.rst
diff --git a/changelog.d/2645.breaking.rst b/changelog.d/2645.breaking.rst
new file mode 100644
index 00000000..b96b492a
--- /dev/null
+++ b/changelog.d/2645.breaking.rst
@@ -0,0 +1,3 @@
+License files excluded via the ``MANIFEST.in`` but matched by either
+the ``license_file`` (deprecated) or ``license_files`` options,
+will be nevertheless included in the source distribution. - by :user:`cdce8p`
diff --git a/changelog.d/2645.change.rst b/changelog.d/2645.change.rst
new file mode 100644
index 00000000..b22385c1
--- /dev/null
+++ b/changelog.d/2645.change.rst
@@ -0,0 +1,4 @@
+Added ``License-File`` (multiple) to the output package metadata.
+The field will contain the path of a license file, matched by the
+``license_file`` (deprecated) and ``license_files`` options,
+relative to ``.dist-info``. - by :user:`cdce8p`
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index 1f120b67..18b81340 100644
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -541,6 +541,7 @@ class manifest_maker(sdist):
self.add_defaults()
if os.path.exists(self.template):
self.read_template()
+ self.add_license_files()
self.prune_file_list()
self.filelist.sort()
self.filelist.remove_duplicates()
@@ -575,7 +576,6 @@ class manifest_maker(sdist):
def add_defaults(self):
sdist.add_defaults(self)
- self.check_license()
self.filelist.append(self.template)
self.filelist.append(self.manifest)
rcfiles = list(walk_revctrl())
@@ -592,6 +592,13 @@ class manifest_maker(sdist):
ei_cmd = self.get_finalized_command('egg_info')
self.filelist.graft(ei_cmd.egg_info)
+ def add_license_files(self):
+ license_files = self.distribution.metadata.license_files or []
+ for lf in license_files:
+ log.info("adding license file '%s'", lf)
+ pass
+ self.filelist.extend(license_files)
+
def prune_file_list(self):
build = self.get_finalized_command('build')
base_dir = self.distribution.get_fullname()
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index a6ea814a..4a014283 100644
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -4,9 +4,6 @@ import os
import sys
import io
import contextlib
-from glob import iglob
-
-from setuptools.extern import ordered_set
from .py36compat import sdist_add_defaults
@@ -190,46 +187,3 @@ class sdist(sdist_add_defaults, orig.sdist):
continue
self.filelist.append(line)
manifest.close()
-
- def check_license(self):
- """Checks if license_file' or 'license_files' is configured and adds any
- valid paths to 'self.filelist'.
- """
- opts = self.distribution.get_option_dict('metadata')
-
- files = ordered_set.OrderedSet()
- try:
- license_files = self.distribution.metadata.license_files
- except TypeError:
- log.warn("warning: 'license_files' option is malformed")
- license_files = ordered_set.OrderedSet()
- patterns = license_files if isinstance(license_files, ordered_set.OrderedSet) \
- else ordered_set.OrderedSet(license_files)
-
- if 'license_file' in opts:
- log.warn(
- "warning: the 'license_file' option is deprecated, "
- "use 'license_files' instead")
- patterns.append(opts['license_file'][1])
-
- if 'license_file' not in opts and 'license_files' not in opts:
- # Default patterns match the ones wheel uses
- # See https://wheel.readthedocs.io/en/stable/user_guide.html
- # -> 'Including license files in the generated wheel file'
- patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*')
-
- for pattern in patterns:
- for path in iglob(pattern):
- if path.endswith('~'):
- log.debug(
- "ignoring license file '%s' as it looks like a backup",
- path)
- continue
-
- if path not in files and os.path.isfile(path):
- log.info(
- "adding license file '%s' (matched pattern '%s')",
- path, pattern)
- files.add(path)
-
- self.filelist.extend(sorted(files))
diff --git a/setuptools/config.py b/setuptools/config.py
index af3a3bcb..ece325e2 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -520,6 +520,11 @@ class ConfigMetadataHandler(ConfigHandler):
'obsoletes': parse_list,
'classifiers': self._get_parser_compound(parse_file, parse_list),
'license': exclude_files_parser('license'),
+ 'license_file': self._deprecated_config_handler(
+ exclude_files_parser('license_file'),
+ "The license_file parameter is deprecated, "
+ "use license_files instead.",
+ DeprecationWarning),
'license_files': parse_list,
'description': parse_file,
'long_description': parse_file,
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 050388de..bc663e63 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -14,6 +14,7 @@ import distutils.dist
from distutils.util import strtobool
from distutils.debug import DEBUG
from distutils.fancy_getopt import translate_longopt
+from glob import iglob
import itertools
from collections import defaultdict
@@ -117,6 +118,8 @@ def read_pkg_file(self, file):
self.provides = None
self.obsoletes = None
+ self.license_files = _read_list('license-file')
+
def single_line(val):
# quick and dirty validation for description pypa/setuptools#1390
@@ -199,6 +202,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME
for extra in self.provides_extras:
write_field('Provides-Extra', extra)
+ self._write_list(file, 'License-File', self.license_files or [])
sequence = tuple, list
@@ -398,7 +402,8 @@ class Distribution(_Distribution):
'long_description_content_type': None,
'project_urls': dict,
'provides_extras': ordered_set.OrderedSet,
- 'license_files': ordered_set.OrderedSet,
+ 'license_file': lambda: None,
+ 'license_files': lambda: None,
}
_patched_dist = None
@@ -557,6 +562,34 @@ class Distribution(_Distribution):
req.marker = None
return req
+ def _finalize_license_files(self):
+ """Compute names of all license files which should be included."""
+ license_files: Optional[List[str]] = self.metadata.license_files
+ patterns: List[str] = license_files if license_files else []
+
+ license_file: Optional[str] = self.metadata.license_file
+ if license_file and license_file not in patterns:
+ patterns.append(license_file)
+
+ if license_files is None and license_file is None:
+ # Default patterns match the ones wheel uses
+ # See https://wheel.readthedocs.io/en/stable/user_guide.html
+ # -> 'Including license files in the generated wheel file'
+ patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*')
+
+ self.metadata.license_files = list(
+ unique_everseen(self._expand_patterns(patterns)))
+
+ @staticmethod
+ def _expand_patterns(patterns):
+ return (
+ path
+ for pattern in patterns
+ for path in iglob(pattern)
+ if not path.endswith('~')
+ and os.path.isfile(path)
+ )
+
# FIXME: 'Distribution._parse_config_files' is too complex (14)
def _parse_config_files(self, filenames=None): # noqa: C901
"""
@@ -680,6 +713,7 @@ class Distribution(_Distribution):
parse_configuration(self, self.command_options,
ignore_option_errors=ignore_option_errors)
self._finalize_requires()
+ self._finalize_license_files()
def fetch_build_eggs(self, requires):
"""Resolve pre-setup requirements"""
@@ -1020,3 +1054,21 @@ class Distribution(_Distribution):
class DistDeprecationWarning(SetuptoolsDeprecationWarning):
"""Class for warning about deprecations in dist in
setuptools. Not ignored by default, unlike DeprecationWarning."""
+
+
+def unique_everseen(iterable, key=None):
+ "List unique elements, preserving order. Remember all elements ever seen."
+ # unique_everseen('AAAABBBCCDAABBB') --> A B C D
+ # unique_everseen('ABBCcAD', str.lower) --> A B C D
+ seen = set()
+ seen_add = seen.add
+ if key is None:
+ for element in itertools.filterfalse(seen.__contains__, iterable):
+ seen_add(element)
+ yield element
+ else:
+ for element in iterable:
+ k = key(element)
+ if k not in seen:
+ seen_add(k)
+ yield element
diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py
index c93ed020..e8b49732 100644
--- a/setuptools/tests/test_egg_info.py
+++ b/setuptools/tests/test_egg_info.py
@@ -541,7 +541,7 @@ class TestEggInfo:
"""),
'MANIFEST.in': "exclude LICENSE",
'LICENSE': "Test license"
- }, False), # license file is manually excluded
+ }, True), # manifest is overwritten by license_file
pytest.param({
'setup.cfg': DALS("""
[metadata]
@@ -637,7 +637,7 @@ class TestEggInfo:
"""),
'MANIFEST.in': "exclude LICENSE",
'LICENSE': "Test license"
- }, [], ['LICENSE']), # license file is manually excluded
+ }, ['LICENSE'], []), # manifest is overwritten by license_files
({
'setup.cfg': DALS("""
[metadata]
@@ -648,7 +648,8 @@ class TestEggInfo:
'MANIFEST.in': "exclude LICENSE-XYZ",
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
- }, ['LICENSE-ABC'], ['LICENSE-XYZ']), # subset is manually excluded
+ # manifest is overwritten by license_files
+ }, ['LICENSE-ABC', 'LICENSE-XYZ'], []),
pytest.param({
'setup.cfg': "",
'LICENSE-ABC': "ABC license",
@@ -678,6 +679,17 @@ class TestEggInfo:
'NOTICE-XYZ': "XYZ notice",
}, ['LICENSE-ABC'], ['NOTICE-XYZ'],
id="no_default_glob_patterns"),
+ pytest.param({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files =
+ LICENSE-ABC
+ LICENSE*
+ """),
+ 'LICENSE-ABC': "ABC license",
+ }, ['LICENSE-ABC'], [],
+ id="files_only_added_once",
+ ),
])
def test_setup_cfg_license_files(
self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
@@ -781,8 +793,8 @@ class TestEggInfo:
'LICENSE-ABC': "ABC license",
'LICENSE-PQR': "PQR license",
'LICENSE-XYZ': "XYZ license"
- # manually excluded
- }, ['LICENSE-XYZ'], ['LICENSE-ABC', 'LICENSE-PQR']),
+ # manifest is overwritten
+ }, ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], []),
pytest.param({
'setup.cfg': DALS("""
[metadata]
@@ -825,6 +837,38 @@ class TestEggInfo:
for lf in excl_licenses:
assert sources_lines.count(lf) == 0
+ def test_license_file_attr_pkg_info(self, tmpdir_cwd, env):
+ """All matched license files should have a corresponding License-File."""
+ self._create_project()
+ build_files({
+ "setup.cfg": DALS("""
+ [metadata]
+ license_files =
+ NOTICE*
+ LICENSE*
+ """),
+ "LICENSE-ABC": "ABC license",
+ "LICENSE-XYZ": "XYZ license",
+ "NOTICE": "included",
+ "IGNORE": "not include",
+ })
+
+ environment.run_setup_py(
+ cmd=['egg_info'],
+ pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)])
+ )
+ egg_info_dir = os.path.join('.', 'foo.egg-info')
+ with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file:
+ pkg_info_lines = pkginfo_file.read().split('\n')
+ license_file_lines = [
+ line for line in pkg_info_lines if line.startswith('License-File:')]
+
+ # Only 'NOTICE', LICENSE-ABC', and 'LICENSE-XYZ' should have been matched
+ # Also assert that order from license_files is keeped
+ assert "License-File: NOTICE" == license_file_lines[0]
+ assert "License-File: LICENSE-ABC" in license_file_lines[1:]
+ assert "License-File: LICENSE-XYZ" in license_file_lines[1:]
+
def test_long_description_content_type(self, tmpdir_cwd, env):
# Test that specifying a `long_description_content_type` keyword arg to
# the `setup` function results in writing a `Description-Content-Type`
diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py
index 589cefb2..82bdb9c6 100644
--- a/setuptools/tests/test_manifest.py
+++ b/setuptools/tests/test_manifest.py
@@ -55,7 +55,6 @@ def touch(filename):
default_files = frozenset(map(make_local_path, [
'README.rst',
'MANIFEST.in',
- 'LICENSE',
'setup.py',
'app.egg-info/PKG-INFO',
'app.egg-info/SOURCES.txt',

@ -1,97 +1,85 @@
## START: Set by rpmautospec
## (rpmautospec version 0.6.5)
## RPMAUTOSPEC: autorelease, autochangelog
%define autorelease(e:s:pb:n) %{?-p:0.}%{lua:
release_number = 9;
base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}"));
print(release_number + base_release_number - 1);
}%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}}
## END: Set by rpmautospec
%global srcname setuptools %global srcname setuptools
# The original RHEL 9 content set is defined by (build)dependencies # used when bootstrapping new Python versions
# of the packages in Fedora ELN. Hence we disable tests here %bcond bootstrap 0
# to prevent pulling many unwanted packages in.
# Once the RHEL 9 content set is defined and/or RHEL 9 forks from ELN,
# the conditional can be removed from the Fedora spec file.
# We intentionally keep this enabled on EPEL.
%if 0%{?rhel} >= 9 && !0%{?epel}
%bcond_with tests
%else
%bcond_without tests
%endif
# WARNING When bootstrapping, disable tests as well,
# because tests need pip.
%bcond_with bootstrap
# Similar to what we have in pythonX.Y.spec files. # Similar to what we have in pythonX.Y.spec files.
# If enabled, provides unversioned executables and other stuff. # If enabled, provides unversioned executables and other stuff.
# Disable it if you build this package in an alternative stack. # Disable it if you build this package in an alternative stack.
%bcond_without main_python %bcond main_python 1
# The original RHEL N+1 content set is defined by (build)dependencies
# of the packages in Fedora ELN. Hence we disable tests and documentation here
# to prevent pulling many unwanted packages in.
# We intentionally keep this enabled on EPEL.
%bcond tests %[%{without bootstrap} && (%{defined fedora} || %{defined epel})]
%if %{without bootstrap}
%global python_wheel_name %{srcname}-%{version}-py3-none-any.whl %global python_wheel_name %{srcname}-%{version}-py3-none-any.whl
%global python3_record %{python3_sitelib}/%{srcname}-%{version}.dist-info/RECORD
%endif
Name: python-setuptools Name: python-setuptools
# When updating, update the bundled libraries versions bellow! # When updating, update the bundled libraries versions bellow!
Version: 53.0.0 Version: 69.0.3
Release: 12%{?dist}.1 Release: %autorelease
Summary: Easily build and distribute Python packages Summary: Easily build and distribute Python packages
# setuptools is MIT # setuptools is MIT
# appdirs is MIT # platformdirs is MIT
# more-itertools is MIT
# ordered-set is MIT # ordered-set is MIT
# packaging is BSD or ASL 2.0 # packaging is BSD-2-Clause OR Apache-2.0
# pyparsing is MIT # importlib-metadata is Apache-2.0
# the setuptools logo has unknown license and possible TM problems, # importlib-resources is Apache-2.0
# but the sdist **does not** contain it, # jaraco.text is MIT
# see https://github.com/pypa/setuptools/issues/2227 # typing-extensions is Python-2.0.1
License: MIT and (BSD or ASL 2.0) # zipp is MIT
# nspektr is MIT
# tomli is MIT
# the setuptools logo is MIT
License: MIT AND Apache-2.0 AND (BSD-2-Clause OR Apache-2.0) AND Python-2.0.1
URL: https://pypi.python.org/pypi/%{srcname} URL: https://pypi.python.org/pypi/%{srcname}
Source0: %{pypi_source %{srcname} %{version}} Source0: %{pypi_source %{srcname} %{version}}
# Two backports related to the License-File metadata field # Some test deps are optional and either not desired or not available in Fedora, thus this patch removes them.
# Fixes https://bugzilla.redhat.com/2033994 Patch: Remove-optional-or-unpackaged-test-deps.patch
#
# license_files - Add support for glob patterns + add default patterns # The `setup.py install` deprecation notice might be confusing for RPM packagers
# https://github.com/pypa/setuptools/pull/2620 # adjust it, but only when $RPM_BUILD_ROOT is set
# included in setuptools 56+ Patch: Adjust-the-setup.py-install-deprecation-message.patch
#
# Add License-File field to package metadata
# https://github.com/pypa/setuptools/pull/2645
# included in setuptools 57+
# depends on the previous one
Patch1: license-file-metadata.patch
# Fix case sensitivity of entry point names and keys in setup.cfg
# Fixes https://bugzilla.redhat.com/2124281
Patch2: https://github.com/pypa/setuptools/pull/2580.patch
# Security fix for CVE-2022-40897
# Regular Expression Denial of Service (ReDoS) in package_index.py
# Resolved upstream: https://github.com/pypa/setuptools/commit/43a9c9bfa6aa626ec2a22540bea28d2ca77964be
Patch3: CVE-2022-40897.patch
# Security fix for CVE-2024-6345 # Security fix for CVE-2024-6345
# Remote code execution via download functions in the package_index module # Remote code execution via download functions in the package_index module
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=2297771 # Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=2297771
# Upstream solution: https://github.com/pypa/setuptools/pull/4332 # Upstream solution: https://github.com/pypa/setuptools/pull/4332
# Patch simplified because upstream doesn't support SVN anymore. # Patch simplified because upstream doesn't support SVN anymore.
Patch4: CVE-2024-6345.patch Patch: CVE-2024-6345.patch
BuildArch: noarch BuildArch: noarch
BuildRequires: python%{python3_pkgversion}-devel BuildRequires: python%{python3_pkgversion}-devel
%if %{with tests} %if %{with tests}
BuildRequires: gcc BuildRequires: gcc
BuildRequires: python%{python3_pkgversion}-pip %endif
BuildRequires: python%{python3_pkgversion}-pytest
BuildRequires: python%{python3_pkgversion}-mock
BuildRequires: python%{python3_pkgversion}-pytest-fixture-config
BuildRequires: python%{python3_pkgversion}-pytest-virtualenv
BuildRequires: python%{python3_pkgversion}-jaraco-envs
%endif # with tests
%if %{without bootstrap}
BuildRequires: python%{python3_pkgversion}-pip
BuildRequires: python%{python3_pkgversion}-wheel
BuildRequires: python%{python3_pkgversion}-setuptools
# python3 bootstrap: this is built before the final build of python3, which # python3 bootstrap: this is built before the final build of python3, which
# adds the dependency on python3-rpm-generators, so we require it manually # adds the dependency on python3-rpm-generators, so we require it manually
# The minimal version is for bundled provides verification script # The minimal version is for bundled provides verification script to accept multiple files as input
BuildRequires: python3-rpm-generators >= 11-8 BuildRequires: python3-rpm-generators >= 12-8
%endif # without bootstrap
%if %{without bootstrap}
BuildRequires: pyproject-rpm-macros >= 0-44
# Not to use the pre-generated egg-info, we use setuptools from previous build to generate it
BuildRequires: python%{python3_pkgversion}-setuptools
%endif
%description %description
Setuptools is a collection of enhancements to the Python distutils that allow Setuptools is a collection of enhancements to the Python distutils that allow
@ -102,34 +90,31 @@ This package also contains the runtime components of setuptools, necessary to
execute the software that requires pkg_resources. execute the software that requires pkg_resources.
# Virtual provides for the packages bundled by setuptools. # Virtual provides for the packages bundled by setuptools.
# Bundled packages are defined in two files: # Bundled packages are defined in multiple files. Generate the list with:
# - pkg_resources/_vendor/vendored.txt, and # %%{_rpmconfigdir}/pythonbundles.py --namespace 'python%%{python3_pkgversion}dist' */_vendor/vendored.txt
# - setuptools/_vendor/vendored.txt
# Merge them to one and then generate the list with:
# %%{_rpmconfigdir}/pythonbundles.py --namespace 'python%%{python3_pkgversion}dist' allvendor.txt
%global bundled %{expand: %global bundled %{expand:
Provides: bundled(python%{python3_pkgversion}dist(appdirs)) = 1.4.3 Provides: bundled(python%{python3_pkgversion}dist(importlib-metadata)) = 6
Provides: bundled(python%{python3_pkgversion}dist(importlib-resources)) = 5.10.2
Provides: bundled(python%{python3_pkgversion}dist(jaraco-text)) = 3.7
Provides: bundled(python%{python3_pkgversion}dist(more-itertools)) = 8.8
Provides: bundled(python%{python3_pkgversion}dist(ordered-set)) = 3.1.1 Provides: bundled(python%{python3_pkgversion}dist(ordered-set)) = 3.1.1
Provides: bundled(python%{python3_pkgversion}dist(packaging)) = 20.4 Provides: bundled(python%{python3_pkgversion}dist(packaging)) = 23.1
Provides: bundled(python%{python3_pkgversion}dist(pyparsing)) = 2.2.1 Provides: bundled(python%{python3_pkgversion}dist(platformdirs)) = 2.6.2
Provides: bundled(python%{python3_pkgversion}dist(tomli)) = 2.0.1
Provides: bundled(python%{python3_pkgversion}dist(typing-extensions)) = 4.0.1
Provides: bundled(python%{python3_pkgversion}dist(typing-extensions)) = 4.4
Provides: bundled(python%{python3_pkgversion}dist(zipp)) = 3.7
} }
%package -n python%{python3_pkgversion}-setuptools %package -n python%{python3_pkgversion}-setuptools
Summary: Easily build and distribute Python 3 packages Summary: Easily build and distribute Python 3 packages
%{bundled} %{bundled}
%if %{with bootstrap}
Provides: python%{python3_pkgversion}dist(setuptools) = %{version}
Provides: python%{python3_version}dist(setuptools) = %{version}
%endif
# For users who might see ModuleNotFoundError: No module named 'pkg_resoureces' # For users who might see ModuleNotFoundError: No module named 'pkg_resoureces'
# NB: Those are two different provides: one contains underscore, the other hyphen
%py_provides python%{python3_pkgversion}-pkg_resources %py_provides python%{python3_pkgversion}-pkg_resources
%py_provides python%{python3_pkgversion}-pkg-resources %py_provides python%{python3_pkgversion}-pkg-resources
# Provide platform-python-setuptools for backwards compatibility with RHEL 8
Provides: platform-python-setuptools = %{version}-%{release}
%description -n python%{python3_pkgversion}-setuptools %description -n python%{python3_pkgversion}-setuptools
Setuptools is a collection of enhancements to the Python 3 distutils that allow Setuptools is a collection of enhancements to the Python 3 distutils that allow
you to more easily build and distribute Python 3 packages, especially ones that you to more easily build and distribute Python 3 packages, especially ones that
@ -142,13 +127,6 @@ execute the software that requires pkg_resources.
%package -n %{python_wheel_pkg_prefix}-%{srcname}-wheel %package -n %{python_wheel_pkg_prefix}-%{srcname}-wheel
Summary: The setuptools wheel Summary: The setuptools wheel
%{bundled} %{bundled}
Provides: %{name}-wheel = %{version}-%{release}
Obsoletes: %{name}-wheel < %{version}-%{release}
# Older versions of python3-libs expect Python wheels at the old unversioned
# location, so we conflict with the old Python versions that wouldn't work with
# the new wheel location.
Conflicts: python3-libs < 3.9.9-2
%description -n %{python_wheel_pkg_prefix}-%{srcname}-wheel %description -n %{python_wheel_pkg_prefix}-%{srcname}-wheel
A Python wheel of setuptools to use with venv. A Python wheel of setuptools to use with venv.
@ -161,75 +139,104 @@ A Python wheel of setuptools to use with venv.
# If we don't have setuptools installed yet, we use the pre-generated .egg-info # If we don't have setuptools installed yet, we use the pre-generated .egg-info
# See https://github.com/pypa/setuptools/pull/2543 # See https://github.com/pypa/setuptools/pull/2543
# And https://github.com/pypa/setuptools/issues/2550 # And https://github.com/pypa/setuptools/issues/2550
rm -r %{srcname}.egg-info # WARNING: We cannot remove this folder since Python 3.11.1,
# see https://github.com/pypa/setuptools/issues/3761
#rm -r %%{srcname}.egg-info
%endif %endif
# Strip shbang # Strip shbang
find setuptools pkg_resources -name \*.py | xargs sed -i -e '1 {/^#!\//d}' find setuptools pkg_resources -name \*.py | xargs sed -i -e '1 {/^#!\//d}'
# Remove bundled exes # Remove bundled exes
rm -f setuptools/*.exe rm -f setuptools/*.exe
# These tests require internet connection # Don't ship these
rm setuptools/tests/test_integration.py rm -r docs/conf.py
# We don't do linting or coverage here
sed -i pytest.ini -e 's/ --flake8//' \
-e 's/ --cov//'
%build
%if %{without bootstrap} %if %{without bootstrap}
%py3_build_wheel %generate_buildrequires
%else %pyproject_buildrequires -r %{?with_tests:-x testing}
%endif
%build
%if %{with bootstrap}
%py3_build %py3_build
%else
%pyproject_wheel
%endif %endif
%install %install
%if %{without bootstrap} %if %{with bootstrap}
%py3_install_wheel %{python_wheel_name} # The setup.py install command tries to import distutils
# but the distutils-precedence.pth file is not yet respected
# and Python 3.12+ no longer has distutils in the standard library.
ln -s setuptools/_distutils distutils
PYTHONPATH=$PWD %py3_install
unlink distutils
%else %else
%py3_install %pyproject_install
%pyproject_save_files setuptools pkg_resources _distutils_hack
%endif %endif
# This is not installed (in 45.2.0 anyway), but better be safe than sorry # https://github.com/pypa/setuptools/issues/2709
rm -rf %{buildroot}%{python3_sitelib}/{setuptools,pkg_resources}/tests rm -rf %{buildroot}%{python3_sitelib}/pkg_resources/tests/
%if %{without bootstrap} %if %{without bootstrap}
sed -i '/^setuptools\/tests\//d' %{buildroot}%{python3_record} sed -i '/\/pkg_resources\/tests\b/d' %{pyproject_files}
%endif
find %{buildroot}%{python3_sitelib} -name '*.exe' | xargs rm -f
# Don't ship these
rm -r docs/{conf.py,_*}
%if %{without bootstrap} # Install the wheel for the python-setuptools-wheel package
mkdir -p %{buildroot}%{python_wheel_dir} mkdir -p %{buildroot}%{python_wheel_dir}
install -p dist/%{python_wheel_name} -t %{buildroot}%{python_wheel_dir} install -p %{_pyproject_wheeldir}/%{python_wheel_name} -t %{buildroot}%{python_wheel_dir}
%endif %endif
%if %{with tests}
%check %check
# Verify bundled provides are up to date # Verify bundled provides are up to date
cat pkg_resources/_vendor/vendored.txt setuptools/_vendor/vendored.txt > allvendor.txt %{_rpmconfigdir}/pythonbundles.py */_vendor/vendored.txt --namespace 'python%{python3_pkgversion}dist' --compare-with '%{bundled}'
%{_rpmconfigdir}/pythonbundles.py allvendor.txt --namespace 'python%{python3_pkgversion}dist' --compare-with '%{bundled}'
# Regression test, the wheel should not be larger than 600 KiB # Regression test, the tests are not supposed to be installed
test ! -d %{buildroot}%{python3_sitelib}/pkg_resources/tests
test ! -d %{buildroot}%{python3_sitelib}/setuptools/tests
%if %{without bootstrap}
# Regression test, the wheel should not be larger than 900 kB
# https://bugzilla.redhat.com/show_bug.cgi?id=1914481#c3 # https://bugzilla.redhat.com/show_bug.cgi?id=1914481#c3
test $(du dist/%{python_wheel_name} | cut -f1) -lt 600 test $(stat --format %%s %{_pyproject_wheeldir}/%{python_wheel_name}) -lt 900000
%pyproject_check_import
%endif
%if %{with tests}
# https://github.com/pypa/setuptools/discussions/2607
rm pyproject.toml
# Upstream tests # Upstream tests
# --ignore=pavement.py: # --ignore=setuptools/tests/test_integration.py
# pavement.py is only used by upstream to do releases and vendoring, we don't ship it # --ignore=setuptools/tests/integration/
PYTHONPATH=$(pwd) %pytest --ignore=pavement.py # --ignore=setuptools/tests/config/test_apply_pyprojecttoml.py
# -k "not test_pip_upgrade_from_source"
# the tests require internet connection
# --ignore=setuptools/tests/test_editable_install.py
# the tests require pip-run which we don't have in Fedora
PRE_BUILT_SETUPTOOLS_WHEEL=%{_pyproject_wheeldir}/%{python_wheel_name} \
PYTHONPATH=$(pwd) %pytest \
--ignore=setuptools/tests/test_integration.py \
--ignore=setuptools/tests/integration/ \
--ignore=setuptools/tests/test_editable_install.py \
--ignore=setuptools/tests/config/test_apply_pyprojecttoml.py \
--ignore=tools/finalize.py \
-k "not test_pip_upgrade_from_source and not test_setup_requires_honors_fetch_params"
%endif # with tests %endif # with tests
%files -n python%{python3_pkgversion}-setuptools %files -n python%{python3_pkgversion}-setuptools %{?!with_bootstrap:-f %{pyproject_files}}
%license LICENSE %license LICENSE
%doc docs/* CHANGES.rst README.rst %doc docs/* NEWS.rst README.rst
%{python3_sitelib}/distutils-precedence.pth
%if %{with bootstrap}
%{python3_sitelib}/setuptools-%{version}-py%{python3_version}.egg-info/
%{python3_sitelib}/pkg_resources/ %{python3_sitelib}/pkg_resources/
%{python3_sitelib}/setuptools*/ %{python3_sitelib}/setuptools/
%{python3_sitelib}/_distutils_hack/ %{python3_sitelib}/_distutils_hack/
%{python3_sitelib}/distutils-precedence.pth %endif
%if %{without bootstrap} %if %{without bootstrap}
%files -n %{python_wheel_pkg_prefix}-%{srcname}-wheel %files -n %{python_wheel_pkg_prefix}-%{srcname}-wheel
@ -241,56 +248,173 @@ PYTHONPATH=$(pwd) %pytest --ignore=pavement.py
%changelog %changelog
* Wed Jul 24 2024 Lumír Balhar <lbalhar@redhat.com> - 53.0.0-12.1 ## START: Generated by rpmautospec
* Tue Oct 29 2024 Troy Dawson <tdawson@redhat.com> - 69.0.3-9
- Bump release for October 2024 mass rebuild:
* Tue Sep 10 2024 Tomáš Hrnčiar <thrnciar@redhat.com> - 69.0.3-8
- Add gating.yaml
* Tue Sep 10 2024 Tomáš Hrnčiar <thrnciar@redhat.com> - 69.0.3-7
- CI: Make STI tests compatible with EL 10
* Wed Aug 07 2024 Tomáš Hrnčiar <thrnciar@redhat.com> - 69.0.3-6
- Add a simple smoke test
* Fri Jul 26 2024 Lumir Balhar <lbalhar@redhat.com> - 69.0.3-5
- Security fix for CVE-2024-6345 - Security fix for CVE-2024-6345
Resolves: RHEL-50466
* Wed Jan 11 2023 Charalampos Stratakis <cstratak@redhat.com> - 53.0.0-12 * Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 69.0.3-4
- Security fix for CVE-2022-40897 - Bump release for June 2024 mass rebuild
Resolves: rhbz#2158559
* Tue Feb 06 2024 Miro Hrončok <miro@hroncok.cz> - 69.0.3-3
- Undo the removal of pkg_resources DeprecationWarning
* Tue Feb 06 2024 Karolina Surma <ksurma@redhat.com> - 69.0.3-1
- Update to 69.0.3
* Fri Jan 26 2024 Fedora Release Engineering <releng@fedoraproject.org> - 68.2.2-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Mon Jan 22 2024 Fedora Release Engineering <releng@fedoraproject.org> - 68.2.2-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Wed Sep 20 2023 Lumír Balhar <lbalhar@redhat.com> - 68.2.2-1
- Update to 68.2.2 (rhbz#2208644)
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 67.7.2-7
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
* Wed Jul 12 2023 Python Maint <python-maint@redhat.com> - 67.7.2-6
- Rebuilt for Python 3.12
* Tue Jun 13 2023 Python Maint <python-maint@redhat.com> - 67.7.2-5
- Bootstrap for Python 3.12
* Tue Jun 13 2023 Python Maint <python-maint@redhat.com> - 67.7.2-4
- Bootstrap for Python 3.12
* Fri May 05 2023 Miro Hrončok <mhroncok@redhat.com> - 67.7.2-2
- Adjust the `setup.py install` deprecation notice when building RPM packages
* Fri Apr 21 2023 Charalampos Stratakis <cstratak@redhat.com> - 67.7.2-1
- Update to 67.7.2
- Fixes: rhbz#2144132
* Wed Sep 07 2022 Miro Hrončok <mhroncok@redhat.com> - 53.0.0-11 * Thu Apr 20 2023 Charalampos Stratakis <cstratak@redhat.com> - 67.6.1-1
- Fix case sensitivity of entry point names and keys in setup.cfg - Update to 67.6.1
- Resolves: rhbz#2124281 - Fixes: rhbz#2144132
* Tue Feb 08 2022 Tomas Orsava <torsava@redhat.com> - 53.0.0-10 * Tue Mar 28 2023 Miro Hrončok <mhroncok@redhat.com> - 65.5.1-3
- Add automatically generated Obsoletes tag with the python39- prefix - Fix tests with wheel 0.40
for smoother upgrade from RHEL8
- Related: rhbz#1990421
* Wed Jan 12 2022 Miro Hrončok <mhroncok@redhat.com> - 53.0.0-9 * Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 65.5.1-2
- Add License-File field to package metadata - Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
- Resolves: rhbz#2033994
* Wed Nov 24 2021 Tomas Orsava <torsava@redhat.com> - 53.0.0-8 * Tue Nov 08 2022 Miro Hrončok <mhroncok@redhat.com> - 65.5.1-1
- Conflict with old Python versions that use the old unversioned wheel location - Update to 65.5.1
- Resolves: rhbz#1982668 - Fixes: rhbz#2140209
* Wed Sep 22 2021 Tomas Orsava <torsava@redhat.com> - 53.0.0-7 * Fri Oct 14 2022 Miro Hrončok <mhroncok@redhat.com> - 65.5.0-1
- Make the python-setuptools-wheel subpackage versioned (python3-setuptools-wheel), - Update to 65.5.0
and move its contents to a versioned directory /usr/share/python3-wheels - Fixes: rhbz#2129562
- Resolves: rhbz#1982668
* Tue Aug 10 2021 Mohan Boddu <mboddu@redhat.com> * Thu Oct 13 2022 Miro Hrončok <mhroncok@redhat.com> - 65.4.1-1
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags - Update to 65.4.1
Related: rhbz#1991688 - Update the RPM License field to use SPDX expressions
* Wed Jul 28 2021 Tomas Orsava <torsava@redhat.com> - 53.0.0-5 * Tue Sep 13 2022 Lumír Balhar <lbalhar@redhat.com> - 65.3.0-1
- Provide the platform-python-setuptools name for backwards compatibility - Update to 65.3.0
with RHEL 8 Resolves: rhbz#2102402
- Related: rhbz#1891487
* Mon Jun 21 2021 Lumír Balhar <lbalhar@redhat.com> - 53.0.0-4 * Thu Jul 28 2022 Tomáš Hrnčiar <thrnciar@redhat.com> - 65.0.2-1
- Add missing bundled provide - ordered-set - Update to 65.0.2
Related: rhbz#1950291 - Fixes: rhbz#2102402
* Thu Apr 22 2021 Miro Hrončok <mhroncok@redhat.com> - 53.0.0-3 * Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 62.6.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
* Tue Jun 14 2022 Charalampos Stratakis <cstratak@redhat.com> - 62.6.0-1
- Update to 62.6.0
- Fixes: rhbz#2064842
* Tue Jun 14 2022 Python Maint <python-maint@redhat.com> - 60.9.3-5
- Rebuilt for Python 3.11
* Mon Jun 13 2022 Python Maint <python-maint@redhat.com> - 60.9.3-4
- Bootstrap for Python 3.11
* Mon Jun 13 2022 Python Maint <python-maint@redhat.com> - 60.9.3-3
- Bootstrap for Python 3.11
* Tue Apr 19 2022 Tomáš Hrnčiar <thrnciar@redhat.com> - 60.9.3-2
- No longer use the deprecated sre_constants module in bundled pyparsing
- Fixes: rhbz#2075487
* Wed Feb 16 2022 Karolina Surma <ksurma@redhat.com> - 60.9.3-1
- Update to 60.9.3
- Fixes rhbz#2033860
* Fri Jan 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 59.6.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
* Wed Dec 08 2021 Tomáš Hrnčiar <thrnciar@redhat.com> - 59.6.0-1
- Update to 59.6.0
- Fixes: rhbz#2023119
- Fixes: rhbz#2031556
* Wed Nov 10 2021 Karolina Surma <ksurma@redhat.com> - 58.5.3-1
- Update to 58.5.3
- Fixes rhbz#2016715
* Tue Oct 19 2021 Tomáš Hrnčiar <thrnciar@redhat.com> - 58.2.0-1
- Update to 58.2.0
- Fixes rhbz#2001228
* Tue Aug 03 2021 Miro Hrončok <mhroncok@redhat.com> - 57.4.0-1
- Update to 57.4.0
- https://setuptools.readthedocs.io/en/latest/history.html#v57-4-0
- Fixes rhbz#1982493
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 57.1.0-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
* Mon Jul 19 2021 Miro Hrončok <mhroncok@redhat.com> - 57.1.0-2
- Modernize packaging
* Fri Jul 09 2021 Tomas Hrnciar <thrnciar@redhat.com> - 57.1.0-1
- Update to 57.1.0
- Fixes rhbz#1979122
* Thu Jun 17 2021 Lumír Balhar <lbalhar@redhat.com> - 57.0.0-1
- Update to 57.0.0
Resolves: rhbz#1963411
* Fri Jun 04 2021 Python Maint <python-maint@redhat.com> - 56.2.0-4
- Rebuilt for Python 3.10
* Tue Jun 01 2021 Python Maint <python-maint@redhat.com> - 56.2.0-3
- Bootstrap for Python 3.10
* Tue Jun 01 2021 Python Maint <python-maint@redhat.com> - 56.2.0-2
- Bootstrap for Python 3.10
* Mon May 17 2021 Miro Hrončok <mhroncok@redhat.com> - 56.2.0-1
- Update to 56.2.0
- Fixes rhbz#1958677
* Thu May 06 2021 Tomas Hrnciar <thrnciar@redhat.com> - 56.1.0-1
- Update to 56.1.0
* Thu Apr 22 2021 Miro Hrončok <mhroncok@redhat.com> - 56.0.0-2
- Provide python3-pkg_resources - Provide python3-pkg_resources
- Provide python3-pkg-resources - Provide python3-pkg-resources
Resolves: rhbz#1947857
* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 53.0.0-2 * Fri Apr 09 2021 Tomas Hrnciar <thrnciar@redhat.com> - 56.0.0-1
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 - Update to 56.0.0
* Tue Mar 16 2021 Tomas Hrnciar <thrnciar@redhat.com> - 54.1.2-1
- Update to 54.1.2
* Tue Feb 02 2021 Miro Hrončok <mhroncok@redhat.com> - 53.0.0-1 * Tue Feb 02 2021 Miro Hrončok <mhroncok@redhat.com> - 53.0.0-1
- Update to 53.0.0 - Update to 53.0.0
@ -1042,3 +1166,5 @@ Resolves: rhbz#1947857
* Thu Feb 26 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6c9-3 * Thu Feb 26 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6c9-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
## END: Generated by rpmautospec

Loading…
Cancel
Save