backlogops-cli 0.4__tar.gz → 0.5__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.
Files changed (43) hide show
  1. {backlogops_cli-0.4 → backlogops_cli-0.5}/PKG-INFO +7 -10
  2. {backlogops_cli-0.4 → backlogops_cli-0.5}/README_pypi.md +5 -8
  3. {backlogops_cli-0.4 → backlogops_cli-0.5}/backlogops_cli.egg-info/PKG-INFO +7 -10
  4. {backlogops_cli-0.4 → backlogops_cli-0.5}/backlogops_cli.egg-info/requires.txt +1 -1
  5. {backlogops_cli-0.4 → backlogops_cli-0.5}/setup.py +2 -2
  6. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/_command_io.py +96 -1
  7. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/add_releases_to_jira.py +23 -54
  8. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/add_to_jira.py +21 -54
  9. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/encrypt_token_file.py +1 -0
  10. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/jira_fields.py +4 -12
  11. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/order_by_deps.py +2 -3
  12. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/order_by_keys.py +2 -3
  13. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/order_by_release.py +2 -3
  14. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/order_releases.py +2 -3
  15. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/order_releases_in_jira.py +6 -16
  16. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/rank_in_jira.py +6 -16
  17. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/read_jira.py +2 -8
  18. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/rename_releases_in_jira.py +6 -16
  19. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/update_backlog_in_jira.py +6 -18
  20. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/update_releases_in_jira.py +6 -17
  21. {backlogops_cli-0.4 → backlogops_cli-0.5}/LICENSE.txt +0 -0
  22. {backlogops_cli-0.4 → backlogops_cli-0.5}/backlogops_cli.egg-info/SOURCES.txt +0 -0
  23. {backlogops_cli-0.4 → backlogops_cli-0.5}/backlogops_cli.egg-info/dependency_links.txt +0 -0
  24. {backlogops_cli-0.4 → backlogops_cli-0.5}/backlogops_cli.egg-info/top_level.txt +0 -0
  25. {backlogops_cli-0.4 → backlogops_cli-0.5}/pyproject.toml +0 -0
  26. {backlogops_cli-0.4 → backlogops_cli-0.5}/setup.cfg +0 -0
  27. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/__init__.py +0 -0
  28. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/__main__.py +0 -0
  29. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/_migrate_warn.py +0 -0
  30. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/_wizard_io.py +0 -0
  31. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/adjust_release_content.py +0 -0
  32. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/bloc_version_reporter.py +0 -0
  33. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/config_wizard.py +0 -0
  34. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/convert.py +0 -0
  35. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/demo_backlog.py +0 -0
  36. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/estimate_ready_date.py +0 -0
  37. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/extract_keys.py +0 -0
  38. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/list.py +0 -0
  39. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/migrate_cfg.py +0 -0
  40. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/plan_release_dates.py +0 -0
  41. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/preset_wizard.py +0 -0
  42. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/py.typed +0 -0
  43. {backlogops_cli-0.4 → backlogops_cli-0.5}/src/backlogops_cli/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backlogops-cli
3
- Version: 0.4
3
+ Version: 0.5
4
4
  Summary: Command line interface for backlog operations.
5
5
  Author: Tom Björkholm
6
6
  Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
@@ -25,7 +25,7 @@ Requires-Python: >=3.12
25
25
  Description-Content-Type: text/markdown
26
26
  License-File: LICENSE.txt
27
27
  Requires-Dist: argcomplete>=3.7.0
28
- Requires-Dist: backlogops>=0.4
28
+ Requires-Dist: backlogops>=0.5
29
29
  Requires-Dist: versionreporter>=0.4
30
30
  Dynamic: author
31
31
  Dynamic: license-file
@@ -50,15 +50,11 @@ There are 3 related packages for backlog operations:
50
50
 
51
51
  ## Project status
52
52
 
53
- backlogops is in beta. The algorithms intended for its initial scope are in
54
- place, so the main work from here is on the user experience rather than on new
55
- core functionality. The three packages share a version number and are released
56
- together.
53
+ The three packages share a version number and are released together.
57
54
 
58
55
  The library is not meant to ever be "finished": using it for real planning keeps
59
56
  surfacing new ideas. Some of those will be added to backlogops itself, and
60
- others will fit better in separate packages built on top of backlogops. Early
61
- adopters are very welcome, and their feedback shapes what comes next.
57
+ others will fit better in separate packages built on top of backlogops.
62
58
 
63
59
  ## Available functionality
64
60
 
@@ -322,9 +318,10 @@ backlogops_cli serves 2 purposes:
322
318
 
323
319
  ## Test summary
324
320
 
325
- - Test result: 2638 passed, 7 deselected in 51s
321
+ - Test result: 2450 passed, 9 deselected in 41s
326
322
  - No flake8 warnings.
327
323
  - No mypy errors found.
324
+ - No pylint warnings.
328
325
  - No python layout warnings.
329
- - Built version(s): 0.4
326
+ - Built version(s): 0.5
330
327
  - Build and test using Python 3.14.6
@@ -16,15 +16,11 @@ There are 3 related packages for backlog operations:
16
16
 
17
17
  ## Project status
18
18
 
19
- backlogops is in beta. The algorithms intended for its initial scope are in
20
- place, so the main work from here is on the user experience rather than on new
21
- core functionality. The three packages share a version number and are released
22
- together.
19
+ The three packages share a version number and are released together.
23
20
 
24
21
  The library is not meant to ever be "finished": using it for real planning keeps
25
22
  surfacing new ideas. Some of those will be added to backlogops itself, and
26
- others will fit better in separate packages built on top of backlogops. Early
27
- adopters are very welcome, and their feedback shapes what comes next.
23
+ others will fit better in separate packages built on top of backlogops.
28
24
 
29
25
  ## Available functionality
30
26
 
@@ -288,9 +284,10 @@ backlogops_cli serves 2 purposes:
288
284
 
289
285
  ## Test summary
290
286
 
291
- - Test result: 2638 passed, 7 deselected in 51s
287
+ - Test result: 2450 passed, 9 deselected in 41s
292
288
  - No flake8 warnings.
293
289
  - No mypy errors found.
290
+ - No pylint warnings.
294
291
  - No python layout warnings.
295
- - Built version(s): 0.4
292
+ - Built version(s): 0.5
296
293
  - Build and test using Python 3.14.6
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backlogops-cli
3
- Version: 0.4
3
+ Version: 0.5
4
4
  Summary: Command line interface for backlog operations.
5
5
  Author: Tom Björkholm
6
6
  Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
@@ -25,7 +25,7 @@ Requires-Python: >=3.12
25
25
  Description-Content-Type: text/markdown
26
26
  License-File: LICENSE.txt
27
27
  Requires-Dist: argcomplete>=3.7.0
28
- Requires-Dist: backlogops>=0.4
28
+ Requires-Dist: backlogops>=0.5
29
29
  Requires-Dist: versionreporter>=0.4
30
30
  Dynamic: author
31
31
  Dynamic: license-file
@@ -50,15 +50,11 @@ There are 3 related packages for backlog operations:
50
50
 
51
51
  ## Project status
52
52
 
53
- backlogops is in beta. The algorithms intended for its initial scope are in
54
- place, so the main work from here is on the user experience rather than on new
55
- core functionality. The three packages share a version number and are released
56
- together.
53
+ The three packages share a version number and are released together.
57
54
 
58
55
  The library is not meant to ever be "finished": using it for real planning keeps
59
56
  surfacing new ideas. Some of those will be added to backlogops itself, and
60
- others will fit better in separate packages built on top of backlogops. Early
61
- adopters are very welcome, and their feedback shapes what comes next.
57
+ others will fit better in separate packages built on top of backlogops.
62
58
 
63
59
  ## Available functionality
64
60
 
@@ -322,9 +318,10 @@ backlogops_cli serves 2 purposes:
322
318
 
323
319
  ## Test summary
324
320
 
325
- - Test result: 2638 passed, 7 deselected in 51s
321
+ - Test result: 2450 passed, 9 deselected in 41s
326
322
  - No flake8 warnings.
327
323
  - No mypy errors found.
324
+ - No pylint warnings.
328
325
  - No python layout warnings.
329
- - Built version(s): 0.4
326
+ - Built version(s): 0.5
330
327
  - Build and test using Python 3.14.6
@@ -1,3 +1,3 @@
1
1
  argcomplete>=3.7.0
2
- backlogops>=0.4
2
+ backlogops>=0.5
3
3
  versionreporter>=0.4
@@ -5,7 +5,7 @@ from setuptools import setup
5
5
 
6
6
  setup(
7
7
  name='backlogops-cli',
8
- version='0.4',
8
+ version='0.5',
9
9
  description='Command line interface for backlog operations.',
10
10
  author='Tom Björkholm',
11
11
  author_email='klausuler_linnet0q@icloud.com',
@@ -15,7 +15,7 @@ setup(
15
15
  package_data={'backlogops_cli': ['src/py.typed']},
16
16
  install_requires=[
17
17
  'argcomplete >= 3.7.0',
18
- 'backlogops >= 0.4',
18
+ 'backlogops >= 0.5',
19
19
  'versionreporter >= 0.4'
20
20
  ]
21
21
  )
@@ -12,9 +12,10 @@ underscore in the module name keeps it out of the command listing.
12
12
 
13
13
  import argparse
14
14
  import sys
15
+ from getpass import getpass
15
16
  from pathlib import Path
16
17
  from collections.abc import Callable
17
- from typing import Optional, TextIO
18
+ from typing import Optional, TextIO, TypeVar
18
19
  import argcomplete
19
20
  from backlogops_cli._migrate_warn import (
20
21
  CliMigrateWarnHook, CliPresetMigrateWarnHook)
@@ -27,6 +28,9 @@ from backlogops import (
27
28
  resolve_output_config, write_backlog_releases, write_content_changes,
28
29
  write_date_changes)
29
30
 
31
+ ResultT = TypeVar('ResultT')
32
+ """Result type of a Jira write, carried from the operation to the report."""
33
+
30
34
  RANK_ANCHOR_CHOICES = {
31
35
  'backlog-top': JiraRankAnchor.BACKLOG_TOP,
32
36
  'backlog-bottom': JiraRankAnchor.BACKLOG_BOTTOM,
@@ -97,6 +101,11 @@ def parsed_args(parser: argparse.ArgumentParser,
97
101
  return parser.parse_args(args)
98
102
 
99
103
 
104
+ def jira_passphrase() -> str:
105
+ """Ask for the Jira API token pass phrase on the terminal."""
106
+ return getpass('Jira API token pass phrase: ')
107
+
108
+
100
109
  def add_input_args(parser: argparse.ArgumentParser) -> None:
101
110
  """Add the input-file and input-config arguments."""
102
111
  parser.add_argument('-i', '--input', dest='input', required=True,
@@ -120,6 +129,18 @@ def add_config_arg(parser: argparse.ArgumentParser) -> None:
120
129
  '$HOME/.backlogops.cfg.')
121
130
 
122
131
 
132
+ def add_preset_arg(parser: argparse.ArgumentParser) -> None:
133
+ """Add the required ``-p``/``--preset`` Jira preset argument."""
134
+ parser.add_argument('-p', '--preset', dest='preset', required=True,
135
+ help='Name of the Jira preset in the configuration.')
136
+
137
+
138
+ def add_quiet_arg(parser: argparse.ArgumentParser) -> None:
139
+ """Add the ``-q``/``--quiet`` flag suppressing the result lists."""
140
+ parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
141
+ help='Do not print the result lists to stdout.')
142
+
143
+
123
144
  def _resolve_config(parsed: argparse.Namespace) -> BacklogOpsConfig:
124
145
  """Return the backlog-ops configuration from ``-c`` or by discovery.
125
146
 
@@ -265,6 +286,23 @@ def build_io_parser(description: str, *, with_input: bool = True,
265
286
  return parser
266
287
 
267
288
 
289
+ def build_jira_parser(description: str, *,
290
+ with_input: bool = True) -> argparse.ArgumentParser:
291
+ """Create a parser with the config, preset and optional input options.
292
+
293
+ The Jira commands all take a backlog-ops configuration and a required
294
+ ``-p``/``--preset``. Most also read an input file, so ``with_input``
295
+ adds the input-file and input-config options by default; a command
296
+ that does not read a backlog file passes ``with_input=False``.
297
+ """
298
+ parser = argparse.ArgumentParser(description=description)
299
+ if with_input:
300
+ add_input_args(parser)
301
+ add_config_arg(parser)
302
+ add_preset_arg(parser)
303
+ return parser
304
+
305
+
268
306
  def _write_output(parsed: argparse.Namespace,
269
307
  config: Optional[BacklogOpsConfig],
270
308
  data: BacklogReleases) -> None:
@@ -317,6 +355,63 @@ def run_write(parsed: argparse.Namespace,
317
355
  return 0
318
356
 
319
357
 
358
+ def run_write_command(
359
+ build_parser: Callable[[], argparse.ArgumentParser],
360
+ produce: Callable[[argparse.Namespace, Optional[BacklogOpsConfig]],
361
+ BacklogReleases],
362
+ args: Optional[list[str]]) -> int:
363
+ """Parse the command line and write the produced backlog and releases.
364
+
365
+ Folds argument parsing and :func:`run_write` so an ordering command's
366
+ ``main`` stays a single delegating call. ``produce`` receives the
367
+ parsed args and the resolved config (or None) and returns the data.
368
+ """
369
+ parsed = parsed_args(build_parser(), args)
370
+ return run_write(parsed, lambda config: produce(parsed, config))
371
+
372
+
373
+ def write_result_file(config: BacklogOpsConfig, path: str,
374
+ data: BacklogReleases, force: bool) -> None:
375
+ """Write one backlog-and-releases result file, honoring ``--force``."""
376
+ out_config = resolve_output_config(None, data_file=path,
377
+ presets=config.output_configs,
378
+ auto_ch_hook=CliPresetMigrateWarnHook())
379
+ write_backlog_releases(data, path, out_config, FormatRules(),
380
+ levels=config.get_levels(),
381
+ file_exists_callback=overwrite_callback(force))
382
+ print(f'Wrote {path}')
383
+
384
+
385
+ def run_added_to_jira(
386
+ parsed: argparse.Namespace,
387
+ add: Callable[
388
+ [argparse.Namespace, BacklogOpsConfig, BacklogReleases], ResultT],
389
+ report: Callable[[ResultT], str], exists_error: type[Exception],
390
+ fail_note: str) -> int:
391
+ """Read the input, add it to Jira, and print the added lists.
392
+
393
+ ``add`` receives the parsed args, the config and the input data, adds
394
+ to Jira, writes any ``--added``/``--existing`` files, and returns the
395
+ result. ``report`` formats that result for stdout, printed unless
396
+ ``-q``/``--quiet`` was given. ``exists_error`` is the key-exists error
397
+ meaning nothing was added; other value or OS errors are reported with
398
+ ``fail_note``.
399
+ """
400
+ try:
401
+ config = required_config(parsed)
402
+ data = read_input(parsed, config)
403
+ result = add(parsed, config, data)
404
+ except exists_error:
405
+ print('Nothing added to Jira.', file=sys.stderr)
406
+ return 1
407
+ except (ValueError, TypeError, KeyError, OSError) as error:
408
+ print(f'{fail_note}: {error}', file=sys.stderr)
409
+ return 1
410
+ if not parsed.quiet:
411
+ print(report(result))
412
+ return 0
413
+
414
+
320
415
  DEFAULT_BUFFER_DAYS = 5
321
416
  """Default slack in calendar days added when fitting dates."""
322
417
 
@@ -20,27 +20,21 @@ asked on the terminal only when it is needed.
20
20
 
21
21
  import argparse
22
22
  import sys
23
- from getpass import getpass
24
23
  from typing import Optional
25
24
  from backlogops import (
26
- AddedReleasesToJira, BacklogOpsConfig, BacklogReleases, FormatRules,
27
- JiraConnections, OnExistingKey, ReleaseExistsError, add_releases_to_jira,
28
- format_release_result, resolve_output_config, write_backlog_releases)
25
+ AddedReleasesToJira, BacklogOpsConfig, BacklogReleases, JiraConnections,
26
+ OnExistingKey, ReleaseExistsError, add_releases_to_jira,
27
+ format_release_result)
29
28
  from backlogops_cli._command_io import (
30
- add_config_arg, add_input_args, overwrite_callback, parsed_args,
31
- read_input, required_config)
32
- from backlogops_cli._migrate_warn import CliPresetMigrateWarnHook
29
+ add_force_arg, add_quiet_arg, build_jira_parser, jira_passphrase,
30
+ parsed_args, run_added_to_jira, write_result_file)
33
31
 
34
32
  DESCRIPTION = 'Add releases to Jira, creating a new version per release'
35
33
 
36
34
 
37
35
  def build_parser() -> argparse.ArgumentParser:
38
36
  """Build the command line parser for the add-releases command."""
39
- parser = argparse.ArgumentParser(description=DESCRIPTION)
40
- add_input_args(parser)
41
- add_config_arg(parser)
42
- parser.add_argument('-p', '--preset', dest='preset', required=True,
43
- help='Name of the Jira preset in the configuration.')
37
+ parser = build_jira_parser(DESCRIPTION)
44
38
  parser.add_argument('--skip-existing', dest='skip_existing',
45
39
  action='store_true',
46
40
  help='Skip releases whose name already exists in Jira '
@@ -52,26 +46,17 @@ def build_parser() -> argparse.ArgumentParser:
52
46
  metavar='FILE',
53
47
  help='Write the releases already in Jira, with the '
54
48
  'input backlog, to this file. Omit to not write it.')
55
- parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
56
- help='Do not print the added and already-present '
57
- 'lists to stdout.')
58
- parser.add_argument('-f', '--force', dest='force', action='store_true',
59
- help='Overwrite existing output files without '
60
- 'asking.')
49
+ add_quiet_arg(parser)
50
+ add_force_arg(parser)
61
51
  return parser
62
52
 
63
53
 
64
- def _passphrase() -> str:
65
- """Ask for the Jira token pass phrase on the terminal."""
66
- return getpass('Jira API token pass phrase: ')
67
-
68
-
69
54
  def _add(parsed: argparse.Namespace, config: BacklogOpsConfig,
70
55
  data: BacklogReleases) -> AddedReleasesToJira:
71
56
  """Add the input releases to Jira using the named preset."""
72
57
  print(f"Adding releases to Jira using preset '{parsed.preset}'...",
73
58
  file=sys.stderr)
74
- connections = JiraConnections(config.get_jira_config(), _passphrase)
59
+ connections = JiraConnections(config.get_jira_config(), jira_passphrase)
75
60
  mode = (OnExistingKey.SKIP if parsed.skip_existing
76
61
  else OnExistingKey.RAISE)
77
62
  result = add_releases_to_jira(connections, parsed.preset, data.releases,
@@ -82,18 +67,6 @@ def _add(parsed: argparse.Namespace, config: BacklogOpsConfig,
82
67
  return result
83
68
 
84
69
 
85
- def _write_file(config: BacklogOpsConfig, path: str, data: BacklogReleases,
86
- force: bool) -> None:
87
- """Write one backlog-and-releases result to a file."""
88
- out_config = resolve_output_config(None, data_file=path,
89
- presets=config.output_configs,
90
- auto_ch_hook=CliPresetMigrateWarnHook())
91
- write_backlog_releases(data, path, out_config, FormatRules(),
92
- levels=config.get_levels(),
93
- file_exists_callback=overwrite_callback(force))
94
- print(f'Wrote {path}')
95
-
96
-
97
70
  def _write_result_files(parsed: argparse.Namespace, config: BacklogOpsConfig,
98
71
  data: BacklogReleases,
99
72
  result: AddedReleasesToJira) -> None:
@@ -101,29 +74,26 @@ def _write_result_files(parsed: argparse.Namespace, config: BacklogOpsConfig,
101
74
  backlog = list(data.backlog)
102
75
  if parsed.added_file is not None:
103
76
  added = BacklogReleases(backlog=backlog, releases=result.stored)
104
- _write_file(config, parsed.added_file, added, parsed.force)
77
+ write_result_file(config, parsed.added_file, added, parsed.force)
105
78
  if parsed.existing_file is not None:
106
79
  present = BacklogReleases(backlog=backlog,
107
80
  releases=result.already_present)
108
- _write_file(config, parsed.existing_file, present, parsed.force)
81
+ write_result_file(config, parsed.existing_file, present, parsed.force)
82
+
83
+
84
+ def _add_and_write(parsed: argparse.Namespace, config: BacklogOpsConfig,
85
+ data: BacklogReleases) -> AddedReleasesToJira:
86
+ """Add the releases to Jira and write any requested result files."""
87
+ result = _add(parsed, config, data)
88
+ _write_result_files(parsed, config, data, result)
89
+ return result
109
90
 
110
91
 
111
92
  def _run(parsed: argparse.Namespace) -> int:
112
93
  """Read the input, add the releases, write files and print the lists."""
113
- try:
114
- config = required_config(parsed)
115
- data = read_input(parsed, config)
116
- result = _add(parsed, config, data)
117
- _write_result_files(parsed, config, data, result)
118
- except ReleaseExistsError:
119
- print('Nothing added to Jira.', file=sys.stderr)
120
- return 1
121
- except (ValueError, TypeError, KeyError, OSError) as error:
122
- print(f'Could not add releases to Jira: {error}', file=sys.stderr)
123
- return 1
124
- if not parsed.quiet:
125
- print(format_release_result(result))
126
- return 0
94
+ return run_added_to_jira(parsed, _add_and_write, format_release_result,
95
+ ReleaseExistsError,
96
+ 'Could not add releases to Jira')
127
97
 
128
98
 
129
99
  def main(args: Optional[list[str]] = None) -> int:
@@ -136,8 +106,7 @@ def main(args: Optional[list[str]] = None) -> int:
136
106
  ``0`` on success, ``1`` when the releases cannot be added or a name
137
107
  already exists in Jira without ``--skip-existing``.
138
108
  """
139
- parsed = parsed_args(build_parser(), args)
140
- return _run(parsed)
109
+ return _run(parsed_args(build_parser(), args))
141
110
 
142
111
 
143
112
  if __name__ == '__main__': # pragma: no cover
@@ -22,27 +22,21 @@ only when it is needed.
22
22
 
23
23
  import argparse
24
24
  import sys
25
- from getpass import getpass
26
25
  from typing import Optional
27
26
  from backlogops import (
28
27
  AddedToJira, BacklogOpsConfig, BacklogReleases, ExistsInJiraError,
29
- FormatRules, JiraConnections, OnExistingKey, add_backlog_to_jira,
30
- format_add_result, resolve_output_config, write_backlog_releases)
28
+ JiraConnections, OnExistingKey, add_backlog_to_jira, format_add_result)
31
29
  from backlogops_cli._command_io import (
32
- add_config_arg, add_input_args, add_rank_arg, overwrite_callback,
33
- parsed_args, rank_anchor, read_input, required_config)
34
- from backlogops_cli._migrate_warn import CliPresetMigrateWarnHook
30
+ add_force_arg, add_quiet_arg, add_rank_arg, build_jira_parser,
31
+ jira_passphrase, parsed_args, rank_anchor, run_added_to_jira,
32
+ write_result_file)
35
33
 
36
34
  DESCRIPTION = 'Add a backlog to Jira, creating a new issue per item'
37
35
 
38
36
 
39
37
  def build_parser() -> argparse.ArgumentParser:
40
38
  """Build the command line parser for the add-to-Jira command."""
41
- parser = argparse.ArgumentParser(description=DESCRIPTION)
42
- add_input_args(parser)
43
- add_config_arg(parser)
44
- parser.add_argument('-p', '--preset', dest='preset', required=True,
45
- help='Name of the Jira preset in the configuration.')
39
+ parser = build_jira_parser(DESCRIPTION)
46
40
  parser.add_argument('--skip-existing', dest='skip_existing',
47
41
  action='store_true',
48
42
  help='Skip items whose key already exists in Jira '
@@ -55,26 +49,17 @@ def build_parser() -> argparse.ArgumentParser:
55
49
  metavar='FILE',
56
50
  help='Write the items already in Jira to this file. '
57
51
  'Omit to not write it.')
58
- parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
59
- help='Do not print the added and already-present '
60
- 'lists to stdout.')
61
- parser.add_argument('-f', '--force', dest='force', action='store_true',
62
- help='Overwrite existing output files without '
63
- 'asking.')
52
+ add_quiet_arg(parser)
53
+ add_force_arg(parser)
64
54
  return parser
65
55
 
66
56
 
67
- def _passphrase() -> str:
68
- """Ask for the Jira token pass phrase on the terminal."""
69
- return getpass('Jira API token pass phrase: ')
70
-
71
-
72
57
  def _add(parsed: argparse.Namespace, config: BacklogOpsConfig,
73
58
  data: BacklogReleases) -> AddedToJira:
74
59
  """Add the input backlog to Jira using the named write preset."""
75
60
  print(f"Adding backlog to Jira using preset '{parsed.preset}'...",
76
61
  file=sys.stderr)
77
- connections = JiraConnections(config.get_jira_config(), _passphrase)
62
+ connections = JiraConnections(config.get_jira_config(), jira_passphrase)
78
63
  mode = (OnExistingKey.SKIP if parsed.skip_existing
79
64
  else OnExistingKey.RAISE)
80
65
  result = add_backlog_to_jira(connections, parsed.preset, data.backlog,
@@ -89,48 +74,31 @@ def _add(parsed: argparse.Namespace, config: BacklogOpsConfig,
89
74
  return result
90
75
 
91
76
 
92
- def _write_backlog_file(config: BacklogOpsConfig, path: str,
93
- data: BacklogReleases, force: bool) -> None:
94
- """Write one returned backlog and the input releases to a file."""
95
- out_config = resolve_output_config(None, data_file=path,
96
- presets=config.output_configs,
97
- auto_ch_hook=CliPresetMigrateWarnHook())
98
- write_backlog_releases(data, path, out_config, FormatRules(),
99
- levels=config.get_levels(),
100
- file_exists_callback=overwrite_callback(force))
101
- print(f'Wrote {path}')
102
-
103
-
104
77
  def _write_result_files(parsed: argparse.Namespace, config: BacklogOpsConfig,
105
78
  data: BacklogReleases, result: AddedToJira) -> None:
106
79
  """Write the added and already-present backlogs to any named files."""
107
80
  releases = list(data.releases)
108
81
  if parsed.added_file is not None:
109
82
  added = BacklogReleases(backlog=result.stored, releases=releases)
110
- _write_backlog_file(config, parsed.added_file, added, parsed.force)
83
+ write_result_file(config, parsed.added_file, added, parsed.force)
111
84
  if parsed.existing_file is not None:
112
85
  present = BacklogReleases(backlog=result.already_present,
113
86
  releases=releases)
114
- _write_backlog_file(config, parsed.existing_file, present,
115
- parsed.force)
87
+ write_result_file(config, parsed.existing_file, present, parsed.force)
88
+
89
+
90
+ def _add_and_write(parsed: argparse.Namespace, config: BacklogOpsConfig,
91
+ data: BacklogReleases) -> AddedToJira:
92
+ """Add the backlog to Jira and write any requested result files."""
93
+ result = _add(parsed, config, data)
94
+ _write_result_files(parsed, config, data, result)
95
+ return result
116
96
 
117
97
 
118
98
  def _run(parsed: argparse.Namespace) -> int:
119
99
  """Read the input, add it to Jira, write files and print the lists."""
120
- try:
121
- config = required_config(parsed)
122
- data = read_input(parsed, config)
123
- result = _add(parsed, config, data)
124
- _write_result_files(parsed, config, data, result)
125
- except ExistsInJiraError:
126
- print('Nothing added to Jira.', file=sys.stderr)
127
- return 1
128
- except (ValueError, TypeError, KeyError, OSError) as error:
129
- print(f'Could not add to Jira: {error}', file=sys.stderr)
130
- return 1
131
- if not parsed.quiet:
132
- print(format_add_result(result))
133
- return 0
100
+ return run_added_to_jira(parsed, _add_and_write, format_add_result,
101
+ ExistsInJiraError, 'Could not add to Jira')
134
102
 
135
103
 
136
104
  def main(args: Optional[list[str]] = None) -> int:
@@ -143,8 +111,7 @@ def main(args: Optional[list[str]] = None) -> int:
143
111
  ``0`` on success, ``1`` when the backlog cannot be added or a key
144
112
  already exists in Jira without ``--skip-existing``.
145
113
  """
146
- parsed = parsed_args(build_parser(), args)
147
- return _run(parsed)
114
+ return _run(parsed_args(build_parser(), args))
148
115
 
149
116
 
150
117
  if __name__ == '__main__': # pragma: no cover
@@ -58,6 +58,7 @@ EXCEPTIONS = (FileNotFoundError, ValueError, FileExistsError,
58
58
  NotADirectoryError, OSError)
59
59
 
60
60
 
61
+ # pylint: disable-next=too-many-return-statements
61
62
  def _run(parsed: argparse.Namespace) -> int:
62
63
  """Run the encrypt-token-file command with the given parsed arguments.
63
64
 
@@ -18,34 +18,25 @@ only when it is needed.
18
18
 
19
19
  import argparse
20
20
  import sys
21
- from getpass import getpass
22
21
  from typing import Optional
23
22
  from jira import JIRAError
24
23
  from backlogops import (
25
24
  JiraConnections, jira_custom_fields, jira_editable_fields)
26
25
  from backlogops_cli._command_io import (
27
- add_config_arg, parsed_args, required_config)
26
+ build_jira_parser, jira_passphrase, parsed_args, required_config)
28
27
 
29
28
  DESCRIPTION = "Print Jira custom fields and an issue's editable fields"
30
29
 
31
30
 
32
31
  def build_parser() -> argparse.ArgumentParser:
33
32
  """Build the command line parser for the field diagnostic command."""
34
- parser = argparse.ArgumentParser(description=DESCRIPTION)
35
- add_config_arg(parser)
36
- parser.add_argument('-p', '--preset', dest='preset', required=True,
37
- help='Name of the Jira preset in the configuration.')
33
+ parser = build_jira_parser(DESCRIPTION, with_input=False)
38
34
  parser.add_argument('--issue', dest='issue', metavar='KEY',
39
35
  help="Also print the fields this issue's edit "
40
36
  'screen offers (for example SCRUM-15).')
41
37
  return parser
42
38
 
43
39
 
44
- def _passphrase() -> str:
45
- """Ask for the Jira token pass phrase on the terminal."""
46
- return getpass('Jira API token pass phrase: ')
47
-
48
-
49
40
  def _print_pairs(heading: str, pairs: list[tuple[str, str]]) -> None:
50
41
  """Print a heading and each field id and display name pair."""
51
42
  print(heading)
@@ -59,7 +50,8 @@ def _run(parsed: argparse.Namespace) -> int:
59
50
  """Print the custom field map and, optionally, editable fields."""
60
51
  try:
61
52
  config = required_config(parsed)
62
- connections = JiraConnections(config.get_jira_config(), _passphrase)
53
+ connections = JiraConnections(config.get_jira_config(),
54
+ jira_passphrase)
63
55
  customs = jira_custom_fields(connections, parsed.preset)
64
56
  editable = (jira_editable_fields(connections, parsed.preset,
65
57
  parsed.issue)
@@ -19,7 +19,7 @@ import sys
19
19
  from typing import Optional
20
20
  from backlogops import BacklogOpsConfig, BacklogReleases, DependencyMode
21
21
  from backlogops_cli._command_io import (
22
- build_io_parser, parsed_args, read_input, run_write)
22
+ build_io_parser, read_input, run_write_command)
23
23
 
24
24
  DESCRIPTION = 'Reorder a backlog so that dependencies are fulfilled'
25
25
 
@@ -61,8 +61,7 @@ def main(args: Optional[list[str]] = None) -> int:
61
61
  ``0`` on success, ``1`` when the data cannot be read, reordered
62
62
  or written.
63
63
  """
64
- parsed = parsed_args(build_parser(), args)
65
- return run_write(parsed, lambda config: _ordered(parsed, config))
64
+ return run_write_command(build_parser, _ordered, args)
66
65
 
67
66
 
68
67
  if __name__ == '__main__': # pragma: no cover
@@ -19,7 +19,7 @@ import sys
19
19
  from typing import Optional
20
20
  from backlogops import BacklogOpsConfig, BacklogReleases, read_key_list
21
21
  from backlogops_cli._command_io import (
22
- build_io_parser, parsed_args, read_input, run_write)
22
+ build_io_parser, read_input, run_write_command)
23
23
 
24
24
  DESCRIPTION = 'Reorder a backlog so that key-list items come first'
25
25
 
@@ -50,8 +50,7 @@ def main(args: Optional[list[str]] = None) -> int:
50
50
  ``0`` on success, ``1`` when the data cannot be read, reordered
51
51
  or written.
52
52
  """
53
- parsed = parsed_args(build_parser(), args)
54
- return run_write(parsed, lambda config: _reordered(parsed, config))
53
+ return run_write_command(build_parser, _reordered, args)
55
54
 
56
55
 
57
56
  if __name__ == '__main__': # pragma: no cover
@@ -24,7 +24,7 @@ import sys
24
24
  from typing import Optional
25
25
  from backlogops import BacklogOpsConfig, BacklogReleases
26
26
  from backlogops_cli._command_io import (
27
- build_io_parser, parsed_args, read_input, run_write)
27
+ build_io_parser, read_input, run_write_command)
28
28
 
29
29
  DESCRIPTION = 'Order the backlog to follow the release order'
30
30
 
@@ -63,8 +63,7 @@ def main(args: Optional[list[str]] = None) -> int:
63
63
  ``0`` on success, ``1`` when the data cannot be read, ordered or
64
64
  written.
65
65
  """
66
- parsed = parsed_args(build_parser(), args)
67
- return run_write(parsed, lambda config: _ordered(parsed, config))
66
+ return run_write_command(build_parser, _ordered, args)
68
67
 
69
68
 
70
69
  if __name__ == '__main__': # pragma: no cover
@@ -20,7 +20,7 @@ import sys
20
20
  from typing import Optional
21
21
  from backlogops import BacklogOpsConfig, BacklogReleases
22
22
  from backlogops_cli._command_io import (
23
- build_io_parser, parsed_args, read_input, run_write)
23
+ build_io_parser, read_input, run_write_command)
24
24
 
25
25
  DESCRIPTION = 'Order the releases by their planned or estimated date'
26
26
 
@@ -53,8 +53,7 @@ def main(args: Optional[list[str]] = None) -> int:
53
53
  ``0`` on success, ``1`` when the data cannot be read, ordered or
54
54
  written.
55
55
  """
56
- parsed = parsed_args(build_parser(), args)
57
- return run_write(parsed, lambda config: _ordered(parsed, config))
56
+ return run_write_command(build_parser, _ordered, args)
58
57
 
59
58
 
60
59
  if __name__ == '__main__': # pragma: no cover
@@ -23,14 +23,14 @@ unlocked by a pass phrase asked on the terminal only when it is needed.
23
23
 
24
24
  import argparse
25
25
  import sys
26
- from getpass import getpass
27
26
  from typing import Optional
28
27
  from backlogops import (
29
28
  BacklogOpsConfig, JiraConnections, OrderedReleasesInJira,
30
29
  format_order_result, order_jira_rel_by_date, order_releases_in_jira,
31
30
  read_name_list)
32
31
  from backlogops_cli._command_io import (
33
- add_config_arg, parsed_args, read_input, required_config)
32
+ add_quiet_arg, build_jira_parser, jira_passphrase, parsed_args,
33
+ read_input, required_config)
34
34
 
35
35
  DESCRIPTION = 'Order releases in Jira by date, a name list or the input order'
36
36
 
@@ -43,10 +43,7 @@ def build_parser() -> argparse.ArgumentParser:
43
43
  with ``-i``/``--input`` is itself the third source; ``-I`` only names its
44
44
  format.
45
45
  """
46
- parser = argparse.ArgumentParser(description=DESCRIPTION)
47
- add_config_arg(parser)
48
- parser.add_argument('-p', '--preset', dest='preset', required=True,
49
- help='Name of the Jira preset in the configuration.')
46
+ parser = build_jira_parser(DESCRIPTION, with_input=False)
50
47
  source = parser.add_mutually_exclusive_group(required=True)
51
48
  source.add_argument('--by-date', dest='by_date', action='store_true',
52
49
  help='Order the versions by their release date, '
@@ -61,16 +58,10 @@ def build_parser() -> argparse.ArgumentParser:
61
58
  parser.add_argument('-I', '--input-config', dest='input_config',
62
59
  help='Format of the -i/--input file: a config file '
63
60
  'or a preset name.')
64
- parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
65
- help='Do not print the result lists to stdout.')
61
+ add_quiet_arg(parser)
66
62
  return parser
67
63
 
68
64
 
69
- def _passphrase() -> str:
70
- """Ask for the Jira token pass phrase on the terminal."""
71
- return getpass('Jira API token pass phrase: ')
72
-
73
-
74
65
  def _names(parsed: argparse.Namespace, config: BacklogOpsConfig) -> list[str]:
75
66
  """Return the wanted order of names from the chosen name source.
76
67
 
@@ -87,7 +78,7 @@ def _order(parsed: argparse.Namespace,
87
78
  """Order the releases in Jira using the chosen order source."""
88
79
  print(f"Ordering releases in Jira using preset '{parsed.preset}'...",
89
80
  file=sys.stderr)
90
- connections = JiraConnections(config.get_jira_config(), _passphrase)
81
+ connections = JiraConnections(config.get_jira_config(), jira_passphrase)
91
82
  if parsed.by_date:
92
83
  result = order_jira_rel_by_date(connections, parsed.preset)
93
84
  else:
@@ -120,8 +111,7 @@ def main(args: Optional[list[str]] = None) -> int:
120
111
  Returns:
121
112
  ``0`` on success, ``1`` when the order cannot be resolved or applied.
122
113
  """
123
- parsed = parsed_args(build_parser(), args)
124
- return _run(parsed)
114
+ return _run(parsed_args(build_parser(), args))
125
115
 
126
116
 
127
117
  if __name__ == '__main__': # pragma: no cover
@@ -24,24 +24,21 @@ terminal only when it is needed.
24
24
 
25
25
  import argparse
26
26
  import sys
27
- from getpass import getpass
28
27
  from typing import Optional
29
28
  from jira import JIRAError
30
29
  from backlogops import (
31
30
  BacklogOpsConfig, JiraConnections, RankedInJira, format_rank_result,
32
31
  jira_rank_move_keys, read_key_list)
33
32
  from backlogops_cli._command_io import (
34
- RANK_ANCHOR_CHOICES, add_config_arg, parsed_args, required_config)
33
+ RANK_ANCHOR_CHOICES, add_quiet_arg, build_jira_parser, jira_passphrase,
34
+ parsed_args, required_config)
35
35
 
36
36
  DESCRIPTION = 'Move key-list items to a chosen anchor in the Jira rank order'
37
37
 
38
38
 
39
39
  def build_parser() -> argparse.ArgumentParser:
40
40
  """Build the command line parser for the rank-in-Jira command."""
41
- parser = argparse.ArgumentParser(description=DESCRIPTION)
42
- add_config_arg(parser)
43
- parser.add_argument('-p', '--preset', dest='preset', required=True,
44
- help='Name of the Jira preset in the configuration.')
41
+ parser = build_jira_parser(DESCRIPTION, with_input=False)
45
42
  parser.add_argument('-k', '--key-list', dest='key_list', required=True,
46
43
  help='Key list file naming the issues to move.')
47
44
  parser.add_argument('--anchor', dest='anchor',
@@ -59,22 +56,16 @@ def build_parser() -> argparse.ArgumentParser:
59
56
  parser.add_argument('--filter', dest='filter', metavar='JQL', default=None,
60
57
  help='JQL filter reading the backlog; it may only '
61
58
  'order by rank. Omit to use the preset filter.')
62
- parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
63
- help='Do not print the result lists to stdout.')
59
+ add_quiet_arg(parser)
64
60
  return parser
65
61
 
66
62
 
67
- def _passphrase() -> str:
68
- """Ask for the Jira token pass phrase on the terminal."""
69
- return getpass('Jira API token pass phrase: ')
70
-
71
-
72
63
  def _rank(parsed: argparse.Namespace,
73
64
  config: BacklogOpsConfig) -> RankedInJira:
74
65
  """Move the key-list items in the Jira rank order using the preset."""
75
66
  print(f"Ranking items in Jira using preset '{parsed.preset}'...",
76
67
  file=sys.stderr)
77
- connections = JiraConnections(config.get_jira_config(), _passphrase)
68
+ connections = JiraConnections(config.get_jira_config(), jira_passphrase)
78
69
  keys = read_key_list(parsed.key_list)
79
70
  result = jira_rank_move_keys(connections, parsed.preset, keys,
80
71
  filter_override=parsed.filter,
@@ -112,8 +103,7 @@ def main(args: Optional[list[str]] = None) -> int:
112
103
  Returns:
113
104
  ``0`` on success, ``1`` when the items cannot be ranked.
114
105
  """
115
- parsed = parsed_args(build_parser(), args)
116
- return _run(parsed)
106
+ return _run(parsed_args(build_parser(), args))
117
107
 
118
108
 
119
109
  if __name__ == '__main__': # pragma: no cover
@@ -18,12 +18,11 @@ phrase asked on the terminal only when it is needed.
18
18
 
19
19
  import argparse
20
20
  import sys
21
- from getpass import getpass
22
21
  from typing import Optional
23
22
  from backlogops import (
24
23
  BacklogOpsConfig, BacklogReleases, read_jira_from_config)
25
24
  from backlogops_cli._command_io import (
26
- build_io_parser, parsed_args, run_write)
25
+ build_io_parser, jira_passphrase, parsed_args, run_write)
27
26
 
28
27
  DESCRIPTION = 'Read a backlog and releases from Jira and store to a file'
29
28
 
@@ -40,11 +39,6 @@ def build_parser() -> argparse.ArgumentParser:
40
39
  return parser
41
40
 
42
41
 
43
- def _passphrase() -> str:
44
- """Ask for the Jira token pass phrase on the terminal."""
45
- return getpass('Jira API token pass phrase: ')
46
-
47
-
48
42
  def _warn_if_inconsistent(data: BacklogReleases) -> None:
49
43
  """Warn when the data read from Jira is not fully consistent.
50
44
 
@@ -67,7 +61,7 @@ def _read_jira(parsed: argparse.Namespace,
67
61
  file=sys.stderr)
68
62
  data = read_jira_from_config(config, parsed.preset,
69
63
  filter_override=parsed.filter,
70
- passphrase=_passphrase)
64
+ passphrase=jira_passphrase)
71
65
  print(f'Read {len(data.backlog)} backlog items and '
72
66
  f'{len(data.releases)} releases from Jira.', file=sys.stderr)
73
67
  _warn_if_inconsistent(data)
@@ -22,13 +22,13 @@ phrase asked on the terminal only when it is needed.
22
22
 
23
23
  import argparse
24
24
  import sys
25
- from getpass import getpass
26
25
  from typing import Optional, Sequence
27
26
  from backlogops import (
28
27
  BacklogOpsConfig, JiraConnections, ReleaseRename, RenamedReleasesInJira,
29
28
  format_rename_result, read_renames, rename_releases_in_jira)
30
29
  from backlogops_cli._command_io import (
31
- add_config_arg, parsed_args, required_config)
30
+ add_quiet_arg, build_jira_parser, jira_passphrase, parsed_args,
31
+ required_config)
32
32
 
33
33
  DESCRIPTION = 'Rename releases in Jira, changing Jira version names'
34
34
 
@@ -40,10 +40,7 @@ def build_parser() -> argparse.ArgumentParser:
40
40
  group, so argparse checks that exactly one way is given. ``--rename``
41
41
  takes both names at once, so its old and new name always travel together.
42
42
  """
43
- parser = argparse.ArgumentParser(description=DESCRIPTION)
44
- add_config_arg(parser)
45
- parser.add_argument('-p', '--preset', dest='preset', required=True,
46
- help='Name of the Jira preset in the configuration.')
43
+ parser = build_jira_parser(DESCRIPTION, with_input=False)
47
44
  source = parser.add_mutually_exclusive_group(required=True)
48
45
  source.add_argument('--rename', dest='rename', nargs=2,
49
46
  metavar=('OLD', 'NEW'),
@@ -53,16 +50,10 @@ def build_parser() -> argparse.ArgumentParser:
53
50
  help='Two column file of old and new names for a '
54
51
  'batch of renames, with columns separated by tab. '
55
52
  '(Spaces are allowed in a release name.)')
56
- parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
57
- help='Do not print the result lists to stdout.')
53
+ add_quiet_arg(parser)
58
54
  return parser
59
55
 
60
56
 
61
- def _passphrase() -> str:
62
- """Ask for the Jira token pass phrase on the terminal."""
63
- return getpass('Jira API token pass phrase: ')
64
-
65
-
66
57
  def _renames(parsed: argparse.Namespace) -> Sequence[ReleaseRename]:
67
58
  """Return the renames from ``--rename`` or the ``--rename-file``.
68
59
 
@@ -81,7 +72,7 @@ def _rename(parsed: argparse.Namespace, config: BacklogOpsConfig,
81
72
  """Rename the releases in Jira using the named preset."""
82
73
  print(f"Renaming releases in Jira using preset '{parsed.preset}'...",
83
74
  file=sys.stderr)
84
- connections = JiraConnections(config.get_jira_config(), _passphrase)
75
+ connections = JiraConnections(config.get_jira_config(), jira_passphrase)
85
76
  result = rename_releases_in_jira(connections, parsed.preset, renames)
86
77
  print(f'Renamed {len(result.renamed)} releases in Jira; '
87
78
  f'{len(result.unchanged)} unchanged; {len(result.missing)} not in '
@@ -112,8 +103,7 @@ def main(args: Optional[list[str]] = None) -> int:
112
103
  Returns:
113
104
  ``0`` on success, ``1`` when the renames cannot be read or applied.
114
105
  """
115
- parsed = parsed_args(build_parser(), args)
116
- return _run(parsed)
106
+ return _run(parsed_args(build_parser(), args))
117
107
 
118
108
 
119
109
  if __name__ == '__main__': # pragma: no cover
@@ -32,15 +32,14 @@ only when it is needed.
32
32
 
33
33
  import argparse
34
34
  import sys
35
- from getpass import getpass
36
35
  from typing import Optional
37
36
  from backlogops import (
38
37
  BacklogOpsConfig, ItemNotInJiraError, JiraConnections, LinkUpdate,
39
38
  OnMissingKey, UpdatedBacklogInJira, format_backlog_updates,
40
39
  update_backlog_in_jira, updatable_backlog_fields)
41
40
  from backlogops_cli._command_io import (
42
- add_config_arg, add_input_args, add_rank_arg, parsed_args, rank_anchor,
43
- read_input, required_config)
41
+ add_quiet_arg, add_rank_arg, build_jira_parser, jira_passphrase,
42
+ parsed_args, rank_anchor, read_input, required_config)
44
43
 
45
44
  DESCRIPTION = 'Update a backlog in Jira, changing only the chosen columns'
46
45
 
@@ -53,11 +52,7 @@ _STORE_ALL = 'all'
53
52
 
54
53
  def build_parser() -> argparse.ArgumentParser:
55
54
  """Build the command line parser for the update-backlog command."""
56
- parser = argparse.ArgumentParser(description=DESCRIPTION)
57
- add_input_args(parser)
58
- add_config_arg(parser)
59
- parser.add_argument('-p', '--preset', dest='preset', required=True,
60
- help='Name of the Jira preset in the configuration.')
55
+ parser = build_jira_parser(DESCRIPTION)
61
56
  _add_column_flags(parser)
62
57
  parser.add_argument('--on-missing', dest='on_missing',
63
58
  choices=sorted(_MISSING_MODES), default='raise',
@@ -70,8 +65,7 @@ def build_parser() -> argparse.ArgumentParser:
70
65
  'removing links it no longer has; add only adds '
71
66
  'missing links.')
72
67
  add_rank_arg(parser)
73
- parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
74
- help='Do not print the result lists to stdout.')
68
+ add_quiet_arg(parser)
75
69
  return parser
76
70
 
77
71
 
@@ -87,11 +81,6 @@ def _add_column_flags(parser: argparse.ArgumentParser) -> None:
87
81
  help='Update every mapped writable column but these.')
88
82
 
89
83
 
90
- def _passphrase() -> str:
91
- """Ask for the Jira token pass phrase on the terminal."""
92
- return getpass('Jira API token pass phrase: ')
93
-
94
-
95
84
  def _resolve_fields(parsed: argparse.Namespace,
96
85
  connections: JiraConnections) -> list[str]:
97
86
  """Return the internal field names to update from the -s/-e flags.
@@ -118,7 +107,7 @@ def _update(parsed: argparse.Namespace, config: BacklogOpsConfig,
118
107
  """Update the input backlog in Jira using the named preset."""
119
108
  print(f"Updating backlog in Jira using preset '{parsed.preset}'...",
120
109
  file=sys.stderr)
121
- connections = JiraConnections(config.get_jira_config(), _passphrase)
110
+ connections = JiraConnections(config.get_jira_config(), jira_passphrase)
122
111
  fields = _resolve_fields(parsed, connections)
123
112
  mode = _MISSING_MODES[parsed.on_missing]
124
113
  link_update = _LINK_MODES[parsed.links]
@@ -169,8 +158,7 @@ def main(args: Optional[list[str]] = None) -> int:
169
158
  ``0`` on success, ``1`` when the backlog cannot be updated or a key
170
159
  is not present in Jira with the raise policy.
171
160
  """
172
- parsed = parsed_args(build_parser(), args)
173
- return _run(parsed)
161
+ return _run(parsed_args(build_parser(), args))
174
162
 
175
163
 
176
164
  if __name__ == '__main__': # pragma: no cover
@@ -23,14 +23,14 @@ only when it is needed.
23
23
 
24
24
  import argparse
25
25
  import sys
26
- from getpass import getpass
27
26
  from typing import Optional
28
27
  from backlogops import (
29
28
  BacklogOpsConfig, ItemNotInJiraError, JiraConnections, OnMissingKey,
30
29
  Releases, UpdatedReleasesInJira, format_release_updates,
31
30
  update_releases_in_jira)
32
31
  from backlogops_cli._command_io import (
33
- add_config_arg, add_input_args, parsed_args, read_input, required_config)
32
+ add_quiet_arg, build_jira_parser, jira_passphrase, parsed_args,
33
+ read_input, required_config)
34
34
 
35
35
  DESCRIPTION = 'Update releases in Jira, setting dates to the planned dates'
36
36
 
@@ -40,11 +40,7 @@ _MISSING_MODES = {'raise': OnMissingKey.RAISE, 'ignore': OnMissingKey.IGNORE,
40
40
 
41
41
  def build_parser() -> argparse.ArgumentParser:
42
42
  """Build the command line parser for the update-releases command."""
43
- parser = argparse.ArgumentParser(description=DESCRIPTION)
44
- add_input_args(parser)
45
- add_config_arg(parser)
46
- parser.add_argument('-p', '--preset', dest='preset', required=True,
47
- help='Name of the Jira preset in the configuration.')
43
+ parser = build_jira_parser(DESCRIPTION)
48
44
  parser.add_argument('--on-missing', dest='on_missing',
49
45
  choices=sorted(_MISSING_MODES), default='raise',
50
46
  help='What to do with a release whose name is not in '
@@ -57,16 +53,10 @@ def build_parser() -> argparse.ArgumentParser:
57
53
  action='store_true',
58
54
  help='Update only the releases named with --release; '
59
55
  'without it every input release is updated.')
60
- parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
61
- help='Do not print the result lists to stdout.')
56
+ add_quiet_arg(parser)
62
57
  return parser
63
58
 
64
59
 
65
- def _passphrase() -> str:
66
- """Ask for the Jira token pass phrase on the terminal."""
67
- return getpass('Jira API token pass phrase: ')
68
-
69
-
70
60
  def _select(parsed: argparse.Namespace, releases: Releases) -> Releases:
71
61
  """Return the releases to update, limited to --release when asked.
72
62
 
@@ -89,7 +79,7 @@ def _update(parsed: argparse.Namespace, config: BacklogOpsConfig,
89
79
  """Update the selected releases in Jira using the named preset."""
90
80
  print(f"Updating releases in Jira using preset '{parsed.preset}'...",
91
81
  file=sys.stderr)
92
- connections = JiraConnections(config.get_jira_config(), _passphrase)
82
+ connections = JiraConnections(config.get_jira_config(), jira_passphrase)
93
83
  mode = _MISSING_MODES[parsed.on_missing]
94
84
  result = update_releases_in_jira(connections, parsed.preset, releases,
95
85
  on_missing_key=mode)
@@ -127,8 +117,7 @@ def main(args: Optional[list[str]] = None) -> int:
127
117
  ``0`` on success, ``1`` when the releases cannot be updated or a
128
118
  name is not present in Jira with the raise policy.
129
119
  """
130
- parsed = parsed_args(build_parser(), args)
131
- return _run(parsed)
120
+ return _run(parsed_args(build_parser(), args))
132
121
 
133
122
 
134
123
  if __name__ == '__main__': # pragma: no cover
File without changes
File without changes