abt-cli 0.0.14 → 0.0.15

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.
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