diff --git a/README.md b/README.md index 9700ef8..48416a7 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,12 @@ However, in Fedora packages, always list executables explicitly to avoid uninten and language (`*.mo`) files with `%lang` macro and appropriate language code. Only license files declared via [PEP 639] `License-File` field are detected. [PEP 639] is still a draft and can be changed in the future. +It is possible to use the `-l` flag to declare that a missing license should +terminate the build or `-L` (the default) to explicitly disable this check. +Packagers are encouraged to use the `-l` flag when the `%license` file is not manually listed in `%files` +to avoid accidentally losing the file in a future version. +When the `%license` file is manually listed in `%files`, +packagers can use the `-L` flag to ensure future compatibility in case the `-l` behavior eventually becomes a default. Note that `%pyproject_save_files` uses data from the [RECORD file](https://www.python.org/dev/peps/pep-0627/). If you wish to rename, remove or otherwise change the installed files of a package diff --git a/macros.pyproject b/macros.pyproject index 7013de2..81c7f29 100644 --- a/macros.pyproject +++ b/macros.pyproject @@ -108,7 +108,7 @@ fi # See this thread http://lists.rpm.org/pipermail/rpm-list/2021-June/002048.html # Since RPM 4.19, 2 signs are needed instead. 4.18.90+ is a pre-release of RPM 4.19. # On the CI, we build tests/escape_percentages.spec to verify the assumptions. -%pyproject_save_files() %{expand:\\\ +%pyproject_save_files(lL) %{expand:\\\ %{expr:v"0%{?rpmversion}" >= v"4.18.90" ? "RPM_PERCENTAGES_COUNT=2" : "RPM_PERCENTAGES_COUNT=8" } \\ %{__python3} %{_rpmconfigdir}/redhat/pyproject_save_files.py \\ --output-files "%{pyproject_files}" \\ @@ -119,7 +119,7 @@ fi --python-version "%{python3_version}" \\ --pyproject-record "%{_pyproject_record}" \\ --prefix "%{_prefix}" \\ - %{*} + %{**} } # -t - Process only top-level modules diff --git a/pyproject-rpm-macros.spec b/pyproject-rpm-macros.spec index 6f54e6b..c60271f 100644 --- a/pyproject-rpm-macros.spec +++ b/pyproject-rpm-macros.spec @@ -13,7 +13,7 @@ License: MIT # Increment Y and reset Z when new macros or features are added # Increment Z when this is a bugfix or a cosmetic change # Dropping support for EOL Fedoras is *not* considered a breaking change -Version: 1.10.0 +Version: 1.11.0 Release: 1%{?dist} # Macro files @@ -171,6 +171,11 @@ export HOSTNAME="rpmbuild" # to speedup tox in network-less mock, see rhbz#1856 %changelog +* Wed Sep 27 2023 Miro HronĨok - 1.11.0-1 +- Add the -l/-L flag to %%pyproject_save_files +- The -l flag can be used to assert at least 1 License-File was detected +- The -L flag explicitly disables this check (which remains the default) + * Wed Sep 13 2023 Python Maint - 1.10.0-1 - Add %%_pyproject_check_import_allow_no_modules for automated environments - Fix handling of tox 4 provision without an explicit tox minversion diff --git a/pyproject_save_files.py b/pyproject_save_files.py index ec2b442..3944882 100644 --- a/pyproject_save_files.py +++ b/pyproject_save_files.py @@ -693,12 +693,15 @@ def dist_metadata(buildroot, record_path): return dist.metadata -def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_version, pyproject_record, prefix, varargs): +def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_version, pyproject_record, prefix, assert_license, varargs): """ Takes arguments from the %{pyproject_save_files} macro Returns tuple: list of paths for the %files section and list of module names for the %check section + + Raises ValueError when assert_license is true and no License-File (PEP 639) + is found. """ # On 32 bit architectures, sitelib equals to sitearch # This saves us browsing one directory twice @@ -710,11 +713,15 @@ def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_versio final_file_list = [] final_module_list = [] + # we assume OK when not asserting + license_ok = not assert_license + for record_path, files in parsed_records.items(): metadata = dist_metadata(buildroot, record_path) paths_dict = classify_paths( record_path, files, metadata, sitedirs, python_version, prefix ) + license_ok = license_ok or bool(paths_dict["metadata"]["licenses"]) final_file_list.extend( generate_file_list(paths_dict, globs, include_auto) @@ -723,6 +730,15 @@ def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_versio generate_module_list(paths_dict, globs) ) + if not license_ok: + raise ValueError( + "No License-File (PEP 639) in upstream metadata found. " + "Adjust the upstream metadata " + "if the project's build backend supports PEP 639 " + "or use `%pyproject_save_files -L` " + "and include the %license file in %files manually." + ) + return final_file_list, final_module_list @@ -734,6 +750,7 @@ def main(cli_args): cli_args.python_version, cli_args.pyproject_record, cli_args.prefix, + cli_args.assert_license, cli_args.varargs, ) @@ -747,7 +764,7 @@ def argparser(): prog="%pyproject_save_files", add_help=False, # custom usage to add +auto - usage="%(prog)s MODULE_GLOB [MODULE_GLOB ...] [+auto]", + usage="%(prog)s [-l|-L] MODULE_GLOB [MODULE_GLOB ...] [+auto]", ) parser.add_argument( '--help', action='help', @@ -764,6 +781,14 @@ def argparser(): r.add_argument("--pyproject-record", type=PosixPath, required=True, help=argparse.SUPPRESS) r.add_argument("--prefix", type=PosixPath, required=True, help=argparse.SUPPRESS) parser.add_argument( + "-l", "--assert-license", action="store_true", default=False, + help="Fail when no License-File (PEP 639) is found.", + ) + parser.add_argument( + "-L", "--no-assert-license", action="store_false", dest="assert_license", + help="Don't fail when no License-File (PEP 639) is found (the default).", + ) + parser.add_argument( "varargs", nargs="+", metavar="MODULE_GLOB", help="Shell-like glob matching top-level module names to save into %%{pyproject_files}", ) diff --git a/tests/escape_percentages.spec b/tests/escape_percentages.spec index 6466b2f..e0592a6 100644 --- a/tests/escape_percentages.spec +++ b/tests/escape_percentages.spec @@ -48,7 +48,7 @@ touch 'escape_percentages/one%%version' %install %pyproject_install -%pyproject_save_files escape_percentages +%pyproject_save_files -L escape_percentages touch '%{buildroot}/two%%version' diff --git a/tests/printrun.spec b/tests/printrun.spec index abca9e1..96a9caa 100644 --- a/tests/printrun.spec +++ b/tests/printrun.spec @@ -33,7 +33,7 @@ Building this tests that lang files are marked with %%lang in filelist. %install %pyproject_install -%pyproject_save_files printrun +auto +%pyproject_save_files -l printrun +auto %check @@ -52,4 +52,3 @@ grep -E '/printrun/__pycache__$' %{pyproject_files} %files -f %{pyproject_files} %doc README* -%license COPYING diff --git a/tests/python-distroinfo.spec b/tests/python-distroinfo.spec index 7303eca..e754324 100644 --- a/tests/python-distroinfo.spec +++ b/tests/python-distroinfo.spec @@ -42,7 +42,7 @@ sed -Ei "s/(, )?'pytest-runner'//" setup.py %install %pyproject_install -%pyproject_save_files distroinfo +%pyproject_save_files -l distroinfo %check @@ -52,4 +52,3 @@ sed -Ei "s/(, )?'pytest-runner'//" setup.py %files -n python3-distroinfo -f %{pyproject_files} %doc README.rst AUTHORS -%license LICENSE diff --git a/tests/python-django.spec b/tests/python-django.spec index e6488db..74f6d72 100644 --- a/tests/python-django.spec +++ b/tests/python-django.spec @@ -40,7 +40,7 @@ find -name "*.po" | xargs rm -f %install %pyproject_install -%pyproject_save_files django +%pyproject_save_files -l django %check @@ -56,6 +56,5 @@ diff tested.lang expected.lang %files -n python3-django -f %{pyproject_files} %doc README.rst -%license LICENSE %{_bindir}/django-admin %{_bindir}/django-admin.py diff --git a/tests/python-dns-lexicon.spec b/tests/python-dns-lexicon.spec index b9f5ca1..512baee 100644 --- a/tests/python-dns-lexicon.spec +++ b/tests/python-dns-lexicon.spec @@ -55,7 +55,8 @@ sed -i \ %install %pyproject_install -%pyproject_save_files lexicon +# the license is not marked as License-File by poetry-core, hence -L +%pyproject_save_files -L lexicon %check diff --git a/tests/python-entrypoints.spec b/tests/python-entrypoints.spec index 95e7791..15c518e 100644 --- a/tests/python-entrypoints.spec +++ b/tests/python-entrypoints.spec @@ -38,7 +38,8 @@ Summary: %{summary} %install %pyproject_install -%pyproject_save_files entrypoints +# the license is not marked as License-File, hence -L +%pyproject_save_files entrypoints -L %check diff --git a/tests/python-flit-core.spec b/tests/python-flit-core.spec index d20d2c2..e6831dc 100644 --- a/tests/python-flit-core.spec +++ b/tests/python-flit-core.spec @@ -42,7 +42,13 @@ cd .. %install %pyproject_install +# there is no license file marked as License-File, hence not using -l %pyproject_save_files flit_core +%check +# internal check for our macros, we assume there is no license +grep -F %%license %{pyproject_files} && exit 1 || true + + %files -n python3-flit-core -f %{pyproject_files} diff --git a/tests/python-getmac.spec b/tests/python-getmac.spec index 61863c8..6010d82 100644 --- a/tests/python-getmac.spec +++ b/tests/python-getmac.spec @@ -40,7 +40,7 @@ Summary: %{summary} %install %pyproject_install -%pyproject_save_files '*' +auto +%pyproject_save_files -l '*' +auto %check diff --git a/tests/python-httpbin.spec b/tests/python-httpbin.spec index 15d2d8e..9e0609a 100644 --- a/tests/python-httpbin.spec +++ b/tests/python-httpbin.spec @@ -56,7 +56,7 @@ sed -Ei 's/\bdef (test_(relative_)?redirect_(to_post|n_(equals_to|higher_than)_1 %install %pyproject_install -%pyproject_save_files httpbin +%pyproject_save_files -l httpbin %if %{with tests} @@ -72,4 +72,3 @@ sed -Ei 's/\bdef (test_(relative_)?redirect_(to_post|n_(equals_to|higher_than)_1 %files -n python3-httpbin -f %{pyproject_files} %doc README* -%license LICENSE* diff --git a/tests/python-ipykernel.spec b/tests/python-ipykernel.spec index abeceac..133d8bb 100644 --- a/tests/python-ipykernel.spec +++ b/tests/python-ipykernel.spec @@ -46,12 +46,11 @@ sed -i '/"debugpy/d' pyproject.toml setup.py %install %pyproject_install -%pyproject_save_files 'ipykernel*' +auto +%pyproject_save_files -l 'ipykernel*' +auto %check %pyproject_check_import -e '*.test*' -e 'ipykernel.gui*' -e 'ipykernel.pylab.*' -e 'ipykernel.trio*' %files -n python3-ipykernel -f %{pyproject_files} -%license COPYING.md %doc README.md diff --git a/tests/python-isort.spec b/tests/python-isort.spec index cde9156..9124d73 100644 --- a/tests/python-isort.spec +++ b/tests/python-isort.spec @@ -41,7 +41,7 @@ Summary: %{summary} %install %pyproject_install -%pyproject_save_files isort +%pyproject_save_files -l isort %check @@ -55,5 +55,4 @@ grep -F %{_bindir}/%{modname} %{pyproject_files} && exit 1 || true %files -n python%{python3_pkgversion}-%{modname} -f %{pyproject_files} %doc README.rst *.md -%license LICENSE %{_bindir}/%{modname} diff --git a/tests/python-ldap.spec b/tests/python-ldap.spec index 9f6da1e..d36029b 100644 --- a/tests/python-ldap.spec +++ b/tests/python-ldap.spec @@ -61,7 +61,7 @@ PYTHONPATH=%{pyproject_build_lib} %{python3} -c 'import _ldap' %install %pyproject_install # We can pass multiple globs -%pyproject_save_files 'ldap*' '*ldap' +%pyproject_save_files -l 'ldap*' '*ldap' %check @@ -104,7 +104,6 @@ test "%{pyproject_build_lib}" == "%{_builddir}/%{buildsubdir}/build/lib.%{python %files -n python3-ldap -f %{pyproject_files} -%license LICENCE %doc CHANGES README TODO Demo # Explicitly listed files can be combined with automation %pycached %{python3_sitearch}/ldif.py diff --git a/tests/python-markupsafe.spec b/tests/python-markupsafe.spec index f6c7310..756d508 100644 --- a/tests/python-markupsafe.spec +++ b/tests/python-markupsafe.spec @@ -26,7 +26,6 @@ Summary: %{summary} # In this spec, we put %%files early to test it still works %files -n python3-markupsafe -f %{pyproject_files} -%license LICENSE.rst %doc CHANGES.rst README.rst @@ -54,7 +53,7 @@ sed -Ei 's/sphinx\.git@([0-9a-f]+)/sphinx.git@\1#egg=sphinx/' requirements/docs. %install %pyproject_install -%pyproject_save_files markupsafe +%pyproject_save_files -l markupsafe %check diff --git a/tests/python-mistune.spec b/tests/python-mistune.spec index dfbf26f..94f0c5a 100644 --- a/tests/python-mistune.spec +++ b/tests/python-mistune.spec @@ -46,7 +46,7 @@ Summary: %summary %install %pyproject_install -%pyproject_save_files mistune +%pyproject_save_files -l mistune %check @@ -61,4 +61,3 @@ test -f %{buildroot}%{python3_sitearch}/mistune.cpython-*.so %files -n python%{python3_pkgversion}-mistune -f %{pyproject_files} %doc README.rst -%license LICENSE diff --git a/tests/python-openqa_client.spec b/tests/python-openqa_client.spec index fdf903d..ef6ac46 100644 --- a/tests/python-openqa_client.spec +++ b/tests/python-openqa_client.spec @@ -45,7 +45,7 @@ sed -i '/mock/d' tests.requires %install %pyproject_install -%pyproject_save_files %{pypi_name} +%pyproject_save_files -l %{pypi_name} %check @@ -54,4 +54,3 @@ sed -i '/mock/d' tests.requires %files -n python3-%{pypi_name} -f %{pyproject_files} %doc README.* -%license COPYING diff --git a/tests/python-pluggy.spec b/tests/python-pluggy.spec index 4bbdd50..10f422a 100644 --- a/tests/python-pluggy.spec +++ b/tests/python-pluggy.spec @@ -43,7 +43,7 @@ Summary: %{summary} %install %pyproject_install # There are no executables, but we are allowed to pass +auto anyway -%pyproject_save_files pluggy +auto +%pyproject_save_files pluggy +auto -l %check @@ -52,4 +52,3 @@ Summary: %{summary} %files -n python3-%{pypi_name} -f %{pyproject_files} %doc README.rst -%license LICENSE diff --git a/tests/python-poetry-core.spec b/tests/python-poetry-core.spec index 570a229..f133a7b 100644 --- a/tests/python-poetry-core.spec +++ b/tests/python-poetry-core.spec @@ -37,8 +37,11 @@ Summary: %{summary} %install %pyproject_install -%pyproject_save_files poetry +# the license is not marked as License-File by poetry-core, hence -L +%pyproject_save_files -L poetry +# internal check for our macros, -l must fail: +%pyproject_save_files -l poetry && exit 1 || true %files -n python3-poetry-core -f %{pyproject_files} %doc README.md diff --git a/tests/python-pytest.spec b/tests/python-pytest.spec index 2e48871..67725a4 100644 --- a/tests/python-pytest.spec +++ b/tests/python-pytest.spec @@ -54,7 +54,7 @@ sed -i 's/setuptools-scm\[toml\]>=6.2.3/setuptools-scm[toml]>=5/' pyproject.toml %install %pyproject_install -%pyproject_save_files '*pytest' +auto +%pyproject_save_files -l '*pytest' +auto %check @@ -70,4 +70,3 @@ sed -i 's/setuptools-scm\[toml\]>=6.2.3/setuptools-scm[toml]>=5/' pyproject.toml %files -n python3-%{pypi_name} -f %{pyproject_files} %doc README.rst %doc CHANGELOG.rst -%license LICENSE diff --git a/tests/python-setuptools.spec b/tests/python-setuptools.spec index 688a867..76f11aa 100644 --- a/tests/python-setuptools.spec +++ b/tests/python-setuptools.spec @@ -76,7 +76,7 @@ sed -i pytest.ini -e 's/ --flake8//' \ %install %pyproject_install -%pyproject_save_files setuptools pkg_resources _distutils_hack +%pyproject_save_files setuptools pkg_resources _distutils_hack -l # https://github.com/pypa/setuptools/issues/2709 rm -rf %{buildroot}%{python3_sitelib}/pkg_resources/tests/ diff --git a/tests/python-setuptools_scm.spec b/tests/python-setuptools_scm.spec index 394d732..3622b18 100644 --- a/tests/python-setuptools_scm.spec +++ b/tests/python-setuptools_scm.spec @@ -52,7 +52,7 @@ Summary: %{summary} %install %pyproject_install -%pyproject_save_files setuptools_scm +%pyproject_save_files -l setuptools_scm %check @@ -73,4 +73,3 @@ test "$(cat %{_pyproject_ghost_distinfo})" == "%ghost %{python3_sitelib}/setupto %files -n python3-setuptools_scm -f %{pyproject_files} %doc README.rst %doc CHANGELOG.rst -%license LICENSE diff --git a/tests/python-userpath.spec b/tests/python-userpath.spec index 69ca8ab..1151432 100644 --- a/tests/python-userpath.spec +++ b/tests/python-userpath.spec @@ -43,7 +43,7 @@ sed -Ei '/^(coverage)$/d' requirements-dev.txt %install %pyproject_install -%pyproject_save_files userpath +%pyproject_save_files -l userpath %check diff --git a/tests/python-virtualenv.spec b/tests/python-virtualenv.spec index 933cf84..a273b74 100644 --- a/tests/python-virtualenv.spec +++ b/tests/python-virtualenv.spec @@ -52,7 +52,7 @@ sed -i 's/_nonwrappers/_hookimpls/' tests/conftest.py %install %pyproject_install -%pyproject_save_files virtualenv +%pyproject_save_files -l virtualenv %{?el9: # old version of setuptools_scm produces files incompatible with # assumptions in virtualenv code, we append the expected attributes: diff --git a/tests/python-zope-event.spec b/tests/python-zope-event.spec index 5fc840f..4a5d4dd 100644 --- a/tests/python-zope-event.spec +++ b/tests/python-zope-event.spec @@ -32,7 +32,7 @@ Summary: %{summary} %install %pyproject_install -%pyproject_save_files zope +auto +%pyproject_save_files -l zope +auto %check # Internal check that the RECORD and REQUESTED files are @@ -42,5 +42,4 @@ test ! $(find %{buildroot}%{python3_sitelib}/ | grep -E "\.dist-info/REQUESTED$" %files -n python3-zope-event -f %{pyproject_files} %doc README.rst -%license LICENSE.txt diff --git a/tests/tldr.spec b/tests/tldr.spec index 6b24d5d..7152642 100644 --- a/tests/tldr.spec +++ b/tests/tldr.spec @@ -36,7 +36,7 @@ Building this tests: %install %pyproject_install -%pyproject_save_files tldr +auto +%pyproject_save_files -l tldr +auto %check # Internal check for our macros: tests we don't ship __pycache__ in bindir @@ -62,5 +62,4 @@ grep '^/app' %{pyproject_files} %endif %files -f %pyproject_files -%license LICENSE %doc README.md