abt-cli 0.0.18 → 0.0.23
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/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
|