abt-cli 0.0.18 → 0.0.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/abt +3 -3
- data/lib/abt.rb +6 -6
- data/lib/abt/ari.rb +20 -0
- data/lib/abt/ari_list.rb +13 -0
- data/lib/abt/base_command.rb +63 -0
- data/lib/abt/cli.rb +51 -52
- data/lib/abt/cli/arguments_parser.rb +7 -26
- data/lib/abt/cli/global_commands.rb +23 -0
- data/lib/abt/cli/global_commands/commands.rb +23 -0
- data/lib/abt/cli/global_commands/examples.rb +23 -0
- data/lib/abt/cli/global_commands/help.rb +23 -0
- data/lib/abt/cli/global_commands/readme.rb +23 -0
- data/lib/abt/cli/global_commands/share.rb +36 -0
- data/lib/abt/cli/global_commands/version.rb +23 -0
- data/lib/abt/cli/prompt.rb +64 -51
- data/lib/abt/docs.rb +48 -25
- data/lib/abt/docs/cli.rb +3 -3
- data/lib/abt/docs/markdown.rb +11 -8
- data/lib/abt/git_config.rb +21 -39
- data/lib/abt/helpers.rb +26 -8
- data/lib/abt/providers/asana/api.rb +9 -9
- data/lib/abt/providers/asana/base_command.rb +20 -38
- data/lib/abt/providers/asana/commands/add.rb +18 -15
- data/lib/abt/providers/asana/commands/branch_name.rb +13 -8
- data/lib/abt/providers/asana/commands/clear.rb +8 -7
- data/lib/abt/providers/asana/commands/current.rb +22 -38
- data/lib/abt/providers/asana/commands/finalize.rb +17 -18
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +20 -13
- data/lib/abt/providers/asana/commands/init.rb +8 -41
- data/lib/abt/providers/asana/commands/pick.rb +27 -26
- data/lib/abt/providers/asana/commands/projects.rb +5 -5
- data/lib/abt/providers/asana/commands/share.rb +6 -8
- data/lib/abt/providers/asana/commands/start.rb +33 -24
- data/lib/abt/providers/asana/commands/tasks.rb +6 -5
- data/lib/abt/providers/asana/configuration.rb +46 -44
- data/lib/abt/providers/asana/path.rb +36 -0
- data/lib/abt/providers/devops/api.rb +23 -11
- data/lib/abt/providers/devops/base_command.rb +22 -43
- data/lib/abt/providers/devops/commands/boards.rb +5 -7
- data/lib/abt/providers/devops/commands/branch_name.rb +14 -10
- data/lib/abt/providers/devops/commands/clear.rb +8 -7
- data/lib/abt/providers/devops/commands/current.rb +24 -49
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +26 -16
- data/lib/abt/providers/devops/commands/init.rb +33 -26
- data/lib/abt/providers/devops/commands/pick.rb +23 -24
- data/lib/abt/providers/devops/commands/share.rb +7 -6
- data/lib/abt/providers/devops/commands/{work-items.rb → work_items.rb} +3 -3
- data/lib/abt/providers/devops/configuration.rb +27 -56
- data/lib/abt/providers/devops/path.rb +51 -0
- data/lib/abt/providers/git/commands/branch.rb +25 -19
- data/lib/abt/providers/harvest/api.rb +8 -8
- data/lib/abt/providers/harvest/base_command.rb +20 -36
- data/lib/abt/providers/harvest/commands/clear.rb +8 -7
- data/lib/abt/providers/harvest/commands/current.rb +27 -35
- data/lib/abt/providers/harvest/commands/init.rb +10 -40
- data/lib/abt/providers/harvest/commands/pick.rb +15 -12
- data/lib/abt/providers/harvest/commands/projects.rb +5 -5
- data/lib/abt/providers/harvest/commands/share.rb +6 -8
- data/lib/abt/providers/harvest/commands/start.rb +5 -3
- data/lib/abt/providers/harvest/commands/stop.rb +13 -13
- data/lib/abt/providers/harvest/commands/tasks.rb +9 -6
- data/lib/abt/providers/harvest/commands/track.rb +60 -38
- data/lib/abt/providers/harvest/configuration.rb +28 -37
- data/lib/abt/providers/harvest/path.rb +36 -0
- data/lib/abt/version.rb +1 -1
- metadata +18 -6
- data/lib/abt/cli/base_command.rb +0 -61
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
class Cli
|
5
|
+
module GlobalCommands
|
6
|
+
class Help < Abt::BaseCommand
|
7
|
+
def self.usage
|
8
|
+
"abt help"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
"Print abt usage text"
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :cli
|
16
|
+
|
17
|
+
def perform
|
18
|
+
puts(Abt::Docs::Cli.help)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
class Cli
|
5
|
+
module GlobalCommands
|
6
|
+
class Readme < Abt::BaseCommand
|
7
|
+
def self.usage
|
8
|
+
"abt readme"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
"Print markdown readme"
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :cli
|
16
|
+
|
17
|
+
def perform
|
18
|
+
puts(Abt::Docs::Markdown.readme)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
class Cli
|
5
|
+
module GlobalCommands
|
6
|
+
class Share < Abt::BaseCommand
|
7
|
+
def self.usage
|
8
|
+
"abt share"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
"Prints all project configuration as a single line of ARIs"
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :cli
|
16
|
+
|
17
|
+
def perform
|
18
|
+
warn("Printing project configuration")
|
19
|
+
puts share_string
|
20
|
+
end
|
21
|
+
|
22
|
+
def share_string
|
23
|
+
@share_string ||= begin
|
24
|
+
aris = Abt.schemes.join(" ")
|
25
|
+
|
26
|
+
input = StringIO.new(aris)
|
27
|
+
output = StringIO.new
|
28
|
+
Abt::Cli.new(argv: ["share"], output: output, input: input).perform
|
29
|
+
|
30
|
+
output.string.strip.gsub(/\s+/, " ")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
class Cli
|
5
|
+
module GlobalCommands
|
6
|
+
class Version < Abt::BaseCommand
|
7
|
+
def self.usage
|
8
|
+
"abt version"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
"Print abt version"
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :cli
|
16
|
+
|
17
|
+
def perform
|
18
|
+
puts(Abt::VERSION)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/abt/cli/prompt.rb
CHANGED
@@ -10,38 +10,44 @@ module Abt
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def text(question)
|
13
|
-
output.print
|
14
|
-
read_user_input
|
13
|
+
output.print("#{question.strip}: ")
|
14
|
+
Abt::Helpers.read_user_input
|
15
15
|
end
|
16
16
|
|
17
17
|
def boolean(text)
|
18
18
|
output.puts text
|
19
19
|
|
20
20
|
loop do
|
21
|
-
output.print
|
22
|
-
|
23
|
-
case read_user_input
|
24
|
-
when
|
25
|
-
when
|
26
|
-
else
|
27
|
-
output.puts 'Invalid choice'
|
28
|
-
next
|
29
|
-
end
|
21
|
+
output.print("(y / n): ")
|
22
|
+
|
23
|
+
case Abt::Helpers.read_user_input
|
24
|
+
when "y", "Y" then return true
|
25
|
+
when "n", "N" then return false
|
26
|
+
else output.puts "Invalid choice" end
|
30
27
|
end
|
31
28
|
end
|
32
29
|
|
33
|
-
def choice(text, options, nil_option
|
34
|
-
output.puts "#{text}:"
|
30
|
+
def choice(text, options, nil_option: false)
|
31
|
+
output.puts "#{text.strip}:"
|
35
32
|
|
36
33
|
if options.length.zero?
|
37
|
-
raise Abort,
|
34
|
+
raise Abort, "No available options" unless nil_option
|
38
35
|
|
39
|
-
output.puts
|
36
|
+
output.puts "No available options"
|
40
37
|
return nil
|
41
38
|
end
|
42
39
|
|
43
40
|
print_options(options)
|
44
|
-
|
41
|
+
select_option(options, nil_option)
|
42
|
+
end
|
43
|
+
|
44
|
+
def search(text, options)
|
45
|
+
output.puts text
|
46
|
+
|
47
|
+
loop do
|
48
|
+
choice = get_search_result(options)
|
49
|
+
break choice unless choice.nil?
|
50
|
+
end
|
45
51
|
end
|
46
52
|
|
47
53
|
private
|
@@ -52,73 +58,80 @@ module Abt
|
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
55
|
-
def
|
56
|
-
|
57
|
-
number = read_option_number(options.length, nil_option)
|
58
|
-
if number.nil?
|
59
|
-
return nil if nil_option
|
61
|
+
def select_option(options, nil_option)
|
62
|
+
number = prompt_valid_option_number(options, nil_option)
|
60
63
|
|
61
|
-
|
62
|
-
end
|
64
|
+
return nil if number.nil?
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
return option
|
68
|
-
end
|
66
|
+
option = options[number - 1]
|
67
|
+
output.puts "Selected: (#{number}) #{option['name']}"
|
68
|
+
option
|
69
69
|
end
|
70
70
|
|
71
|
-
def
|
72
|
-
output.print
|
73
|
-
|
74
|
-
output.print nil_option_string(nil_option)
|
75
|
-
output.print '): '
|
76
|
-
|
77
|
-
input = read_user_input
|
71
|
+
def prompt_valid_option_number(options, nil_option)
|
72
|
+
output.print(options_info(options, nil_option))
|
73
|
+
input = Abt::Helpers.read_user_input
|
78
74
|
|
79
75
|
return nil if nil_option && input == nil_option_character(nil_option)
|
80
76
|
|
81
77
|
option_number = input.to_i
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
78
|
+
return option_number if (1..options.length).cover?(option_number)
|
79
|
+
|
80
|
+
output.puts "Invalid selection"
|
81
|
+
|
82
|
+
# Prompt again if the selection was invalid
|
83
|
+
prompt_valid_option_number(options, nil_option)
|
84
|
+
end
|
86
85
|
|
87
|
-
|
86
|
+
def options_info(options, nil_option)
|
87
|
+
str = "("
|
88
|
+
str += options.length > 1 ? "1-#{options.length}" : "1"
|
89
|
+
str += nil_option_string(nil_option)
|
90
|
+
str += "): "
|
91
|
+
str
|
88
92
|
end
|
89
93
|
|
90
94
|
def nil_option_string(nil_option)
|
91
|
-
return
|
95
|
+
return "" unless nil_option
|
92
96
|
|
93
97
|
", #{nil_option_character(nil_option)}: #{nil_option_description(nil_option)}"
|
94
98
|
end
|
95
99
|
|
96
100
|
def nil_option_character(nil_option)
|
97
|
-
return
|
101
|
+
return "q" if nil_option == true
|
98
102
|
|
99
103
|
nil_option[0]
|
100
104
|
end
|
101
105
|
|
102
106
|
def nil_option_description(nil_option)
|
103
|
-
return
|
107
|
+
return "back" if nil_option == true
|
104
108
|
return nil_option if nil_option.is_a?(String)
|
105
109
|
|
106
110
|
nil_option[1]
|
107
111
|
end
|
108
112
|
|
109
|
-
def
|
110
|
-
|
113
|
+
def get_search_result(options)
|
114
|
+
matches = matches_for_string(text("Enter search"), options)
|
115
|
+
if matches.empty?
|
116
|
+
output.puts("No matches")
|
117
|
+
return
|
118
|
+
end
|
119
|
+
|
120
|
+
output.puts("Showing the 10 first matches") if matches.size > 10
|
121
|
+
choice("Select a match", matches[0...10], nil_option: true)
|
111
122
|
end
|
112
123
|
|
113
|
-
def
|
114
|
-
|
115
|
-
candidates = ['/dev/tty', 'CON:'] # Unix: '/dev/tty', Windows: 'CON:'
|
116
|
-
selected = candidates.find { |candidate| File.exist?(candidate) }
|
117
|
-
raise Abort, 'Unable to prompt for user input' if selected.nil?
|
124
|
+
def matches_for_string(string, options)
|
125
|
+
search_string = sanitize_string(string)
|
118
126
|
|
119
|
-
|
127
|
+
options.select do |option|
|
128
|
+
sanitize_string(option["name"]).include?(search_string)
|
120
129
|
end
|
121
130
|
end
|
131
|
+
|
132
|
+
def sanitize_string(string)
|
133
|
+
string.downcase.gsub(/[^\w]/, "")
|
134
|
+
end
|
122
135
|
end
|
123
136
|
end
|
124
137
|
end
|
data/lib/abt/docs.rb
CHANGED
@@ -9,48 +9,71 @@ module Abt
|
|
9
9
|
class << self
|
10
10
|
def basic_examples
|
11
11
|
{
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
"Getting started:" => {
|
13
|
+
"abt init asana harvest" => "Setup asana and harvest project for local git repo",
|
14
|
+
"abt pick harvest" => "Pick harvest task. This will likely stay the same throughout the project",
|
15
|
+
"abt pick asana | abt start harvest" => "Pick asana task and start tracking time",
|
16
|
+
"abt stop harvest" => "Stop time tracker",
|
17
|
+
"abt start asana harvest" => "Continue working, e.g., after a break",
|
18
|
+
"abt finalize asana" => "Finalize the selected asana task"
|
19
19
|
}
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
23
|
-
def extended_examples
|
23
|
+
def extended_examples # rubocop:disable Metrics/MethodLength
|
24
24
|
{
|
25
|
-
|
26
|
-
|
27
|
-
'abt pick harvest -d | abt track harvest -c "Name of meeting"' =>
|
25
|
+
"Tracking meetings (without switching current task setting):" => {
|
26
|
+
"abt pick asana -d | abt track harvest" => "Track on asana meeting task",
|
27
|
+
'abt pick harvest -d | abt track harvest -c "Name of meeting"' => "Track on separate harvest-task"
|
28
28
|
},
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
"Many commands output ARIs that can be piped into other commands:" => {
|
30
|
+
"abt tasks asana | grep -i <name of task>" => nil,
|
31
|
+
"abt tasks asana | grep -i <name of task> | abt start" => nil
|
32
32
|
},
|
33
|
-
|
34
|
-
'abt share asana harvest | tr "\n" " "' =>
|
35
|
-
'abt share asana harvest | tr "\n" " " | pbcopy' =>
|
36
|
-
|
33
|
+
"Sharing ARIs:" => {
|
34
|
+
'abt share asana harvest | tr "\n" " "' => "Print current asana and harvest ARIs on a single line",
|
35
|
+
'abt share asana harvest | tr "\n" " " | pbcopy' => "Copy ARIs to clipboard (mac only)",
|
36
|
+
"abt start <ARIs from coworker>" => "Work on a task your coworker shared with you",
|
37
|
+
"abt current <ARIs from coworker> | abt start" => "Set task as current, then start it"
|
37
38
|
},
|
38
|
-
|
39
|
-
'abt start harvest -c "comment"' =>
|
40
|
-
'abt start harvest -c "comment" -- asana' =>
|
41
|
-
|
39
|
+
"Flags:" => {
|
40
|
+
'abt start harvest -c "comment"' => "Add command flags after ARIs",
|
41
|
+
'abt start harvest -c "comment" -- asana' =>
|
42
|
+
"Use -- to end a list of flags, so that it can be followed by another ARI",
|
43
|
+
'abt pick harvest | abt start -c "comment"' =>
|
44
|
+
"Flags placed directly after a command applies to the piped in ARI"
|
42
45
|
}
|
43
46
|
}
|
44
47
|
end
|
45
48
|
|
46
49
|
def providers
|
47
|
-
@providers ||=
|
48
|
-
|
50
|
+
@providers ||= begin
|
51
|
+
providers = {}
|
52
|
+
|
53
|
+
providers["Global"] = global_command_definitions
|
54
|
+
|
55
|
+
Abt.schemes.sort.each_with_object(providers) do |scheme, definition|
|
56
|
+
definition[scheme] = command_definitions(scheme)
|
57
|
+
end
|
58
|
+
|
59
|
+
providers
|
49
60
|
end
|
50
61
|
end
|
51
62
|
|
52
63
|
private
|
53
64
|
|
65
|
+
def global_command_definitions
|
66
|
+
global_command_names = Abt::Cli::GlobalCommands.command_names
|
67
|
+
global_command_names.each_with_object({}) do |name, definition|
|
68
|
+
command_class = Abt::Cli::GlobalCommands.command_class(name)
|
69
|
+
full_name = "abt #{name}"
|
70
|
+
|
71
|
+
if command_class.respond_to?(:usage) && command_class.respond_to?(:description)
|
72
|
+
definition[full_name] = [command_class.usage.strip, command_class.description.strip]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
54
77
|
def command_definitions(scheme)
|
55
78
|
provider = Abt.scheme_provider(scheme)
|
56
79
|
provider.command_names.each_with_object({}) do |name, definition|
|
@@ -58,7 +81,7 @@ module Abt
|
|
58
81
|
full_name = "abt #{name} #{scheme}"
|
59
82
|
|
60
83
|
if command_class.respond_to?(:usage) && command_class.respond_to?(:description)
|
61
|
-
definition[full_name] = [command_class.usage, command_class.description]
|
84
|
+
definition[full_name] = [command_class.usage.strip, command_class.description.strip]
|
62
85
|
end
|
63
86
|
end
|
64
87
|
end
|
data/lib/abt/docs/cli.rb
CHANGED
@@ -45,14 +45,14 @@ module Abt
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def usage_line
|
48
|
-
|
48
|
+
"abt <command> [<ARI>] [<options> --] [<ARI>] ..."
|
49
49
|
end
|
50
50
|
|
51
51
|
def formatted_examples(example_groups)
|
52
52
|
lines = []
|
53
53
|
|
54
54
|
example_groups.each_with_index do |(title, examples), index|
|
55
|
-
lines <<
|
55
|
+
lines << "" unless index.zero?
|
56
56
|
lines << title
|
57
57
|
|
58
58
|
max_length = examples.keys.map(&:length).max
|
@@ -68,7 +68,7 @@ module Abt
|
|
68
68
|
lines = []
|
69
69
|
|
70
70
|
Docs.providers.each_with_index do |(scheme, commands_definition), index|
|
71
|
-
lines <<
|
71
|
+
lines << "" unless index.zero?
|
72
72
|
lines << "#{inflector.humanize(scheme)}:"
|
73
73
|
|
74
74
|
max_length = commands_definition.keys.map(&:length).max
|
data/lib/abt/docs/markdown.rb
CHANGED
@@ -15,7 +15,7 @@ module Abt
|
|
15
15
|
|
16
16
|
## How does abt work?
|
17
17
|
|
18
|
-
Abt is a hybrid
|
18
|
+
Abt is a hybrid of having small scripts each doing one thing:
|
19
19
|
- `start-asana --project-gid xxxx --task-gid yyyy`
|
20
20
|
- `start-harvest --project-id aaaa --task-id bbbb`
|
21
21
|
|
@@ -50,13 +50,12 @@ module Abt
|
|
50
50
|
def example_commands
|
51
51
|
lines = []
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
lines << '' unless index.zero?
|
53
|
+
complete_examples.each_with_index do |(title, commands), index|
|
54
|
+
lines << "" unless index.zero?
|
56
55
|
lines << title
|
57
56
|
|
58
57
|
commands.each do |(command, description)|
|
59
|
-
formatted_description = description.nil? ?
|
58
|
+
formatted_description = description.nil? ? "" : ": #{description}"
|
60
59
|
lines << "- `#{command}`#{formatted_description}"
|
61
60
|
end
|
62
61
|
end
|
@@ -68,10 +67,10 @@ module Abt
|
|
68
67
|
lines = []
|
69
68
|
|
70
69
|
Docs.providers.each_with_index do |(scheme, commands), index|
|
71
|
-
lines <<
|
70
|
+
lines << "" unless index.zero?
|
72
71
|
lines << "### #{inflector.humanize(scheme)}"
|
73
|
-
lines <<
|
74
|
-
lines <<
|
72
|
+
lines << "| Command | Description |"
|
73
|
+
lines << "| :------ | :---------- |"
|
75
74
|
|
76
75
|
max_length = commands.values.map(&:first).map(&:length).max
|
77
76
|
|
@@ -84,6 +83,10 @@ module Abt
|
|
84
83
|
lines.join("\n")
|
85
84
|
end
|
86
85
|
|
86
|
+
def complete_examples
|
87
|
+
Docs.basic_examples.merge(Docs.extended_examples)
|
88
|
+
end
|
89
|
+
|
87
90
|
def inflector
|
88
91
|
Dry::Inflector.new
|
89
92
|
end
|