diff --git a/.ci/scripts/calc_constraints.py b/.ci/scripts/calc_constraints.py new file mode 100755 index 0000000..d906425 --- /dev/null +++ b/.ci/scripts/calc_constraints.py @@ -0,0 +1,112 @@ +#!/bin/python3 + +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/.github/workflows/build.yml b/.github/workflows/build.yml index 7c5544e..d29c485 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" - uses: "actions/cache@v4" with: path: "~/.cache/pip" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c447319..b1dacdf 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -18,7 +18,7 @@ jobs: steps: - name: "Checkout repository" - uses: "actions/checkout@v4" + uses: "actions/checkout@v5" - name: "Initialize CodeQL" uses: "github/codeql-action/init@v3" with: diff --git a/.github/workflows/collect_changes.yml b/.github/workflows/collect_changes.yml index 04caafe..7db5e6e 100644 --- a/.github/workflows/collect_changes.yml +++ b/.github/workflows/collect_changes.yml @@ -8,7 +8,7 @@ jobs: collect-changes: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" with: ref: "main" fetch-depth: 0 @@ -25,10 +25,18 @@ jobs: python3 .ci/scripts/collect_changes.py - name: "Create Pull Request" uses: "peter-evans/create-pull-request@v7" + id: "create_pr" with: token: "${{ secrets.RELEASE_TOKEN }}" title: "Update Changelog" body: "" branch: "update_changes" delete-branch: true + - 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/cookiecutter.yml b/.github/workflows/cookiecutter.yml index fd860c5..4f706c4 100644 --- a/.github/workflows/cookiecutter.yml +++ b/.github/workflows/cookiecutter.yml @@ -2,6 +2,8 @@ name: "Update CI from cookiecutter" on: workflow_dispatch: + schedule: + - cron: "30 14 * * 0" defaults: run: @@ -11,11 +13,11 @@ jobs: update-ci: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" with: repository: "pulp/pulp-cli" path: "pulp-cli" - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" with: token: "${{ secrets.RELEASE_TOKEN }}" path: "pulp-cli-ostree" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cd0fea0..d5fb2a8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: - "3.11" - "3.13" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" - uses: "actions/cache@v4" with: path: "~/.cache/pip" @@ -23,7 +23,7 @@ jobs: ${{ runner.os }}-pip- - name: "Download wheels" - uses: "actions/download-artifact@v4" + uses: "actions/download-artifact@v5" with: name: "pulp_cli_packages" - name: "Set up Python" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index cd9f540..a91a349 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -26,7 +26,7 @@ jobs: check-commits: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" with: fetch-depth: 0 - name: "Set up Python" diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml index ef9c6ff..912d1aa 100644 --- a/.github/workflows/pr_checks.yml +++ b/.github/workflows/pr_checks.yml @@ -19,7 +19,7 @@ jobs: permissions: pull-requests: "write" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" with: fetch-depth: 0 - uses: "actions/setup-python@v5" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fc2b4cc..e5392d7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,9 +14,9 @@ jobs: needs: "build" runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" - name: "Download wheels" - uses: "actions/download-artifact@v4" + uses: "actions/download-artifact@v5" with: name: "pulp_cli_packages" - name: "Set up Python" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aa7b131..751f35a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: name: "Release" runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" with: token: "${{ secrets.RELEASE_TOKEN }}" - name: "Set up Python" diff --git a/.github/workflows/release_branch.yml b/.github/workflows/release_branch.yml index 9d23a47..f81ea9b 100644 --- a/.github/workflows/release_branch.yml +++ b/.github/workflows/release_branch.yml @@ -7,7 +7,7 @@ jobs: create-release-branch: runs-on: "ubuntu-latest" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" with: token: "${{ secrets.RELEASE_TOKEN }}" - name: "Set up Python" @@ -26,12 +26,20 @@ jobs: .ci/scripts/create_release_branch.sh - name: "Create Pull Request" uses: "peter-evans/create-pull-request@v7" + id: "create_pr" with: token: "${{ secrets.RELEASE_TOKEN }}" title: "Bump dev-version" body: "" branch: "bump_version" delete-branch: true + - 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 - name: "Add Backport Label for new Branch" uses: "actions/github-script@v7" with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d35b61c..e21fde4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,24 +19,21 @@ jobs: include: - image_tag: "nightly" pulp_api_root: "/relocated/djnd/" - python: "3.11" - - image_tag: "3.28" - lower_bounds: true - python: "3.8" - - image_tag: "3.26" python: "3.12" - - image_tag: "3.25" - python: "3.8" - - image_tag: "3.24" - pulp_api_root: "/relocated/djnd/" - python: "3.9" - - image_tag: "3.35" - python: "3.10" - image_tag: "latest" - lower_bounds: true python: "3.11" + upper_bounds: true + - image_tag: "3.73" + pulp_domain_enabled: "true" + pulp_enabled_plugins: "['pulp_ostree', 'pulp_file']" + python: "3.10" + - image_tag: "3.63" + python: "3.9" + - image_tag: "3.28" + lower_bounds: true + python: "3.13" steps: - - uses: "actions/checkout@v4" + - uses: "actions/checkout@v5" - uses: "actions/cache@v4" with: path: "~/.cache/pip" @@ -45,7 +42,7 @@ jobs: ${{ runner.os }}-pip- - name: "Download wheels" - uses: "actions/download-artifact@v4" + uses: "actions/download-artifact@v5" with: name: "pulp_cli_packages" - name: "Set up Python" diff --git a/CHANGES/+python3-9.removal b/CHANGES/+python3-9.removal new file mode 100644 index 0000000..abad523 --- /dev/null +++ b/CHANGES/+python3-9.removal @@ -0,0 +1 @@ +Removed support for Python<3.9. diff --git a/lint_requirements.txt b/lint_requirements.txt index 408ff57..ec0bf54 100644 --- a/lint_requirements.txt +++ b/lint_requirements.txt @@ -1,10 +1,10 @@ # Lint requirements black==25.1.0 -flake8==7.2.0 +flake8==7.3.0 flake8-pyproject==1.2.3 isort==6.0.1 -mypy==1.16.0 -shellcheck-py==0.10.0.1 +mypy==1.17.1 +shellcheck-py==0.11.0.1 # Type annotation stubs types-pygments diff --git a/pulp-glue-ostree/pyproject.toml b/pulp-glue-ostree/pyproject.toml index 62ff978..dc34071 100644 --- a/pulp-glue-ostree/pyproject.toml +++ b/pulp-glue-ostree/pyproject.toml @@ -7,7 +7,7 @@ name = "pulp-glue-ostree" version = "0.6.0.dev" description = "Version agnostic glue library to talk to pulpcore's REST API. (OSTree plugin)" readme = "README.md" -requires-python = ">=3.8" +requires-python = ">=3.9" license = {text = "GPLv2+"} authors = [ {name = "Pulp Team", email = "pulp-list@redhat.com"}, @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "pulp-glue>=0.23.1,<0.32", + "pulp-glue>=0.23.1,<0.36", ] [project.urls] diff --git a/pyproject.toml b/pyproject.toml index 848524b..6ae6908 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "pulp-cli-ostree" version = "0.6.0.dev" description = "Command line interface to talk to pulpcore's REST API. (OSTree plugin commands)" readme = "README.md" -requires-python = ">=3.8" +requires-python = ">=3.9" license = {text = "GPLv2+"} authors = [ {name = "Pulp Team", email = "pulp-list@redhat.com"}, @@ -23,7 +23,7 @@ classifiers=[ "Typing :: Typed", ] dependencies = [ - "pulp-cli>=0.23.1,<0.32", + "pulp-cli>=0.23.1,<0.36", "pulp-glue-ostree==0.6.0.dev", ] diff --git a/releasing.md b/releasing.md new file mode 100644 index 0000000..20d185a --- /dev/null +++ b/releasing.md @@ -0,0 +1,20 @@ +# Releasing (for internal use) + +## Using Workflows + +### Create a new Y-Release Branch + + 1. Trigger the "Create Release Branch" workflow on the "main" branch. + 1. Watch for the "Bump Version" PR, verify that it deletes all the changes snippets present on the new release branch, approve and merge it. + +### Release from a Release Branch + + 1. Trigger the "pulp-cli Release" workflow on the corresponding release branch. + 1. Lean back and see the magic happen. + 1. Wait for the "pulp-cli Publish" workflow to succeed. + 1. Verify that a new version appeared on PyPI. + 1. Verify that the docs have been updated. + 1. [only Y-releases] Announce the release at https://discourse.pulpproject.org/c/announcements/6. + 1. Look for the "Update Changelog" PR, approve and merge it. + +If some thing goes wrong look at `.ci/scripts/create_release_branch.sh` and `.ci/scripts/release.sh` and follow the intentions encoded there. diff --git a/test_requirements.txt b/test_requirements.txt index 48a015f..583d739 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -3,3 +3,4 @@ pygments pytest pytest-subtests python-gnupg +trustme>=1.1.0,<1.3