suite-py 1.47.2__py3-none-any.whl → 1.49.0__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.47.2"
2
+ __version__ = "1.49.0"
suite_py/cli.py CHANGED
@@ -47,8 +47,10 @@ from suite_py.lib.config import Config
47
47
  from suite_py.lib.handler import git_handler as git
48
48
  from suite_py.lib.handler import prompt_utils
49
49
  from suite_py.lib.handler.captainhook_handler import CaptainHook
50
+ from suite_py.lib.handler.git_handler import GitHandler
50
51
  from suite_py.lib.handler.okta_handler import Okta
51
52
  from suite_py.lib.handler.pre_commit_handler import PreCommit
53
+ from suite_py.lib.handler.youtrack_handler import YoutrackHandler
52
54
  from suite_py.lib.tokens import Tokens
53
55
 
54
56
  # pylint: enable=wrong-import-position
@@ -176,17 +178,21 @@ def main(ctx, project, timeout, verbose):
176
178
  if timeout:
177
179
  config.user["captainhook_timeout"] = timeout
178
180
 
179
- project = os.path.basename(project)
180
181
  tokens = Tokens()
181
182
  okta = Okta(config, tokens)
182
183
  captainhook = CaptainHook(config, okta, tokens)
184
+ project = os.path.basename(project)
185
+ git_handler = GitHandler(project, config)
186
+ youtrack_handler = YoutrackHandler(config, tokens)
183
187
 
184
188
  ctx.obj = Context(
189
+ captainhook=captainhook,
190
+ config=config,
191
+ git_handler=git_handler,
192
+ okta=okta,
185
193
  project=project,
186
194
  tokens=tokens,
187
- okta=okta,
188
- config=config,
189
- captainhook=captainhook,
195
+ youtrack_handler=youtrack_handler,
190
196
  )
191
197
 
192
198
  ctx.obj.call(metrics.setup)
@@ -233,12 +239,27 @@ def bump(obj: Context, project: Optional[str] = None, version: Optional[str] = N
233
239
  is_flag=True,
234
240
  help="Stash uncommitted changes before creating the branch and reapply them afterward",
235
241
  )
242
+ @click.option(
243
+ "--parent-branch",
244
+ type=click.STRING,
245
+ help="Parent branch to create the new branch from",
246
+ )
247
+ @click.option(
248
+ "--branch-name",
249
+ type=click.STRING,
250
+ help="Branch name template. Supports {card_id}, {type}, {summary} placeholders (ex. '{card_id}/{type}/{summary}')",
251
+ )
236
252
  @click.pass_obj
237
253
  @catch_exceptions
238
- def cli_create_branch(obj, card, autostash):
254
+ def cli_create_branch(obj, card, autostash, parent_branch, branch_name):
239
255
  from suite_py.commands.create_branch import CreateBranch
240
256
 
241
- obj.call(CreateBranch, card=card, autostash=autostash).run()
257
+ obj.call(CreateBranch).run(
258
+ card_id=card,
259
+ autostash=autostash,
260
+ parent_branch=parent_branch,
261
+ branch_name=branch_name,
262
+ )
242
263
 
243
264
 
244
265
  @main.command("lock", help="Lock project on staging or prod")
@@ -326,12 +347,34 @@ def release():
326
347
  @release.command(
327
348
  "create", help="Create a github release (and deploy it if GHA are used)"
328
349
  )
350
+ @click.argument(
351
+ "commit",
352
+ required=False,
353
+ type=str,
354
+ metavar="[COMMIT]",
355
+ )
356
+ @click.option(
357
+ "--interactive",
358
+ "-i",
359
+ is_flag=True,
360
+ help="Interactively choose which unreleased commit to release (ignored if COMMIT is provided)",
361
+ )
329
362
  @click.pass_obj
330
363
  @catch_exceptions
331
- def cli_release_create(obj):
364
+ def cli_release_create(obj, commit, interactive): # type: ignore[override]
365
+ """Create a release.
366
+
367
+ Optionally pass a COMMIT (full or short SHA) to create the release at that
368
+ specific commit instead of the latest commit (HEAD). Example:
369
+
370
+ suite-py release create 644a699
371
+
372
+ If you don't provide a COMMIT you can pass --interactive / -i to select
373
+ one among the unreleased commits (those after the last tag).
374
+ """
332
375
  from suite_py.commands.release import Release
333
376
 
334
- obj.call(Release, action="create").run()
377
+ obj.call(Release, action="create", commit=commit, interactive=interactive).run()
335
378
 
336
379
 
337
380
  @main.command("status", help="Current status of a project")
@@ -3,17 +3,21 @@ from inspect import signature
3
3
 
4
4
  from suite_py.lib.config import Config
5
5
  from suite_py.lib.handler.captainhook_handler import CaptainHook
6
+ from suite_py.lib.handler.git_handler import GitHandler
6
7
  from suite_py.lib.handler.okta_handler import Okta
8
+ from suite_py.lib.handler.youtrack_handler import YoutrackHandler
7
9
  from suite_py.lib.tokens import Tokens
8
10
 
9
11
 
10
12
  @dataclasses.dataclass
11
13
  class Context:
12
- project: str
13
- config: Config
14
14
  captainhook: CaptainHook
15
- tokens: Tokens
15
+ config: Config
16
+ git_handler: GitHandler
16
17
  okta: Okta
18
+ project: str
19
+ tokens: Tokens
20
+ youtrack_handler: YoutrackHandler
17
21
 
18
22
  # Call the function to_call with kwargs, injecting fields from self as default arguments
19
23
  def call(self, to_call, **kwargs):
@@ -11,25 +11,37 @@ from suite_py.lib.handler.youtrack_handler import YoutrackHandler
11
11
 
12
12
 
13
13
  class CreateBranch:
14
- def __init__(self, project, card, config, tokens, autostash=False):
15
- self._project = project
16
- self._card = card
14
+ def __init__(
15
+ self,
16
+ config,
17
+ git_handler: GitHandler,
18
+ youtrack_handler: YoutrackHandler,
19
+ ):
17
20
  self._config = config
18
- self._youtrack = YoutrackHandler(config, tokens)
19
- self._git = GitHandler(project, config)
20
- self._autostash = autostash
21
+ self._git_handler = git_handler
22
+ self._youtrack = youtrack_handler
21
23
 
22
24
  @metrics.command("create-branch")
23
- def run(self):
24
- if not self._git.is_detached() and self._git.is_dirty() and not self._autostash:
25
+ def run(
26
+ self,
27
+ autostash=False,
28
+ branch_name=None,
29
+ card_id=None,
30
+ parent_branch=None,
31
+ ):
32
+ if (
33
+ not self._git_handler.is_detached()
34
+ and self._git_handler.is_dirty()
35
+ and not autostash
36
+ ):
25
37
  # Default behaviour is to pull when not detached.
26
38
  # Can't do that with uncommitted changes.
27
39
  logger.error("You have some uncommitted changes, I can't continue")
28
40
  sys.exit(-1)
29
41
 
30
42
  try:
31
- if self._card:
32
- issue = self._youtrack.get_issue(self._card)
43
+ if card_id:
44
+ issue = self._youtrack.get_issue(card_id)
33
45
  else:
34
46
  issue = self._youtrack.get_issue(self._ask_card())
35
47
  except Exception:
@@ -38,7 +50,7 @@ class CreateBranch:
38
50
  )
39
51
  sys.exit(-1)
40
52
 
41
- self._checkout_branch(issue, self._autostash)
53
+ self._checkout_branch(issue, autostash, parent_branch, branch_name)
42
54
 
43
55
  user = self._youtrack.get_current_user()
44
56
  self._youtrack.assign_to(issue["id"], user["login"])
@@ -95,33 +107,45 @@ class CreateBranch:
95
107
  )
96
108
  return []
97
109
 
98
- def _checkout_branch(self, issue, autostash=False):
110
+ def _checkout_branch(
111
+ self, issue, autostash=False, parent_branch=None, branch_name_template=None
112
+ ):
99
113
  default_parent_branch_name = self._config.user.get(
100
- "default_parent_branch", self._git.current_branch_name()
114
+ "default_parent_branch", self._git_handler.current_branch_name()
101
115
  )
102
116
 
103
- parent_branch_name = prompt_utils.ask_questions_input(
104
- "Insert initial branch: ", default_parent_branch_name
117
+ parent_branch_name = parent_branch or prompt_utils.ask_questions_input(
118
+ "Enter parent branch: ", default_parent_branch_name
105
119
  )
106
120
 
107
121
  full_branch_name = ""
108
122
  branch_name = _normalize_git_ref_segment(issue["summary"])
109
123
  branch_type = _normalize_git_ref_segment(issue["Type"])
110
124
 
111
- while True:
112
- branch_name = str(
113
- prompt_utils.ask_questions_input("Enter branch name: ", branch_name)
125
+ if branch_name_template:
126
+ # Use the template and replace placeholders
127
+ full_branch_name = branch_name_template.format(
128
+ card_id=issue["idReadable"], type=branch_type, summary=branch_name
114
129
  )
115
130
 
116
- full_branch_name = f"{issue['idReadable']}/{branch_type}/{branch_name}"
117
- if is_branch_name_valid(full_branch_name):
118
- break
131
+ if not is_branch_name_valid(full_branch_name):
132
+ logger.error(f"Invalid branch name from template: {full_branch_name}")
133
+ sys.exit(-1)
134
+ else:
135
+ while True:
136
+ branch_name = str(
137
+ prompt_utils.ask_questions_input("Enter branch name: ", branch_name)
138
+ )
119
139
 
120
- logger.error(f"Invalid branch name: {full_branch_name}. Try again?")
140
+ full_branch_name = f"{issue['idReadable']}/{branch_type}/{branch_name}"
141
+ if is_branch_name_valid(full_branch_name):
142
+ break
121
143
 
122
- self._git.checkout(parent_branch_name, autostash=autostash)
144
+ logger.error(f"Invalid branch name: {full_branch_name}. Try again?")
123
145
 
124
- self._git.checkout(full_branch_name, new=True, autostash=autostash)
146
+ self._git_handler.checkout(parent_branch_name, autostash=autostash)
147
+
148
+ self._git_handler.checkout(full_branch_name, new=True, autostash=autostash)
125
149
 
126
150
 
127
151
  # Normalize a string into a valid segment(ie. the part of the branch name between the /)
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import re
3
3
  import sys
4
+ from concurrent.futures import ThreadPoolExecutor, as_completed
4
5
 
5
6
  from suite_py.commands import common
6
7
  from suite_py.lib import logger, metrics
@@ -16,11 +17,24 @@ from suite_py.lib.handler.youtrack_handler import YoutrackHandler
16
17
  class Release:
17
18
  # pylint: disable=too-many-instance-attributes
18
19
  # pylint: disable=too-many-positional-arguments
19
- def __init__(self, action, project, captainhook, config, tokens):
20
+ def __init__(
21
+ self,
22
+ action,
23
+ project,
24
+ captainhook,
25
+ config,
26
+ tokens,
27
+ commit=None,
28
+ interactive=False,
29
+ ):
20
30
  self._action = action
21
31
  self._project = project
22
32
  self._config = config
23
33
  self._tokens = tokens
34
+ # Optional commit (sha or short sha) to release instead of HEAD
35
+ self._commit = commit
36
+ # Whether to prompt the user to choose commit among unreleased ones
37
+ self._interactive = interactive
24
38
  self._changelog_handler = ChangelogHandler()
25
39
  self._youtrack = YoutrackHandler(config, tokens)
26
40
  self._captainhook = captainhook
@@ -52,62 +66,249 @@ class Release:
52
66
 
53
67
  def _create(self):
54
68
  latest = self._version.get_latest_version()
69
+ commits, new_version, message = self._gather_commits_and_version(latest)
70
+ message = self._augment_message_with_changelog(new_version, message)
71
+ message = common.ask_for_release_description(message)
72
+ sha = self._resolve_target_sha(commits)
73
+ self._create_release(new_version, message, sha)
55
74
 
56
- commits = []
57
- if latest != "":
58
- logger.info(f"The current release is {latest}")
59
- commits = self._github.get_commits_since_release(self._repo, latest)
60
-
61
- _check_migrations_deploy(commits)
62
-
63
- message = "\n".join(
64
- [
65
- "* "
66
- + c.commit.message.splitlines()[0]
67
- + " by "
68
- + c.commit.author.name
69
- for c in commits
70
- ]
71
- )
75
+ def _gather_commits_and_version(self, latest):
76
+ """Return (commits, new_version, base_message)."""
77
+ if latest == "":
78
+ return self._first_release_flow()
72
79
 
73
- logger.info(f"\nCommits list:\n{message}\n")
80
+ logger.info(f"The current release is {latest}")
81
+ commits = self._github.get_commits_since_release(self._repo, latest)
82
+ if not commits:
83
+ logger.warning(
84
+ "No commits found after the latest release tag; nothing to release."
85
+ )
86
+ sys.exit(0)
87
+ # If user did not pass a target commit, ask interactively which commit to release.
88
+ # Only perform interactive selection if user explicitly requested it
89
+ if self._interactive:
90
+ self._select_commit_if_needed(commits)
91
+ commits = self._maybe_trim_commits(commits)
92
+ _check_migrations_deploy(commits)
93
+ message = self._build_commits_message(commits)
94
+ logger.info(f"\nCommits list:\n{message}\n")
95
+ if not prompt_utils.ask_confirm("Do you want to continue?"):
96
+ sys.exit()
97
+ new_version = self._version.select_new_version(latest, allow_prerelease=True)
98
+ return commits, new_version, message
74
99
 
75
- if not prompt_utils.ask_confirm("Do you want to continue?"):
76
- return
100
+ def _first_release_flow(self):
101
+ logger.warning(f"No tags found, I'm about to push the tag {DEFAULT_VERSION}")
102
+ if not prompt_utils.ask_confirm(
103
+ "Are you sure you want to continue?", default=False
104
+ ):
105
+ sys.exit()
106
+ return [], DEFAULT_VERSION, f"First release with tag {DEFAULT_VERSION}"
77
107
 
78
- new_version = self._version.select_new_version(
79
- latest, allow_prerelease=True
108
+ def _maybe_trim_commits(self, commits):
109
+ if not self._commit:
110
+ return commits
111
+ try:
112
+ resolved_commit = self._repo.get_commit(self._commit)
113
+ target_sha = resolved_commit.sha
114
+ except Exception:
115
+ logger.error(
116
+ f"The provided commit '{self._commit}' was not found in the repository."
80
117
  )
81
-
82
- else:
83
- # Se non viene trovata la release e non ci sono tag, viene saltato il check delle migrations e l'update delle card su youtrack
84
- logger.warning(
85
- f"No tags found, I'm about to push the tag {DEFAULT_VERSION}"
118
+ sys.exit(-1)
119
+ target_index = None
120
+ for idx, c in enumerate(commits):
121
+ if (
122
+ c.sha == target_sha
123
+ or getattr(c, "commit", None)
124
+ and c.commit.sha == target_sha
125
+ ):
126
+ target_index = idx
127
+ break
128
+ if target_index is None:
129
+ logger.error(
130
+ "The specified commit is not part of the unreleased commits (i.e., not after the latest release)."
86
131
  )
132
+ sys.exit(-1)
133
+ # We want ONLY the selected commit and the older commits (towards the previous release),
134
+ # excluding any *newer* commits that appear before it in the list.
135
+ return commits[target_index:]
136
+
137
+ def _select_commit_if_needed(self, commits):
138
+ """Interactively ask the user which commit to release if none was provided.
139
+
140
+ Presents the unreleased commits (newest first) allowing the user to pick a
141
+ target commit. The chosen commit's SHA (full) is stored in `self._commit` so
142
+ that downstream logic (_maybe_trim_commits / _resolve_target_sha) works
143
+ unchanged.
144
+ """
145
+ if self._commit or not commits:
146
+ return
147
+
148
+ choices = []
149
+
150
+ def build_choice(c, icon):
151
+ summary = c.commit.message.splitlines()[0]
152
+ return {
153
+ "name": f"{icon} {c.sha[:8]} | {summary}",
154
+ "value": c.sha,
155
+ }
156
+
157
+ with ThreadPoolExecutor(max_workers=min(8, len(commits))) as executor:
158
+ future_map = {
159
+ executor.submit(self._get_commit_status_icon, c.sha): c for c in commits
160
+ }
161
+
162
+ icon_results = {}
163
+ for future in as_completed(future_map):
164
+ c = future_map[future]
165
+ try:
166
+ icon_results[c.sha] = future.result()
167
+ except Exception:
168
+ icon_results[c.sha] = "?"
169
+ for c in commits:
170
+ choices.append(build_choice(c, icon_results.get(c.sha, "?")))
171
+
172
+ subtitle = "(only one available)" if len(choices) == 1 else "(newest first):"
173
+ selected = prompt_utils.ask_choices(
174
+ f"Select commit to release {subtitle}", choices
175
+ )
176
+ self._commit = selected
177
+
178
+ def _get_commit_status_icon(self, sha):
179
+ """Return an icon representing the simplified CI status for a commit.
180
+
181
+ Final exposed statuses (icons):
182
+ success (✅), failure (❌), in_progress (🏗️), cancelled (🚫), unknown (❓)
183
+
184
+ Everything else is folded into one of those buckets:
185
+ pending / queued / waiting -> in_progress
186
+ skipped / neutral -> unknown (unless at least one success present)
187
+ Preference order for determining state:
188
+ 1. Check runs API
189
+ 2. Combined status API
190
+ 3. Fallback unknown
191
+ """
192
+ check_runs = self._safe_get_check_runs(sha)
193
+ state = self._classify_from_check_runs(check_runs) if check_runs else None
194
+ if state is None:
195
+ state = self._classify_from_combined_status(sha)
196
+ return self._icon_for_state(state)
197
+
198
+ def _safe_get_check_runs(self, sha):
199
+ try:
200
+ commit = self._repo.get_commit(sha)
201
+ return list(commit.get_check_runs())
202
+ except Exception:
203
+ return []
204
+
205
+ def _classify_from_check_runs(self, check_runs):
206
+ statuses = {r.status for r in check_runs if getattr(r, "status", None)}
207
+ conclusions = {
208
+ r.conclusion for r in check_runs if getattr(r, "conclusion", None)
209
+ }
210
+
211
+ if self._any_in_progress(statuses):
212
+ return "in_progress"
213
+ if self._any_failure(conclusions):
214
+ return "failure"
215
+ if "cancelled" in conclusions:
216
+ return "cancelled"
217
+ if self._is_success(conclusions):
218
+ return "success"
219
+ return "unknown"
220
+
221
+ @staticmethod
222
+ def _any_in_progress(statuses):
223
+ return any(
224
+ s in {"in_progress", "queued", "waiting", "pending"} for s in statuses
225
+ )
226
+
227
+ @staticmethod
228
+ def _any_failure(conclusions):
229
+ return any(
230
+ c in {"failure", "timed_out", "action_required", "stale"}
231
+ for c in conclusions
232
+ )
233
+
234
+ @staticmethod
235
+ def _is_success(conclusions):
236
+ return bool(conclusions) and (
237
+ all(c == "success" for c in conclusions)
238
+ or ("success" in conclusions and not Release._any_failure(conclusions))
239
+ )
240
+
241
+ def _classify_from_combined_status(self, sha):
242
+ try:
243
+ status = self._github.get_build_status(self._project, sha)
244
+ combined = (status.state or "unknown").lower()
245
+ except Exception:
246
+ combined = "unknown"
247
+ if combined in {"pending", "queued", "waiting", "in_progress"}:
248
+ return "in_progress"
249
+ if combined == "success":
250
+ return "success"
251
+ if combined == "failure":
252
+ return "failure"
253
+ if combined == "cancelled":
254
+ return "cancelled"
255
+ return "unknown"
256
+
257
+ @staticmethod
258
+ def _icon_for_state(state):
259
+ icon_mapping = {
260
+ "success": "✅",
261
+ "failure": "❌",
262
+ "in_progress": "🏗️",
263
+ "cancelled": "🚫",
264
+ "unknown": "❓",
265
+ }
266
+ return icon_mapping.get(state, "❓")
267
+
268
+ @staticmethod
269
+ def _build_commits_message(commits):
270
+ return "\n".join(
271
+ [
272
+ "* " + c.commit.message.splitlines()[0] + " by " + c.commit.author.name
273
+ for c in commits
274
+ ]
275
+ )
276
+
277
+ def _augment_message_with_changelog(self, new_version, message):
278
+ if not self._changelog_handler.changelog_exists():
279
+ return message
280
+ latest_tag, latest_entry = self._changelog_handler.get_latest_entry_with_tag()
281
+ if latest_tag != new_version:
87
282
  if not prompt_utils.ask_confirm(
88
- "Are you sure you want to continue?", default=False
283
+ "You didn't update your changelog, are you sure you want to proceed?"
89
284
  ):
90
285
  sys.exit()
91
- new_version = DEFAULT_VERSION
92
- message = f"First release with tag {new_version}"
93
-
94
- if self._changelog_handler.changelog_exists():
95
- (
96
- latest_tag,
97
- latest_entry,
98
- ) = self._changelog_handler.get_latest_entry_with_tag()
99
-
100
- if latest_tag != new_version:
101
- if not prompt_utils.ask_confirm(
102
- "You didn't update your changelog, are you sure you want to proceed?"
103
- ):
104
- sys.exit()
105
- else:
106
- message = f"{latest_entry}\n\n# Commits\n\n{message}"
286
+ return message
287
+ return f"{latest_entry}\n\n# Commits\n\n{message}"
107
288
 
108
- message = common.ask_for_release_description(message)
109
- sha = commits[0].commit.sha if len(commits) > 0 else ""
110
- self._create_release(new_version, message, sha)
289
+ def _resolve_target_sha(self, commits):
290
+ if not self._commit:
291
+ return commits[0].commit.sha if commits else ""
292
+ try:
293
+ sha = self._repo.get_commit(self._commit).sha
294
+ except Exception:
295
+ logger.error(
296
+ f"Unable to resolve commit '{self._commit}' during release creation."
297
+ )
298
+ sys.exit(-1)
299
+ self._validate_target_commit_not_tagged(sha)
300
+ return sha
301
+
302
+ def _validate_target_commit_not_tagged(self, sha):
303
+ try:
304
+ for t in self._repo.get_tags():
305
+ if t.commit.sha == sha:
306
+ logger.error(
307
+ f"The commit {sha[:7]} is already tagged with {t.name}. Aborting."
308
+ )
309
+ sys.exit(-1)
310
+ except Exception:
311
+ logger.warning("Could not verify if commit already tagged; continuing.")
111
312
 
112
313
  def _create_release(self, new_version, message, commit):
113
314
  new_release = self._repo.create_git_release(
@@ -51,10 +51,17 @@ to disable it globally
51
51
  * is there a `.git/hooks/pre-commit` shell script that contains keyword "gitleaks"
52
52
  * is there a `.husky/pre-commit` shell script that contains keyword "security-hooks" (primait/security-hooks repo)
53
53
  """
54
- path = os.path.join(self._git.hooks_path(), "pre-commit")
55
- keywords = ["gitleaks", "security-hooks"]
54
+ checks = [
55
+ (os.path.join(self._git.hooks_path(), "pre-commit"), "gitleaks"),
56
+ (
57
+ os.path.join(self._git.get_path(), ".husky", "pre-commit"),
58
+ "security-hooks",
59
+ ),
60
+ ]
56
61
 
57
- return any(self._script_contains_keyword(path, keyword) for keyword in keywords)
62
+ return any(
63
+ self._script_contains_keyword(path, keyword) for path, keyword in checks
64
+ )
58
65
 
59
66
  def _is_pre_commit_py_hook_setup(self):
60
67
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: suite-py
3
- Version: 1.47.2
3
+ Version: 1.49.0
4
4
  Summary:
5
5
  Author: larrywax, EugenioLaghi, michelangelomo
6
6
  Author-email: devops@prima.it
@@ -28,7 +28,7 @@ Requires-Dist: pytest (>=7.0.0)
28
28
  Requires-Dist: python-dateutil (>=2.8.2)
29
29
  Requires-Dist: requests (>=2.26.0)
30
30
  Requires-Dist: requests-toolbelt (>=0.9.1)
31
- Requires-Dist: rich (==14.1.0)
31
+ Requires-Dist: rich (==14.2.0)
32
32
  Requires-Dist: semver (>=3.0.4,<4.0.0)
33
33
  Requires-Dist: termcolor (>=1.1.0)
34
34
  Requires-Dist: truststore (>=0.7,<0.11) ; python_version >= "3.10"
@@ -1,19 +1,19 @@
1
1
  suite_py/__init__.py,sha256=REmi3D0X2G1ZWnYpKs8Ffm3NIj-Hw6dMuvz2b9NW344,142
2
- suite_py/__version__.py,sha256=kvsaCaiN9_S0SML3PK6KymI83Qay7-_HSBXG0J1EN00,49
3
- suite_py/cli.py,sha256=hBbNXb9vVk8lSJXRrb1GctTp3EUjMYFWS9mn_HaNG9I,11155
2
+ suite_py/__version__.py,sha256=Eb0XNt9hhrNhC3w5nV7sDvRlGwgZ9ZlajjY8sZ0n9XQ,49
3
+ suite_py/cli.py,sha256=IV73nNqBwLKLAfucLxyIne_V7uBQT6OBY3Z2VryQt2M,12582
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
7
7
  suite_py/commands/check.py,sha256=jCX59g6DgTA55yD_75mgcqJ5zCjRwl_eIRGDeUFjUWY,3316
8
8
  suite_py/commands/common.py,sha256=aWCEvO3hqdheuMUmZcHuc9EGZPQTk7VkzkHJk283MxQ,566
9
- suite_py/commands/context.py,sha256=6dssSqoABPb9gLmL3Y7W2iAGxrY_oma665X8zrSkNNQ,1079
10
- suite_py/commands/create_branch.py,sha256=6sveTtOg_0lQl-MYizcnnyYyAunA_dXunQAxv7szllE,4577
9
+ suite_py/commands/context.py,sha256=NAMZjJtTIi8xKIhdWEYNKwz0f4wUubW2eAj08gxRRbw,1267
10
+ suite_py/commands/create_branch.py,sha256=eilFIsCkMjU51maeJlMMMMBpnM6rlUauXIFA-92j7no,5277
11
11
  suite_py/commands/estimate_cone.py,sha256=_RekBWzPlzInlZRpSIeKUVkx-A8Phx0IEYVouTbN7z4,3411
12
12
  suite_py/commands/login.py,sha256=A59e1HsbN7Ocv2L_2H0Eb7MZK7AzLkLb72QxBthnIqU,258
13
13
  suite_py/commands/merge_pr.py,sha256=fXIE8mT9MjvvpqE-uVdXGBVFGhn0eQzcBxNr-N8SyAY,5171
14
14
  suite_py/commands/open_pr.py,sha256=djaF2OsYbQo0YLTEbNRTYYFzsNT0sl7VRxqtxSX1PKc,7150
15
15
  suite_py/commands/project_lock.py,sha256=b7OkGysue_Sl13VIT7B5CTBppCvrB_Q6iC0IJRBSHp8,1909
16
- suite_py/commands/release.py,sha256=u6pdT1Vk9qBLnfgxcWtpk_KV-hh_NL9yHfFQz6ZcbUs,7909
16
+ suite_py/commands/release.py,sha256=e0I6xVDVoa3nb1gMfKrm6NmFhMWADfCMAUi0hrcUw50,15545
17
17
  suite_py/commands/set_token.py,sha256=fehIqKjKhE-BJGFhgkPTo3Ntr0MvpgLd6EC5yjKuRs8,1508
18
18
  suite_py/commands/status.py,sha256=0JUK53_d1-U3WNS742JD2QTiGmCGZONo3jJx8WR7q70,1122
19
19
  suite_py/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -27,7 +27,7 @@ suite_py/lib/handler/git_handler.py,sha256=8UVttqo65CTg5LCvOBTDXb_sNxLKUrpevCGio
27
27
  suite_py/lib/handler/github_handler.py,sha256=xnBATLOTnOLpiYE29WwUrtDr7hxusfId9a1KbfK1OyA,2952
28
28
  suite_py/lib/handler/metrics_handler.py,sha256=-Tp62pFIiYsBkDga0nQG3lWU-gxH68wEjIIIJeU1jHk,3159
29
29
  suite_py/lib/handler/okta_handler.py,sha256=UiRcBDmFkMFi9H7Me1QaruC8yPI5fFbnLGzOf3kfxG0,2805
30
- suite_py/lib/handler/pre_commit_handler.py,sha256=NLqwIkggh0FRoiKFVCMdRIkaafKrfnNXgaDHGlfpQMA,4144
30
+ suite_py/lib/handler/pre_commit_handler.py,sha256=D2C3b07naOf7wq-OOrrlwWsk5Rk5Zu49-_sNTevDG8I,4300
31
31
  suite_py/lib/handler/prompt_utils.py,sha256=vgk1O7h-iYEAZv1sXtMh8xIgH1djI398rzxRIgZWZcg,2474
32
32
  suite_py/lib/handler/version_handler.py,sha256=DXTx4yCAbFVC6CdMqPJ-LiN5YM-dT2zklG8POyKTP5A,6774
33
33
  suite_py/lib/handler/youtrack_handler.py,sha256=_CwzeFhj7kMYb61CN8IYF0IE55Zrj6vZT38SUZLA5B8,10192
@@ -40,7 +40,7 @@ suite_py/lib/requests/session.py,sha256=P32H3cWnCWunu91WIj2iDM5U3HzaBglg60VN_C9J
40
40
  suite_py/lib/symbol.py,sha256=z3QYBuNIwD3qQ3zF-cLOomIr_-C3bO_u5UIDAHMiyTo,60
41
41
  suite_py/lib/tokens.py,sha256=4DbsHDFLIxs40t3mRw_ZyhmejZQ0Bht7iAL8dTCTQd4,5458
42
42
  suite_py/templates/login.html,sha256=fJLls2SB84oZTSrxTdA5q1PqfvIHcCD4fhVWfyco7Ig,861
43
- suite_py-1.47.2.dist-info/METADATA,sha256=Im2wlMuvnyUlrS78imSKRRMLFhWQsk7XoH2Ij8thbCI,1250
44
- suite_py-1.47.2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
45
- suite_py-1.47.2.dist-info/entry_points.txt,sha256=dVKLC-9Infy-dHJT_MkK6LcDjOgBCJ8lfPkURJhBjxE,46
46
- suite_py-1.47.2.dist-info/RECORD,,
43
+ suite_py-1.49.0.dist-info/METADATA,sha256=a6KzrK5fxwkUyjiSRjHxKbBLf1hZvQbw5qDiDMejPxY,1250
44
+ suite_py-1.49.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
45
+ suite_py-1.49.0.dist-info/entry_points.txt,sha256=dVKLC-9Infy-dHJT_MkK6LcDjOgBCJ8lfPkURJhBjxE,46
46
+ suite_py-1.49.0.dist-info/RECORD,,