diff --git a/.ci/run_container.sh b/.ci/run_container.sh index ed8deb3..d1a837c 100755 --- a/.ci/run_container.sh +++ b/.ci/run_container.sh @@ -69,8 +69,8 @@ else fi export PULP_CONTENT_ORIGIN -PULP_DJANGO_SECRET="$(python3 -c "import secrets; print(secrets.token_urlsafe(50))")" -export PULP_DJANGO_SECRET +PULP_SECRET_KEY="$(python3 -c "import secrets; print(secrets.token_urlsafe(50))")" +export PULP_SECRET_KEY "${CONTAINER_RUNTIME}" \ run ${RM:+--rm} \ @@ -82,7 +82,7 @@ export PULP_DJANGO_SECRET ${PULP_DOMAIN_ENABLED:+--env PULP_DOMAIN_ENABLED} \ ${PULP_ENABLED_PLUGINS:+--env PULP_ENABLED_PLUGINS} \ --env PULP_CONTENT_ORIGIN \ - --env PULP_DJANGO_SECRET \ + --env PULP_SECRET_KEY \ --detach \ --name "pulp-ephemeral" \ --volume "${PULP_CLI_TEST_TMPDIR}/settings:/etc/pulp${SELINUX:+:Z}" \ diff --git a/.ci/scripts/check_cli_dependencies.py b/.ci/scripts/check_cli_dependencies.py index 46da068..74b0b1f 100755 --- a/.ci/scripts/check_cli_dependencies.py +++ b/.ci/scripts/check_cli_dependencies.py @@ -26,8 +26,8 @@ def dependencies(path: Path) -> t.Iterator[Requirement]: base_path = Path(__file__).parent.parent.parent glue_path = base_path / GLUE_DIR - cli_dependency = next((r for r in dependencies(base_path) if r.name == "pulp-cli")) - glue_dependency = next((r for r in dependencies(glue_path) if r.name == "pulp-glue")) + cli_dependency = next(r for r in dependencies(base_path) if r.name == "pulp-cli") + glue_dependency = next(r for r in dependencies(glue_path) if r.name == "pulp-glue") if cli_dependency.specifier != glue_dependency.specifier: print("🪢 CLI and GLUE dependencies mismatch:") diff --git a/.ci/scripts/check_click_for_mypy.py b/.ci/scripts/check_click_for_mypy.py index 33ecf4c..d4b1aea 100755 --- a/.ci/scripts/check_click_for_mypy.py +++ b/.ci/scripts/check_click_for_mypy.py @@ -5,7 +5,7 @@ # "packaging>=25.0,<25.1", # ] # /// - +import sys from importlib import metadata from packaging.version import Version @@ -15,4 +15,4 @@ if click_version < Version("8.1.1"): print("🚧 Linting with mypy is currently only supported with click>=8.1.1. 🚧") print("🔧 Please run `pip install click>=8.1.1` first. 🔨") - exit(1) + sys.exit(1) diff --git a/.ci/scripts/collect_changes.py b/.ci/scripts/collect_changes.py index 80d7477..9ed2b48 100755 --- a/.ci/scripts/collect_changes.py +++ b/.ci/scripts/collect_changes.py @@ -9,10 +9,12 @@ import itertools import re +import typing as t from pathlib import Path import tomllib from git import GitCommandError, Repo +from packaging.version import Version from packaging.version import parse as parse_version # Read Towncrier settings @@ -51,7 +53,7 @@ ) -def get_changelog(repo, branch): +def get_changelog(repo: Repo, branch: str) -> str: branch_tc_settings = tomllib.loads(repo.git.show(f"{branch}:pyproject.toml"))["tool"][ "towncrier" ] @@ -59,7 +61,7 @@ def get_changelog(repo, branch): return repo.git.show(f"{branch}:{branch_changelog_file}") + "\n" -def _tokenize_changes(splits): +def _tokenize_changes(splits: list[str]) -> t.Iterator[list[Version | str]]: assert len(splits) % 3 == 0 for i in range(len(splits) // 3): title = splits[3 * i] @@ -67,13 +69,13 @@ def _tokenize_changes(splits): yield [version, title + splits[3 * i + 2]] -def split_changelog(changelog): +def split_changelog(changelog: str) -> tuple[str, list[list[Version | str]]]: preamble, rest = changelog.split(START_STRING, maxsplit=1) split_rest = re.split(TITLE_REGEX, rest) return preamble + START_STRING + split_rest[0], list(_tokenize_changes(split_rest[1:])) -def main(): +def main() -> None: repo = Repo(Path.cwd()) remote = repo.remotes[0] branches = [ref for ref in remote.refs if re.match(r"^([0-9]+)\.([0-9]+)$", ref.remote_head)] @@ -91,7 +93,7 @@ def main(): except GitCommandError: print("No changelog found on this branch.") continue - dummy, changes = split_changelog(changelog) + _dummy, changes = split_changelog(changelog) new_changes = sorted(main_changes + changes, key=lambda x: x[0], reverse=True) # Now remove duplicates (retain the first one) main_changes = [new_changes[0]] diff --git a/pulp-glue-gem/pyproject.toml b/pulp-glue-gem/pyproject.toml index 2f4c4d9..eea0565 100644 --- a/pulp-glue-gem/pyproject.toml +++ b/pulp-glue-gem/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "pulp-glue<0.41,>=0.23.1", + "pulp-glue<0.41,>=0.32.4", ] [project.urls] @@ -65,5 +65,9 @@ line-length = 100 [tool.ruff.lint] # This section is managed by the cookiecutter templates. select = ["E4", "E7", "E9", "F"] -extend-select = ["I", "INT", "PTH"] +extend-select = ["FURB", "I", "INT", "PTH", "SIM1", "TID", "T10", "UP"] + +[tool.ruff.lint.flake8-tidy-imports.banned-api] +# This section is managed by the cookiecutter templates. +"distutils".msg = "The 'distutils' module has been deprecated since Python 3.9." diff --git a/pyproject.toml b/pyproject.toml index a46f528..a0631fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers=[ "Typing :: Typed", ] dependencies = [ - "pulp-cli<0.41,>=0.23.1", + "pulp-cli<0.41,>=0.32.4", "pulp-glue-gem==0.7.0.dev", ] @@ -194,13 +194,18 @@ line-length = 100 [tool.ruff.lint] # This section is managed by the cookiecutter templates. select = ["E4", "E7", "E9", "F"] -extend-select = ["I", "INT", "PTH"] +extend-select = ["FURB", "I", "INT", "PTH", "SIM1", "TID", "T10", "UP"] [tool.ruff.lint.isort] # This section is managed by the cookiecutter templates. sections = { second-party = ["pulp_glue"] } section-order = ["future", "standard-library", "third-party", "second-party", "first-party", "local-folder"] +[tool.ruff.lint.flake8-tidy-imports.banned-api] +# This section is managed by the cookiecutter templates. +"distutils".msg = "The 'distutils' module has been deprecated since Python 3.9." +"pulpcore.cli.common.generic".msg = "This module moved to 'pulp_cli.generic'." + [tool.uv.sources] # This section is managed by the cookiecutter templates. @@ -226,7 +231,7 @@ dev = [ lint = [ {include-group = "test"}, "mypy~=1.20.0", - "ruff~=0.15.1", + "ruff~=0.16.0", "shellcheck-py~=0.11.0.1", "types-pygments", "types-pyyaml", diff --git a/src/pulpcore/cli/gem/__init__.py b/src/pulpcore/cli/gem/__init__.py index 29db6b5..58f74cb 100644 --- a/src/pulpcore/cli/gem/__init__.py +++ b/src/pulpcore/cli/gem/__init__.py @@ -1,7 +1,7 @@ from typing import Any import click -from pulpcore.cli.common.generic import pulp_group +from pulp_cli.generic import pulp_group from pulp_glue.common.i18n import get_translation diff --git a/src/pulpcore/cli/gem/content.py b/src/pulpcore/cli/gem/content.py index 224624a..c7131c1 100644 --- a/src/pulpcore/cli/gem/content.py +++ b/src/pulpcore/cli/gem/content.py @@ -1,7 +1,7 @@ import typing as t import click -from pulpcore.cli.common.generic import ( +from pulp_cli.generic import ( PulpCLIContext, chunk_size_option, create_command, @@ -41,8 +41,8 @@ def _sha256_callback(ctx: click.Context, param: click.Parameter, value: str) -> def _sha256_artifact_callback( - ctx: click.Context, param: click.Parameter, value: t.Optional[str] -) -> t.Optional[t.Union[str, PulpEntityContext]]: + ctx: click.Context, param: click.Parameter, value: str | None +) -> str | PulpEntityContext | None: # Pass None and "" verbatim if value: pulp_ctx = ctx.find_object(PulpCLIContext) @@ -133,7 +133,7 @@ def upload( /, file: t.IO[bytes], chunk_size: int, - repository: t.Optional[PulpRepositoryContext], + repository: PulpRepositoryContext | None, ) -> None: """Create a file content unit by uploading a file""" assert isinstance(entity_ctx, PulpGemContentContext) diff --git a/src/pulpcore/cli/gem/distribution.py b/src/pulpcore/cli/gem/distribution.py index 7b63b34..d8c76ec 100644 --- a/src/pulpcore/cli/gem/distribution.py +++ b/src/pulpcore/cli/gem/distribution.py @@ -1,5 +1,5 @@ import click -from pulpcore.cli.common.generic import ( +from pulp_cli.generic import ( PulpCLIContext, common_distribution_create_options, create_command, diff --git a/src/pulpcore/cli/gem/publication.py b/src/pulpcore/cli/gem/publication.py index af6148a..d70eb14 100644 --- a/src/pulpcore/cli/gem/publication.py +++ b/src/pulpcore/cli/gem/publication.py @@ -1,5 +1,5 @@ import click -from pulpcore.cli.common.generic import ( +from pulp_cli.generic import ( PulpCLIContext, create_command, destroy_command, diff --git a/src/pulpcore/cli/gem/remote.py b/src/pulpcore/cli/gem/remote.py index ab1ff1b..db97dc0 100644 --- a/src/pulpcore/cli/gem/remote.py +++ b/src/pulpcore/cli/gem/remote.py @@ -1,5 +1,5 @@ import click -from pulpcore.cli.common.generic import ( +from pulp_cli.generic import ( PulpCLIContext, common_remote_create_options, common_remote_update_options, diff --git a/src/pulpcore/cli/gem/repository.py b/src/pulpcore/cli/gem/repository.py index 08292e1..1fb7ed2 100644 --- a/src/pulpcore/cli/gem/repository.py +++ b/src/pulpcore/cli/gem/repository.py @@ -1,7 +1,7 @@ import typing as t import click -from pulpcore.cli.common.generic import ( +from pulp_cli.generic import ( PulpCLIContext, create_command, create_content_json_callback, @@ -136,14 +136,14 @@ def sync( repository_ctx: PulpRepositoryContext, /, remote: EntityFieldDefinition, - mirror: t.Optional[bool], + mirror: bool | None, ) -> None: """ Sync the repository from a remote source. If remote is not specified sync will try to use the default remote associated with the repository """ - body: t.Dict[str, t.Any] = {} + body: dict[str, t.Any] = {} repository = repository_ctx.entity if mirror is not None: body["mirror"] = mirror diff --git a/tests/scripts/pulp_gem/test_distribution.sh b/tests/scripts/pulp_gem/test_distribution.sh index 2e59215..ca50ce0 100755 --- a/tests/scripts/pulp_gem/test_distribution.sh +++ b/tests/scripts/pulp_gem/test_distribution.sh @@ -14,13 +14,6 @@ cleanup() { } trap cleanup EXIT -if [ "$VERIFY_SSL" = "false" ] -then - curl_opt="-k" -else - curl_opt="" -fi - expect_succ pulp gem remote create --name "cli_test_gem_remote" --url "$GEM_REMOTE_URL" expect_succ pulp gem repository create --name "cli_test_gem_repository" expect_succ pulp gem repository sync --repository "cli_test_gem_repository" --remote "cli_test_gem_remote" @@ -32,9 +25,6 @@ expect_succ pulp gem distribution create \ --base-path "cli_test_gem_distro" \ --publication "$PUBLICATION_HREF" HREF="$(echo "$OUTPUT" | jq -r '.pulp_href')" -BASE_URL="$(echo "$OUTPUT" | jq -r '.base_url')" - -expect_succ curl $curl_opt --head --fail "${BASE_URL}specs.4.8" expect_succ pulp gem distribution update \ --distribution "$HREF" \ diff --git a/tests/test_help_pages.py b/tests/test_help_pages.py index 91be5ac..0acf7ad 100644 --- a/tests/test_help_pages.py +++ b/tests/test_help_pages.py @@ -10,7 +10,7 @@ load_plugins() -def traverse_commands(command: click.Command, args: t.List[str]) -> t.Iterator[t.List[str]]: +def traverse_commands(command: click.Command, args: list[str]) -> t.Iterator[list[str]]: yield args if isinstance(command, click.Group): @@ -18,15 +18,14 @@ def traverse_commands(command: click.Command, args: t.List[str]) -> t.Iterator[t yield from traverse_commands(sub, args + [name]) params = command.params - if params: - if "--type" in params[0].opts: - # iterate over commands with specific context types - assert isinstance(params[0].type, click.Choice) - for context_type in params[0].type.choices: - yield args + ["--type", context_type] + if params and "--type" in params[0].opts: + # iterate over commands with specific context types + assert isinstance(params[0].type, click.Choice) + for context_type in params[0].type.choices: + yield args + ["--type", context_type] - for name, sub in command.commands.items(): - yield from traverse_commands(sub, args + ["--type", context_type, name]) + for name, sub in command.commands.items(): + yield from traverse_commands(sub, args + ["--type", context_type, name]) def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: