diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad7e9bb..f3cbcbe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ env: python_cache_windows_path: ~\AppData\Local\pip\Cache python_cache_ubuntu_path: ~/.cache/pip pipenv_version: 2022.11.25 - python_version: '3.10' + python_version: '3.11' jobs: # Check that a news file has been added to this branch when a PR is created @@ -166,7 +166,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest ] # FIXME add other platforms when much quicker macOS-latest, windows-latest] - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] multi-platform: - ${{ github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' }} # include: @@ -180,7 +180,7 @@ jobs: include: - os: macOS-latest multi-platform: false - python-version: 3.9 + python-version: 3.11 # - os: windows-latest # multi-platform: false # python-version: 3.9 diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 0000000..554f027 --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,26 @@ +name: "Run type checker" +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + +jobs: + type-check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + - name: Install pipenv + shell: bash + run: | + python -m pip install --upgrade pipenv + pipenv install --dev + - name: Type check with mypy + run: | + pipenv run mypy ./continuous_delivery_scripts \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 61d6380..d60bf4b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: GitHub Release env: pipenv_version: 2022.11.25 - python_version: '3.10' + python_version: '3.11' on: workflow_dispatch: diff --git a/.gitignore b/.gitignore index d86a058..3a169aa 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ Pipfile.lock # macOS .DS_Store +.tool-versions # Python *.pyc diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index f13f2e0..09e42da 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -51,7 +51,6 @@ Setup Pipenv to use Python 3 (Python 2 is not supported) and install package dev ```bash cd continuous-delivery-scripts/ -pipenv --three pipenv install --dev ``` diff --git a/Pipfile b/Pipfile index 5331a10..5897056 100644 --- a/Pipfile +++ b/Pipfile @@ -23,3 +23,5 @@ allow_prereleases = false [packages] continuous-delivery-scripts = {editable = true, path = "."} +types-toml = "*" +types-setuptools = "*" diff --git a/continuous_delivery_scripts/assert_news.py b/continuous_delivery_scripts/assert_news.py index 0f494c1..8464592 100644 --- a/continuous_delivery_scripts/assert_news.py +++ b/continuous_delivery_scripts/assert_news.py @@ -8,7 +8,7 @@ import pathlib import re import sys -from typing import List, Union +from typing import Union, Optional, Iterable, Any, List from continuous_delivery_scripts.utils.configuration import configuration, ConfigurationVariable from continuous_delivery_scripts.utils.git_helpers import ProjectTempClone, LocalProjectRepository, GitWrapper @@ -98,6 +98,12 @@ def validate_news_files(git: GitWrapper, root_dir: str, news_dir: str) -> None: validate_news_file(absolute_file_path) +def _convert_to_string_iter(list: Optional[List[Any]]) -> Iterable[str]: + if list is None: + return [] + return [str(item) for item in list] + + def generate_news_file(git: GitWrapper, news_dir: pathlib.Path) -> pathlib.Path: """Adds a news file if the branch corresponds to an dependency update. @@ -114,8 +120,9 @@ def generate_news_file(git: GitWrapper, news_dir: pathlib.Path) -> pathlib.Path: if not configuration.get_value(ConfigurationVariable.AUTOGENERATE_NEWS_FILE_ON_DEPENDENCY_UPDATE): raise EnvironmentError(f"Branch {current_branch} must contain a news file.") + list_groups = _convert_to_string_iter(groups) message = str(configuration.get_value(ConfigurationVariable.DEPENDENCY_UPDATE_NEWS_MESSAGE)).format( - message=", ".join(groups) + message=", ".join(list_groups) ) logger.info(f"Generating a news file with content: {message}...") return create_news_file( @@ -130,7 +137,7 @@ def _commit_news_file(git: GitWrapper, news_file: pathlib.Path, local: bool) -> logger.info(f"Committing news file {str(news_file)}...") if not local: git.configure_for_github() - git.add(str(news_file)) + git.add(news_file) git.commit("📰 Automatic changes ⚙ Adding news file") if not local: git.push() diff --git a/continuous_delivery_scripts/get_version.py b/continuous_delivery_scripts/get_version.py index 5dc813e..6ae6b13 100644 --- a/continuous_delivery_scripts/get_version.py +++ b/continuous_delivery_scripts/get_version.py @@ -5,6 +5,7 @@ """Determine the project new version.""" import sys +from typing import Optional import argparse import logging from continuous_delivery_scripts.utils.versioning import calculate_version, determine_version_string @@ -15,7 +16,7 @@ logger = logging.getLogger(__name__) -def get_project_version_string(commit_type: CommitType) -> str: +def get_project_version_string(commit_type: CommitType) -> Optional[str]: """Determine the project version string. Args: diff --git a/continuous_delivery_scripts/language_specifics.py b/continuous_delivery_scripts/language_specifics.py index ec0a270..e78845c 100644 --- a/continuous_delivery_scripts/language_specifics.py +++ b/continuous_delivery_scripts/language_specifics.py @@ -16,8 +16,8 @@ logger = logging.getLogger(__name__) -def _retrieve_all_subclasses(subclass: Type[BaseLanguage]) -> Set[Type[BaseLanguage]]: - subclasses = set() +def _retrieve_all_subclasses(subclass: Type) -> Set[Type]: + subclasses: set = set() if not subclass: return subclasses if subclass != BaseLanguage: @@ -31,10 +31,10 @@ def all_language_plugins() -> Dict[str, BaseLanguage]: """Fetches all language plugins which inherit from BaseLanguage. Returns: - A list of classes containing language plugins + A list of classes containing language plugins """ all_plugins = _retrieve_all_subclasses(BaseLanguage) - return {la.get_related_language().lower().strip(): la for la in [lang() for lang in all_plugins]} # type: ignore + return {la.get_related_language().lower().strip(): la for la in [lang() for lang in all_plugins]} def fetch_project_language_plugin(all_plugins: Dict[str, BaseLanguage], language: str) -> BaseLanguage: @@ -45,7 +45,7 @@ def fetch_project_language_plugin(all_plugins: Dict[str, BaseLanguage], language language: the language to select Returns: - A language plugin corresponding to the language requested + A language plugin corresponding to the language requested """ return cast(BaseLanguage, all_plugins.get(_sanitise_program_language(language))) diff --git a/continuous_delivery_scripts/plugins/ci.py b/continuous_delivery_scripts/plugins/ci.py index ad8be07..63c653f 100644 --- a/continuous_delivery_scripts/plugins/ci.py +++ b/continuous_delivery_scripts/plugins/ci.py @@ -30,7 +30,7 @@ def get_related_language(self) -> str: """Gets the related language.""" return get_language_from_file_name(__file__) - def get_version_tag(self, version: str): + def get_version_tag(self, version: str) -> str: """Gets tag based on version.""" cleansed_version = version.strip().lstrip("v") return f"v{cleansed_version}" diff --git a/continuous_delivery_scripts/plugins/golang.py b/continuous_delivery_scripts/plugins/golang.py index 238d43e..d9879a7 100644 --- a/continuous_delivery_scripts/plugins/golang.py +++ b/continuous_delivery_scripts/plugins/golang.py @@ -94,7 +94,7 @@ def _call_goreleaser_check(version: str) -> None: check_call(_generate_goreleaser_check_command_list(), cwd=ROOT_DIR, env=env) -def _determine_go_module_tag(version) -> Optional[str]: +def _determine_go_module_tag(version: str) -> Optional[str]: """Determines go module for tagging. See https://golang.org/ref/mod#vcs-version. @@ -121,7 +121,7 @@ def get_related_language(self) -> str: """Gets the related language.""" return get_language_from_file_name(__file__) - def get_version_tag(self, version: str): + def get_version_tag(self, version: str) -> str: """Gets tag based on version.""" cleansed_version = version.strip().lstrip("v") return f"v{cleansed_version}" diff --git a/continuous_delivery_scripts/spdx_report/spdx_document.py b/continuous_delivery_scripts/spdx_report/spdx_document.py index 6e76c9b..bb47772 100644 --- a/continuous_delivery_scripts/spdx_report/spdx_document.py +++ b/continuous_delivery_scripts/spdx_report/spdx_document.py @@ -31,7 +31,7 @@ def __init__( package_metadata: PackageMetadata, other_document_refs: List[DependencySpdxDocumentRef] = list(), is_dependency: bool = False, - document_namespace: str = None, + document_namespace: Optional[str] = None, ): """Constructor.""" self._project_root = Path(configuration.get_value(ConfigurationVariable.PROJECT_ROOT)) diff --git a/continuous_delivery_scripts/spdx_report/spdx_helpers.py b/continuous_delivery_scripts/spdx_report/spdx_helpers.py index 4fad0a3..030481b 100644 --- a/continuous_delivery_scripts/spdx_report/spdx_helpers.py +++ b/continuous_delivery_scripts/spdx_report/spdx_helpers.py @@ -33,7 +33,7 @@ logger = logging.getLogger(__name__) # Copyright similar to the regex defined in flake8-copyright -COPYRIGHT_PATTERN = r"((?i)Copyright(?i).*$)" +COPYRIGHT_PATTERN = r"(?i)Copyright.*$" COPYRIGHT_REGEX_PATTERN = re.compile(COPYRIGHT_PATTERN, re.MULTILINE) # Specification of the identifier based on https://spdx.org/spdx-specification-21-web-version#h.twlc0ztnng3b # and https://spdx.org/ids-how @@ -73,7 +73,7 @@ def determine_file_copyright_text(path: Path) -> Optional[str]: match = scan_file_for_pattern(path, COPYRIGHT_REGEX_PATTERN) if not match: return None - return str(match.group(1).strip()) + return str(match.group(0).strip()) def determine_spdx_value(value: Optional[str]) -> Union[str, UnKnown, SPDXNone]: diff --git a/continuous_delivery_scripts/spdx_report/spdx_summary.py b/continuous_delivery_scripts/spdx_report/spdx_summary.py index 75d02df..1c03521 100644 --- a/continuous_delivery_scripts/spdx_report/spdx_summary.py +++ b/continuous_delivery_scripts/spdx_report/spdx_summary.py @@ -27,7 +27,7 @@ def generate_file_based_on_template( - output_dir: Path, template_name: str, template_args: dict, suffix: str = None + output_dir: Path, template_name: str, template_args: dict, suffix: Optional[str] = None ) -> None: """Write file based on template and arguments.""" logger.info("Loading template '%s'.", template_name) diff --git a/continuous_delivery_scripts/utils/configuration.py b/continuous_delivery_scripts/utils/configuration.py index ba2a648..5795d9c 100644 --- a/continuous_delivery_scripts/utils/configuration.py +++ b/continuous_delivery_scripts/utils/configuration.py @@ -240,7 +240,7 @@ class FileConfig(GenericConfig): PATH_TOKEN = {"DIR", "ROOT", "PATH"} CONFIG_FILE_NAME = "pyproject.toml" - def __init__(self, file_path: str = None) -> None: + def __init__(self, file_path: Optional[str] = None) -> None: """Constructor. Args: diff --git a/continuous_delivery_scripts/utils/git_helpers.py b/continuous_delivery_scripts/utils/git_helpers.py index 4c3441b..e402f17 100644 --- a/continuous_delivery_scripts/utils/git_helpers.py +++ b/continuous_delivery_scripts/utils/git_helpers.py @@ -114,7 +114,7 @@ def checkout(self, branch: Any) -> None: """ self.repo.git.checkout(branch) - def _add_one_file_or_one_dir(self, path: str) -> None: + def _add_one_file_or_one_dir(self, path: Path) -> None: if not path: raise ValueError("Unspecified path.") self._add_one_path(Path(path)) @@ -132,7 +132,7 @@ def _add_one_path(self, path_model: Path) -> None: logger.info(f"Adding {unix_relative_path} to repository.") self.repo.git.add(unix_relative_path) - def add(self, path: Union[list, set, str]) -> None: + def add(self, path: Union[list, set, Path]) -> None: """Adds a file or a list of files. Args: @@ -488,7 +488,7 @@ def is_dirty(self) -> bool: Repository is considered dirty when git status returns elements which are not committed. """ - return self.repo.is_dirty(untracked_files=True) + return bool(self.repo.is_dirty(untracked_files=True)) def clean(self) -> None: """Cleans the repository. @@ -588,17 +588,19 @@ def is_current_branch_feature(self) -> bool: is_release = self.is_release_branch(current_branch) return not (is_master or is_beta or is_release) - def is_current_branch_of_type(self, pattern: str) -> (bool, Optional[List[Any]]): + def is_current_branch_of_type(self, pattern: str) -> Tuple[bool, Optional[List[Any]]]: """Returns boolean indicating whether the current branch follows the pattern and the list of groups if any.""" return self._is_branch_of_type(self.get_current_branch(), pattern) - def _is_branch_of_type(self, branch_name: Optional[str], pattern: Optional[str]) -> (bool, Optional[List[Any]]): + def _is_branch_of_type( + self, branch_name: Optional[str], pattern: Optional[str] + ) -> Tuple[bool, Optional[List[Any]]]: if not pattern: return False, None if not branch_name: return False, None match = re.search(pattern, str(branch_name)) - return True if match else False, match.groups() if match else None + return True if match else False, list(match.groups()) if match else None @property def uncommitted_changes(self) -> List[Path]: @@ -643,7 +645,7 @@ def apply_uncommitted_changes(self, other_repo: "GitWrapper") -> None: """Applies the uncommitted changes found in current repository to another. Args: - other_repo: repository to apply changes to + other_repo: repository to apply changes to """ dest_root = other_repo.root for f in self.uncommitted_changes: @@ -738,7 +740,7 @@ def get_corresponding_path(self, path_in_initial_repo: Path) -> Path: path_in_initial_repo: path to a file/directory in initial repository. Returns: - corresponding path. + corresponding path. """ if not path_in_initial_repo.is_absolute(): return Path(self.root).joinpath(path_in_initial_repo) diff --git a/continuous_delivery_scripts/utils/language_specifics_base.py b/continuous_delivery_scripts/utils/language_specifics_base.py index 9a94fc4..3a1cf5e 100644 --- a/continuous_delivery_scripts/utils/language_specifics_base.py +++ b/continuous_delivery_scripts/utils/language_specifics_base.py @@ -79,8 +79,8 @@ def tag_release(self, git: GitWrapper, version: str, shortcuts: Dict[str, bool]) """Tags release commit.""" logger.info(f"Tagging commit as release {version}") git.create_tag(self.get_version_tag(version), message=f"release {version}") - for shortcut, version in shortcuts.items(): - if version: + for shortcut, versions in shortcuts.items(): + if versions: git.create_tag(self.get_version_tag(shortcut), message=f"{shortcut} release") else: git.create_tag(shortcut, message=shortcut) diff --git a/continuous_delivery_scripts/utils/versioning.py b/continuous_delivery_scripts/utils/versioning.py index bbb25b5..7957594 100644 --- a/continuous_delivery_scripts/utils/versioning.py +++ b/continuous_delivery_scripts/utils/versioning.py @@ -73,11 +73,11 @@ def determine_version_string( commit_count = version_elements.get(auto_version_tool.Constants.COMMIT_COUNT_FIELD, None) if not commit_count: with LocalProjectRepository() as git: - commit_count = git.get_commit_count() + commit_count = str(git.get_commit_count()) commit_hash = version_elements.get(auto_version_tool.Constants.COMMIT_FIELD, None) if not commit_hash: with LocalProjectRepository() as git: - commit_hash = git.get_commit_hash() + commit_hash = str(git.get_commit_hash()) return "%s-%s.%s+%s" % ( new_version, auto_version_tool.config.BUILD_TOKEN, @@ -129,11 +129,11 @@ def determine_version_shortcuts( commit_count = version_elements.get(auto_version_tool.Constants.COMMIT_COUNT_FIELD, None) if not commit_count: with LocalProjectRepository() as git: - commit_count = git.get_commit_count() + commit_count = str(git.get_commit_count()) commit_hash = version_elements.get(auto_version_tool.Constants.COMMIT_FIELD, None) if not commit_hash: with LocalProjectRepository() as git: - commit_hash = git.get_commit_hash() + commit_hash = str(git.get_commit_hash()) shortcuts[f"{auto_version_tool.config.BUILD_TOKEN}.{commit_count}+{commit_hash}"] = False return shortcuts diff --git a/news/20230303162821.bugfix b/news/20230303162821.bugfix new file mode 100644 index 0000000..b0aeb56 --- /dev/null +++ b/news/20230303162821.bugfix @@ -0,0 +1 @@ +Support python 3.11 diff --git a/setup.py b/setup.py index f482bc2..d628d2d 100644 --- a/setup.py +++ b/setup.py @@ -32,6 +32,7 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Build Tools", ], description="Continuous Delivery scripts to increase automation",