Escape weird paths generated by %pyproject_save_files
This commit is contained in:
parent
299caacb5b
commit
d204ac14cd
@ -104,6 +104,10 @@ export HOSTNAME="rpmbuild" # to speedup tox in network-less mock, see rhbz#1856
|
|||||||
%license LICENSE
|
%license LICENSE
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Jul 09 2021 Miro Hrončok <miro@hroncok.cz> - 0-44
|
||||||
|
- Escape weird paths generated by %%pyproject_save_files
|
||||||
|
- Fixes rhbz#1976363
|
||||||
|
|
||||||
* Thu Jul 01 2021 Tomas Hrnciar <thrnciar@redhat.com> - 0-43
|
* Thu Jul 01 2021 Tomas Hrnciar <thrnciar@redhat.com> - 0-43
|
||||||
- Generate BuildRequires from file
|
- Generate BuildRequires from file
|
||||||
- Fixes: rhbz#1936448
|
- Fixes: rhbz#1936448
|
||||||
|
@ -7,6 +7,10 @@ from collections import defaultdict
|
|||||||
from pathlib import PosixPath, PurePosixPath
|
from pathlib import PosixPath, PurePosixPath
|
||||||
|
|
||||||
|
|
||||||
|
# From RPM's build/files.c strtokWithQuotes delim argument
|
||||||
|
RPM_FILES_DELIMETERS = ' \n\t'
|
||||||
|
|
||||||
|
|
||||||
class BuildrootPath(PurePosixPath):
|
class BuildrootPath(PurePosixPath):
|
||||||
"""
|
"""
|
||||||
This path represents a path in a buildroot.
|
This path represents a path in a buildroot.
|
||||||
@ -224,6 +228,53 @@ def classify_paths(
|
|||||||
return paths
|
return paths
|
||||||
|
|
||||||
|
|
||||||
|
def escape_rpm_path(path):
|
||||||
|
"""
|
||||||
|
Escape special characters in string-paths or BuildrootPaths
|
||||||
|
|
||||||
|
E.g. a space in path otherwise makes RPM think it's multiple paths,
|
||||||
|
unless we put it in "quotes".
|
||||||
|
Or a literal % symbol in path might be expanded as a macro if not escaped.
|
||||||
|
|
||||||
|
Due to limitations in RPM, paths with spaces and double quotes are not supported.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
>>> escape_rpm_path(BuildrootPath('/usr/lib/python3.9/site-packages/setuptools'))
|
||||||
|
'/usr/lib/python3.9/site-packages/setuptools'
|
||||||
|
|
||||||
|
>>> escape_rpm_path('/usr/lib/python3.9/site-packages/setuptools/script (dev).tmpl')
|
||||||
|
'"/usr/lib/python3.9/site-packages/setuptools/script (dev).tmpl"'
|
||||||
|
|
||||||
|
>>> escape_rpm_path('/usr/share/data/100%valid.path')
|
||||||
|
'/usr/share/data/100%%%%%%%%valid.path'
|
||||||
|
|
||||||
|
>>> escape_rpm_path('/usr/share/data/100 % valid.path')
|
||||||
|
'"/usr/share/data/100 %%%%%%%% valid.path"'
|
||||||
|
|
||||||
|
>>> escape_rpm_path('/usr/share/data/1000 %% valid.path')
|
||||||
|
'"/usr/share/data/1000 %%%%%%%%%%%%%%%% valid.path"'
|
||||||
|
|
||||||
|
>>> escape_rpm_path('/usr/share/data/spaces and "quotes"')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
NotImplementedError: ...
|
||||||
|
"""
|
||||||
|
orig_path = path = str(path)
|
||||||
|
if "%" in path:
|
||||||
|
# Escaping by 8 %s has been verified in RPM 4.16 and 4.17, but probably not stable
|
||||||
|
# See this thread http://lists.rpm.org/pipermail/rpm-list/2021-June/002048.html
|
||||||
|
# On the CI, we build tests/escape_percentages.spec to verify this assumption
|
||||||
|
path = path.replace("%", "%" * 8)
|
||||||
|
if any(symbol in path for symbol in RPM_FILES_DELIMETERS):
|
||||||
|
if '"' in path:
|
||||||
|
# As far as we know, RPM cannot list such file individually
|
||||||
|
# See this thread http://lists.rpm.org/pipermail/rpm-list/2021-June/002048.html
|
||||||
|
raise NotImplementedError(f'" symbol in path with spaces is not supported by %pyproject_save_files: {orig_path!r}')
|
||||||
|
return f'"{path}"'
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
def generate_file_list(paths_dict, module_globs, include_others=False):
|
def generate_file_list(paths_dict, module_globs, include_others=False):
|
||||||
"""
|
"""
|
||||||
This function takes the classified paths_dict and turns it into lines
|
This function takes the classified paths_dict and turns it into lines
|
||||||
@ -238,16 +289,16 @@ def generate_file_list(paths_dict, module_globs, include_others=False):
|
|||||||
files = set()
|
files = set()
|
||||||
|
|
||||||
if include_others:
|
if include_others:
|
||||||
files.update(f"{p}" for p in paths_dict["other"]["files"])
|
files.update(f"{escape_rpm_path(p)}" for p in paths_dict["other"]["files"])
|
||||||
try:
|
try:
|
||||||
for lang_code in paths_dict["lang"][None]:
|
for lang_code in paths_dict["lang"][None]:
|
||||||
files.update(f"%lang({lang_code}) {path}" for path in paths_dict["lang"][None][lang_code])
|
files.update(f"%lang({lang_code}) {escape_rpm_path(p)}" for p in paths_dict["lang"][None][lang_code])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
files.update(f"{p}" for p in paths_dict["metadata"]["files"])
|
files.update(f"{escape_rpm_path(p)}" for p in paths_dict["metadata"]["files"])
|
||||||
for macro in "dir", "doc", "license":
|
for macro in "dir", "doc", "license":
|
||||||
files.update(f"%{macro} {p}" for p in paths_dict["metadata"][f"{macro}s"])
|
files.update(f"%{macro} {escape_rpm_path(p)}" for p in paths_dict["metadata"][f"{macro}s"])
|
||||||
|
|
||||||
modules = paths_dict["modules"]
|
modules = paths_dict["modules"]
|
||||||
done_modules = set()
|
done_modules = set()
|
||||||
@ -259,12 +310,12 @@ def generate_file_list(paths_dict, module_globs, include_others=False):
|
|||||||
if name not in done_modules:
|
if name not in done_modules:
|
||||||
try:
|
try:
|
||||||
for lang_code in paths_dict["lang"][name]:
|
for lang_code in paths_dict["lang"][name]:
|
||||||
files.update(f"%lang({lang_code}) {path}" for path in paths_dict["lang"][name][lang_code])
|
files.update(f"%lang({lang_code}) {escape_rpm_path(p)}" for p in paths_dict["lang"][name][lang_code])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
for module in modules[name]:
|
for module in modules[name]:
|
||||||
files.update(f"%dir {p}" for p in module["dirs"])
|
files.update(f"%dir {escape_rpm_path(p)}" for p in module["dirs"])
|
||||||
files.update(f"{p}" for p in module["files"])
|
files.update(f"{escape_rpm_path(p)}" for p in module["files"])
|
||||||
done_modules.add(name)
|
done_modules.add(name)
|
||||||
done_globs.add(glob)
|
done_globs.add(glob)
|
||||||
|
|
||||||
|
25
tests/escape_percentages.spec
Normal file
25
tests/escape_percentages.spec
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Name: escape_percentages
|
||||||
|
Version: 0
|
||||||
|
Release: 0
|
||||||
|
Summary: ...
|
||||||
|
License: MIT
|
||||||
|
BuildArch: noarch
|
||||||
|
|
||||||
|
%description
|
||||||
|
This spec file verifies that escaping percentage signs in paths is possible via
|
||||||
|
exactly 8 percentage signs in a filelist and directly in the %%files section.
|
||||||
|
It serves as a regression test for pyproject_save_files:escape_rpm_path().
|
||||||
|
When this breaks, the function needs to be adapted.
|
||||||
|
|
||||||
|
%install
|
||||||
|
# the paths on disk will have 1 percentage sign if we type 2 in the spec
|
||||||
|
# we use the word 'version' after the sign, as that is a known existing macro
|
||||||
|
touch '%{buildroot}/one%%version'
|
||||||
|
touch '%{buildroot}/two%%version'
|
||||||
|
|
||||||
|
# the filelist will contain 8 percentage signs when we type 16 in spec
|
||||||
|
echo '/one%%%%%%%%%%%%%%%%version' > filelist
|
||||||
|
test $(wc -c filelist | cut -f1 -d' ') -eq 20 # 8 signs + /one (4) + version (7) + newline (1)
|
||||||
|
|
||||||
|
%files -f filelist
|
||||||
|
/two%%%%%%%%version
|
@ -59,11 +59,6 @@ sed -i pytest.ini -e 's/ --flake8//' \
|
|||||||
rm -rf %{buildroot}%{python3_sitelib}/pkg_resources/tests/
|
rm -rf %{buildroot}%{python3_sitelib}/pkg_resources/tests/
|
||||||
sed -i '/tests/d' %{pyproject_files}
|
sed -i '/tests/d' %{pyproject_files}
|
||||||
|
|
||||||
# Paths with spaces are not properly protected by %%pyproject_save_files
|
|
||||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1976363
|
|
||||||
# This workaround will most likely break once fixed
|
|
||||||
sed -Ei 's|/(.+) (.+)|"/\1 \2"|' %{pyproject_files}
|
|
||||||
|
|
||||||
|
|
||||||
%check
|
%check
|
||||||
# https://github.com/pypa/setuptools/discussions/2607
|
# https://github.com/pypa/setuptools/discussions/2607
|
||||||
|
@ -79,6 +79,9 @@
|
|||||||
- fake_requirements:
|
- fake_requirements:
|
||||||
dir: .
|
dir: .
|
||||||
run: ./mocktest.sh fake-requirements
|
run: ./mocktest.sh fake-requirements
|
||||||
|
- escape_percentages:
|
||||||
|
dir: .
|
||||||
|
run: rpmbuild -ba escape_percentages.spec
|
||||||
required_packages:
|
required_packages:
|
||||||
- mock
|
- mock
|
||||||
- rpmdevtools
|
- rpmdevtools
|
||||||
|
Loading…
Reference in New Issue
Block a user