abt-cli 0.0.18 → 0.0.23

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/bin/abt +3 -3
  3. data/lib/abt.rb +6 -6
  4. data/lib/abt/ari.rb +20 -0
  5. data/lib/abt/ari_list.rb +13 -0
  6. data/lib/abt/base_command.rb +63 -0
  7. data/lib/abt/cli.rb +51 -52
  8. data/lib/abt/cli/arguments_parser.rb +7 -26
  9. data/lib/abt/cli/global_commands.rb +23 -0
  10. data/lib/abt/cli/global_commands/commands.rb +23 -0
  11. data/lib/abt/cli/global_commands/examples.rb +23 -0
  12. data/lib/abt/cli/global_commands/help.rb +23 -0
  13. data/lib/abt/cli/global_commands/readme.rb +23 -0
  14. data/lib/abt/cli/global_commands/share.rb +36 -0
  15. data/lib/abt/cli/global_commands/version.rb +23 -0
  16. data/lib/abt/cli/prompt.rb +64 -51
  17. data/lib/abt/docs.rb +48 -25
  18. data/lib/abt/docs/cli.rb +3 -3
  19. data/lib/abt/docs/markdown.rb +11 -8
  20. data/lib/abt/git_config.rb +21 -39
  21. data/lib/abt/helpers.rb +26 -8
  22. data/lib/abt/providers/asana/api.rb +9 -9
  23. data/lib/abt/providers/asana/base_command.rb +20 -38
  24. data/lib/abt/providers/asana/commands/add.rb +18 -15
  25. data/lib/abt/providers/asana/commands/branch_name.rb +13 -8
  26. data/lib/abt/providers/asana/commands/clear.rb +8 -7
  27. data/lib/abt/providers/asana/commands/current.rb +22 -38
  28. data/lib/abt/providers/asana/commands/finalize.rb +17 -18
  29. data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +20 -13
  30. data/lib/abt/providers/asana/commands/init.rb +8 -41
  31. data/lib/abt/providers/asana/commands/pick.rb +27 -26
  32. data/lib/abt/providers/asana/commands/projects.rb +5 -5
  33. data/lib/abt/providers/asana/commands/share.rb +6 -8
  34. data/lib/abt/providers/asana/commands/start.rb +33 -24
  35. data/lib/abt/providers/asana/commands/tasks.rb +6 -5
  36. data/lib/abt/providers/asana/configuration.rb +46 -44
  37. data/lib/abt/providers/asana/path.rb +36 -0
  38. data/lib/abt/providers/devops/api.rb +23 -11
  39. data/lib/abt/providers/devops/base_command.rb +22 -43
  40. data/lib/abt/providers/devops/commands/boards.rb +5 -7
  41. data/lib/abt/providers/devops/commands/branch_name.rb +14 -10
  42. data/lib/abt/providers/devops/commands/clear.rb +8 -7
  43. data/lib/abt/providers/devops/commands/current.rb +24 -49
  44. data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +26 -16
  45. data/lib/abt/providers/devops/commands/init.rb +33 -26
  46. data/lib/abt/providers/devops/commands/pick.rb +23 -24
  47. data/lib/abt/providers/devops/commands/share.rb +7 -6
  48. data/lib/abt/providers/devops/commands/{work-items.rb → work_items.rb} +3 -3
  49. data/lib/abt/providers/devops/configuration.rb +27 -56
  50. data/lib/abt/providers/devops/path.rb +51 -0
  51. data/lib/abt/providers/git/commands/branch.rb +25 -19
  52. data/lib/abt/providers/harvest/api.rb +8 -8
  53. data/lib/abt/providers/harvest/base_command.rb +20 -36
  54. data/lib/abt/providers/harvest/commands/clear.rb +8 -7
  55. data/lib/abt/providers/harvest/commands/current.rb +27 -35
  56. data/lib/abt/providers/harvest/commands/init.rb +10 -40
  57. data/lib/abt/providers/harvest/commands/pick.rb +15 -12
  58. data/lib/abt/providers/harvest/commands/projects.rb +5 -5
  59. data/lib/abt/providers/harvest/commands/share.rb +6 -8
  60. data/lib/abt/providers/harvest/commands/start.rb +5 -3
  61. data/lib/abt/providers/harvest/commands/stop.rb +13 -13
  62. data/lib/abt/providers/harvest/commands/tasks.rb +9 -6
  63. data/lib/abt/providers/harvest/commands/track.rb +60 -38
  64. data/lib/abt/providers/harvest/configuration.rb +28 -37
  65. data/lib/abt/providers/harvest/path.rb +36 -0
  66. data/lib/abt/version.rb +1 -1
  67. metadata +18 -6
  68. data/lib/abt/cli/base_command.rb +0 -61
@@ -6,27 +6,28 @@ module Abt
6
6
  module Commands
7
7
  class Clear < BaseCommand
8
8
  def self.usage
9
- 'abt clear devops'
9
+ "abt clear devops"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Clear DevOps configuration'
13
+ "Clear DevOps configuration"
14
14
  end
15
15
 
16
16
  def self.flags
17
17
  [
18
- ['-g', '--global', 'Clear global instead of local DevOp configuration (credentials etc.)'],
19
- ['-a', '--all', 'Clear all DevOp configuration']
18
+ ["-g", "--global",
19
+ "Clear global instead of local DevOp configuration (credentials etc.)"],
20
+ ["-a", "--all", "Clear all DevOp configuration"]
20
21
  ]
21
22
  end
22
23
 
23
24
  def perform
24
- if flags[:global] && flags[:all]
25
- abort('Flags --global and --all cannot be used at the same time')
26
- end
25
+ abort("Flags --global and --all cannot be used at the same time") if flags[:global] && flags[:all]
27
26
 
28
27
  config.clear_local unless flags[:global]
29
28
  config.clear_global if flags[:global] || flags[:all]
29
+
30
+ warn("Configuration cleared")
30
31
  end
31
32
  end
32
33
  end
@@ -6,27 +6,29 @@ module Abt
6
6
  module Commands
7
7
  class Current < BaseCommand
8
8
  def self.usage
9
- 'abt current devops[:<organization-name>/<project-name>/<board-id>[/<work-item-id>]]'
9
+ "abt current devops[:<organization-name>/<project-name>/<board-id>[/<work-item-id>]]"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Get or set DevOps configuration for current git repository'
13
+ "Get or set DevOps configuration for current git repository"
14
14
  end
15
15
 
16
16
  def perform
17
+ require_local_config!
17
18
  require_board!
19
+ ensure_valid_configuration!
18
20
 
19
- if same_args_as_config? || !config.local_available?
20
- show_current_configuration
21
- else
22
- cli.warn 'Updating configuration'
23
- update_configuration
21
+ if path != config.path && config.local_available?
22
+ config.path = path
23
+ warn("Configuration updated")
24
24
  end
25
+
26
+ print_configuration
25
27
  end
26
28
 
27
29
  private
28
30
 
29
- def show_current_configuration
31
+ def print_configuration
30
32
  if work_item_id.nil?
31
33
  print_board(organization_name, project_name, board)
32
34
  else
@@ -34,57 +36,30 @@ module Abt
34
36
  end
35
37
  end
36
38
 
37
- def update_configuration
38
- ensure_board_is_valid!
39
-
40
- if work_item_id.nil?
41
- update_board_config
42
- config.work_item_id = nil
43
-
44
- print_board(organization_name, project_name, board)
45
- else
46
- ensure_work_item_is_valid!
47
-
48
- update_board_config
49
- config.work_item_id = work_item_id
50
-
51
- print_work_item(organization_name, project_name, board, work_item)
52
- end
53
- end
54
-
55
- def update_board_config
56
- config.organization_name = organization_name
57
- config.project_name = project_name
58
- config.board_id = board_id
59
- end
60
-
61
- def ensure_board_is_valid!
39
+ def ensure_valid_configuration!
62
40
  if board.nil?
63
- cli.abort 'Board could not be found, ensure that settings for organization, project, and board are correct'
41
+ abort("Board could not be found, ensure that settings for organization, project, and board are correct")
64
42
  end
65
- end
66
-
67
- def ensure_work_item_is_valid!
68
- cli.abort "No such work item: ##{work_item_id}" if work_item.nil?
43
+ abort("No such work item: ##{work_item_id}") if work_item_id && work_item.nil?
69
44
  end
70
45
 
71
46
  def board
72
47
  @board ||= begin
73
- cli.warn 'Fetching board...'
74
- api.get("work/boards/#{board_id}")
75
- rescue HttpError::NotFoundError
76
- nil
77
- end
48
+ warn("Fetching board...")
49
+ api.get("work/boards/#{board_id}")
50
+ rescue HttpError::NotFoundError
51
+ nil
52
+ end
78
53
  end
79
54
 
80
55
  def work_item
81
56
  @work_item ||= begin
82
- cli.warn 'Fetching work item...'
83
- work_item = api.get_paged('wit/workitems', ids: work_item_id)[0]
84
- sanitize_work_item(work_item)
85
- rescue HttpError::NotFoundError
86
- nil
87
- end
57
+ warn("Fetching work item...")
58
+ work_item = api.get_paged("wit/workitems", ids: work_item_id)[0]
59
+ sanitize_work_item(work_item)
60
+ rescue HttpError::NotFoundError
61
+ nil
62
+ end
88
63
  end
89
64
  end
90
65
  end
@@ -6,43 +6,53 @@ module Abt
6
6
  module Commands
7
7
  class HarvestTimeEntryData < BaseCommand
8
8
  def self.usage
9
- 'abt harvest-time-entry-data devops[:<organization-name>/<project-name>/<board-id>/<work-item-id>]'
9
+ "abt harvest-time-entry-data devops[:<organization-name>/<project-name>/<board-id>/<work-item-id>]"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Print Harvest time entry data for DevOps work item as json. Used by harvest start script.'
13
+ "Print Harvest time entry data for DevOps work item as json. Used by harvest start script."
14
14
  end
15
15
 
16
16
  def perform
17
17
  require_work_item!
18
18
 
19
- body = {
19
+ puts Oj.dump(body, mode: :json)
20
+ rescue HttpError::NotFoundError
21
+ args = [organization_name, project_name, board_id, work_item_id].compact
22
+
23
+ error_message = [
24
+ "Unable to find work item for configuration:",
25
+ "devops:#{args.join('/')}"
26
+ ].join("\n")
27
+ abort(error_message)
28
+ end
29
+
30
+ private
31
+
32
+ def body
33
+ {
20
34
  notes: notes,
21
35
  external_reference: {
22
- id: work_item['id'],
23
- group_id: 'AzureDevOpsWorkItem',
24
- permalink: work_item['url']
36
+ id: work_item["id"],
37
+ group_id: "AzureDevOpsWorkItem",
38
+ permalink: work_item["url"]
25
39
  }
26
40
  }
27
-
28
- cli.puts Oj.dump(body, mode: :json)
29
41
  end
30
42
 
31
- private
32
-
33
43
  def notes
34
44
  [
35
- 'Azure DevOps',
36
- work_item['fields']['System.WorkItemType'],
45
+ "Azure DevOps",
46
+ work_item["fields"]["System.WorkItemType"],
37
47
  "##{work_item['id']}",
38
- '-',
39
- work_item['name']
40
- ].join(' ')
48
+ "-",
49
+ work_item["name"]
50
+ ].join(" ")
41
51
  end
42
52
 
43
53
  def work_item
44
54
  @work_item ||= begin
45
- work_item = api.get_paged('wit/workitems', ids: work_item_id)[0]
55
+ work_item = api.get_paged("wit/workitems", ids: work_item_id)[0]
46
56
  sanitize_work_item(work_item)
47
57
  end
48
58
  end
@@ -9,62 +9,69 @@ module Abt
9
9
  VS_URL_REGEX = %r{^https://(?<organization>[^.]+)\.visualstudio\.com/(?<project>[^/]+)}.freeze
10
10
 
11
11
  def self.usage
12
- 'abt init devops'
12
+ "abt init devops"
13
13
  end
14
14
 
15
15
  def self.description
16
- 'Pick DevOps board for current git repository'
16
+ "Pick DevOps board for current git repository"
17
17
  end
18
18
 
19
19
  def perform
20
- cli.abort 'Must be run inside a git repository' unless config.local_available?
21
-
22
- @organization_name = config.organization_name = organization_name_from_url
23
- @project_name = config.project_name = project_name_from_url
24
-
25
- board = cli.prompt.choice 'Select a project work board', boards
26
-
27
- config.board_id = board['id']
20
+ require_local_config!
21
+ board = cli.prompt.choice("Select a project work board", boards)
28
22
 
23
+ config.path = Path.from_ids(
24
+ organization_name: organization_name,
25
+ project_name: project_name,
26
+ board_id: board["id"]
27
+ )
29
28
  print_board(organization_name, project_name, board)
30
29
  end
31
30
 
32
31
  private
33
32
 
34
33
  def boards
35
- @boards ||= api.get_paged('work/boards')
34
+ @boards ||= api.get_paged("work/boards")
36
35
  end
37
36
 
38
- def project_name_from_url
39
- if (match = AZURE_DEV_URL_REGEX.match(project_url)) ||
40
- (match = VS_URL_REGEX.match(project_url))
41
- match[:project]
37
+ def project_name
38
+ @project_name ||= begin
39
+ if (match = AZURE_DEV_URL_REGEX.match(project_url)) ||
40
+ (match = VS_URL_REGEX.match(project_url))
41
+ match[:project]
42
+ end
42
43
  end
43
44
  end
44
45
 
45
- def organization_name_from_url
46
- if (match = AZURE_DEV_URL_REGEX.match(project_url)) ||
47
- (match = VS_URL_REGEX.match(project_url))
48
- match[:organization]
46
+ def organization_name
47
+ @organization_name ||= begin
48
+ if (match = AZURE_DEV_URL_REGEX.match(project_url)) ||
49
+ (match = VS_URL_REGEX.match(project_url))
50
+ match[:organization]
51
+ end
49
52
  end
50
53
  end
51
54
 
52
55
  def project_url
53
56
  @project_url ||= begin
54
57
  loop do
55
- url = cli.prompt.text([
56
- 'Please provide the URL for the devops project',
57
- 'For instance https://{organization}.visualstudio.com/{project} or https://dev.azure.com/{organization}/{project}',
58
- '',
59
- 'Enter URL'
60
- ].join("\n"))
58
+ url = cli.prompt.text(project_url_prompt_text)
61
59
 
62
60
  break url if AZURE_DEV_URL_REGEX =~ url || VS_URL_REGEX =~ url
63
61
 
64
- cli.warn 'Invalid URL'
62
+ warn("Invalid URL")
65
63
  end
66
64
  end
67
65
  end
66
+
67
+ def project_url_prompt_text
68
+ <<~TXT
69
+ Please provide the URL for the devops project
70
+ For instance https://{organization}.visualstudio.com/{project} or https://dev.azure.com/{organization}/{project}
71
+
72
+ Enter URL
73
+ TXT
74
+ end
68
75
  end
69
76
  end
70
77
  end
@@ -6,55 +6,54 @@ module Abt
6
6
  module Commands
7
7
  class Pick < BaseCommand
8
8
  def self.usage
9
- 'abt pick devops[:<organization-name>/<project-name>/<board-id>]'
9
+ "abt pick devops[:<organization-name>/<project-name>/<board-id>]"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Pick work item for current git repository'
13
+ "Pick work item for current git repository"
14
14
  end
15
15
 
16
16
  def self.flags
17
17
  [
18
- ['-d', '--dry-run', 'Keep existing configuration']
18
+ ["-d", "--dry-run", "Keep existing configuration"]
19
19
  ]
20
20
  end
21
21
 
22
22
  def perform
23
- cli.abort 'Must be run inside a git repository' unless config.local_available?
23
+ require_local_config!
24
24
  require_board!
25
25
 
26
- cli.warn "#{project_name} - #{board['name']}"
26
+ warn("#{project_name} - #{board['name']}")
27
27
 
28
28
  work_item = select_work_item
29
29
  print_work_item(organization_name, project_name, board, work_item)
30
30
 
31
31
  return if flags[:"dry-run"]
32
32
 
33
- update_config!(work_item)
33
+ update_config(work_item)
34
34
  end
35
35
 
36
36
  private
37
37
 
38
- def update_config!(work_item)
39
- config.organization_name = organization_name
40
- config.project_name = project_name
41
- config.board_id = board_id
42
- config.work_item_id = work_item['id']
38
+ def update_config(work_item)
39
+ config.path = Path.from_ids(
40
+ organization_name: organization_name,
41
+ project_name: project_name,
42
+ board_id: board_id,
43
+ work_item_id: work_item["id"]
44
+ )
43
45
  end
44
46
 
45
47
  def select_work_item
46
- loop do
47
- column = cli.prompt.choice 'Which column?', columns
48
- cli.warn 'Fetching work items...'
49
- work_items = work_items_in_column(column)
50
-
51
- if work_items.length.zero?
52
- cli.warn 'Section is empty'
53
- next
54
- end
55
-
56
- work_item = cli.prompt.choice 'Select a work item', work_items, true
57
- return work_item if work_item
48
+ column = cli.prompt.choice("Which column?", columns)
49
+ warn("Fetching work items...")
50
+ work_items = work_items_in_column(column)
51
+
52
+ if work_items.length.zero?
53
+ warn("Section is empty")
54
+ select_work_item
55
+ else
56
+ cli.prompt.choice("Select a work item", work_items, nil_option: true) || select_work_item
58
57
  end
59
58
  end
60
59
 
@@ -72,7 +71,7 @@ module Abt
72
71
  end
73
72
 
74
73
  def columns
75
- board['columns']
74
+ board["columns"]
76
75
  end
77
76
 
78
77
  def board
@@ -6,18 +6,19 @@ module Abt
6
6
  module Commands
7
7
  class Share < BaseCommand
8
8
  def self.usage
9
- 'abt share devops[:<organization-name>/<project-name>/<board-id>[/<work-item-id>]]'
9
+ "abt share devops[:<organization-name>/<project-name>/<board-id>[/<work-item-id>]]"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Print DevOps config string'
13
+ "Print DevOps ARI"
14
14
  end
15
15
 
16
16
  def perform
17
- require_work_item!
18
-
19
- args = [organization_name, project_name, board_id, work_item_id].compact
20
- cli.print_ari('devops', args.join('/'))
17
+ if path != ""
18
+ cli.print_ari("devops", path)
19
+ elsif cli.output.isatty
20
+ warn("No configuration for project. Did you initialize DevOps?")
21
+ end
21
22
  end
22
23
  end
23
24
  end
@@ -6,11 +6,11 @@ module Abt
6
6
  module Commands
7
7
  class WorkItems < BaseCommand
8
8
  def self.usage
9
- 'abt work-items devops'
9
+ "abt work-items devops"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'List all work items on board - useful for piping into grep etc.'
13
+ "List all work items on board - useful for piping into grep etc."
14
14
  end
15
15
 
16
16
  def perform
@@ -25,7 +25,7 @@ module Abt
25
25
 
26
26
  def work_items
27
27
  @work_items ||= begin
28
- cli.warn 'Fetching work items...'
28
+ warn("Fetching work items...")
29
29
  api.work_item_query(
30
30
  <<~WIQL
31
31
  SELECT [System.Id]