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