Compare commits
27 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
398bc5a3a1 | ||
|
|
66df8eba2b | ||
|
|
9de8403851 | ||
|
|
82f71c1257 | ||
|
|
fb10e373de | ||
|
|
29a0225dc0 | ||
|
|
1a62987559 | ||
|
|
74a28a6262 | ||
|
|
23bd76152a | ||
|
|
0b0ac66461 | ||
|
|
bf63b51a3c | ||
|
|
8f77d4b3d5 | ||
|
|
5bd84d818f | ||
|
|
7ff4c68fbe | ||
|
|
d5eb57c3de | ||
|
|
11f7c31776 | ||
|
|
f6d9375c48 | ||
|
|
c3dde8dbb4 | ||
|
|
76ecac728b | ||
|
|
c072a10b22 | ||
|
|
c327bd5bee | ||
|
|
9079654b2f | ||
|
|
ad3c57536b | ||
|
|
b539d27ed0 | ||
|
|
7dc3ff8bbb | ||
|
|
4baebb6953 | ||
|
|
17fa249bdd |
6 changed files with 274 additions and 55 deletions
|
|
@ -1,9 +0,0 @@
|
|||
diff --git a/lexicon/cli.py b/lexicon/cli.py
|
||||
index ed2f0ef..73e88ef 100644
|
||||
--- a/lexicon/cli.py
|
||||
+++ b/lexicon/cli.py
|
||||
@@ -1,4 +1,3 @@
|
||||
-#!/usr/bin/env python
|
||||
"""Module for Lexicon command-line interface"""
|
||||
import json
|
||||
import logging
|
||||
38
create-local-tld-cache.py
Normal file
38
create-local-tld-cache.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
from pathlib import Path
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
sys.stderr.write('usage: %s <buildroot-sitelib>\n' % sys.argv[0])
|
||||
|
||||
buildroot_sitelib_dir = sys.argv[1]
|
||||
sys.path.insert(0, buildroot_sitelib_dir)
|
||||
|
||||
from tldextract.cache import get_cache_dir, DiskCache
|
||||
|
||||
if 'TLDEXTRACT_CACHE' not in os.environ:
|
||||
raise ValueError('must set "TLDEXTRACT_CACHE" environment variable')
|
||||
|
||||
local_cache = Path('/usr/share/publicsuffix/public_suffix_list.dat')
|
||||
if not local_cache.exists():
|
||||
raise FileNotFoundError(local_cache)
|
||||
|
||||
url_local_cache = f'file://{local_cache}'
|
||||
subprocess.run(['/usr/bin/tldextract', '--update', f'--suffix_list_url={url_local_cache}'], check=True)
|
||||
|
||||
cache = DiskCache(cache_dir=get_cache_dir())
|
||||
namespace = 'publicsuffix.org-tlds'
|
||||
cache_with_local_data = cache._key_to_cachefile_path(namespace, {'urls': (url_local_cache,), 'fallback_to_snapshot': True})
|
||||
func = lambda **kwargs: json.loads(Path(cache_with_local_data).read_text())
|
||||
|
||||
kwargs = {
|
||||
'cache': cache,
|
||||
'urls': ('https://publicsuffix.org/list/public_suffix_list.dat', 'https://raw.githubusercontent.com/publicsuffix/list/master/public_suffix_list.dat'),
|
||||
'fallback_to_snapshot': True,
|
||||
}
|
||||
cache.run_and_cache(func, namespace=namespace, kwargs=kwargs, hashed_argnames=('urls', 'fallback_to_snapshot'))
|
||||
|
||||
85
disable-poetry-light.patch
Normal file
85
disable-poetry-light.patch
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
diff -uNr lexicon-3.17.0/pyproject.toml lexicon-3.17.0.mod/pyproject.toml
|
||||
--- lexicon-3.17.0/pyproject.toml 2023-11-06 16:21:46.000000000 -0600
|
||||
+++ lexicon-3.17.0.mod/pyproject.toml 2024-02-01 04:28:53.861285119 -0600
|
||||
@@ -61,32 +61,6 @@
|
||||
# Extra "full" list must contain all other extras
|
||||
full = ["boto3", "localzone", "softlayer", "zeep", "oci"]
|
||||
|
||||
-[tool.poetry.group.dev.dependencies]
|
||||
-esbonio = "*"
|
||||
-packaging = "*"
|
||||
-pytest = "*"
|
||||
-pytest-cov = "*"
|
||||
-pytest-xdist = "*"
|
||||
-vcrpy = "*"
|
||||
-# We add flake8 conditionally to recent Python version to get modern versions
|
||||
-# of flake8 cleaned of several issues. It just means that linting cannot be done
|
||||
-# on the initial release of Python 3.8 (version 3.8.0). This is a decent constraint.
|
||||
-flake8 = { version = "*", python = ">=3.8.1" }
|
||||
-isort = "*"
|
||||
-tox = ">=4"
|
||||
-black = "*"
|
||||
-mypy = "*"
|
||||
-toml = "*"
|
||||
-types-PyYAML = "*"
|
||||
-types-pytz = "*"
|
||||
-types-requests = "*"
|
||||
-types-toml = "*"
|
||||
-
|
||||
-[tool.poetry.group.docs.dependencies]
|
||||
-sphinx = "*"
|
||||
-sphinx_rtd_theme = "*"
|
||||
-toml = "*"
|
||||
-
|
||||
[tool.poetry.scripts]
|
||||
lexicon = "lexicon._private.cli:main"
|
||||
|
||||
diff -uNr lexicon-3.17.0/setup.cfg lexicon-3.17.0.mod/setup.cfg
|
||||
--- lexicon-3.17.0/setup.cfg 2023-11-06 16:21:46.000000000 -0600
|
||||
+++ lexicon-3.17.0.mod/setup.cfg 2024-02-01 04:05:39.191101574 -0600
|
||||
@@ -5,45 +5,3 @@
|
||||
[tox:tox]
|
||||
envlist = cover,lint,mypy
|
||||
isolated_build = true
|
||||
-
|
||||
-# Standard env will run all test available for all providers.
|
||||
-[testenv]
|
||||
-allowlist_externals = poetry
|
||||
-extras =
|
||||
- full
|
||||
-setenv =
|
||||
- PYTEST_ADDOPTS = {env:PYTEST_ADDOPTS:--numprocesses auto}
|
||||
- PYTHONHASHSEED = 0
|
||||
-commands_pre =
|
||||
- poetry install --extras full --no-root --sync
|
||||
-commands =
|
||||
- poetry run pytest tests/ \
|
||||
- --junitxml=junit/test-results.xml --dist=loadfile
|
||||
-
|
||||
-# Cover env will run all test available for all providers with coverage enabled.
|
||||
-[testenv:cover]
|
||||
-commands =
|
||||
- poetry run pytest tests/ \
|
||||
- --junitxml=junit/test-results.xml --dist=loadfile \
|
||||
- --cov=lexicon --cov-report=term-missing --cov-report=xml
|
||||
-
|
||||
-# Light env will run all tests except for providers with optional dependencies.
|
||||
-[testenv:light]
|
||||
-extras =
|
||||
-commands_pre =
|
||||
- poetry install --no-root --sync
|
||||
-commands =
|
||||
- poetry run pytest tests/ \
|
||||
- --junitxml=junit/test-results.xml --dist=loadfile \
|
||||
- --xfail-providers-with-missing-deps
|
||||
-
|
||||
-# Lint env will check for code quality and errors, and fails if it does not match the minimal requirements.
|
||||
-[testenv:lint]
|
||||
-commands =
|
||||
- poetry run flake8 src tests
|
||||
-
|
||||
-
|
||||
-# Mypy env will check for types in the Lexicon codebase.
|
||||
-[testenv:mypy]
|
||||
-commands =
|
||||
- poetry run mypy src tests
|
||||
14
python-dns-lexicon-tox-config.patch
Normal file
14
python-dns-lexicon-tox-config.patch
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
diff --git a/pyproject.toml b/pyproject.toml
|
||||
index 4122ff9..936bbdc 100644
|
||||
--- a/pyproject.toml
|
||||
+++ b/pyproject.toml
|
||||
@@ -126,8 +126,7 @@ extend-ignore = [ "E203", "E501" ]
|
||||
envlist = [ "cover", "lint" , "mypy" ]
|
||||
|
||||
[tool.tox.env_run_base]
|
||||
-runner = "uv-venv-lock-runner"
|
||||
-with_dev = true
|
||||
+with_dev = false
|
||||
extras = [ "full" ]
|
||||
setenv.PYTEST_ADDOPTS = "--numprocesses auto"
|
||||
setenv.PYTHONHASHSEED = "0"
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
|
||||
%global forgeurl https://github.com/AnalogJ/lexicon
|
||||
Version: 3.11.7
|
||||
%global forgeurl https://github.com/dns-lexicon/dns-lexicon
|
||||
%global forgeversion 3.21.1
|
||||
Version: %{forgeversion}
|
||||
%forgemeta
|
||||
|
||||
%global pypi_name dns-lexicon
|
||||
|
||||
%if 0%{?rhel} >= 8
|
||||
# EPEL is currently missing dependencies used by the extras metapackages
|
||||
# EPEL is currently missing dependancies used by the tests
|
||||
# EPEL is currently missing dependencies used by the tests
|
||||
%bcond_with tests
|
||||
%bcond_with extras
|
||||
%else
|
||||
|
|
@ -15,24 +16,35 @@ Version: 3.11.7
|
|||
%bcond_without extras
|
||||
%endif
|
||||
|
||||
# disable tests for now
|
||||
%bcond_without tests
|
||||
|
||||
Name: python-%{pypi_name}
|
||||
Release: 1%{?dist}
|
||||
Release: 5%{?dist}
|
||||
Summary: Manipulate DNS records on various DNS providers in a standardized/agnostic way
|
||||
|
||||
License: MIT
|
||||
URL: %{forgeurl}
|
||||
# pypi releases don't contain necessary data to run the tests
|
||||
Source0: %{forgesource}
|
||||
Source1: create-local-tld-cache.py
|
||||
Patch: python-dns-lexicon-tox-config.patch
|
||||
BuildArch: noarch
|
||||
|
||||
Patch0: 0000-remove-shebang.patch
|
||||
|
||||
BuildRequires: python3-devel
|
||||
|
||||
# epel is missing full poetry and light packages needed for tests
|
||||
%if 0%{?rhel >= 8}
|
||||
#Patch: disable-poetry-light.patch
|
||||
%endif
|
||||
|
||||
# required to run the test suite
|
||||
%if %{with tests}
|
||||
BuildRequires: python3-pytest
|
||||
BuildRequires: python3-pytest-vcr
|
||||
BuildRequires: python3-pytest-xdist
|
||||
BuildRequires: publicsuffix-list
|
||||
BuildRequires: python3-tldextract
|
||||
%endif
|
||||
|
||||
|
||||
|
|
@ -86,17 +98,6 @@ This is the Python 3 version of the package.
|
|||
|
||||
|
||||
|
||||
%package -n python3-%{pypi_name}+ddns
|
||||
Summary: Meta-package for python3-%{pypi_name} and ddns provider
|
||||
%{?python_provide:%python_provide python3-%{pypi_name}+ddns}
|
||||
Requires: python3-%{pypi_name} = %{version}-%{release}
|
||||
|
||||
%description -n python3-%{pypi_name}+ddns
|
||||
This package installs no files. It requires python3-%{pypi_name} and all
|
||||
dependencies necessary to use the ddns provider.
|
||||
|
||||
|
||||
|
||||
%package -n python3-%{pypi_name}+gransy
|
||||
Summary: Meta-package for python3-%{pypi_name} and gransy provider
|
||||
%{?python_provide:%python_provide python3-%{pypi_name}+gransy}
|
||||
|
|
@ -108,6 +109,28 @@ dependencies necessary to use the gransy provider.
|
|||
|
||||
|
||||
|
||||
%package -n python3-%{pypi_name}+localzone
|
||||
Summary: Meta-package for python3-%{pypi_name} and localzone provider
|
||||
%{?python_provide:%python_provide python3-%{pypi_name}+localzone}
|
||||
Requires: python3-%{pypi_name} = %{version}-%{release}
|
||||
|
||||
%description -n python3-%{pypi_name}+localzone
|
||||
This package installs no files. It requires python3-%{pypi_name} and all
|
||||
dependencies necessary to use the localzone provider.
|
||||
|
||||
|
||||
|
||||
%package -n python3-%{pypi_name}+oci
|
||||
Summary: Meta-package for python3-%{pypi_name} and oci provider
|
||||
%{?python_provide:%python_provide python3-%{pypi_name}+oci}
|
||||
Requires: python3-%{pypi_name} = %{version}-%{release}
|
||||
|
||||
%description -n python3-%{pypi_name}+oci
|
||||
This package installs no files. It requires python3-%{pypi_name} and all
|
||||
dependencies necessary to use the oci provider.
|
||||
|
||||
|
||||
|
||||
%package -n python3-%{pypi_name}+route53
|
||||
Summary: Meta-package for python3-%{pypi_name} and Route 53 provider
|
||||
%{?python_provide:%python_provide python3-%{pypi_name}+route53}
|
||||
|
|
@ -120,51 +143,52 @@ dependencies necessary to use the Route 53 provider.
|
|||
|
||||
|
||||
%prep
|
||||
%autosetup -n lexicon-%{version} -p1
|
||||
%autosetup -n %{pypi_name}-%{version} -p1
|
||||
# Remove bundled egg-info
|
||||
rm -rf %{pypi_name}.egg-info
|
||||
rm setup.py
|
||||
rm -f uv.lock
|
||||
|
||||
%generate_buildrequires
|
||||
%if %{with extras}
|
||||
%pyproject_buildrequires -r -t -e light -x ddns,gransy,route53
|
||||
%pyproject_buildrequires -r -t -e light -x gransy,localzone,oci,route53
|
||||
%else
|
||||
%pyproject_buildrequires -r -t -e light
|
||||
%pyproject_buildrequires -r
|
||||
%endif
|
||||
|
||||
|
||||
%build
|
||||
# remove shebang
|
||||
sed -i '1d' src/lexicon/_private/cli.py
|
||||
%pyproject_wheel
|
||||
|
||||
%if %{with tests}
|
||||
%check
|
||||
# The following tests use tldextract which tries to fetch
|
||||
# https://raw.githubusercontent.com/publicsuffix/list/master/public_suffix_list.dat
|
||||
# on first invocation
|
||||
export TLDEXTRACT_CACHE=%{_builddir}/tldextract-cache
|
||||
|
||||
# tldextract tries to fetch "public_suffix_list.dat" from the internet on first
|
||||
# invocation.
|
||||
# (see https://github.com/john-kurkowski/tldextract/tree/master#note-about-caching)
|
||||
# - AutoProviderTests
|
||||
# - NamecheapProviderTests
|
||||
# - NamecheapManagedProviderTests
|
||||
# Disabling those until tldextract 3.3.0+ is available on Fedora.
|
||||
# With tldextract 3.3.0+ we can use Fedora's public suffix list by running
|
||||
# tldextract --update --suffix_list_url "file:///usr/share/publicsuffix/public_suffix_list.dat"
|
||||
# prior to running the tests
|
||||
TEST_SELECTOR="not AutoProviderTests and not NamecheapProviderTests and not NamecheapManagedProviderTests"
|
||||
# The "publicsuffix-list" package provides that data however we need to use
|
||||
# that to populate a local cache directory.Most of the work is done via:
|
||||
# $ tldextract --update --suffix_list_url "file:///usr/share/publicsuffix/public_suffix_list.dat"
|
||||
#
|
||||
# However tldextract uses the "file://" url as cache key while the tests use
|
||||
# "https://publicsuffix.org/list/public_suffix_list.dat". I did not find a way
|
||||
# get tldextract to use the https url so a small Python script will handle that.
|
||||
/usr/bin/python3 %{SOURCE1} %{buildroot}%{python3_sitelib}
|
||||
|
||||
# lexicon providers which do not work in Fedora due to missing dependencies:
|
||||
# - SoftLayerProviderTests
|
||||
# - LocalzoneProviderTests
|
||||
# - OciProviderTests
|
||||
# - OciInstancePrincipalProviderTests
|
||||
TEST_SELECTOR+=" and not SoftLayerProviderTests and not LocalzoneProviderTests and not OciProviderTests and not OciInstancePrincipalProviderTests"
|
||||
TEST_SELECTOR="not SoftLayerProviderTests"
|
||||
|
||||
%if %{without extras}
|
||||
TEST_SELECTOR+=" and not DDNSProviderTests and not GransyProviderTests and not Route53ProviderTests"
|
||||
TEST_SELECTOR+=" and not GransyProviderTests and not LocalzoneProviderTests and not OciProviderTests and not OciInstancePrincipalProviderTests and not Route53ProviderTests"
|
||||
%endif
|
||||
# The %%tox macro lacks features so we need to use pytest directly:
|
||||
# Miro Hrončok, 2020-09-11:
|
||||
# > I am afraid the %%tox macro can only work with "static" deps declaration,
|
||||
# > not with arbitrary installers invoked as commands, sorry about that.
|
||||
py.test-3 -v -k "${TEST_SELECTOR}" lexicon
|
||||
|
||||
# We do not use "--xfail-providers-with-missing-deps" because we want to detect
|
||||
# missing dependencies unless we already know that a certain provider will not
|
||||
# work.
|
||||
%pytest -v -k "${TEST_SELECTOR}" -n auto --dist=loadfile tests/
|
||||
%endif
|
||||
|
||||
%install
|
||||
|
|
@ -172,7 +196,6 @@ py.test-3 -v -k "${TEST_SELECTOR}" lexicon
|
|||
install -pm 0755 %{buildroot}/%{_bindir}/lexicon %{buildroot}/%{_bindir}/lexicon-%{python3_version}
|
||||
cd %{buildroot}/%{_bindir}
|
||||
ln -s lexicon-%{python3_version} lexicon-3
|
||||
rm -rf %{buildroot}%{python3_sitelib}/lexicon/tests
|
||||
|
||||
|
||||
%files -n python3-%{pypi_name}
|
||||
|
|
@ -188,10 +211,13 @@ rm -rf %{buildroot}%{python3_sitelib}/lexicon/tests
|
|||
# {{{
|
||||
%if %{with extras}
|
||||
|
||||
%files -n python3-%{pypi_name}+ddns
|
||||
%files -n python3-%{pypi_name}+gransy
|
||||
%{?python_extras_subpkg:%ghost %{python3_sitelib}/dns_lexicon-%{version}.dist-info}
|
||||
|
||||
%files -n python3-%{pypi_name}+gransy
|
||||
%files -n python3-%{pypi_name}+localzone
|
||||
%{?python_extras_subpkg:%ghost %{python3_sitelib}/dns_lexicon-%{version}.dist-info}
|
||||
|
||||
%files -n python3-%{pypi_name}+oci
|
||||
%{?python_extras_subpkg:%ghost %{python3_sitelib}/dns_lexicon-%{version}.dist-info}
|
||||
|
||||
%files -n python3-%{pypi_name}+route53
|
||||
|
|
@ -201,6 +227,71 @@ rm -rf %{buildroot}%{python3_sitelib}/lexicon/tests
|
|||
# }}}
|
||||
|
||||
%changelog
|
||||
* Fri Sep 19 2025 Python Maint <python-maint@redhat.com> - 3.21.1-5
|
||||
- Rebuilt for Python 3.14.0rc3 bytecode
|
||||
|
||||
* Fri Aug 15 2025 Python Maint <python-maint@redhat.com> - 3.21.1-4
|
||||
- Rebuilt for Python 3.14.0rc2 bytecode
|
||||
|
||||
* Fri Jul 25 2025 Fedora Release Engineering <releng@fedoraproject.org> - 3.21.1-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild
|
||||
|
||||
* Fri Jun 06 2025 Python Maint <python-maint@redhat.com> - 3.21.1-2
|
||||
- Rebuilt for Python 3.14
|
||||
|
||||
* Mon Apr 28 2025 Felix Schwarz <fschwarz@fedoraproject.org> - 3.21.1-1
|
||||
- update to 3.21.1
|
||||
|
||||
* Sat Apr 19 2025 Felix Schwarz <fschwarz@fedoraproject.org> - 3.21.0-1
|
||||
- update to 3.21.0
|
||||
|
||||
* Thu Jan 23 2025 Felix Schwarz <fschwarz@fedoraproject.org> - 3.20.1-1
|
||||
- update to 3.20.1
|
||||
|
||||
* Sat Jan 18 2025 Fedora Release Engineering <releng@fedoraproject.org> - 3.18.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild
|
||||
|
||||
* Sat Nov 23 2024 Ben Maconi <turboben@fedoraproject.org> - 3.18.0-1
|
||||
- Updated to 3.18.0
|
||||
|
||||
* Fri Jul 19 2024 Fedora Release Engineering <releng@fedoraproject.org> - 3.17.0-6
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild
|
||||
|
||||
* Thu Jun 27 2024 Python Maint <python-maint@redhat.com> - 3.17.0-5
|
||||
- Rebuilt for Python 3.13
|
||||
|
||||
* Thu Feb 29 2024 Jonathan Wright <jonathan@almalinux.org> - 3.17.0-4
|
||||
- Update spec for building on epel9
|
||||
|
||||
* Fri Jan 26 2024 Fedora Release Engineering <releng@fedoraproject.org> - 3.17.0-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Mon Jan 22 2024 Fedora Release Engineering <releng@fedoraproject.org> - 3.17.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Thu Nov 16 2023 Nick Bebout <nb@fedoraproject.org> - 3.17.0-1
|
||||
- Update to 3.17.0
|
||||
|
||||
* Fri Oct 13 2023 Jonathan Wright <jonathan@almalinux.org> - 3.15.1-1
|
||||
- Update to 3.15.1 rhbz#2232054
|
||||
|
||||
* Tue Aug 8 2023 Christian Schuermann <spike@fedoraproject.org> 3.13.0-1
|
||||
- Update to 3.13.0
|
||||
|
||||
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 3.12.0-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
|
||||
|
||||
* Tue Jul 04 2023 Python Maint <python-maint@redhat.com> - 3.12.0-2
|
||||
- Rebuilt for Python 3.12
|
||||
|
||||
* Sun Jun 11 2023 Christian Schuermann <spike@fedoraproject.org> 3.12.0-1
|
||||
- Update to 3.12.0
|
||||
- Add new duckdns extra package
|
||||
- Add localzone and oci extra packages since dependancies are now available on Fedora
|
||||
|
||||
* Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 3.11.7-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
|
||||
|
||||
* Thu Oct 27 2022 Christian Schuermann <spike@fedoraproject.org> 3.11.7-1
|
||||
- Update to 3.11.7
|
||||
|
||||
|
|
|
|||
2
sources
2
sources
|
|
@ -1 +1 @@
|
|||
SHA512 (lexicon-3.11.7.tar.gz) = 7d3becea6b9d60a4589e9d7e8de010d0b7e6a2c127d1ccc6981d1bff88d09f5a708fd3e9e22f66b8a0a77a331784340662de73607dd25d69c0342795c6afc765
|
||||
SHA512 (dns-lexicon-3.21.1.tar.gz) = 0f53001edf2425afe7b7c4315e974ee47c956b659964998b942943788711c57634eea2d7974e21005be7f0edf16800e6577c49c8789e34f2133c4f5be8ac7ac1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue