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 +4 -4
- data/lib/abt/cli.rb +27 -11
- data/lib/abt/cli/{dialogs.rb → prompt.rb} +37 -18
- data/lib/abt/providers/asana/commands/add.rb +3 -3
- data/lib/abt/providers/asana/commands/init.rb +2 -2
- data/lib/abt/providers/asana/commands/pick.rb +2 -2
- data/lib/abt/providers/asana/commands/start.rb +2 -2
- data/lib/abt/providers/asana/configuration.rb +3 -3
- data/lib/abt/providers/devops/commands/init.rb +2 -2
- data/lib/abt/providers/devops/commands/pick.rb +2 -2
- data/lib/abt/providers/devops/configuration.rb +2 -2
- data/lib/abt/providers/git/commands/branch.rb +1 -1
- data/lib/abt/providers/harvest/commands/init.rb +2 -2
- data/lib/abt/providers/harvest/commands/pick.rb +1 -1
- data/lib/abt/providers/harvest/commands/start.rb +1 -1
- data/lib/abt/providers/harvest/commands/track.rb +1 -1
- data/lib/abt/providers/harvest/configuration.rb +2 -2
- data/lib/abt/version.rb +1 -1
- metadata +3 -4
- data/lib/abt/cli/io.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f176b71200cc6fcc13ab56507e4d48234785e2307ab350afca7be13193ef2a5e
|
4
|
+
data.tar.gz: '093172885ed64949153cd73457748f446e012887f78d092f2483164f005fbfd0'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
61
|
+
true
|
48
62
|
when '--help', '-h', 'help', 'commands'
|
49
63
|
puts(Abt::Docs::Cli.content)
|
50
|
-
|
64
|
+
true
|
51
65
|
when 'help-md'
|
52
66
|
puts(Abt::Docs::Markdown.content)
|
53
|
-
|
67
|
+
true
|
54
68
|
when '--version', '-v', 'version'
|
55
69
|
puts(Abt::VERSION)
|
56
|
-
|
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
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
12
|
-
|
17
|
+
def boolean(text)
|
18
|
+
output.puts text
|
13
19
|
|
14
20
|
loop do
|
15
|
-
|
21
|
+
output.print '(y / n): '
|
16
22
|
|
17
|
-
case read_user_input
|
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
|
-
|
27
|
+
output.puts 'Invalid choice'
|
22
28
|
next
|
23
29
|
end
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
27
|
-
def
|
28
|
-
|
33
|
+
def choice(text, options, nil_option = false)
|
34
|
+
output.puts "#{text}:"
|
29
35
|
|
30
36
|
if options.length.zero?
|
31
|
-
|
37
|
+
raise AbortError, 'No available options' unless nil_option
|
32
38
|
|
33
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
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.
|
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-
|
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/
|
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
|