diff --git a/.ci/gen_certs.py b/.ci/gen_certs.py index 5026eff..d55b0b9 100644 --- a/.ci/gen_certs.py +++ b/.ci/gen_certs.py @@ -1,15 +1,18 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "trustme>=1.2.1,<1.3.0", +# ] +# /// + import argparse import os import sys -import typing as t import trustme -def main(argv: t.Optional[t.List[str]] = None) -> None: - if argv is None: - argv = sys.argv[1:] - +def main() -> None: parser = argparse.ArgumentParser(prog="gen_certs") parser.add_argument( "-d", @@ -18,7 +21,7 @@ def main(argv: t.Optional[t.List[str]] = None) -> None: help="Directory where certificates and keys are written to. Defaults to cwd.", ) - args = parser.parse_args(argv) + args = parser.parse_args(sys.argv[1:]) cert_dir = args.dir if not os.path.isdir(cert_dir): diff --git a/.ci/scripts/calc_constraints.py b/.ci/scripts/calc_constraints.py new file mode 100755 index 0000000..ca8e11e --- /dev/null +++ b/.ci/scripts/calc_constraints.py @@ -0,0 +1,119 @@ +#!/bin/python3 +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "packaging>=25.0,<25.1", +# "tomli>=2.3.0,<2.4.0;python_version<'3.11'", +# ] +# /// + +import argparse +import fileinput +import sys + +from packaging.requirements import Requirement +from packaging.version import Version + +try: + import tomllib +except ImportError: + import tomli as tomllib + + +def split_comment(line): + split_line = line.split("#", maxsplit=1) + try: + comment = " # " + split_line[1].strip() + except IndexError: + comment = "" + return split_line[0].strip(), comment + + +def to_upper_bound(req): + try: + requirement = Requirement(req) + except ValueError: + return f"# UNPARSABLE: {req}" + else: + for spec in requirement.specifier: + if spec.operator == "~=": + return f"# NO BETTER CONSTRAINT: {req}" + if spec.operator == "<=": + operator = "==" + max_version = spec.version + return f"{requirement.name}{operator}{max_version}" + if spec.operator == "<": + operator = "~=" + version = Version(spec.version) + if version.micro != 0: + max_version = f"{version.major}.{version.minor}.{version.micro - 1}" + elif version.minor != 0: + max_version = f"{version.major}.{version.minor - 1}" + elif version.major != 0: + max_version = f"{version.major - 1}.0" + else: + return f"# NO BETTER CONSTRAINT: {req}" + return f"{requirement.name}{operator}{max_version}" + return f"# NO UPPER BOUND: {req}" + + +def to_lower_bound(req): + try: + requirement = Requirement(req) + except ValueError: + return f"# UNPARSABLE: {req}" + else: + for spec in requirement.specifier: + if spec.operator == ">=": + if requirement.name == "pulpcore": + # Currently an exception to allow for pulpcore bugfix releases. + # TODO Semver libraries should be allowed too. + operator = "~=" + else: + operator = "==" + min_version = spec.version + return f"{requirement.name}{operator}{min_version}" + return f"# NO LOWER BOUND: {req}" + + +def main(): + """Calculate constraints for the lower bound of dependencies where possible.""" + parser = argparse.ArgumentParser( + prog=sys.argv[0], + description="Calculate constraints for the lower or upper bound of dependencies where " + "possible.", + ) + parser.add_argument("-u", "--upper", action="store_true") + parser.add_argument("filename", nargs="*") + args = parser.parse_args() + + modifier = to_upper_bound if args.upper else to_lower_bound + + req_files = [filename for filename in args.filename if not filename.endswith("pyproject.toml")] + pyp_files = [filename for filename in args.filename if filename.endswith("pyproject.toml")] + if req_files: + with fileinput.input(files=req_files) as req_file: + for line in req_file: + if line.strip().startswith("#"): + # Shortcut comment only lines + print(line.strip()) + else: + req, comment = split_comment(line) + new_req = modifier(req) + print(new_req + comment) + for filename in pyp_files: + with open(filename, "rb") as fp: + pyproject = tomllib.load(fp) + for req in pyproject["project"]["dependencies"]: + new_req = modifier(req) + print(new_req) + optional_dependencies = pyproject["project"].get("optional-dependencies") + if optional_dependencies: + for opt in optional_dependencies.values(): + for req in opt: + new_req = modifier(req) + print(new_req) + + +if __name__ == "__main__": + main() diff --git a/.ci/scripts/check_cli_dependencies.py b/.ci/scripts/check_cli_dependencies.py index 714eea4..cd99f60 100755 --- a/.ci/scripts/check_cli_dependencies.py +++ b/.ci/scripts/check_cli_dependencies.py @@ -1,8 +1,15 @@ #!/bin/env python3 -import tomllib +# /// script +# requires-python = ">=3.11" +# dependencies = [ +# "packaging>=25.0,<25.1", +# ] +# /// + import typing as t from pathlib import Path +import tomllib from packaging.requirements import Requirement GLUE_DIR = "pulp-glue-deb" diff --git a/.ci/scripts/check_click_for_mypy.py b/.ci/scripts/check_click_for_mypy.py index 408a7c6..33ecf4c 100755 --- a/.ci/scripts/check_click_for_mypy.py +++ b/.ci/scripts/check_click_for_mypy.py @@ -1,4 +1,10 @@ #!/bin/env python3 +# /// script +# requires-python = ">=3.11" +# dependencies = [ +# "packaging>=25.0,<25.1", +# ] +# /// from importlib import metadata diff --git a/.ci/scripts/collect_changes.py b/.ci/scripts/collect_changes.py index e6cb0da..499265c 100755 --- a/.ci/scripts/collect_changes.py +++ b/.ci/scripts/collect_changes.py @@ -1,10 +1,17 @@ #!/bin/env python3 +# /// script +# requires-python = ">=3.11" +# dependencies = [ +# "gitpython>=3.1.46,<3.2.0", +# "packaging>=25.0,<25.1", +# ] +# /// import itertools import os import re -import tomllib +import tomllib from git import GitCommandError, Repo from packaging.version import parse as parse_version diff --git a/.ci/scripts/pr_labels.py b/.ci/scripts/pr_labels.py index 9d637b6..49eb4ad 100755 --- a/.ci/scripts/pr_labels.py +++ b/.ci/scripts/pr_labels.py @@ -1,12 +1,18 @@ #!/bin/env python3 +# /// script +# requires-python = ">=3.11" +# dependencies = [ +# "gitpython>=3.1.46,<3.2.0", +# ] +# /// # This script is running with elevated privileges from the main branch against pull requests. import re import sys -import tomllib from pathlib import Path +import tomllib from git import Repo diff --git a/.ci/scripts/validate_commit_message.py b/.ci/scripts/validate_commit_message.py index 99d9a45..ac19868 100644 --- a/.ci/scripts/validate_commit_message.py +++ b/.ci/scripts/validate_commit_message.py @@ -1,10 +1,17 @@ +# /// script +# requires-python = ">=3.11" +# dependencies = [ +# "gitpython>=3.1.46,<3.2.0", +# ] +# /// + import os import re import subprocess import sys -import tomllib from pathlib import Path +import tomllib from github import Github with open("pyproject.toml", "rb") as fp: diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 145c364..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -version: 2 -updates: -- package-ecosystem: "pip" - directory: "/" - schedule: - interval: "daily" - commit-message: - prefix: "[PIP] " - open-pull-requests-limit: 10 -- package-ecosystem: "pip" - directory: "/pulp-glue-deb" - schedule: - interval: "daily" - commit-message: - prefix: "[PIP] " - open-pull-requests-limit: 10 -- package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - commit-message: - prefix: "[GHA] " - open-pull-requests-limit: 10 -... diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f3c112..c4b3136 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,9 +17,9 @@ jobs: ${{ runner.os }}-pip- - name: "Set up Python" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: - python-version: "3.11" + python-version: "3.14" - name: "Install python dependencies" run: | pip install build setuptools wheel diff --git a/.github/workflows/collect_changes.yml b/.github/workflows/collect_changes.yml index 7db5e6e..12da36c 100644 --- a/.github/workflows/collect_changes.yml +++ b/.github/workflows/collect_changes.yml @@ -12,9 +12,9 @@ jobs: with: ref: "main" fetch-depth: 0 - - uses: "actions/setup-python@v5" + - uses: "actions/setup-python@v6" with: - python-version: "3.11" + python-version: "3.x" - name: "Setup git" run: | git config user.name pulpbot diff --git a/.github/workflows/cookiecutter.yml b/.github/workflows/cookiecutter.yml index 7ac02a3..9d21125 100644 --- a/.github/workflows/cookiecutter.yml +++ b/.github/workflows/cookiecutter.yml @@ -22,9 +22,9 @@ jobs: token: "${{ secrets.RELEASE_TOKEN }}" path: "pulp-cli-deb" - name: "Set up Python" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: - python-version: "3.11" + python-version: "3.x" - name: "Setup git" run: | git config user.name pulpbot @@ -57,4 +57,51 @@ jobs: env: GH_TOKEN: "${{ secrets.RELEASE_TOKEN }}" continue-on-error: true + update-dependencies: + runs-on: "ubuntu-latest" + steps: + - uses: "actions/checkout@v5" + with: + repository: "pulp/pulp-cli" + path: "pulp-cli" + - uses: "actions/checkout@v5" + with: + token: "${{ secrets.RELEASE_TOKEN }}" + path: "pulp-cli-deb" + - name: "Set up Python" + uses: "actions/setup-python@v6" + with: + python-version: "3.x" + - name: "Setup git" + run: | + git config user.name pulpbot + git config user.email pulp-infra@redhat.com + - name: "Install python dependencies" + run: | + pip install packaging tomlkit + - name: "Apply cookiecutter templates" + run: | + ../pulp-cli/cookiecutter/update_pulp_cli.py + if [ "$(git status --porcelain)" ] + then + git add . + git commit -m "Update CLI and GLUE" + fi + - name: "Create Pull Request" + uses: "peter-evans/create-pull-request@v7" + id: "create_pr" + with: + token: "${{ secrets.RELEASE_TOKEN }}" + title: "Update CLI and GLUE" + body: "" + branch: "update_cli" + delete-branch: true + path: "pulp-cli-deb" + - name: "Mark PR automerge" + run: | + gh pr merge --rebase --auto "${{ steps.create_pr.outputs.pull-request-number }}" + if: "steps.create_pr.outputs.pull-request-number" + env: + GH_TOKEN: "${{ secrets.RELEASE_TOKEN }}" + continue-on-error: true ... diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 05394e5..8bb1a5d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: matrix: python: - "3.11" - - "3.13" + - "3.14" steps: - uses: "actions/checkout@v5" - uses: "actions/cache@v4" @@ -27,7 +27,7 @@ jobs: with: name: "pulp_cli_packages" - name: "Set up Python" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "${{ matrix.python }}" - name: "Install python dependencies" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a91a349..41d8d7f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -30,9 +30,9 @@ jobs: with: fetch-depth: 0 - name: "Set up Python" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: - python-version: "3.11" + python-version: "3.x" - name: "Install python dependencies" run: | pip install toml pygithub diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml index 912d1aa..0d3a228 100644 --- a/.github/workflows/pr_checks.yml +++ b/.github/workflows/pr_checks.yml @@ -22,15 +22,15 @@ jobs: - uses: "actions/checkout@v5" with: fetch-depth: 0 - - uses: "actions/setup-python@v5" + - uses: "actions/setup-python@v6" with: - python-version: "3.12" + python-version: "3.x" - name: "Determine PR labels" run: | pip install GitPython==3.1.42 git fetch origin ${{ github.event.pull_request.head.sha }} python .ci/scripts/pr_labels.py "origin/${{ github.base_ref }}" "${{ github.event.pull_request.head.sha }}" >> "$GITHUB_ENV" - - uses: "actions/github-script@v7" + - uses: "actions/github-script@v8" name: "Apply PR Labels" with: script: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3b4770e..c6cfb19 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,7 +20,7 @@ jobs: with: name: "pulp_cli_packages" - name: "Set up Python" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "3.x" - name: "Install dependencies" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 751f35a..3c40ec1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: with: token: "${{ secrets.RELEASE_TOKEN }}" - name: "Set up Python" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "3.x" - name: "Install dependencies" diff --git a/.github/workflows/release_branch.yml b/.github/workflows/release_branch.yml index f81ea9b..be0c5e5 100644 --- a/.github/workflows/release_branch.yml +++ b/.github/workflows/release_branch.yml @@ -11,9 +11,9 @@ jobs: with: token: "${{ secrets.RELEASE_TOKEN }}" - name: "Set up Python" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: - python-version: "3.11" + python-version: "3.x" - name: "Setup git" run: | git config user.name pulpbot @@ -41,7 +41,7 @@ jobs: GH_TOKEN: "${{ secrets.RELEASE_TOKEN }}" continue-on-error: true - name: "Add Backport Label for new Branch" - uses: "actions/github-script@v7" + uses: "actions/github-script@v8" with: script: | const { NEW_BRANCH } = process.env; diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d0240b1..13b4f24 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: with: name: "pulp_cli_packages" - name: "Set up Python" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "${{ matrix.python }}" allow-prereleases: true diff --git a/Makefile b/Makefile index ad84c4b..f3ba9ee 100644 --- a/Makefile +++ b/Makefile @@ -3,29 +3,28 @@ LANGUAGES=de GLUE_PLUGINS=$(notdir $(wildcard pulp-glue-deb/pulp_glue/*)) CLI_PLUGINS=$(notdir $(wildcard pulpcore/cli/*)) +.PHONY: info info: @echo Pulp glue @echo plugins: $(GLUE_PLUGINS) @echo Pulp CLI @echo plugins: $(CLI_PLUGINS) +.PHONY: build build: cd pulp-glue-deb; pyproject-build -n pyproject-build -n -black: format - +.PHONY: format format: - isort . - cd pulp-glue-deb; isort . - black . + ruff format + ruff check --fix +.PHONY: lint lint: find tests .ci -name '*.sh' -print0 | xargs -0 shellcheck -x - isort -c --diff . - cd pulp-glue-deb; isort -c --diff . - black --diff --check . - flake8 + ruff format --check --diff + ruff check --diff .ci/scripts/check_cli_dependencies.py .ci/scripts/check_click_for_mypy.py MYPYPATH=pulp-glue-deb mypy @@ -36,15 +35,19 @@ tests/cli.toml: cp $@.example $@ @echo "In order to configure the tests to talk to your test server, you might need to edit $@ ." +.PHONY: test test: | tests/cli.toml python3 -m pytest -v tests pulp-glue-deb/tests +.PHONY: livetest livetest: | tests/cli.toml python3 -m pytest -v tests pulp-glue-deb/tests -m live +.PHONY: unittest unittest: python3 -m pytest -v tests pulp-glue-deb/tests -m "not live" +.PHONY: unittest_glue unittest_glue: python3 -m pytest -v pulp-glue-deb/tests -m "not live" @@ -56,6 +59,7 @@ pulpcore/cli/%/locale/messages.pot: pulpcore/cli/%/*.py xgettext -d $* -o $@ pulpcore/cli/$*/*.py sed -i 's/charset=CHARSET/charset=UTF-8/g' $@ +.PHONY: extract_messages extract_messages: $(foreach GLUE_PLUGIN,$(GLUE_PLUGINS),pulp-glue-deb/pulp_glue/$(GLUE_PLUGIN)/locale/messages.pot) $(foreach CLI_PLUGIN,$(CLI_PLUGINS),pulpcore/cli/$(CLI_PLUGIN)/locale/messages.pot) $(foreach LANGUAGE,$(LANGUAGES),pulp-glue-deb/pulp_glue/%/locale/$(LANGUAGE)/LC_MESSAGES/messages.po): pulp-glue-deb/pulp_glue/%/locale/messages.pot @@ -71,6 +75,7 @@ $(foreach LANGUAGE,$(LANGUAGES),pulpcore/cli/%/locale/$(LANGUAGE)/LC_MESSAGES/me %.mo: %.po msgfmt -o $@ $< +.PHONY: compile_messages compile_messages: $(foreach LANGUAGE,$(LANGUAGES),$(foreach GLUE_PLUGIN,$(GLUE_PLUGINS),pulp-glue-deb/pulp_glue/$(GLUE_PLUGIN)/locale/$(LANGUAGE)/LC_MESSAGES/messages.mo)) $(foreach LANGUAGE,$(LANGUAGES),$(foreach CLI_PLUGIN,$(CLI_PLUGINS),pulpcore/cli/$(CLI_PLUGIN)/locale/$(LANGUAGE)/LC_MESSAGES/messages.mo)) -.PHONY: build info black lint test + .PRECIOUS: $(foreach LANGUAGE,$(LANGUAGES),$(foreach GLUE_PLUGIN,$(GLUE_PLUGINS),pulp-glue-deb/pulp_glue/$(GLUE_PLUGIN)/locale/$(LANGUAGE)/LC_MESSAGES/messages.po)) $(foreach LANGUAGE,$(LANGUAGES),$(foreach CLI_PLUGIN,$(CLI_PLUGINS),pulpcore/cli/$(CLI_PLUGIN)/locale/$(LANGUAGE)/LC_MESSAGES/messages.po)) diff --git a/lint_requirements.txt b/lint_requirements.txt index ec0bf54..c17cc78 100644 --- a/lint_requirements.txt +++ b/lint_requirements.txt @@ -1,9 +1,6 @@ # Lint requirements -black==25.1.0 -flake8==7.3.0 -flake8-pyproject==1.2.3 -isort==6.0.1 -mypy==1.17.1 +ruff==0.14.10 +mypy==1.19.1 shellcheck-py==0.11.0.1 # Type annotation stubs diff --git a/pulp-glue-deb/pyproject.toml b/pulp-glue-deb/pyproject.toml index 93bbdd7..0cea41c 100644 --- a/pulp-glue-deb/pyproject.toml +++ b/pulp-glue-deb/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "pulp-glue>=0.23.2,<0.36", + "pulp-glue<0.38,>=0.23.2", ] [project.urls] diff --git a/pyproject.toml b/pyproject.toml index ff15e17..843e3da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers=[ "Typing :: Typed", ] dependencies = [ - "pulp-cli>=0.23.2,<0.36", + "pulp-cli<0.38,>=0.23.2", "pulp-glue-deb==0.4.2.dev", ] @@ -114,16 +114,6 @@ name = "Misc" showcontent = true -[tool.black] -# This section is managed by the cookiecutter templates. -line-length = 100 - -[tool.isort] -# This section is managed by the cookiecutter templates. -profile = "black" -line_length = 100 -extend_skip = ["pulp-glue-deb"] - [tool.pytest.ini_options] markers = [ "script: tests provided as shell scripts", @@ -200,12 +190,12 @@ search = "\"pulp-glue-deb=={current_version}\"" replace = "\"pulp-glue-deb=={new_version}\"" -[tool.flake8] +[tool.ruff] +# This section is managed by the cookiecutter templates. +line-length = 100 +extend-exclude = ["cookiecutter"] + +[tool.ruff.lint] # This section is managed by the cookiecutter templates. -exclude = ["./docs/*"] -ignore = ["W503", "Q000", "Q003", "D100", "D104", "D106", "D200", "D202", "D205", "D400", "D401", "D402"] -# E203: whitespace before ':'; https://github.com/psf/black/issues/279 -# E401: multiple imports on one line -extend-ignore = ["E203", "E401"] -max-line-length = 100 +extend-select = ["I"]