kira-setup 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 wemake.services
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: kira-setup
3
+ Version: 0.2.0
4
+ Summary: Kira's CLI to setup new projects
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: wemake.services,gitlab-bot,gitlab,kira,kira-bot
8
+ Author: sobolevn
9
+ Author-email: mail@sobolevn.me
10
+ Requires-Python: >=3.11,<3.15
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: Implementation :: CPython
13
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Development Status :: 5 - Production/Stable
21
+ Classifier: Environment :: Console
22
+ Classifier: Typing :: Typed
23
+ Classifier: Intended Audience :: Developers
24
+ Classifier: Operating System :: OS Independent
25
+ Requires-Dist: python-gitlab (>=8.4,<9.0)
26
+ Requires-Dist: termcolor (>=3.3,<4.0)
27
+ Project-URL: Homepage, https://github.com/wemake-services/kira-setup
28
+ Project-URL: Repository, https://github.com/wemake-services/kira-setup
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Kira Setup Bot
32
+
33
+ [![wemake.services](https://img.shields.io/badge/%20-wemake.services-green.svg?label=%20&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC%2FxhBQAAAAFzUkdCAK7OHOkAAAAbUExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP%2F%2F%2F5TvxDIAAAAIdFJOUwAjRA8xXANAL%2Bv0SAAAADNJREFUGNNjYCAIOJjRBdBFWMkVQeGzcHAwksJnAPPZGOGAASzPzAEHEGVsLExQwE7YswCb7AFZSF3bbAAAAABJRU5ErkJggg%3D%3D)](https://wemake.services)
34
+ [![kira-family](https://img.shields.io/badge/kira-family-pink.svg)](https://github.com/wemake-services/kira)
35
+ [![Build Status](https://github.com/wemake-services/kira-setup/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/wemake-services/kira-setup/actions/workflows/test.yml)
36
+ [![wemake-python-styleguide](https://img.shields.io/badge/style-wemake-000000.svg)](https://github.com/wemake-services/wemake-python-styleguide)
37
+ [![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/wemake-services/kira-setup/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
38
+
39
+ CLI utility to automate routine work with new projects.
40
+
41
+ Part of the [`@kira`](https://github.com/wemake-services/kira) bots family.
42
+
43
+ `kira-setup` does not create a repository. It configures an existing GitLab
44
+ project so new repositories start with the same engineering standards.
45
+
46
+ ## Features
47
+
48
+ - [x] Configures GitLab project defaults for a stricter merge flow
49
+ - [x] Requires passing pipelines before merge
50
+ - [x] Requires all discussions to be resolved before merge
51
+ - [x] Sets fast-forward only merge strategy
52
+ - [x] Configures merge request approval rules
53
+ - [x] Protects the `master` branch and release tags matching `v*`
54
+ - [x] Enforces push rules for branch names and commit messages
55
+ - [x] Prevents secret pushes and enables member checks
56
+ - [x] Creates a standard label set for triage and workflow
57
+ - [x] Configures container registry cleanup policy
58
+
59
+ ## Installation
60
+
61
+ Requirements:
62
+
63
+ - Python 3.11+
64
+ - An existing GitLab project
65
+ - A GitLab access token with permission to manage project settings
66
+
67
+ ```bash
68
+ pip install kira-setup
69
+ ```
70
+
71
+ ## Quick Start
72
+
73
+ Run the CLI against an existing project:
74
+
75
+ ```bash
76
+ kira-setup group-or-user/project-name --token YOUR_ACCESS_TOKEN
77
+ ```
78
+
79
+ For self-hosted GitLab:
80
+
81
+ ```bash
82
+ kira-setup group-or-user/project-name \
83
+ --token YOUR_ACCESS_TOKEN \
84
+ --domain gitlab.example.com
85
+ ```
86
+
87
+ To skip specific setup steps, repeat `--skip` with the stable step name:
88
+
89
+ ```bash
90
+ kira-setup group-or-user/project-name \
91
+ --token YOUR_ACCESS_TOKEN \
92
+ --skip labels \
93
+ --skip protect-tags
94
+ ```
95
+
96
+ Available `--skip` values:
97
+
98
+ - `star`
99
+ - `configure`
100
+ - `push-rules`
101
+ - `approval-rules`
102
+ - `labels`
103
+ - `protect-branches`
104
+ - `protect-tags`
105
+ - `cleanup-policy`
106
+
107
+ ## Why Use It
108
+
109
+ We use this CLI to make repository setup repeatable across projects.
110
+ Instead of applying the same GitLab rules manually every time, `kira-setup`
111
+ brings a new repository to the expected baseline in a single command.
112
+
113
+ ## Related Projects
114
+
115
+ - [`kira`](https://github.com/wemake-services/kira): the full bots family
116
+ - [`kira-stale`](https://github.com/wemake-services/kira-stale): stale issue and
117
+ pull request automation
118
+ - [`kira-release`](https://github.com/wemake-services/kira-release): automated
119
+ semantic releases
120
+
@@ -0,0 +1,89 @@
1
+ # Kira Setup Bot
2
+
3
+ [![wemake.services](https://img.shields.io/badge/%20-wemake.services-green.svg?label=%20&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC%2FxhBQAAAAFzUkdCAK7OHOkAAAAbUExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP%2F%2F%2F5TvxDIAAAAIdFJOUwAjRA8xXANAL%2Bv0SAAAADNJREFUGNNjYCAIOJjRBdBFWMkVQeGzcHAwksJnAPPZGOGAASzPzAEHEGVsLExQwE7YswCb7AFZSF3bbAAAAABJRU5ErkJggg%3D%3D)](https://wemake.services)
4
+ [![kira-family](https://img.shields.io/badge/kira-family-pink.svg)](https://github.com/wemake-services/kira)
5
+ [![Build Status](https://github.com/wemake-services/kira-setup/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/wemake-services/kira-setup/actions/workflows/test.yml)
6
+ [![wemake-python-styleguide](https://img.shields.io/badge/style-wemake-000000.svg)](https://github.com/wemake-services/wemake-python-styleguide)
7
+ [![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/wemake-services/kira-setup/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
8
+
9
+ CLI utility to automate routine work with new projects.
10
+
11
+ Part of the [`@kira`](https://github.com/wemake-services/kira) bots family.
12
+
13
+ `kira-setup` does not create a repository. It configures an existing GitLab
14
+ project so new repositories start with the same engineering standards.
15
+
16
+ ## Features
17
+
18
+ - [x] Configures GitLab project defaults for a stricter merge flow
19
+ - [x] Requires passing pipelines before merge
20
+ - [x] Requires all discussions to be resolved before merge
21
+ - [x] Sets fast-forward only merge strategy
22
+ - [x] Configures merge request approval rules
23
+ - [x] Protects the `master` branch and release tags matching `v*`
24
+ - [x] Enforces push rules for branch names and commit messages
25
+ - [x] Prevents secret pushes and enables member checks
26
+ - [x] Creates a standard label set for triage and workflow
27
+ - [x] Configures container registry cleanup policy
28
+
29
+ ## Installation
30
+
31
+ Requirements:
32
+
33
+ - Python 3.11+
34
+ - An existing GitLab project
35
+ - A GitLab access token with permission to manage project settings
36
+
37
+ ```bash
38
+ pip install kira-setup
39
+ ```
40
+
41
+ ## Quick Start
42
+
43
+ Run the CLI against an existing project:
44
+
45
+ ```bash
46
+ kira-setup group-or-user/project-name --token YOUR_ACCESS_TOKEN
47
+ ```
48
+
49
+ For self-hosted GitLab:
50
+
51
+ ```bash
52
+ kira-setup group-or-user/project-name \
53
+ --token YOUR_ACCESS_TOKEN \
54
+ --domain gitlab.example.com
55
+ ```
56
+
57
+ To skip specific setup steps, repeat `--skip` with the stable step name:
58
+
59
+ ```bash
60
+ kira-setup group-or-user/project-name \
61
+ --token YOUR_ACCESS_TOKEN \
62
+ --skip labels \
63
+ --skip protect-tags
64
+ ```
65
+
66
+ Available `--skip` values:
67
+
68
+ - `star`
69
+ - `configure`
70
+ - `push-rules`
71
+ - `approval-rules`
72
+ - `labels`
73
+ - `protect-branches`
74
+ - `protect-tags`
75
+ - `cleanup-policy`
76
+
77
+ ## Why Use It
78
+
79
+ We use this CLI to make repository setup repeatable across projects.
80
+ Instead of applying the same GitLab rules manually every time, `kira-setup`
81
+ brings a new repository to the expected baseline in a single command.
82
+
83
+ ## Related Projects
84
+
85
+ - [`kira`](https://github.com/wemake-services/kira): the full bots family
86
+ - [`kira-stale`](https://github.com/wemake-services/kira-stale): stale issue and
87
+ pull request automation
88
+ - [`kira-release`](https://github.com/wemake-services/kira-release): automated
89
+ semantic releases
File without changes
@@ -0,0 +1,57 @@
1
+ import argparse
2
+ from types import MappingProxyType
3
+ from typing import Final
4
+
5
+ import gitlab
6
+ from gitlab.v4.objects import Project
7
+
8
+ from kira_setup.logging import report_progress
9
+ from kira_setup.pipelines import (
10
+ container_registry,
11
+ labels,
12
+ merge_requests,
13
+ project,
14
+ protected,
15
+ )
16
+
17
+ PIPELINE_STEPS: Final = MappingProxyType({
18
+ # Project:
19
+ 'star': project.star,
20
+ 'configure': project.configure,
21
+ 'push-rules': project.push_rules,
22
+ # Merge Requests:
23
+ 'approval-rules': merge_requests.approval_rules,
24
+ # Labels:
25
+ 'labels': labels.create_labels,
26
+ # Protection Rules:
27
+ 'protect-branches': protected.branches,
28
+ 'protect-tags': protected.tags,
29
+ # Container Registry:
30
+ 'cleanup-policy': container_registry.cleanup_policy,
31
+ })
32
+
33
+
34
+ def _get_project(context: argparse.Namespace) -> Project:
35
+ gl = gitlab.Gitlab(
36
+ f'https://{context.domain}',
37
+ context.token,
38
+ timeout=10,
39
+ )
40
+
41
+ return gl.projects.get(context.path)
42
+
43
+
44
+ def _start_pipeline(
45
+ current_project: Project,
46
+ context: argparse.Namespace,
47
+ ) -> None:
48
+ for step, action in PIPELINE_STEPS.items():
49
+ if step in context.skip:
50
+ continue
51
+
52
+ report_progress(action)(current_project)
53
+
54
+
55
+ def start_pipeline(context: argparse.Namespace) -> None:
56
+ """Main function to start the whole project-setup pipeline."""
57
+ _start_pipeline(_get_project(context), context)
@@ -0,0 +1,31 @@
1
+ import argparse
2
+
3
+ from kira_setup.api import PIPELINE_STEPS, start_pipeline
4
+
5
+
6
+ def _create_parser() -> argparse.ArgumentParser:
7
+ parser = argparse.ArgumentParser()
8
+ parser.add_argument('path', help='Project group / name')
9
+ parser.add_argument('-t', '--token', type=str, help='Auth token')
10
+ parser.add_argument(
11
+ '-d',
12
+ '--domain',
13
+ default='gitlab.com',
14
+ type=str,
15
+ help='GitLab domain address, change if you use custom installation',
16
+ )
17
+ parser.add_argument(
18
+ '--skip',
19
+ action='append',
20
+ choices=PIPELINE_STEPS.keys(),
21
+ default=[],
22
+ help='Skip a pipeline step, repeat this option to skip multiple steps',
23
+ )
24
+ return parser
25
+
26
+
27
+ def main() -> None:
28
+ """Runs all pipeline actions."""
29
+ parser = _create_parser()
30
+ args = parser.parse_args()
31
+ start_pipeline(args)
@@ -0,0 +1,27 @@
1
+ from collections.abc import Callable
2
+ from contextlib import suppress
3
+ from functools import wraps
4
+ from typing import ParamSpec, TypeVar
5
+
6
+ from gitlab.exceptions import GitlabCreateError
7
+
8
+ ParamT = ParamSpec('ParamT')
9
+ ReturnT = TypeVar('ReturnT')
10
+
11
+
12
+ def idempotent(
13
+ function: Callable[ParamT, ReturnT],
14
+ ) -> Callable[ParamT, ReturnT | None]:
15
+ """Shallows 304 errors, making actions repeatable."""
16
+
17
+ @wraps(function)
18
+ def decorator(
19
+ *args: ParamT.args,
20
+ **kwargs: ParamT.kwargs,
21
+ ) -> ReturnT | None:
22
+ with suppress(GitlabCreateError):
23
+ return function(*args, **kwargs)
24
+
25
+ return None
26
+
27
+ return decorator
@@ -0,0 +1,32 @@
1
+ from collections.abc import Callable
2
+ from functools import wraps
3
+ from typing import ParamSpec, TypeVar
4
+
5
+ from termcolor import colored
6
+
7
+ ParamT = ParamSpec('ParamT')
8
+ ReturnT = TypeVar('ReturnT')
9
+
10
+
11
+ def report_progress(
12
+ function: Callable[ParamT, ReturnT],
13
+ ) -> Callable[ParamT, ReturnT]:
14
+ """Decorates a function to print its execution status."""
15
+
16
+ @wraps(function)
17
+ def decorator(
18
+ *args: ParamT.args,
19
+ **kwargs: ParamT.kwargs,
20
+ ) -> ReturnT:
21
+ try:
22
+ function_result = function(*args, **kwargs)
23
+ except Exception as exc:
24
+ message = f'{function.__name__} failed due to exception: {exc}'
25
+ print(colored(message, 'red')) # noqa: WPS421
26
+ raise
27
+ else:
28
+ message = f'{function.__name__} succeed'
29
+ print(colored(message, 'green')) # noqa: WPS421
30
+ return function_result
31
+
32
+ return decorator
File without changes
@@ -0,0 +1,18 @@
1
+ from gitlab.v4.objects import Project
2
+
3
+
4
+ def cleanup_policy(project: Project) -> None:
5
+ """
6
+ Configure container registry cleanup policy.
7
+
8
+ API: https://docs.gitlab.com/user/packages/container_registry/reduce_container_registry_storage/#cleanup-policy
9
+ """
10
+ project.container_expiration_policy_attributes = {
11
+ 'enabled': True,
12
+ 'cadence': '1d', # Run cleanup every day
13
+ 'keep_n': 5, # 5 newest semver tags
14
+ 'name_regex': r'^\d+\.\d+\.\d+$', # Plain semver only
15
+ 'name_regex_keep': '^(latest|dev)$', # Always keep
16
+ }
17
+
18
+ project.save()
@@ -0,0 +1,52 @@
1
+ from types import MappingProxyType
2
+ from typing import Final
3
+
4
+ from gitlab.v4.objects import Project
5
+
6
+ from kira_setup.decorators import idempotent
7
+
8
+ _LABELS: Final = MappingProxyType({
9
+ # Common:
10
+ 'bug': '#FF0000',
11
+ 'feature': '#428BCA',
12
+ 'documentation': '#69D100',
13
+ 'research': '#5843AD',
14
+ # Deadline:
15
+ 'deadline::soft': '#AD4363',
16
+ 'deadline::hard': '#D10069',
17
+ 'deadline::miss': '#CC0033',
18
+ # Validation:
19
+ 'validation::labels': '#A295D6',
20
+ 'validation::stale': '#A295D6',
21
+ 'validation::invalid': '#7F8C8D',
22
+ 'validation::estimate': '#D9534F',
23
+ # Notification:
24
+ 'notification::first': '#D1D100',
25
+ 'notification::last': '#F0AD4E',
26
+ })
27
+
28
+
29
+ def _is_prioritized(label: str) -> int:
30
+ """
31
+ Tells if label is prioritized or not.
32
+
33
+ >>> assert _is_prioritized('feature')
34
+ >>> assert not _is_prioritized('deadline:miss')
35
+
36
+ """
37
+ return 0 if '::' in label else 1
38
+
39
+
40
+ def create_labels(project: Project) -> None:
41
+ """
42
+ Creates all labels that are required for our projects.
43
+
44
+ API: https://docs.gitlab.com/api/labels/
45
+ """
46
+ safe_create = idempotent(project.labels.create)
47
+ for label, color in _LABELS.items():
48
+ safe_create({
49
+ 'name': label,
50
+ 'color': color,
51
+ 'priority': _is_prioritized(label),
52
+ })
@@ -0,0 +1,16 @@
1
+ from gitlab.v4.objects import Project
2
+
3
+
4
+ def approval_rules(project: Project) -> None:
5
+ """
6
+ Configures how approvals work for merge requests.
7
+
8
+ API: https://docs.gitlab.com/api/merge_request_approvals/
9
+ """
10
+ approvals = project.approvals.get()
11
+
12
+ approvals.approvals_before_merge = 1
13
+ approvals.reset_approvals_on_push = True
14
+ approvals.disable_overriding_approvers_per_merge_request = True
15
+
16
+ approvals.save()
@@ -0,0 +1,75 @@
1
+ from gitlab.exceptions import GitlabGetError, GitlabParsingError
2
+ from gitlab.v4.objects import Project
3
+
4
+ from kira_setup.decorators import idempotent
5
+
6
+ #: Used to enforce one-task = one-branch,
7
+ #: also has a fallback for dependabot updates:
8
+ branch_regex = r'^(issue-\d+)|(dependabot.*)|(renovate.*)|(master)$'
9
+
10
+ #: Enforces conventional commits,
11
+ #: see https://github.com/wemake-services/kira-release
12
+ commit_regex = r"""
13
+ ^(revert: )?
14
+ (feat|fix|docs|build|refactor|chore)
15
+ (\(.+\))?:
16
+ .{1,50}(refs #\d+)?
17
+ """
18
+
19
+
20
+ @idempotent
21
+ def star(project: Project) -> None:
22
+ """
23
+ Stars the given project.
24
+
25
+ API: https://docs.gitlab.com/api/project_starring/
26
+ """
27
+ project.star()
28
+
29
+
30
+ def configure(project: Project) -> None:
31
+ """
32
+ Configures basic things for a new project.
33
+
34
+ API: https://docs.gitlab.com/api/projects/#manage-projects/
35
+ """
36
+ project.resolve_outdated_diff_discussions = True
37
+ project.only_allow_merge_if_pipeline_succeeds = True
38
+ project.only_allow_merge_if_all_discussions_are_resolved = True
39
+ project.merge_method = 'ff'
40
+
41
+ project.save()
42
+
43
+
44
+ def push_rules(project: Project) -> None:
45
+ """
46
+ Sets all required push rules for the project.
47
+
48
+ API: https://docs.gitlab.com/api/project_push_rules/
49
+
50
+ GitLab may return either `404` or `null` when push rules have not been
51
+ created yet. `python-gitlab` expects a dictionary response
52
+ for `project.pushrules.get()`, so a `null` response becomes
53
+ `None` and raises `GitlabParsingError`.
54
+
55
+ To handle both cases, this function first tries to fetch existing push
56
+ rules and falls back to creating them when the server reports that they
57
+ do not exist yet.
58
+ """
59
+ payload = {
60
+ 'deny_delete_tag': True,
61
+ 'member_check': True,
62
+ 'prevent_secrets': True,
63
+ 'branch_name_regex': branch_regex,
64
+ 'commit_message_regex': commit_regex.replace('\n', ''),
65
+ }
66
+
67
+ try:
68
+ rules = project.pushrules.get()
69
+ except (GitlabGetError, GitlabParsingError):
70
+ project.pushrules.create(payload)
71
+ else:
72
+ for rule_name, rule_value in payload.items():
73
+ setattr(rules, rule_name, rule_value)
74
+
75
+ rules.save()
@@ -0,0 +1,31 @@
1
+ from gitlab import const
2
+ from gitlab.v4.objects import Project
3
+
4
+ from kira_setup.decorators import idempotent
5
+
6
+
7
+ @idempotent
8
+ def branches(project: Project) -> None:
9
+ """
10
+ Creates protected branch rules.
11
+
12
+ API: https://docs.gitlab.com/api/protected_branches/
13
+ """
14
+ project.protectedbranches.create({
15
+ 'name': 'master',
16
+ 'merge_access_level': const.DEVELOPER_ACCESS,
17
+ 'push_access_level': const.MAINTAINER_ACCESS,
18
+ })
19
+
20
+
21
+ @idempotent
22
+ def tags(project: Project) -> None:
23
+ """
24
+ Creates protected tag rules.
25
+
26
+ API: https://docs.gitlab.com/api/protected_tags/
27
+ """
28
+ project.protectedtags.create({
29
+ 'name': 'v*',
30
+ 'create_access_level': const.MAINTAINER_ACCESS,
31
+ })
@@ -0,0 +1,166 @@
1
+ [project]
2
+ name = "kira-setup"
3
+ version = "0.2.0"
4
+ description = "Kira's CLI to setup new projects"
5
+ requires-python = ">=3.11,<3.15"
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ dynamic = ["dependencies", "classifiers"]
9
+
10
+ authors = [
11
+ { name = "sobolevn", email = "mail@sobolevn.me" },
12
+ { name = "Alexander Kondratev", email = "mail@kondratev.dev" },
13
+ ]
14
+
15
+ keywords = [
16
+ "wemake.services",
17
+ "gitlab-bot",
18
+ "gitlab",
19
+ "kira",
20
+ "kira-bot",
21
+ ]
22
+
23
+ classifiers = [
24
+ 'Programming Language :: Python',
25
+ 'Programming Language :: Python :: Implementation :: CPython',
26
+ 'Programming Language :: Python :: Implementation :: PyPy',
27
+ 'Programming Language :: Python :: 3',
28
+ 'Programming Language :: Python :: 3 :: Only',
29
+ 'Programming Language :: Python :: 3.11',
30
+ 'Programming Language :: Python :: 3.12',
31
+ 'Programming Language :: Python :: 3.13',
32
+ 'Programming Language :: Python :: 3.14',
33
+ "Development Status :: 5 - Production/Stable",
34
+ "Environment :: Console",
35
+ "Typing :: Typed",
36
+ "Intended Audience :: Developers",
37
+ "Operating System :: OS Independent",
38
+ ]
39
+
40
+ [project.scripts]
41
+ kira-setup = "kira_setup.cli:main"
42
+
43
+ [project.urls]
44
+ repository = "https://github.com/wemake-services/kira-setup"
45
+ homepage = "https://github.com/wemake-services/kira-setup"
46
+
47
+ [build-system]
48
+ requires = ["poetry-core>=2.0"]
49
+ build-backend = "poetry.core.masonry.api"
50
+
51
+
52
+ [tool.poetry.dependencies]
53
+ python-gitlab = "^8.4"
54
+ termcolor = "^3.3"
55
+
56
+ [tool.poetry.group.dev.dependencies]
57
+ mypy = "^2.1"
58
+ wemake-python-styleguide = "^1.6"
59
+ ruff = "^0.15"
60
+ safety = "^3.8"
61
+
62
+
63
+ [tool.ruff]
64
+ # Ruff config: https://docs.astral.sh/ruff/settings
65
+ preview = true
66
+ fix = true
67
+ target-version = "py311"
68
+ line-length = 80
69
+
70
+ [tool.ruff.format]
71
+ quote-style = "single"
72
+ docstring-code-format = true
73
+
74
+ [tool.ruff.lint]
75
+ select = [
76
+ "A", # flake8-builtins
77
+ "ASYNC",
78
+ "B", # flake8-bugbear
79
+ "C4", # flake8-comprehensions
80
+ "C90", # maccabe
81
+ "COM", # flake8-commas
82
+ "D", # pydocstyle
83
+ "DTZ", # flake8-datetimez
84
+ "E", # pycodestyle
85
+ "ERA", # flake8-eradicate
86
+ "EXE", # flake8-executable
87
+ "F", # pyflakes
88
+ "FA", # flake8-future-annotations
89
+ "FBT", # flake8-boolean-trap
90
+ "FLY", # pyflint
91
+ "FURB", # refurb
92
+ "G", # flake8-logging-format
93
+ "I", # isort
94
+ "ICN", # flake8-import-conventions
95
+ "ISC", # flake8-implicit-str-concat
96
+ "LOG", # flake8-logging
97
+ "N", # pep8-naming
98
+ "PERF", # perflint
99
+ "PIE", # flake8-pie
100
+ "PL", # pylint
101
+ "PT", # flake8-pytest-style
102
+ "PTH", # flake8-use-pathlib
103
+ "Q", # flake8-quotes
104
+ "RET", # flake8-return
105
+ "RSE", # flake8-raise
106
+ "RUF", # ruff
107
+ "S", # flake8-bandit
108
+ "SIM", # flake8-simpify
109
+ "SLF", # flake8-self
110
+ "SLOT", # flake8-slots
111
+ "T100", # flake8-debugger
112
+ "TRY", # tryceratops
113
+ "UP", # pyupgrade
114
+ "W", # pycodestyle
115
+ "YTT", # flake8-2020
116
+ ]
117
+ ignore = [
118
+ "A005", # allow to shadow stdlib and builtin module names
119
+ # Different doc rules that we don't really care about:
120
+ "D100",
121
+ "D104",
122
+ "D106",
123
+ "D203",
124
+ "D212",
125
+ "D401",
126
+ "D404",
127
+ "D405",
128
+ "ISC001", # implicit string concat conflicts with `ruff format`
129
+ "ISC003", # prefer explicit string concat over implicit concat
130
+ "PLR09", # we have our own complexity rules
131
+ "PLR2004", # do not report magic numbers
132
+ "PLR6301", # do not require classmethod / staticmethod when self not used
133
+ "RUF067", # wps handles `__init__.py` logic better
134
+ "TRY003", # long exception messages from `tryceratops`
135
+ ]
136
+ external = ["WPS"]
137
+
138
+ # Plugin configs:
139
+ flake8-quotes.inline-quotes = "single"
140
+ mccabe.max-complexity = 6
141
+ pydocstyle.convention = "google"
142
+
143
+
144
+ [tool.mypy]
145
+ # Mypy configuration:
146
+ # https://mypy.readthedocs.io/en/latest/config_file.html
147
+ enable_error_code = [
148
+ "truthy-bool",
149
+ "truthy-iterable",
150
+ "redundant-expr",
151
+ "unused-awaitable",
152
+ "ignore-without-code",
153
+ "possibly-undefined",
154
+ "redundant-self",
155
+ "explicit-override",
156
+ "unimported-reveal",
157
+ "deprecated",
158
+ "exhaustive-match",
159
+ ]
160
+
161
+ explicit_package_bases = true
162
+ ignore_missing_imports = true
163
+ extra_checks = true
164
+ strict = true
165
+ strict_equality_for_none = true
166
+ warn_unreachable = true