suite-py 1.42.2__py3-none-any.whl → 1.43.1__py3-none-any.whl

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.
suite_py/__version__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # -*- encoding: utf-8 -*-
2
- __version__ = "1.42.2"
2
+ __version__ = "1.43.1"
suite_py/cli.py CHANGED
@@ -38,7 +38,6 @@ from typing import Optional
38
38
 
39
39
  import click
40
40
  import requests
41
- from autoupgrade import Package
42
41
  from click.exceptions import ClickException
43
42
 
44
43
  from suite_py.__version__ import __version__
@@ -49,6 +48,7 @@ from suite_py.lib.handler import git_handler as git
49
48
  from suite_py.lib.handler import prompt_utils
50
49
  from suite_py.lib.handler.captainhook_handler import CaptainHook
51
50
  from suite_py.lib.handler.okta_handler import Okta
51
+ from suite_py.lib.handler.pre_commit_handler import PreCommit
52
52
  from suite_py.lib.tokens import Tokens
53
53
 
54
54
  # pylint: enable=wrong-import-position
@@ -94,6 +94,8 @@ def upgrade_suite_py_if_needed(break_on_missing_package: bool = False) -> None:
94
94
  # If available, upgrade (if needed)
95
95
  latest_version = fetch_latest_version()
96
96
  if latest_version is None or latest_version > installed_version:
97
+ from autoupgrade import Package
98
+
97
99
  # If we fail to fetch the latest version, fallback to attempting the upgrade anyway
98
100
  Package("suite_py").upgrade()
99
101
  except Exception as error:
@@ -195,6 +197,9 @@ def main(ctx, project, timeout, verbose):
195
197
  or ctx.invoked_subcommand not in ALLOW_NO_HOME_SUBCOMMAND
196
198
  ):
197
199
  os.chdir(os.path.join(config.user["projects_home"], ctx.obj.project))
200
+ # Warn on missing pre_commit hook
201
+ if ctx.invoked_subcommand not in ALLOW_NO_GIT_SUBCOMMAND:
202
+ ctx.obj.call(PreCommit).check_and_warn()
198
203
 
199
204
 
200
205
  @main.result_callback()
@@ -15,6 +15,7 @@ from suite_py.lib.handler.youtrack_handler import YoutrackHandler
15
15
 
16
16
  class Release:
17
17
  # pylint: disable=too-many-instance-attributes
18
+ # pylint: disable=too-many-positional-arguments
18
19
  def __init__(self, action, project, captainhook, config, tokens):
19
20
  self._action = action
20
21
  self._project = project
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  import requests
3
3
 
4
+ from suite_py.__version__ import __version__
4
5
  from suite_py.lib.handler.github_handler import GithubHandler
5
6
  from suite_py.lib.handler.okta_handler import Okta
6
- from suite_py.__version__ import __version__
7
7
 
8
8
 
9
9
  class UnauthorizedError(Exception):
@@ -269,6 +269,45 @@ class GitHandler:
269
269
  logger.error(f"Error during git reset on {self._repo}: {e}")
270
270
  sys.exit(-1)
271
271
 
272
+ def get_git_config(self, option):
273
+ """
274
+ Get git config value.
275
+ Git configs are very versitile and allow arbitrary string KV pairs to be stored,
276
+ support per-repo, or more complex conditional and global configurations,
277
+ making it the perfect place for us to store a lot of our configs.
278
+ """
279
+ proc = subprocess.run(
280
+ ["git", "config", "get", option],
281
+ cwd=self._path,
282
+ check=False,
283
+ capture_output=True,
284
+ )
285
+ if proc.returncode == 0:
286
+ return proc.stdout.decode().strip()
287
+ return None
288
+
289
+ def hooks_path(self):
290
+ """
291
+ Get path to git-hooks
292
+ See: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks for more details
293
+ """
294
+ try:
295
+ proc = subprocess.run(
296
+ ["git", "rev-parse", "--git-path", "hooks"],
297
+ cwd=self._path,
298
+ check=True,
299
+ capture_output=True,
300
+ )
301
+ hooks = proc.stdout.decode().strip()
302
+
303
+ # Return absolute path to the hooks dir,
304
+ # since we overwrite the subprocesses cwd.
305
+ abs_path = os.path.join(self._path, hooks)
306
+ return abs_path
307
+ except CalledProcessError as e:
308
+ logger.error(f"Error getting hooks path {self._repo}: {e}")
309
+ sys.exit(-1)
310
+
272
311
 
273
312
  def get_root_folder(directory):
274
313
  return (
@@ -19,6 +19,7 @@ class GithubHandler:
19
19
  def get_user(self):
20
20
  return self._client.get_user()
21
21
 
22
+ # pylint: disable=too-many-positional-arguments
22
23
  def create_pr(self, repo, branch, title, body="", base="master", is_draft=False):
23
24
  return self.get_repo(repo).create_pull(
24
25
  title=title, head=branch, base=base, body=body, draft=is_draft
@@ -0,0 +1,96 @@
1
+ import os
2
+
3
+ import yaml
4
+
5
+ from suite_py.lib import logger
6
+ from suite_py.lib.config import Config
7
+ from suite_py.lib.handler.git_handler import GitHandler
8
+
9
+
10
+ class PreCommit:
11
+ """
12
+ Handler checking whether the user has the prima pre-commit hooks setup and print a warning if they are missing
13
+ """
14
+
15
+ def __init__(self, project: str, config: Config):
16
+ self._git = GitHandler(project, config)
17
+
18
+ def check_and_warn(self):
19
+ if self.is_enabled() and not self.is_pre_commit_hooks_installed():
20
+ self.warn_missing_pre_commit_hook()
21
+
22
+ def is_pre_commit_hooks_installed(self):
23
+ """
24
+ Apply some heuteristics to check whether the gitleaks pre-commit hook is installed.
25
+ This is extremely imperfect, and only supports direct calls in shell scripts.
26
+ More hooks, like husky should be added later
27
+ """
28
+ return self.is_vanilla_hook_setup() or self.is_pre_commit_py_hook_setup()
29
+
30
+ def warn_missing_pre_commit_hook(self):
31
+ logger.warning(
32
+ """
33
+ Looks like the current repo is missing the gitleaks pre-commit hook!
34
+ Please install it per the security guide:
35
+ https://www.notion.so/helloprima/Install-Gitleaks-pre-commit-hook-aaaa6beafafa4c298b537afcb52bb25a
36
+
37
+ If you have installed them already you can report the false positive to team-platform-shared-services (on Slack) and run:
38
+ git config suite-py.disable-pre-commit-warning true
39
+ to disable the check for this repo, or
40
+ git config --global suite-py.disable-pre-commit-warning true
41
+ to disable it globally
42
+ """
43
+ )
44
+
45
+ def is_vanilla_hook_setup(self):
46
+ """
47
+ Check whether the gitleaks hook is setup as a regular bash script
48
+ """
49
+ pre_commit_file = self.read_pre_commit_hook()
50
+
51
+ # Assume everything is a shell script.
52
+ # Technically you could use a binary, or even python code,
53
+ # But those are out of scope for us, and the user should just disable the warning themselves
54
+ lines = pre_commit_file.splitlines()
55
+
56
+ # Filter out lines that start with '#' since those are probably just comments.
57
+ without_comments = filter(lambda l: not l.strip().startswith("#"), lines)
58
+
59
+ return any("gitleaks" in line for line in without_comments)
60
+
61
+ def is_pre_commit_py_hook_setup(self):
62
+ """
63
+ Check whether the gitleaks hook is setup with the pre-commit python framework
64
+ """
65
+ # If the framework is not setup skip
66
+ if "pre-commit" not in self.read_pre_commit_hook():
67
+ logger.debug("pre-commit.com not installed, skipping config check")
68
+ return False
69
+
70
+ config_path = os.path.join(self._git.get_path(), ".pre-commit-config.yaml")
71
+ try:
72
+ with open(config_path, encoding="utf-8") as f:
73
+ config = yaml.safe_load(f)
74
+ except FileNotFoundError:
75
+ logger.debug("pre-commit config file(%s) not found", config_path)
76
+ return False
77
+ except yaml.YAMLError:
78
+ logger.warning(".pre-commit-config.yaml file is invalid!", exc_info=True)
79
+ return False
80
+
81
+ return any(
82
+ repo.get("repo", "") == "git@github.com:primait/security-hooks.git"
83
+ for repo in config.get("repos", [])
84
+ )
85
+
86
+ def read_pre_commit_hook(self):
87
+ pre_commit_file_path = os.path.join(self._git.hooks_path(), "pre-commit")
88
+ logger.debug("Reading pre-commit script from %s", pre_commit_file_path)
89
+ try:
90
+ with open(pre_commit_file_path, encoding="utf-8") as f:
91
+ return f.read()
92
+ except FileNotFoundError:
93
+ return ""
94
+
95
+ def is_enabled(self):
96
+ return self._git.get_git_config("suite-py.disable-pre-commit-warning") != "true"
suite_py/lib/logger.py CHANGED
@@ -1,33 +1,40 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  import logging
3
3
 
4
- import logzero
5
- from logzero import logger as _logger
4
+ from rich.logging import RichHandler
6
5
 
7
- DEFAULT_FORMAT = "%(color)s[%(levelname)1.4s]%(end_color)s %(message)s"
8
- # Set a custom formatter
9
- formatter = logzero.LogFormatter(fmt=DEFAULT_FORMAT)
6
+ # Rich handles color and formatting internally
7
+ # This format will make it so we print stuff like "[I] message"
8
+ LOG_FORMAT = "%(message)s"
9
+
10
+ LOGGER_NAME = "suite_py_logger"
10
11
 
11
12
 
12
13
  def setup(verbose):
13
- logzero.setup_default_logger(formatter=formatter)
14
14
  level = logging.DEBUG if verbose else logging.INFO
15
15
 
16
- _logger.setLevel(level)
17
- _logger.debug("Logging as %s", level)
16
+ logging.basicConfig(
17
+ level=level,
18
+ format=LOG_FORMAT,
19
+ datefmt="[%X]",
20
+ handlers=[RichHandler(rich_tracebacks=True, markup=False)],
21
+ )
22
+
23
+ logger = logging.getLogger(LOGGER_NAME)
24
+ logger.debug("Logging as %s", level)
18
25
 
19
26
 
20
27
  def debug(message, *args, **kwargs):
21
- _logger.debug(message, *args, **kwargs)
28
+ logging.getLogger(LOGGER_NAME).debug(message, *args, **kwargs)
22
29
 
23
30
 
24
31
  def info(message, *args, **kwargs):
25
- _logger.info(message, *args, **kwargs)
32
+ logging.getLogger(LOGGER_NAME).info(message, *args, **kwargs)
26
33
 
27
34
 
28
35
  def warning(message, *args, **kwargs):
29
- _logger.warning(message, *args, **kwargs)
36
+ logging.getLogger(LOGGER_NAME).warning(message, *args, **kwargs)
30
37
 
31
38
 
32
39
  def error(message, *args, **kwargs):
33
- _logger.error(message, *args, **kwargs)
40
+ logging.getLogger(LOGGER_NAME).error(message, *args, **kwargs)
suite_py/lib/metrics.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  import functools
3
- from suite_py.lib.config import Config
4
3
 
4
+ from suite_py.lib.config import Config
5
5
  from suite_py.lib.handler.captainhook_handler import CaptainHook
6
6
  from suite_py.lib.handler.metrics_handler import Metrics
7
7
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: suite-py
3
- Version: 1.42.2
3
+ Version: 1.43.1
4
4
  Summary:
5
5
  Author: larrywax, EugenioLaghi, michelangelomo
6
6
  Author-email: devops@prima.it
@@ -13,30 +13,22 @@ Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Requires-Dist: Click (>=7.0)
15
15
  Requires-Dist: InquirerPy (>=0.2.0)
16
- Requires-Dist: Jinja2 (>=3.1.4,<4.0.0)
17
16
  Requires-Dist: PyGithub (>=1.57)
18
17
  Requires-Dist: PyYaml (>=5.4)
19
18
  Requires-Dist: autoupgrade-prima (>=0.6)
20
- Requires-Dist: black (>=22.6,<25.0)
21
19
  Requires-Dist: boto3 (>=1.17.84)
22
20
  Requires-Dist: cement (>=3.0)
23
21
  Requires-Dist: colorama (>=0.4.3)
24
- Requires-Dist: cryptography (==43.0.1)
25
22
  Requires-Dist: halo (>=0.0.28)
26
- Requires-Dist: inquirer (==3.1.4)
23
+ Requires-Dist: inquirer (>=3.1.4,<4.0.0)
27
24
  Requires-Dist: itsdangerous (==2.2.0)
28
25
  Requires-Dist: keyring (>=23.9.1,<26.0.0)
29
- Requires-Dist: kubernetes (==31.0.0)
30
- Requires-Dist: logzero (==1.7.0)
31
- Requires-Dist: markupsafe (==2.0.1)
32
- Requires-Dist: pptree (==3.1)
33
26
  Requires-Dist: pylint (>=2.14.5,<4.0.0)
34
- Requires-Dist: pyreadline (>=2.1,<3.0)
35
27
  Requires-Dist: pytest (>=7.0.0)
36
28
  Requires-Dist: python-dateutil (>=2.8.2)
37
29
  Requires-Dist: requests (>=2.26.0)
38
30
  Requires-Dist: requests-toolbelt (>=0.9.1)
39
- Requires-Dist: rich (==13.9.2)
40
- Requires-Dist: semver (>=2.13.0,<3.0.0)
31
+ Requires-Dist: rich (==13.9.4)
32
+ Requires-Dist: semver (>=3.0.4,<4.0.0)
41
33
  Requires-Dist: termcolor (>=1.1.0)
42
- Requires-Dist: truststore (>=0.7,<0.10) ; python_version >= "3.10"
34
+ Requires-Dist: truststore (>=0.7,<0.11) ; python_version >= "3.10"
@@ -1,6 +1,6 @@
1
1
  suite_py/__init__.py,sha256=REmi3D0X2G1ZWnYpKs8Ffm3NIj-Hw6dMuvz2b9NW344,142
2
- suite_py/__version__.py,sha256=epapGv8LINFClZsTAecNa7bKBm9IBjycsyHCcdVZdlc,49
3
- suite_py/cli.py,sha256=iJDqnKEPuvbX7OBawtkW38T_37xLFktohFz1YBjPUbs,10047
2
+ suite_py/__version__.py,sha256=L-FgAa7sAxqwqFnOr7JH__nj1oSq6fR0wb9Uj9anwSE,49
3
+ suite_py/cli.py,sha256=dqLHo7HADRcXj0xD278bYZjCywx4YUfVFxCvQvjNoi4,10271
4
4
  suite_py/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  suite_py/commands/ask_review.py,sha256=yN__Ac-fiZBPShjRDhyCCQZGfVlQE16KozoJk4UtiNw,3788
6
6
  suite_py/commands/bump.py,sha256=oFZU1hPfD11ujFC5G7wFyQOf2alY3xp2SO1h1ldjf3s,5406
@@ -12,25 +12,26 @@ suite_py/commands/login.py,sha256=A59e1HsbN7Ocv2L_2H0Eb7MZK7AzLkLb72QxBthnIqU,25
12
12
  suite_py/commands/merge_pr.py,sha256=fXIE8mT9MjvvpqE-uVdXGBVFGhn0eQzcBxNr-N8SyAY,5171
13
13
  suite_py/commands/open_pr.py,sha256=e6IT6f8L_HKsOEtrGHZQ2HWrFwmvIgXGNCK_TU0WE9k,7009
14
14
  suite_py/commands/project_lock.py,sha256=b7OkGysue_Sl13VIT7B5CTBppCvrB_Q6iC0IJRBSHp8,1909
15
- suite_py/commands/release.py,sha256=Mh6esivJ234cRE5mYWPOZSgpujO47RprySh0U_9BD9s,7857
15
+ suite_py/commands/release.py,sha256=u6pdT1Vk9qBLnfgxcWtpk_KV-hh_NL9yHfFQz6ZcbUs,7909
16
16
  suite_py/commands/set_token.py,sha256=fehIqKjKhE-BJGFhgkPTo3Ntr0MvpgLd6EC5yjKuRs8,1508
17
17
  suite_py/commands/status.py,sha256=0JUK53_d1-U3WNS742JD2QTiGmCGZONo3jJx8WR7q70,1122
18
18
  suite_py/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  suite_py/lib/config.py,sha256=52YV0K0GK1WRbeM70IDXkpG8oZD1Zvxipn1b-XekwNA,3907
20
20
  suite_py/lib/handler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  suite_py/lib/handler/aws_handler.py,sha256=dRvRDicikfRbuFtCLPbevaX-yC-fO4LwXFdyqLPJ8OI,8815
22
- suite_py/lib/handler/captainhook_handler.py,sha256=sp1dxZXO1QJaq304JhtkTrQjn8Z4971e2uFTM7Dh5wg,2983
22
+ suite_py/lib/handler/captainhook_handler.py,sha256=R30_Vvh2ck7fM5fwbpm3UV_FtlQr2xnx6RJpkG1Gn14,2983
23
23
  suite_py/lib/handler/changelog_handler.py,sha256=-ppnRl3smBA_ys8tPqXmytS4eyntlwfawC2fiXFcwlw,4818
24
24
  suite_py/lib/handler/frequent_reviewers_handler.py,sha256=EIJX5FEMWzrxuXS9A17hu1vfxgJSOHSBX_ahCEZ2FVA,2185
25
- suite_py/lib/handler/git_handler.py,sha256=boxhl9lQz6fjEJ10ib1KrDW-geCVjhA_6nKwv2ll01g,11333
26
- suite_py/lib/handler/github_handler.py,sha256=AnFL54yOZ5GDIU91wQat4s-d1WTcmg_B_5M7-Rop3wA,2900
25
+ suite_py/lib/handler/git_handler.py,sha256=Y2hKR5sUKBhfRfQrMNjyCScJpdyPJ7XWK9f3ykx7Lf0,12710
26
+ suite_py/lib/handler/github_handler.py,sha256=xnBATLOTnOLpiYE29WwUrtDr7hxusfId9a1KbfK1OyA,2952
27
27
  suite_py/lib/handler/metrics_handler.py,sha256=-Tp62pFIiYsBkDga0nQG3lWU-gxH68wEjIIIJeU1jHk,3159
28
28
  suite_py/lib/handler/okta_handler.py,sha256=UiRcBDmFkMFi9H7Me1QaruC8yPI5fFbnLGzOf3kfxG0,2805
29
+ suite_py/lib/handler/pre_commit_handler.py,sha256=nwqVgC-KnUjn7NVIHZ0VmDdUfp6Q1ieFIth-1_U76Gw,3740
29
30
  suite_py/lib/handler/prompt_utils.py,sha256=vgk1O7h-iYEAZv1sXtMh8xIgH1djI398rzxRIgZWZcg,2474
30
31
  suite_py/lib/handler/version_handler.py,sha256=DXTx4yCAbFVC6CdMqPJ-LiN5YM-dT2zklG8POyKTP5A,6774
31
32
  suite_py/lib/handler/youtrack_handler.py,sha256=eTGBBXjlN_ay1cawtnZ2IG6l78dDyKdMN1x6PxcvtA0,7499
32
- suite_py/lib/logger.py,sha256=q_qRtpg0Eh7r5tB-Rwz87dnWBwP-a2dIvggkiQikH8M,782
33
- suite_py/lib/metrics.py,sha256=CqAIwPIRm0AJR4qmCTj1rw-NCwdSL4Huj2eKhLxgLgU,1629
33
+ suite_py/lib/logger.py,sha256=d162j5BWDDkss424-aTviJNR4uFulP8_P__wn42MvtU,1012
34
+ suite_py/lib/metrics.py,sha256=urTBVzIc1Ys6OHPOO32fLPPRcQU45tzM3XMJ7mUl5dc,1629
34
35
  suite_py/lib/oauth.py,sha256=DhXimUu7MDscQsMyt3L04_kw0ZmuqmhYG6KZTPlKqNQ,5083
35
36
  suite_py/lib/requests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
37
  suite_py/lib/requests/auth.py,sha256=wN_WtGFmDUWRFilWzOmUaRBvP2n3EPpPMqex9Zjddko,228
@@ -38,7 +39,7 @@ suite_py/lib/requests/session.py,sha256=P32H3cWnCWunu91WIj2iDM5U3HzaBglg60VN_C9J
38
39
  suite_py/lib/symbol.py,sha256=z3QYBuNIwD3qQ3zF-cLOomIr_-C3bO_u5UIDAHMiyTo,60
39
40
  suite_py/lib/tokens.py,sha256=4DbsHDFLIxs40t3mRw_ZyhmejZQ0Bht7iAL8dTCTQd4,5458
40
41
  suite_py/templates/login.html,sha256=fJLls2SB84oZTSrxTdA5q1PqfvIHcCD4fhVWfyco7Ig,861
41
- suite_py-1.42.2.dist-info/METADATA,sha256=2DH1B8DsS5SlkRpahd3ZQfGa6YLwBfP6U1PmnTxZih0,1533
42
- suite_py-1.42.2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
43
- suite_py-1.42.2.dist-info/entry_points.txt,sha256=dVKLC-9Infy-dHJT_MkK6LcDjOgBCJ8lfPkURJhBjxE,46
44
- suite_py-1.42.2.dist-info/RECORD,,
42
+ suite_py-1.43.1.dist-info/METADATA,sha256=L6N5i7z_6f7dxVpkcVmF9gHtpgoLQwooI2zMFRKHihY,1250
43
+ suite_py-1.43.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
44
+ suite_py-1.43.1.dist-info/entry_points.txt,sha256=dVKLC-9Infy-dHJT_MkK6LcDjOgBCJ8lfPkURJhBjxE,46
45
+ suite_py-1.43.1.dist-info/RECORD,,