abt-cli 0.0.13 → 0.0.18

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/bin/abt +1 -1
  3. data/lib/abt.rb +6 -3
  4. data/lib/abt/cli.rb +91 -53
  5. data/lib/abt/cli/arguments_parser.rb +70 -0
  6. data/lib/abt/cli/base_command.rb +61 -0
  7. data/lib/abt/cli/{dialogs.rb → prompt.rb} +37 -18
  8. data/lib/abt/docs.rb +30 -24
  9. data/lib/abt/docs/cli.rb +42 -11
  10. data/lib/abt/docs/markdown.rb +38 -11
  11. data/lib/abt/git_config.rb +25 -14
  12. data/lib/abt/helpers.rb +1 -1
  13. data/lib/abt/providers/asana/base_command.rb +13 -13
  14. data/lib/abt/providers/asana/commands/add.rb +6 -6
  15. data/lib/abt/providers/asana/commands/branch_name.rb +44 -0
  16. data/lib/abt/providers/asana/commands/clear.rb +17 -6
  17. data/lib/abt/providers/asana/commands/current.rb +3 -3
  18. data/lib/abt/providers/asana/commands/finalize.rb +3 -3
  19. data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +3 -3
  20. data/lib/abt/providers/asana/commands/init.rb +5 -5
  21. data/lib/abt/providers/asana/commands/pick.rb +16 -8
  22. data/lib/abt/providers/asana/commands/projects.rb +3 -3
  23. data/lib/abt/providers/asana/commands/share.rb +5 -5
  24. data/lib/abt/providers/asana/commands/start.rb +14 -8
  25. data/lib/abt/providers/asana/commands/tasks.rb +3 -3
  26. data/lib/abt/providers/asana/configuration.rb +8 -16
  27. data/lib/abt/providers/devops/base_command.rb +14 -15
  28. data/lib/abt/providers/devops/commands/boards.rb +6 -4
  29. data/lib/abt/providers/devops/commands/branch_name.rb +45 -0
  30. data/lib/abt/providers/devops/commands/clear.rb +17 -6
  31. data/lib/abt/providers/devops/commands/current.rb +3 -3
  32. data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +3 -3
  33. data/lib/abt/providers/devops/commands/init.rb +5 -5
  34. data/lib/abt/providers/devops/commands/pick.rb +14 -7
  35. data/lib/abt/providers/devops/commands/share.rb +4 -4
  36. data/lib/abt/providers/devops/commands/work-items.rb +3 -3
  37. data/lib/abt/providers/devops/configuration.rb +7 -15
  38. data/lib/abt/providers/git.rb +19 -0
  39. data/lib/abt/providers/git/commands/branch.rb +74 -0
  40. data/lib/abt/providers/harvest/base_command.rb +13 -13
  41. data/lib/abt/providers/harvest/commands/clear.rb +17 -6
  42. data/lib/abt/providers/harvest/commands/current.rb +3 -3
  43. data/lib/abt/providers/harvest/commands/init.rb +5 -5
  44. data/lib/abt/providers/harvest/commands/pick.rb +15 -7
  45. data/lib/abt/providers/harvest/commands/projects.rb +3 -3
  46. data/lib/abt/providers/harvest/commands/share.rb +5 -5
  47. data/lib/abt/providers/harvest/commands/start.rb +6 -42
  48. data/lib/abt/providers/harvest/commands/stop.rb +3 -3
  49. data/lib/abt/providers/harvest/commands/tasks.rb +3 -3
  50. data/lib/abt/providers/harvest/commands/track.rb +49 -11
  51. data/lib/abt/providers/harvest/configuration.rb +7 -13
  52. data/lib/abt/version.rb +1 -1
  53. metadata +9 -7
  54. data/lib/abt/cli/io.rb +0 -23
  55. data/lib/abt/providers/asana/commands/clear_global.rb +0 -24
  56. data/lib/abt/providers/devops/commands/clear_global.rb +0 -24
  57. data/lib/abt/providers/harvest/commands/clear_global.rb +0 -24
@@ -5,17 +5,28 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Clear < BaseCommand
8
- def self.command
9
- 'clear asana'
8
+ def self.usage
9
+ 'abt clear asana'
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Clear project/task for current git repository'
13
+ 'Clear asana configuration'
14
14
  end
15
15
 
16
- def call
17
- cli.warn 'Clearing Asana project configuration'
18
- config.clear_local
16
+ def self.flags
17
+ [
18
+ ['-g', '--global', 'Clear global instead of local asana configuration (credentials etc.)'],
19
+ ['-a', '--all', 'Clear all asana configuration']
20
+ ]
21
+ end
22
+
23
+ def perform
24
+ if flags[:global] && flags[:all]
25
+ abort('Flags --global and --all cannot be used at the same time')
26
+ end
27
+
28
+ config.clear_local unless flags[:global]
29
+ config.clear_global if flags[:global] || flags[:all]
19
30
  end
20
31
  end
21
32
  end
@@ -5,15 +5,15 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Current < BaseCommand
8
- def self.command
9
- 'current asana[:<project-gid>[/<task-gid>]]'
8
+ def self.usage
9
+ 'abt current asana[:<project-gid>[/<task-gid>]]'
10
10
  end
11
11
 
12
12
  def self.description
13
13
  'Get or set project and or task for current git repository'
14
14
  end
15
15
 
16
- def call
16
+ def perform
17
17
  require_project!
18
18
 
19
19
  if same_args_as_config? || !config.local_available?
@@ -5,15 +5,15 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Finalize < BaseCommand
8
- def self.command
9
- 'finalize asana[:<project-gid>/<task-gid>]'
8
+ def self.usage
9
+ 'abt finalize asana[:<project-gid>/<task-gid>]'
10
10
  end
11
11
 
12
12
  def self.description
13
13
  'Move current/specified task to section (column) for finalized tasks'
14
14
  end
15
15
 
16
- def call
16
+ def perform
17
17
  unless config.local_available?
18
18
  cli.abort 'This is a no-op for tasks outside the current project'
19
19
  end
@@ -5,15 +5,15 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class HarvestTimeEntryData < BaseCommand
8
- def self.command
9
- 'harvest-time-entry-data asana[:<project-gid>/<task-gid>]'
8
+ def self.usage
9
+ 'abt harvest-time-entry-data asana[:<project-gid>/<task-gid>]'
10
10
  end
11
11
 
12
12
  def self.description
13
13
  'Print Harvest time entry data for Asana task as json. Used by harvest start script.'
14
14
  end
15
15
 
16
- def call
16
+ def perform
17
17
  require_task!
18
18
  ensure_current_is_valid!
19
19
 
@@ -5,8 +5,8 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Init < BaseCommand
8
- def self.command
9
- 'init asana'
8
+ def self.usage
9
+ 'abt init asana'
10
10
  end
11
11
 
12
12
  def self.description
@@ -18,7 +18,7 @@ module Abt
18
18
  @cli = cli
19
19
  end
20
20
 
21
- def call
21
+ def perform
22
22
  cli.abort 'Must be run inside a git repository' unless config.local_available?
23
23
 
24
24
  projects # Load projects up front to make it obvious that searches are instant
@@ -35,14 +35,14 @@ module Abt
35
35
  cli.warn 'Select a project'
36
36
 
37
37
  loop do
38
- matches = matches_for_string cli.prompt('Enter search')
38
+ matches = matches_for_string cli.prompt.text('Enter search')
39
39
  if matches.empty?
40
40
  cli.warn 'No matches'
41
41
  next
42
42
  end
43
43
 
44
44
  cli.warn 'Showing the 10 first matches' if matches.size > 10
45
- choice = cli.prompt_choice 'Select a project', matches[0...10], true
45
+ choice = cli.prompt.choice 'Select a project', matches[0...10], true
46
46
  break choice unless choice.nil?
47
47
  end
48
48
  end
@@ -5,15 +5,21 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Pick < BaseCommand
8
- def self.command
9
- 'pick asana[:<project-gid>]'
8
+ def self.usage
9
+ 'abt pick asana[:<project-gid>]'
10
10
  end
11
11
 
12
12
  def self.description
13
13
  'Pick task for current git repository'
14
14
  end
15
15
 
16
- def call
16
+ def self.flags
17
+ [
18
+ ['-d', '--dry-run', 'Keep existing configuration']
19
+ ]
20
+ end
21
+
22
+ def perform
17
23
  cli.abort 'Must be run inside a git repository' unless config.local_available?
18
24
  require_project!
19
25
 
@@ -21,10 +27,12 @@ module Abt
21
27
 
22
28
  task = select_task
23
29
 
24
- config.project_gid = project_gid # We might have gotten the project ID as an argument
25
- config.task_gid = task['gid']
26
-
27
30
  print_task(project, task)
31
+
32
+ return if flags[:"dry-run"]
33
+
34
+ config.project_gid = project_gid # We might have gotten the project ID from a path
35
+ config.task_gid = task['gid']
28
36
  end
29
37
 
30
38
  private
@@ -35,7 +43,7 @@ module Abt
35
43
 
36
44
  def select_task
37
45
  loop do
38
- section = cli.prompt_choice 'Which section?', sections
46
+ section = cli.prompt.choice 'Which section?', sections
39
47
  cli.warn 'Fetching tasks...'
40
48
  tasks = tasks_in_section(section)
41
49
 
@@ -44,7 +52,7 @@ module Abt
44
52
  next
45
53
  end
46
54
 
47
- task = cli.prompt_choice 'Select a task', tasks, true
55
+ task = cli.prompt.choice 'Select a task', tasks, true
48
56
  return task if task
49
57
  end
50
58
  end
@@ -5,15 +5,15 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Projects < BaseCommand
8
- def self.command
9
- 'projects asana'
8
+ def self.usage
9
+ 'abt projects asana'
10
10
  end
11
11
 
12
12
  def self.description
13
13
  'List all available projects - useful for piping into grep etc.'
14
14
  end
15
15
 
16
- def call
16
+ def perform
17
17
  projects.map do |project|
18
18
  print_project(project)
19
19
  end
@@ -5,21 +5,21 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Share < BaseCommand
8
- def self.command
9
- 'share asana[:<project-gid>[/<task-gid>]]'
8
+ def self.usage
9
+ 'abt share asana[:<project-gid>[/<task-gid>]]'
10
10
  end
11
11
 
12
12
  def self.description
13
13
  'Print project/task config string'
14
14
  end
15
15
 
16
- def call
16
+ def perform
17
17
  require_project!
18
18
 
19
19
  if task_gid.nil?
20
- cli.print_provider_command('asana', project_gid)
20
+ cli.print_ari('asana', project_gid)
21
21
  else
22
- cli.print_provider_command('asana', "#{project_gid}/#{task_gid}")
22
+ cli.print_ari('asana', "#{project_gid}/#{task_gid}")
23
23
  end
24
24
  end
25
25
  end
@@ -5,15 +5,21 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Start < BaseCommand
8
- def self.command
9
- 'start asana[:<project-gid>/<task-gid>]'
8
+ def self.usage
9
+ 'abt start asana[:<project-gid>/<task-gid>]'
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Set current task and move it to a section (column) of your choice'
13
+ 'Move current or specified task to WIP section (column) and assign it to you'
14
14
  end
15
15
 
16
- def call
16
+ def self.flags
17
+ [
18
+ ['-s', '--set', 'Set specified task as current']
19
+ ]
20
+ end
21
+
22
+ def perform
17
23
  require_task!
18
24
 
19
25
  maybe_override_current_task
@@ -25,12 +31,12 @@ module Abt
25
31
  private
26
32
 
27
33
  def maybe_override_current_task
28
- return if arg_str.nil?
34
+ return unless flags[:set]
35
+ return if path.nil?
29
36
  return if same_args_as_config?
30
37
  return unless config.local_available?
31
38
 
32
- should_override = cli.prompt_boolean 'Set selected task as current?'
33
- Current.new(arg_str: arg_str, cli: cli).call if should_override
39
+ Current.new(path: path, cli: cli).call
34
40
  end
35
41
 
36
42
  def update_assignee_if_needed
@@ -41,7 +47,7 @@ module Abt
41
47
  update_assignee
42
48
  elsif current_assignee['gid'] == current_user['gid']
43
49
  cli.warn 'You are already assigned to this task'
44
- elsif cli.prompt_boolean "Task is assigned to: #{current_assignee['name']}, take over?"
50
+ elsif cli.prompt.boolean "Task is assigned to: #{current_assignee['name']}, take over?"
45
51
  cli.warn "Reassigning task to user: #{current_user['name']}"
46
52
  update_assignee
47
53
  end
@@ -5,15 +5,15 @@ module Abt
5
5
  module Asana
6
6
  module Commands
7
7
  class Tasks < BaseCommand
8
- def self.command
9
- 'tasks asana'
8
+ def self.usage
9
+ 'abt tasks asana'
10
10
  end
11
11
 
12
12
  def self.description
13
13
  'List available tasks on project - useful for piping into grep etc.'
14
14
  end
15
15
 
16
- def call
16
+ def perform
17
17
  require_project!
18
18
 
19
19
  tasks.each do |task|
@@ -49,7 +49,7 @@ module Abt
49
49
  def project_gid=(value)
50
50
  return if project_gid == value
51
51
 
52
- clear_local
52
+ clear_local(verbose: false)
53
53
  git['projectGid'] = value unless value.nil?
54
54
  end
55
55
 
@@ -57,26 +57,18 @@ module Abt
57
57
  git['taskGid'] = value
58
58
  end
59
59
 
60
- def clear_local
61
- cli.abort 'No local configuration was found' unless local_available?
62
-
63
- git['projectGid'] = nil
64
- git['taskGid'] = nil
65
- git['wipSectionGid'] = nil
66
- git['finalizedSectionGid'] = nil
60
+ def clear_local(verbose: true)
61
+ git.clear(output: verbose ? cli.err_output : nil)
67
62
  end
68
63
 
69
- def clear_global
70
- git.global.keys.each do |key|
71
- cli.puts 'Deleting configuration: ' + key
72
- git.global[key] = nil
73
- end
64
+ def clear_global(verbose: true)
65
+ git.global.clear(output: verbose ? cli.err_output : nil)
74
66
  end
75
67
 
76
68
  def access_token
77
69
  return git.global['accessToken'] unless git.global['accessToken'].nil?
78
70
 
79
- git.global['accessToken'] = cli.prompt([
71
+ git.global['accessToken'] = cli.prompt.text([
80
72
  'Please provide your personal access token for Asana.',
81
73
  'If you don\'t have one, create one here: https://app.asana.com/0/developer-console',
82
74
  '',
@@ -103,7 +95,7 @@ module Abt
103
95
  def prompt_section(message)
104
96
  cli.warn 'Fetching sections...'
105
97
  sections = api.get_paged("projects/#{project_gid}/sections")
106
- cli.prompt_choice(message, sections)
98
+ cli.prompt.choice(message, sections)
107
99
  end
108
100
 
109
101
  def prompt_workspace
@@ -115,7 +107,7 @@ module Abt
115
107
  workspace = workspaces.first
116
108
  cli.warn "Selected Asana workspace #{workspace['name']}"
117
109
  else
118
- workspace = cli.prompt_choice('Select Asana workspace', workspaces)
110
+ workspace = cli.prompt.choice('Select Asana workspace', workspaces)
119
111
  end
120
112
 
121
113
  git.global['workspaceGid'] = workspace['gid']
@@ -3,19 +3,18 @@
3
3
  module Abt
4
4
  module Providers
5
5
  module Devops
6
- class BaseCommand
7
- attr_reader :arg_str, :organization_name, :project_name, :board_id, :work_item_id, :cli, :config
6
+ class BaseCommand < Abt::Cli::BaseCommand
7
+ attr_reader :organization_name, :project_name, :board_id, :work_item_id, :config
8
8
 
9
- def initialize(arg_str:, cli:)
10
- @arg_str = arg_str
9
+ def initialize(path:, cli:, **)
10
+ super
11
11
 
12
12
  @config = Configuration.new(cli: cli)
13
- @cli = cli
14
13
 
15
- if arg_str.nil?
16
- use_current_args
14
+ if path.nil?
15
+ use_current_path
17
16
  else
18
- use_arg_str(arg_str)
17
+ use_path(path)
19
18
  end
20
19
  end
21
20
 
@@ -55,28 +54,28 @@ module Abt
55
54
  end
56
55
 
57
56
  def print_board(organization_name, project_name, board)
58
- arg_str = "#{organization_name}/#{project_name}/#{board['id']}"
57
+ path = "#{organization_name}/#{project_name}/#{board['id']}"
59
58
 
60
- cli.print_provider_command('devops', arg_str, board['name'])
59
+ cli.print_ari('devops', path, board['name'])
61
60
  # cli.warn board['url'] if board.key?('url') && cli.output.isatty # TODO: Web URL
62
61
  end
63
62
 
64
63
  def print_work_item(organization, project, board, work_item)
65
- arg_str = "#{organization}/#{project}/#{board['id']}/#{work_item['id']}"
64
+ path = "#{organization}/#{project}/#{board['id']}/#{work_item['id']}"
66
65
 
67
- cli.print_provider_command('devops', arg_str, work_item['name'])
66
+ cli.print_ari('devops', path, work_item['name'])
68
67
  cli.warn work_item['url'] if work_item.key?('url') && cli.output.isatty
69
68
  end
70
69
 
71
- def use_current_args
70
+ def use_current_path
72
71
  @organization_name = config.organization_name
73
72
  @project_name = config.project_name
74
73
  @board_id = config.board_id
75
74
  @work_item_id = config.work_item_id
76
75
  end
77
76
 
78
- def use_arg_str(arg_str)
79
- args = arg_str.to_s.split('/')
77
+ def use_path(path)
78
+ args = path.to_s.split('/')
80
79
 
81
80
  if args.length < 3
82
81
  cli.abort 'Argument format is <organization>/<project>/<board-id>[/<work-item-id>]'
@@ -5,16 +5,18 @@ module Abt
5
5
  module Devops
6
6
  module Commands
7
7
  class Boards < BaseCommand
8
- def self.command
9
- 'boards devops'
8
+ def self.usage
9
+ 'abt boards devops'
10
10
  end
11
11
 
12
12
  def self.description
13
13
  'List all boards - useful for piping into grep etc'
14
14
  end
15
15
 
16
- def call
17
- cli.abort 'No organization selected. Did you initialize DevOps?' if organization_name.nil?
16
+ def perform
17
+ if organization_name.nil?
18
+ cli.abort 'No organization selected. Did you initialize DevOps?'
19
+ end
18
20
  cli.abort 'No project selected. Did you initialize DevOps?' if project_name.nil?
19
21
 
20
22
  boards.map do |board|