abt-cli 0.0.18 → 0.0.23

Sign up to get free protection for your applications and to get access to all the features.
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]