From 85886aba03e8be7488d20f4740daef308f31b771 Mon Sep 17 00:00:00 2001 From: pulpbot Date: Sun, 9 Aug 2026 14:45:00 +0000 Subject: [PATCH] Update cookiecutter --- .ci/gen_certs.py | 24 +++-- .ci/run_container.sh | 4 + .ci/scripts/calc_constraints.py | 119 ------------------------- .ci/scripts/check_cli_dependencies.py | 4 +- .ci/scripts/check_click_for_mypy.py | 4 +- .ci/scripts/collect_changes.py | 28 +++--- .ci/scripts/pr_labels.py | 2 +- .ci/scripts/validate_commit_message.py | 19 ++-- .github/workflows/build.yml | 21 ++--- .github/workflows/codeql.yml | 2 +- .github/workflows/collect_changes.yml | 17 ++-- .github/workflows/cookiecutter.yml | 16 ++-- .github/workflows/lint.yml | 22 ++--- .github/workflows/pr.yml | 25 +++--- .github/workflows/pr_checks.yml | 20 +++-- .github/workflows/publish.yml | 24 +++-- .github/workflows/release.yml | 13 +-- .github/workflows/release_branch.yml | 12 +-- .github/workflows/test.yml | 38 ++++---- .gitignore | 7 +- Makefile | 65 +++++++++++--- lint_requirements.txt | 14 --- pulp-glue-ostree/pyproject.toml | 7 +- pyproject.toml | 53 ++++++++++- test_requirements.txt | 6 -- 25 files changed, 261 insertions(+), 305 deletions(-) delete mode 100755 .ci/scripts/calc_constraints.py delete mode 100644 lint_requirements.txt delete mode 100644 test_requirements.txt diff --git a/.ci/gen_certs.py b/.ci/gen_certs.py index d55b0b9..e6eed98 100644 --- a/.ci/gen_certs.py +++ b/.ci/gen_certs.py @@ -6,8 +6,8 @@ # /// import argparse -import os import sys +from pathlib import Path import trustme @@ -17,14 +17,14 @@ def main() -> None: parser.add_argument( "-d", "--dir", - default=os.getcwd(), + default=".", help="Directory where certificates and keys are written to. Defaults to cwd.", ) args = parser.parse_args(sys.argv[1:]) - cert_dir = args.dir + cert_dir = Path(args.dir) - if not os.path.isdir(cert_dir): + if not cert_dir.is_dir(): raise ValueError(f"--dir={cert_dir} is not a directory") key_type = trustme.KeyType["ECDSA"] @@ -32,28 +32,26 @@ def main() -> None: # Generate the CA certificate ca = trustme.CA(key_type=key_type) # Write the certificate the client should trust - ca_cert_path = os.path.join(cert_dir, "ca.pem") + ca_cert_path = cert_dir / "ca.pem" ca.cert_pem.write_to_path(path=ca_cert_path) # Generate the server certificate server_cert = ca.issue_cert("localhost", "127.0.0.1", "::1", key_type=key_type) # Write the certificate and private key the server should use - server_key_path = os.path.join(cert_dir, "server.key") - server_cert_path = os.path.join(cert_dir, "server.pem") + server_key_path = cert_dir / "server.key" + server_cert_path = cert_dir / "server.pem" server_cert.private_key_pem.write_to_path(path=server_key_path) - with open(server_cert_path, mode="w") as f: - f.truncate() + server_cert_path.write_text("") for blob in server_cert.cert_chain_pems: blob.write_to_path(path=server_cert_path, append=True) # Generate the client certificate client_cert = ca.issue_cert("admin@example.com", common_name="admin", key_type=key_type) # Write the certificate and private key the client should use - client_key_path = os.path.join(cert_dir, "client.key") - client_cert_path = os.path.join(cert_dir, "client.pem") + client_key_path = cert_dir / "client.key" + client_cert_path = cert_dir / "client.pem" client_cert.private_key_pem.write_to_path(path=client_key_path) - with open(client_cert_path, mode="w") as f: - f.truncate() + client_cert_path.write_text("") for blob in client_cert.cert_chain_pems: blob.write_to_path(path=client_cert_path, append=True) diff --git a/.ci/run_container.sh b/.ci/run_container.sh index b3a1c7e..d1a837c 100755 --- a/.ci/run_container.sh +++ b/.ci/run_container.sh @@ -69,6 +69,9 @@ else fi export PULP_CONTENT_ORIGIN +PULP_SECRET_KEY="$(python3 -c "import secrets; print(secrets.token_urlsafe(50))")" +export PULP_SECRET_KEY + "${CONTAINER_RUNTIME}" \ run ${RM:+--rm} \ --env S6_KEEP_ENV=1 \ @@ -79,6 +82,7 @@ export PULP_CONTENT_ORIGIN ${PULP_DOMAIN_ENABLED:+--env PULP_DOMAIN_ENABLED} \ ${PULP_ENABLED_PLUGINS:+--env PULP_ENABLED_PLUGINS} \ --env PULP_CONTENT_ORIGIN \ + --env PULP_SECRET_KEY \ --detach \ --name "pulp-ephemeral" \ --volume "${PULP_CLI_TEST_TMPDIR}/settings:/etc/pulp${SELINUX:+:Z}" \ diff --git a/.ci/scripts/calc_constraints.py b/.ci/scripts/calc_constraints.py deleted file mode 100755 index ca8e11e..0000000 --- a/.ci/scripts/calc_constraints.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/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 19dc19e..dd10fc7 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 499265c..9ed2b48 100755 --- a/.ci/scripts/collect_changes.py +++ b/.ci/scripts/collect_changes.py @@ -1,6 +1,6 @@ #!/bin/env python3 # /// script -# requires-python = ">=3.11" +# requires-python = ">=3.13" # dependencies = [ # "gitpython>=3.1.46,<3.2.0", # "packaging>=25.0,<25.1", @@ -8,15 +8,17 @@ # /// import itertools -import os 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 -with open("pyproject.toml", "rb") as fp: +with Path("pyproject.toml").open("rb") as fp: tc_settings = tomllib.load(fp)["tool"]["towncrier"] CHANGELOG_FILE = tc_settings.get("filename", "NEWS.rst") @@ -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,21 +69,20 @@ 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(): - repo = Repo(os.getcwd()) +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)] branches.sort(key=lambda ref: parse_version(ref.remote_head), reverse=True) branches = [ref.name for ref in branches] - with open(CHANGELOG_FILE, "r") as f: - main_changelog = f.read() + main_changelog = Path(CHANGELOG_FILE).read_text() preamble, main_changes = split_changelog(main_changelog) old_length = len(main_changes) @@ -92,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]] @@ -103,10 +104,9 @@ def main(): new_length = len(main_changes) if old_length < new_length: print(f"{new_length - old_length} new versions have been added.") - with open(CHANGELOG_FILE, "w") as fp: + with Path(CHANGELOG_FILE).open("w") as fp: fp.write(preamble) - for change in main_changes: - fp.write(change[1]) + fp.writelines(change[1] for change in main_changes) repo.git.commit("-m", "Update Changelog", CHANGELOG_FILE) diff --git a/.ci/scripts/pr_labels.py b/.ci/scripts/pr_labels.py index 49eb4ad..b2350b6 100755 --- a/.ci/scripts/pr_labels.py +++ b/.ci/scripts/pr_labels.py @@ -19,7 +19,7 @@ def main(): assert len(sys.argv) == 3 - with open("pyproject.toml", "rb") as fp: + with Path("pyproject.toml").open("rb") as fp: PYPROJECT_TOML = tomllib.load(fp) BLOCKING_REGEX = re.compile(r"DRAFT|WIP|NO\s*MERGE|DO\s*NOT\s*MERGE|EXPERIMENT") ISSUE_REGEX = re.compile(r"(?:fixes|closes)[\s:]+#(\d+)") diff --git a/.ci/scripts/validate_commit_message.py b/.ci/scripts/validate_commit_message.py index 3f970a1..a8929d3 100644 --- a/.ci/scripts/validate_commit_message.py +++ b/.ci/scripts/validate_commit_message.py @@ -12,9 +12,8 @@ from pathlib import Path import tomllib -from github import Github -with open("pyproject.toml", "rb") as fp: +with Path("pyproject.toml").open("rb") as fp: PYPROJECT_TOML = tomllib.load(fp) KEYWORDS = ["fixes", "closes"] BLOCKING_REGEX = [ @@ -33,14 +32,16 @@ if NOISSUE_MARKER in message: sys.exit("Do not add '[noissue]' in the commit message.") -if any((re.match(pattern, message) for pattern in BLOCKING_REGEX)): +if any(re.match(pattern, message) for pattern in BLOCKING_REGEX): sys.exit("This PR is not ready for consumption.") -g = Github(os.environ.get("GITHUB_TOKEN")) -repo = g.get_repo("pulp/pulp-cli-ostree") +def check_status(issue: str) -> None: + from github import Github + + g = Github(os.environ.get("GITHUB_TOKEN")) + repo = g.get_repo("pulp/pulp-cli-ostree") -def check_status(issue): gi = repo.get_issue(int(issue)) if gi.pull_request: sys.exit(f"Error: issue #{issue} is a pull request.") @@ -48,7 +49,7 @@ def check_status(issue): sys.exit(f"Error: issue #{issue} is closed.") -def check_changelog(issue): +def check_changelog(issue: str) -> None: matches = list(Path("CHANGES").rglob(f"{issue}.*")) if len(matches) < 1: @@ -58,7 +59,7 @@ def check_changelog(issue): sys.exit(f"Invalid extension for changelog entry '{match}'.") -print("Checking commit message for {sha}.".format(sha=sha[0:7])) +print(f"Checking commit message for {sha[0:7]}.") # validate the issue attached to the commit issue_regex = r"(?:{keywords})[\s:]+#(\d+)".format(keywords=("|").join(KEYWORDS)) @@ -72,4 +73,4 @@ def check_changelog(issue): check_status(issue) check_changelog(issue) -print("Commit message for {sha} passed.".format(sha=sha[0:7])) +print(f"Commit message for {sha[0:7]} passed.") diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a95148..d7c6a4b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,30 +8,27 @@ jobs: build: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v6" - - uses: "actions/cache@v5" - with: - path: "~/.cache/pip" - key: "${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/*constraints.lock', '**/setup.py', '**/pyproject.toml') }}" - restore-keys: | - ${{ runner.os }}-pip- - + - name: "Checkout" + uses: "actions/checkout@v6" - name: "Set up Python" uses: "actions/setup-python@v6" with: python-version: "3.14" - - name: "Install python dependencies" - run: | - pip install build setuptools wheel + allow-prereleases: true + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true - name: "Build wheels" run: | make build + touch .root - name: "Upload wheels" uses: "actions/upload-artifact@v6" with: name: "pulp_cli_packages" path: | - pulp-glue-ostree/dist/ + .root dist/ if-no-files-found: "error" retention-days: 5 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3c8da25..facb1a8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -17,7 +17,7 @@ jobs: security-events: "write" steps: - - name: "Checkout repository" + - name: "Checkout" uses: "actions/checkout@v6" - name: "Initialize CodeQL" uses: "github/codeql-action/init@v4" diff --git a/.github/workflows/collect_changes.yml b/.github/workflows/collect_changes.yml index f5ead77..5e08242 100644 --- a/.github/workflows/collect_changes.yml +++ b/.github/workflows/collect_changes.yml @@ -8,21 +8,26 @@ jobs: collect-changes: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v6" + - name: "Checkout" + uses: "actions/checkout@v6" with: - ref: "main" fetch-depth: 0 - - uses: "actions/setup-python@v6" + - name: "Set up Python" + uses: "actions/setup-python@v6" with: - python-version: "3.x" + python-version: "3.14" + allow-prereleases: true + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true - name: "Setup git" run: | git config user.name pulpbot git config user.email pulp-infra@redhat.com - name: "Collect changes" run: | - pip install GitPython packaging - python3 .ci/scripts/collect_changes.py + uv run --script .ci/scripts/collect_changes.py - name: "Create Pull Request" uses: "peter-evans/create-pull-request@v8" id: "create_pr" diff --git a/.github/workflows/cookiecutter.yml b/.github/workflows/cookiecutter.yml index 68d0c1d..c9ca7ae 100644 --- a/.github/workflows/cookiecutter.yml +++ b/.github/workflows/cookiecutter.yml @@ -21,10 +21,6 @@ jobs: with: token: "${{ secrets.RELEASE_TOKEN }}" path: "pulp-cli-ostree" - - name: "Setup git" - run: | - git config user.name pulpbot - git config user.email pulp-infra@redhat.com - name: "Set up Python" uses: "actions/setup-python@v6" with: @@ -34,6 +30,10 @@ jobs: uses: "astral-sh/setup-uv@v7" with: enable-cache: true + - name: "Setup git" + run: | + git config user.name pulpbot + git config user.email pulp-infra@redhat.com - name: "Apply cookiecutter templates" run: | uv run ../pulp-cli/cookiecutter/apply_templates.py @@ -70,10 +70,6 @@ jobs: with: token: "${{ secrets.RELEASE_TOKEN }}" path: "pulp-cli-ostree" - - name: "Setup git" - run: | - git config user.name pulpbot - git config user.email pulp-infra@redhat.com - name: "Set up Python" uses: "actions/setup-python@v6" with: @@ -83,6 +79,10 @@ jobs: uses: "astral-sh/setup-uv@v7" with: enable-cache: true + - name: "Setup git" + run: | + git config user.name pulpbot + git config user.email pulp-infra@redhat.com - name: "Apply cookiecutter templates" run: | uv run ../pulp-cli/cookiecutter/update_pulp_cli.py diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 152315f..fee95cd 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,25 +14,17 @@ jobs: - "3.11" - "3.14" steps: - - uses: "actions/checkout@v6" - - uses: "actions/cache@v5" - with: - path: "~/.cache/pip" - key: "${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/*constraints.lock', '**/setup.py', '**/pyproject.toml') }}" - restore-keys: | - ${{ runner.os }}-pip- - - - name: "Download wheels" - uses: "actions/download-artifact@v8" - with: - name: "pulp_cli_packages" + - name: "Checkout" + uses: "actions/checkout@v6" - name: "Set up Python" uses: "actions/setup-python@v6" with: python-version: "${{ matrix.python }}" - - name: "Install python dependencies" - run: | - pip install dist/pulp_cli_ostree-*.whl pulp-glue-ostree/dist/pulp_glue_ostree-*.whl -r lint_requirements.txt + allow-prereleases: true + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true - name: "Lint code" run: | make lint diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index e201323..5617875 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -9,15 +9,15 @@ concurrency: cancel-in-progress: true jobs: - build: - uses: "./.github/workflows/build.yml" lint: - needs: - - "build" uses: "./.github/workflows/lint.yml" - test: + build: needs: - "lint" + uses: "./.github/workflows/build.yml" + test: + needs: + - "build" uses: "./.github/workflows/test.yml" codeql: needs: @@ -26,16 +26,19 @@ jobs: check-commits: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v6" + - name: "Checkout" + uses: "actions/checkout@v6" with: fetch-depth: 0 - name: "Set up Python" uses: "actions/setup-python@v6" with: - python-version: "3.x" - - name: "Install python dependencies" - run: | - pip install toml pygithub + python-version: "3.14" + allow-prereleases: true + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true - name: "Check commit message" env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" @@ -43,7 +46,7 @@ jobs: run: | for SHA in $(curl -H "Authorization: token $GITHUB_TOKEN" "$GITHUB_CONTEXT" | jq -r '.[].sha') do - python .ci/scripts/validate_commit_message.py "$SHA" + uv run -q .ci/scripts/validate_commit_message.py "$SHA" VALUE=$? if [ "$VALUE" -gt 0 ]; then exit "$VALUE" diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml index 43f5c94..c031756 100644 --- a/.github/workflows/pr_checks.yml +++ b/.github/workflows/pr_checks.yml @@ -19,19 +19,25 @@ jobs: permissions: pull-requests: "write" steps: - - uses: "actions/checkout@v6" + - name: "Checkout" + uses: "actions/checkout@v6" with: fetch-depth: 0 - - uses: "actions/setup-python@v6" + - name: "Set up Python" + uses: "actions/setup-python@v6" with: - python-version: "3.x" + python-version: "3.14" + allow-prereleases: true + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true - 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@v8" - name: "Apply PR Labels" + uv run -q .ci/scripts/pr_labels.py "origin/${{ github.base_ref }}" "${{ github.event.pull_request.head.sha }}" >> "$GITHUB_ENV" + - name: "Apply PR Labels" + uses: "actions/github-script@v8" with: script: | const { ADD_LABELS, REMOVE_LABELS } = process.env; diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f9476d9..9256150 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,8 @@ jobs: needs: "build" runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v6" + - name: "Checkout" + uses: "actions/checkout@v6" - name: "Download wheels" uses: "actions/download-artifact@v8" with: @@ -22,18 +23,15 @@ jobs: - name: "Set up Python" uses: "actions/setup-python@v6" with: - python-version: "3.x" - - name: "Install dependencies" - run: | - python -m pip install --upgrade pip - pip install twine - - name: "Build and publish" + python-version: "3.14" + allow-prereleases: true + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true + - name: "Publish" env: - TWINE_USERNAME: "__token__" - TWINE_PASSWORD: "${{ secrets.PYPI_API_TOKEN }}" + UV_PUBLISH_TOKEN: "${{ secrets.PYPI_API_TOKEN }}" run: | - cd pulp-glue-ostree - twine upload dist/* - cd .. - twine upload dist/* + uv publish ... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4cbd745..bda5708 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,16 +15,17 @@ jobs: - name: "Set up Python" uses: "actions/setup-python@v6" with: - python-version: "3.x" - - name: "Install dependencies" - run: | - python -m pip install --upgrade pip - pip install bump-my-version~=0.20.0 towncrier~=23.11.0 + python-version: "3.14" + allow-prereleases: true + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true - name: "Setup git" run: | git config user.name pulpbot git config user.email pulp-infra@redhat.com - name: "Release" run: | - .ci/scripts/release.sh + uv run --with bump-my-version~=0.20.0 --with towncrier~=23.11.0 .ci/scripts/release.sh ... diff --git a/.github/workflows/release_branch.yml b/.github/workflows/release_branch.yml index a75f0f3..f20a5ef 100644 --- a/.github/workflows/release_branch.yml +++ b/.github/workflows/release_branch.yml @@ -13,17 +13,19 @@ jobs: - name: "Set up Python" uses: "actions/setup-python@v6" with: - python-version: "3.x" + python-version: "3.14" + allow-prereleases: true + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true - name: "Setup git" run: | git config user.name pulpbot git config user.email pulp-infra@redhat.com - - name: "Install python dependencies" - run: | - pip install bump-my-version~=0.20.0 - name: "Create Release Branch" run: | - .ci/scripts/create_release_branch.sh + uv run --with bump-my-version~=0.20.0 .ci/scripts/create_release_branch.sh - name: "Create Pull Request" uses: "peter-evans/create-pull-request@v8" id: "create_pr" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 10b5175..556123f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,14 +33,8 @@ jobs: lower_bounds: true python: "3.13" steps: - - uses: "actions/checkout@v6" - - uses: "actions/cache@v5" - with: - path: "~/.cache/pip" - key: "${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/*constraints.lock', '**/setup.py', '**/pyproject.toml') }}" - restore-keys: | - ${{ runner.os }}-pip- - + - name: "Checkout" + uses: "actions/checkout@v6" - name: "Download wheels" uses: "actions/download-artifact@v8" with: @@ -53,18 +47,10 @@ jobs: - name: "Install Binary Test Dependencies" run: | sudo apt-get install ostree - - name: "Install Python Test Dependencies" - run: | - if [ "${{matrix.lower_bounds}}" ] - then - pip install dist/pulp_cli_ostree-*.whl pulp-glue-ostree/dist/pulp_glue_ostree-*.whl -r test_requirements.txt -c lower_bounds_constraints.lock - elif [ "${{matrix.upper_bounds}}" ] - then - .ci/scripts/calc_constraints.py pyproject.toml pulp-glue-ostree/pyproject.toml --upper > upper_bounds_constraints.lock - pip install dist/pulp_cli_ostree-*.whl pulp-glue-ostree/dist/pulp_glue_ostree-*.whl -r test_requirements.txt -c upper_bounds_constraints.lock - else - pip install dist/pulp_cli_ostree-*.whl pulp-glue-ostree/dist/pulp_glue_ostree-*.whl -r test_requirements.txt - fi + - name: "Install uv" + uses: "astral-sh/setup-uv@v7" + with: + enable-cache: true - name: "Run tests" env: CONTAINER_RUNTIME: "${{ matrix.container_runtime }}" @@ -78,5 +64,15 @@ jobs: PULP_ENABLED_PLUGINS: "${{ matrix.pulp_enabled_plugins }}" OAS_VERSION: "${{ matrix.oas_version }}" run: | - .ci/run_container.sh make test + if [ "${{matrix.lower_bounds}}" ] + then + RESOLUTION=("--resolution" "lowest-direct") + elif [ "${{matrix.upper_bounds}}" ] + then + RESOLUTION=("--resolution" "highest") + else + RESOLUTION=() + fi + + uv run "${RESOLUTION[@]}" --isolated --with dist/pulp_glue*.whl --with dist/pulp_cli*.whl --only-group test .ci/run_container.sh make _test ... diff --git a/.gitignore b/.gitignore index ed761f6..4e13d34 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ *.egg-info __pycache__/ build/ -tests/cli.toml -GPG-PRIVATE-KEY-fixture-signing site/ dist/ *.po~ +uv.lock + +tests/cli.toml +GPG-PRIVATE-KEY-fixture-signing +.root diff --git a/Makefile b/Makefile index a21b9ef..450350f 100644 --- a/Makefile +++ b/Makefile @@ -11,45 +11,82 @@ info: .PHONY: build build: - cd pulp-glue-ostree; pyproject-build -n - pyproject-build -n + uv build --all + +.PHONY: _format +_format: + ruff format + ruff check --select I --fix .PHONY: format format: - ruff format + uv run --isolated --group lint $(MAKE) _format + +.PHONY: _autofix +_autofix: ruff check --fix -.PHONY: lint -lint: +.PHONY: autofix +autofix: + uv run --isolated --group lint $(MAKE) _autofix + +.PHONY: _lint +_lint: find tests .ci -name '*.sh' -print0 | xargs -0 shellcheck -x ruff format --check --diff - ruff check --diff + ruff check --output-format concise .ci/scripts/check_cli_dependencies.py .ci/scripts/check_click_for_mypy.py mypy cd pulp-glue-ostree; mypy @echo "🙊 Code 🙈 LGTM 🙉 !" +.PHONY: lint +lint: + uv run --isolated --group lint $(MAKE) _lint + 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 + pytest -v tests pulp-glue-ostree/tests + .PHONY: test -test: | tests/cli.toml - python3 -m pytest -v tests pulp-glue-ostree/tests +test: + uv run $(MAKE) _test + +PYTEST_MARK ?= live + +.PHONY: _livetest +_livetest: | tests/cli.toml + pytest -v tests pulp-glue-ostree/tests -m "$(PYTEST_MARK)" .PHONY: livetest -livetest: | tests/cli.toml - python3 -m pytest -v tests pulp-glue-ostree/tests -m live +livetest: + uv run $(MAKE) _livetest + +.PHONY: _paralleltest +_paralleltest: | tests/cli.toml + pytest -v tests pulp-glue-ostree/tests -m live -n 8 .PHONY: paralleltest -paralleltest: | tests/cli.toml - python3 -m pytest -v tests pulp-glue-ostree/tests -m live -n 8 +paralleltest: + uv run $(MAKE) _paralleltest + +.PHONY: _unittest +_unittest: + pytest -v tests pulp-glue-ostree/tests -m "not live" .PHONY: unittest unittest: - python3 -m pytest -v tests pulp-glue-ostree/tests -m "not live" + uv run $(MAKE) _unittest + +.PHONY: _unittest_glue +_unittest_glue: + pytest -v pulp-glue-ostree/tests -m "not live" .PHONY: unittest_glue unittest_glue: - python3 -m pytest -v pulp-glue-ostree/tests -m "not live" + uv run $(MAKE) _unittest_glue diff --git a/lint_requirements.txt b/lint_requirements.txt deleted file mode 100644 index 4a5e7ce..0000000 --- a/lint_requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Lint requirements -ruff~=0.15.1 -mypy~=1.20.0 -shellcheck-py~=0.11.0.1 - -# Type annotation stubs -types-pygments -types-PyYAML -types-requests -types-setuptools -types-toml - -# Install the actual bits for mypy --r test_requirements.txt diff --git a/pulp-glue-ostree/pyproject.toml b/pulp-glue-ostree/pyproject.toml index 59e821b..3463d54 100644 --- a/pulp-glue-ostree/pyproject.toml +++ b/pulp-glue-ostree/pyproject.toml @@ -64,5 +64,10 @@ line-length = 100 [tool.ruff.lint] # This section is managed by the cookiecutter templates. -extend-select = ["I"] +select = ["E4", "E7", "E9", "F"] +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 8daa094..7eb5479 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,7 +122,7 @@ markers = [ strict = true warn_unused_ignores = false show_error_codes = true -files = "src/**/*.py, tests/*.py" +files = "src/**/*.py, tests/**/*.py" mypy_path = ["src", "pulp-glue-ostree/src"] namespace_packages = true explicit_package_bases = true @@ -190,14 +190,61 @@ replace = "\"pulp-glue-ostree=={new_version}\"" [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. -extend-select = ["I"] +select = ["E4", "E7", "E9", "F"] +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. +pulp-glue-ostree = { workspace = true } + +[tool.uv.workspace] +# This section is managed by the cookiecutter templates. +members = ["pulp-glue-ostree"] + +[tool.uv.build-backend] +# This section is managed by the cookiecutter templates. +module-name = ["pulpcore.cli"] +namespace = true +source-exclude = ["*.pot", "*.po", "**/*\\~"] + + +[dependency-groups] +dev = [ + {include-group = "lint"}, + "pylsp-mypy>=0.7.0", + "pylsp-rope>=0.1.17,<0.1.18", +] +lint = [ + {include-group = "test"}, + "mypy~=1.20.0", + "ruff~=0.16.0", + "shellcheck-py~=0.11.0.1", + "types-pygments", + "types-pyyaml", + "types-requests", + "types-setuptools", + "types-toml", +] +test = [ + "pygments>=2.19.2", + "pytest>=7.0.0,<9.2", + "pytest-xdist>=3.8.0,<3.9", + "python-gnupg>=0.5.0,<0.6", + "secretstorage>=3.5.0", + "trustme>=1.1.0,<1.3", +] diff --git a/test_requirements.txt b/test_requirements.txt deleted file mode 100644 index 583d739..0000000 --- a/test_requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Test requirements -pygments -pytest -pytest-subtests -python-gnupg -trustme>=1.1.0,<1.3