abt-cli 0.0.14 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6355433a65cf9e49738ab971a8886e657cefff8d3a8322e44d140a18fa7a6be
4
- data.tar.gz: f71c9909c3c8063f24365d80db1ae91223876be98cda4e6681a5f37e017af2ca
3
+ metadata.gz: f176b71200cc6fcc13ab56507e4d48234785e2307ab350afca7be13193ef2a5e
4
+ data.tar.gz: '093172885ed64949153cd73457748f446e012887f78d092f2483164f005fbfd0'
5
5
  SHA512:
6
- metadata.gz: 809f27b09f6bbf41eefca395f95621b9bd7c48cb3f78cd5e7bf62c48a87895669fa74aebad4ea31fdee6ed484bf8944bb6216059b93066db2cfe118bc65c6e34
7
- data.tar.gz: '032284e3f7e465618c2c039dbafa84855548bcee41d56725606fd2b48e58c008ba89010be8c25e78f714f3c1e16cc8a30f3e8021a0a5a531617dc0d54393ce1e'
6
+ metadata.gz: 6350132f05617d7e7222f0b7e07255091119ad0edac59277795c891668a6de4fb3ea9e376cdc93ab2672e130b5222d6ff310d80da8979dfaf060ff0e2479e447
7
+ data.tar.gz: 14eea7d7cb849f39d1aef4643df214541178d4f8702cecc962ffcff099b0c9143be68b8562aac0fdbc75b2bb2424872bd34661136104ac233df2097031d49394
data/lib/abt/cli.rb CHANGED
@@ -8,10 +8,7 @@ module Abt
8
8
  class Cli
9
9
  class AbortError < StandardError; end
10
10
 
11
- include Dialogs
12
- include Io
13
-
14
- attr_reader :command, :args, :input, :output, :err_output
11
+ attr_reader :command, :args, :input, :output, :err_output, :prompt
15
12
 
16
13
  def initialize(argv: ARGV, input: STDIN, output: STDOUT, err_output: STDERR)
17
14
  (@command, *@args) = argv
@@ -19,12 +16,13 @@ module Abt
19
16
  @input = input
20
17
  @output = output
21
18
  @err_output = err_output
19
+ @prompt = Abt::Cli::Prompt.new(output: err_output)
22
20
 
23
21
  @args += args_from_input unless input.isatty # Add piped arguments
24
22
  end
25
23
 
26
24
  def perform
27
- handle_global_commands!
25
+ return if handle_global_commands!
28
26
 
29
27
  abort('No provider arguments') if args.empty?
30
28
 
@@ -37,6 +35,22 @@ module Abt
37
35
  output.puts command
38
36
  end
39
37
 
38
+ def warn(*args)
39
+ err_output.puts(*args)
40
+ end
41
+
42
+ def puts(*args)
43
+ output.puts(*args)
44
+ end
45
+
46
+ def print(*args)
47
+ output.print(*args)
48
+ end
49
+
50
+ def abort(message)
51
+ raise AbortError, message
52
+ end
53
+
40
54
  private
41
55
 
42
56
  def handle_global_commands! # rubocop:disable Metrics/MethodLength
@@ -44,21 +58,23 @@ module Abt
44
58
  when nil
45
59
  warn("No command specified\n\n")
46
60
  puts(Abt::Docs::Cli.content)
47
- exit
61
+ true
48
62
  when '--help', '-h', 'help', 'commands'
49
63
  puts(Abt::Docs::Cli.content)
50
- exit
64
+ true
51
65
  when 'help-md'
52
66
  puts(Abt::Docs::Markdown.content)
53
- exit
67
+ true
54
68
  when '--version', '-v', 'version'
55
69
  puts(Abt::VERSION)
56
- exit
70
+ true
71
+ else
72
+ false
57
73
  end
58
74
  end
59
75
 
60
76
  def args_from_input
61
- input_string = input.read
77
+ input_string = input.read.strip
62
78
 
63
79
  abort 'No input from pipe' if input_string.nil? || input_string.empty?
64
80
 
@@ -85,7 +101,7 @@ module Abt
85
101
  used_providers << provider if process_provider_command(provider, command, arg_str)
86
102
  end
87
103
 
88
- warn 'No matching providers found for command' if used_providers.empty? && output.isatty
104
+ abort 'No matching providers found for command' if used_providers.empty? && output.isatty
89
105
  end
90
106
 
91
107
  def process_provider_command(provider_name, command_name, arg_str)
@@ -2,35 +2,41 @@
2
2
 
3
3
  module Abt
4
4
  class Cli
5
- module Dialogs
6
- def prompt(question)
7
- err_output.print "#{question}: "
8
- read_user_input.strip
5
+ class Prompt
6
+ attr_reader :output
7
+
8
+ def initialize(output:)
9
+ @output = output
10
+ end
11
+
12
+ def text(question)
13
+ output.print "#{question}: "
14
+ read_user_input
9
15
  end
10
16
 
11
- def prompt_boolean(text)
12
- warn text
17
+ def boolean(text)
18
+ output.puts text
13
19
 
14
20
  loop do
15
- err_output.print '(y / n): '
21
+ output.print '(y / n): '
16
22
 
17
- case read_user_input.strip
23
+ case read_user_input
18
24
  when 'y', 'Y' then return true
19
25
  when 'n', 'N' then return false
20
26
  else
21
- warn 'Invalid choice'
27
+ output.puts 'Invalid choice'
22
28
  next
23
29
  end
24
30
  end
25
31
  end
26
32
 
27
- def prompt_choice(text, options, nil_option = false)
28
- warn "#{text}:"
33
+ def choice(text, options, nil_option = false)
34
+ output.puts "#{text}:"
29
35
 
30
36
  if options.length.zero?
31
- abort 'No available options' unless nil_option
37
+ raise AbortError, 'No available options' unless nil_option
32
38
 
33
- warn 'No available options'
39
+ output.puts 'No available options'
34
40
  return nil
35
41
  end
36
42
 
@@ -42,7 +48,7 @@ module Abt
42
48
 
43
49
  def print_options(options)
44
50
  options.each_with_index do |option, index|
45
- warn "(#{index + 1}) #{option['name']}"
51
+ output.puts "(#{index + 1}) #{option['name']}"
46
52
  end
47
53
  end
48
54
 
@@ -57,13 +63,16 @@ module Abt
57
63
 
58
64
  option = options[number - 1]
59
65
 
60
- warn "Selected: (#{number}) #{option['name']}"
66
+ output.puts "Selected: (#{number}) #{option['name']}"
61
67
  return option
62
68
  end
63
69
  end
64
70
 
65
71
  def read_option_number(options_length, nil_option)
66
- err_output.print "(1-#{options_length}#{nil_option_string(nil_option)}): "
72
+ output.print '('
73
+ output.print options_length > 1 ? "1-#{options_length}" : '1'
74
+ output.print nil_option_string(nil_option)
75
+ output.print '): '
67
76
 
68
77
  input = read_user_input
69
78
 
@@ -71,7 +80,7 @@ module Abt
71
80
 
72
81
  option_number = input.to_i
73
82
  if option_number <= 0 || option_number > options_length
74
- warn 'Invalid selection'
83
+ output.puts 'Invalid selection'
75
84
  return nil
76
85
  end
77
86
 
@@ -98,7 +107,17 @@ module Abt
98
107
  end
99
108
 
100
109
  def read_user_input
101
- open('/dev/tty', &:gets).strip
110
+ open(tty_path, &:gets).strip # rubocop:disable Security/Open
111
+ end
112
+
113
+ def tty_path
114
+ @tty_path ||= begin
115
+ candidates = ['/dev/tty', 'CON:'] # Unix: '/dev/tty', Windows: 'CON:'
116
+ selected = candidates.find { |candidate| File.exist?(candidate) }
117
+ raise AbortError, 'Unable to prompt for user input' if selected.nil?
118
+
119
+ selected
120
+ end
102
121
  end
103
122
  end
104
123
  end
@@ -45,11 +45,11 @@ module Abt
45
45
  end
46
46
 
47
47
  def name
48
- @name ||= cli.prompt 'Enter task description'
48
+ @name ||= cli.prompt.text 'Enter task description'
49
49
  end
50
50
 
51
51
  def notes
52
- @notes ||= cli.prompt 'Enter task notes'
52
+ @notes ||= cli.prompt.text 'Enter task notes'
53
53
  end
54
54
 
55
55
  def project
@@ -57,7 +57,7 @@ module Abt
57
57
  end
58
58
 
59
59
  def section
60
- @section ||= cli.prompt_choice 'Add to section?', sections, ['q', 'Don\'t add to section']
60
+ @section ||= cli.prompt.choice 'Add to section?', sections, ['q', 'Don\'t add to section']
61
61
  end
62
62
 
63
63
  def sections
@@ -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
@@ -35,7 +35,7 @@ module Abt
35
35
 
36
36
  def select_task
37
37
  loop do
38
- section = cli.prompt_choice 'Which section?', sections
38
+ section = cli.prompt.choice 'Which section?', sections
39
39
  cli.warn 'Fetching tasks...'
40
40
  tasks = tasks_in_section(section)
41
41
 
@@ -44,7 +44,7 @@ module Abt
44
44
  next
45
45
  end
46
46
 
47
- task = cli.prompt_choice 'Select a task', tasks, true
47
+ task = cli.prompt.choice 'Select a task', tasks, true
48
48
  return task if task
49
49
  end
50
50
  end
@@ -29,7 +29,7 @@ module Abt
29
29
  return if same_args_as_config?
30
30
  return unless config.local_available?
31
31
 
32
- should_override = cli.prompt_boolean 'Set selected task as current?'
32
+ should_override = cli.prompt.boolean 'Set selected task as current?'
33
33
  Current.new(arg_str: arg_str, cli: cli).call if should_override
34
34
  end
35
35
 
@@ -41,7 +41,7 @@ module Abt
41
41
  update_assignee
42
42
  elsif current_assignee['gid'] == current_user['gid']
43
43
  cli.warn 'You are already assigned to this task'
44
- elsif cli.prompt_boolean "Task is assigned to: #{current_assignee['name']}, take over?"
44
+ elsif cli.prompt.boolean "Task is assigned to: #{current_assignee['name']}, take over?"
45
45
  cli.warn "Reassigning task to user: #{current_user['name']}"
46
46
  update_assignee
47
47
  end
@@ -76,7 +76,7 @@ module Abt
76
76
  def access_token
77
77
  return git.global['accessToken'] unless git.global['accessToken'].nil?
78
78
 
79
- git.global['accessToken'] = cli.prompt([
79
+ git.global['accessToken'] = cli.prompt.text([
80
80
  'Please provide your personal access token for Asana.',
81
81
  'If you don\'t have one, create one here: https://app.asana.com/0/developer-console',
82
82
  '',
@@ -103,7 +103,7 @@ module Abt
103
103
  def prompt_section(message)
104
104
  cli.warn 'Fetching sections...'
105
105
  sections = api.get_paged("projects/#{project_gid}/sections")
106
- cli.prompt_choice(message, sections)
106
+ cli.prompt.choice(message, sections)
107
107
  end
108
108
 
109
109
  def prompt_workspace
@@ -115,7 +115,7 @@ module Abt
115
115
  workspace = workspaces.first
116
116
  cli.warn "Selected Asana workspace #{workspace['name']}"
117
117
  else
118
- workspace = cli.prompt_choice('Select Asana workspace', workspaces)
118
+ workspace = cli.prompt.choice('Select Asana workspace', workspaces)
119
119
  end
120
120
 
121
121
  git.global['workspaceGid'] = workspace['gid']
@@ -22,7 +22,7 @@ module Abt
22
22
  @organization_name = config.organization_name = organization_name_from_url
23
23
  @project_name = config.project_name = project_name_from_url
24
24
 
25
- board = cli.prompt_choice 'Select a project work board', boards
25
+ board = cli.prompt.choice 'Select a project work board', boards
26
26
 
27
27
  config.board_id = board['id']
28
28
 
@@ -52,7 +52,7 @@ module Abt
52
52
  def project_url
53
53
  @project_url ||= begin
54
54
  loop do
55
- url = cli.prompt([
55
+ url = cli.prompt.text([
56
56
  'Please provide the URL for the devops project',
57
57
  'For instance https://{organization}.visualstudio.com/{project} or https://dev.azure.com/{organization}/{project}',
58
58
  '',
@@ -37,7 +37,7 @@ module Abt
37
37
 
38
38
  def select_work_item
39
39
  loop do
40
- column = cli.prompt_choice 'Which column?', columns
40
+ column = cli.prompt.choice 'Which column?', columns
41
41
  cli.warn 'Fetching work items...'
42
42
  work_items = work_items_in_column(column)
43
43
 
@@ -46,7 +46,7 @@ module Abt
46
46
  next
47
47
  end
48
48
 
49
- work_item = cli.prompt_choice 'Select a work item', work_items, true
49
+ work_item = cli.prompt.choice 'Select a work item', work_items, true
50
50
  return work_item if work_item
51
51
  end
52
52
  end
@@ -78,7 +78,7 @@ module Abt
78
78
 
79
79
  return git.global[username_key] unless git.global[username_key].nil?
80
80
 
81
- git.global[username_key] = cli.prompt([
81
+ git.global[username_key] = cli.prompt.text([
82
82
  "Please provide your username for the DevOps organization (#{organization_name}).",
83
83
  '',
84
84
  'Enter username'
@@ -90,7 +90,7 @@ module Abt
90
90
 
91
91
  return git.global[access_token_key] unless git.global[access_token_key].nil?
92
92
 
93
- git.global[access_token_key] = cli.prompt([
93
+ git.global[access_token_key] = cli.prompt.text([
94
94
  "Please provide your personal access token for the DevOps organization (#{organization_name}).",
95
95
  'If you don\'t have one, follow the guide here: https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate',
96
96
  '',
@@ -36,7 +36,7 @@ module Abt
36
36
 
37
37
  def create_and_switch
38
38
  cli.warn "No such branch: #{branch_name}"
39
- cli.abort('Aborting') unless cli.prompt_boolean 'Create branch?'
39
+ cli.abort('Aborting') unless cli.prompt.boolean 'Create branch?'
40
40
 
41
41
  Open3.popen3("git switch -c #{branch_name}") do |_i, _o, _e, thread|
42
42
  thread.value
@@ -31,14 +31,14 @@ module Abt
31
31
  cli.warn 'Select a project'
32
32
 
33
33
  loop do
34
- matches = matches_for_string cli.prompt('Enter search')
34
+ matches = matches_for_string cli.prompt.text('Enter search')
35
35
  if matches.empty?
36
36
  warn 'No matches'
37
37
  next
38
38
  end
39
39
 
40
40
  cli.warn 'Showing the 10 first matches' if matches.size > 10
41
- choice = cli.prompt_choice 'Select a project', matches[0...10], true
41
+ choice = cli.prompt.choice 'Select a project', matches[0...10], true
42
42
  break choice['project'] unless choice.nil?
43
43
  end
44
44
  end
@@ -18,7 +18,7 @@ module Abt
18
18
  require_project!
19
19
 
20
20
  cli.warn project['name']
21
- task = cli.prompt_choice 'Select a task', tasks
21
+ task = cli.prompt.choice 'Select a task', tasks
22
22
 
23
23
  config.project_id = project_id # We might have gotten the project ID as an argument
24
24
  config.task_id = task['id']
@@ -44,7 +44,7 @@ module Abt
44
44
  return if arg_str.nil?
45
45
  return if same_args_as_config?
46
46
  return unless config.local_available?
47
- return unless cli.prompt_boolean 'Set selected task as current?'
47
+ return unless cli.prompt.boolean 'Set selected task as current?'
48
48
 
49
49
  input = StringIO.new("harvest:#{project_id}/#{task_id}")
50
50
  output = StringIO.new
@@ -41,7 +41,7 @@ module Abt
41
41
  body.merge! external_link_data
42
42
  else
43
43
  cli.warn 'No external link provided'
44
- body[:notes] ||= cli.prompt('Fill in comment (optional)')
44
+ body[:notes] ||= cli.prompt.text('Fill in comment (optional)')
45
45
  end
46
46
 
47
47
  api.post('time_entries', Oj.dump(body, mode: :json))
@@ -53,7 +53,7 @@ module Abt
53
53
  def access_token
54
54
  return git.global['accessToken'] unless git.global['accessToken'].nil?
55
55
 
56
- git.global['accessToken'] = cli.prompt([
56
+ git.global['accessToken'] = cli.prompt.text([
57
57
  'Please provide your personal access token for Harvest.',
58
58
  'If you don\'t have one, create one here: https://id.getharvest.com/developers',
59
59
  '',
@@ -64,7 +64,7 @@ module Abt
64
64
  def account_id
65
65
  return git.global['accountId'] unless git.global['accountId'].nil?
66
66
 
67
- git.global['accountId'] = cli.prompt([
67
+ git.global['accountId'] = cli.prompt.text([
68
68
  'Please provide harvest account id.',
69
69
  'This information is shown next to your generated access token',
70
70
  '',
data/lib/abt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Abt
4
- VERSION = '0.0.14'
4
+ VERSION = '0.0.15'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abt-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jesper Sørensen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-04 00:00:00.000000000 Z
11
+ date: 2021-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-inflector
@@ -76,8 +76,7 @@ extra_rdoc_files: []
76
76
  files:
77
77
  - "./lib/abt.rb"
78
78
  - "./lib/abt/cli.rb"
79
- - "./lib/abt/cli/dialogs.rb"
80
- - "./lib/abt/cli/io.rb"
79
+ - "./lib/abt/cli/prompt.rb"
81
80
  - "./lib/abt/docs.rb"
82
81
  - "./lib/abt/docs/cli.rb"
83
82
  - "./lib/abt/docs/markdown.rb"
data/lib/abt/cli/io.rb DELETED
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- class Cli
5
- module Io
6
- def warn(*args)
7
- err_output.puts(*args)
8
- end
9
-
10
- def puts(*args)
11
- output.puts(*args)
12
- end
13
-
14
- def print(*args)
15
- output.print(*args)
16
- end
17
-
18
- def abort(message)
19
- raise AbortError, message
20
- end
21
- end
22
- end
23
- end