abt-cli 0.0.18 → 0.0.19
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/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 +6 -9
- data/lib/abt/cli/arguments_parser.rb +1 -23
- data/lib/abt/cli/prompt.rb +5 -4
- data/lib/abt/docs.rb +6 -5
- data/lib/abt/docs/markdown.rb +1 -1
- data/lib/abt/git_config.rb +20 -36
- data/lib/abt/providers/asana/base_command.rb +13 -33
- data/lib/abt/providers/asana/commands/add.rb +9 -7
- data/lib/abt/providers/asana/commands/branch_name.rb +9 -4
- data/lib/abt/providers/asana/commands/clear.rb +2 -0
- data/lib/abt/providers/asana/commands/current.rb +19 -34
- data/lib/abt/providers/asana/commands/finalize.rb +3 -3
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +9 -4
- data/lib/abt/providers/asana/commands/init.rb +6 -6
- data/lib/abt/providers/asana/commands/pick.rb +16 -11
- data/lib/abt/providers/asana/commands/projects.rb +1 -1
- data/lib/abt/providers/asana/commands/share.rb +2 -6
- data/lib/abt/providers/asana/commands/start.rb +14 -12
- data/lib/abt/providers/asana/commands/tasks.rb +4 -3
- data/lib/abt/providers/asana/configuration.rb +18 -24
- data/lib/abt/providers/asana/path.rb +36 -0
- data/lib/abt/providers/devops/api.rb +12 -0
- data/lib/abt/providers/devops/base_command.rb +13 -38
- data/lib/abt/providers/devops/commands/boards.rb +2 -2
- data/lib/abt/providers/devops/commands/branch_name.rb +7 -3
- data/lib/abt/providers/devops/commands/clear.rb +2 -0
- data/lib/abt/providers/devops/commands/current.rb +14 -38
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +9 -1
- data/lib/abt/providers/devops/commands/init.rb +15 -15
- data/lib/abt/providers/devops/commands/pick.rb +5 -12
- data/lib/abt/providers/devops/commands/share.rb +3 -4
- data/lib/abt/providers/devops/commands/work-items.rb +1 -1
- data/lib/abt/providers/devops/configuration.rb +17 -46
- data/lib/abt/providers/devops/path.rb +50 -0
- data/lib/abt/providers/git/commands/branch.rb +14 -8
- data/lib/abt/providers/harvest/base_command.rb +14 -32
- data/lib/abt/providers/harvest/commands/clear.rb +2 -0
- data/lib/abt/providers/harvest/commands/current.rb +24 -31
- data/lib/abt/providers/harvest/commands/init.rb +5 -6
- data/lib/abt/providers/harvest/commands/pick.rb +3 -4
- data/lib/abt/providers/harvest/commands/projects.rb +1 -1
- data/lib/abt/providers/harvest/commands/share.rb +4 -8
- data/lib/abt/providers/harvest/commands/stop.rb +7 -7
- data/lib/abt/providers/harvest/commands/tasks.rb +4 -1
- data/lib/abt/providers/harvest/commands/track.rb +25 -18
- data/lib/abt/providers/harvest/configuration.rb +20 -29
- data/lib/abt/providers/harvest/path.rb +36 -0
- data/lib/abt/version.rb +1 -1
- metadata +8 -3
- data/lib/abt/cli/base_command.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 295db35510e60a5b46ea7f20caa01aaad247bb30cb60e61b912cab5f754637ac
|
4
|
+
data.tar.gz: e686eb40fc1ac88315c138eac56b96500c29c3f012b4bdb1d97f993a507f80b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f18aa2b4f33bb6b51689522bca357ddb0fbe7a2d2e968bcc6754959f01bf6d54b9f93c387e8a050d165063cad26e6b360f245d2e7fa317bc1fbad8c0fa0d3379
|
7
|
+
data.tar.gz: ab46d17a29d4e447472c8c4d9242aa2e96bd5195bad4083d0476054f312d358ad5555526b44edacdf2512713e646435200689f82e8a097c1dfa306ff2ae1c4df
|
data/lib/abt/ari.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
class Ari
|
5
|
+
attr_reader :scheme, :path, :flags
|
6
|
+
|
7
|
+
def initialize(scheme:, path: nil, flags: [])
|
8
|
+
@scheme = scheme
|
9
|
+
@path = path
|
10
|
+
@flags = flags
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
str = scheme
|
15
|
+
str += ":#{path}" if path
|
16
|
+
|
17
|
+
[str, *flags].join(' ')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/abt/ari_list.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
class BaseCommand
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def self.usage
|
8
|
+
raise NotImplementedError, 'Command classes must implement .usage'
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
raise NotImplementedError, 'Command classes must implement .description'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.flags
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :ari, :cli, :flags
|
20
|
+
|
21
|
+
def_delegators(:@cli, :warn, :puts, :print, :abort, :exit_with_message)
|
22
|
+
|
23
|
+
def initialize(ari:, cli:)
|
24
|
+
@cli = cli
|
25
|
+
@ari = ari
|
26
|
+
@flags = parse_flags(ari.flags)
|
27
|
+
end
|
28
|
+
|
29
|
+
def perform
|
30
|
+
raise NotImplementedError, 'Command classes must implement #perform'
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def parse_flags(flags)
|
36
|
+
result = {}
|
37
|
+
|
38
|
+
flag_parser.parse!(flags.dup, into: result)
|
39
|
+
|
40
|
+
exit_with_message(flag_parser.help) if result[:help]
|
41
|
+
|
42
|
+
result
|
43
|
+
rescue OptionParser::InvalidOption => e
|
44
|
+
abort e.message
|
45
|
+
end
|
46
|
+
|
47
|
+
def flag_parser
|
48
|
+
@flag_parser ||= OptionParser.new do |opts|
|
49
|
+
opts.banner = <<~TXT
|
50
|
+
#{self.class.description}
|
51
|
+
|
52
|
+
Usage: #{self.class.usage}
|
53
|
+
TXT
|
54
|
+
|
55
|
+
opts.on('-h', '--help')
|
56
|
+
|
57
|
+
self.class.flags.each do |(*flag)|
|
58
|
+
opts.on(*flag)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/abt/cli.rb
CHANGED
@@ -17,7 +17,6 @@ module Abt
|
|
17
17
|
@output = output
|
18
18
|
@err_output = err_output
|
19
19
|
@prompt = Abt::Cli::Prompt.new(output: err_output)
|
20
|
-
|
21
20
|
@aris = ArgumentsParser.new(sanitized_piped_args + remaining_args).parse
|
22
21
|
end
|
23
22
|
|
@@ -105,26 +104,24 @@ module Abt
|
|
105
104
|
|
106
105
|
def process_aris
|
107
106
|
used_schemes = []
|
108
|
-
aris.each do |ari|
|
109
|
-
scheme = ari.scheme
|
110
|
-
path = ari.path
|
111
107
|
|
112
|
-
|
108
|
+
aris.each do |ari|
|
109
|
+
if used_schemes.include?(ari.scheme)
|
113
110
|
warn "Dropping command for already used scheme: #{ari}"
|
114
111
|
next
|
115
112
|
end
|
116
113
|
|
117
|
-
command_class = get_command_class(scheme)
|
114
|
+
command_class = get_command_class(ari.scheme)
|
118
115
|
next if command_class.nil?
|
119
116
|
|
120
117
|
print_command(command, ari) if output.isatty
|
121
118
|
begin
|
122
|
-
command_class.new(
|
119
|
+
command_class.new(ari: ari, cli: self).perform
|
123
120
|
rescue Exit => e
|
124
121
|
puts e.message
|
125
122
|
end
|
126
123
|
|
127
|
-
used_schemes << scheme
|
124
|
+
used_schemes << ari.scheme
|
128
125
|
end
|
129
126
|
|
130
127
|
return unless used_schemes.empty? && output.isatty
|
@@ -140,7 +137,7 @@ module Abt
|
|
140
137
|
end
|
141
138
|
|
142
139
|
def print_command(name, ari)
|
143
|
-
warn "===== #{name} #{ari} ====="
|
140
|
+
warn "===== #{name.upcase} #{ari} ====="
|
144
141
|
end
|
145
142
|
end
|
146
143
|
end
|
@@ -3,28 +3,6 @@
|
|
3
3
|
module Abt
|
4
4
|
class Cli
|
5
5
|
class ArgumentsParser
|
6
|
-
class Ari
|
7
|
-
attr_reader :scheme, :path, :flags
|
8
|
-
|
9
|
-
def initialize(scheme:, path:, flags:)
|
10
|
-
@scheme = scheme
|
11
|
-
@path = path
|
12
|
-
@flags = flags
|
13
|
-
end
|
14
|
-
|
15
|
-
def to_s
|
16
|
-
str = scheme
|
17
|
-
str += ":#{path}" if path
|
18
|
-
|
19
|
-
[str, *flags].join(' ')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
class Aris < Array
|
23
|
-
def to_s
|
24
|
-
map(&:to_s).join(' -- ')
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
6
|
attr_reader :arguments
|
29
7
|
|
30
8
|
def initialize(arguments)
|
@@ -32,7 +10,7 @@ module Abt
|
|
32
10
|
end
|
33
11
|
|
34
12
|
def parse
|
35
|
-
result =
|
13
|
+
result = AriList.new
|
36
14
|
rest = arguments.dup
|
37
15
|
|
38
16
|
until rest.empty?
|
data/lib/abt/cli/prompt.rb
CHANGED
@@ -69,10 +69,11 @@ module Abt
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def read_option_number(options_length, nil_option)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
str = '('
|
73
|
+
str += options_length > 1 ? "1-#{options_length}" : '1'
|
74
|
+
str += nil_option_string(nil_option)
|
75
|
+
str += '): '
|
76
|
+
output.print str
|
76
77
|
|
77
78
|
input = read_user_input
|
78
79
|
|
data/lib/abt/docs.rb
CHANGED
@@ -26,14 +26,15 @@ module Abt
|
|
26
26
|
'abt pick asana -d | abt track harvest' => 'Track on asana meeting task',
|
27
27
|
'abt pick harvest -d | abt track harvest -c "Name of meeting"' => 'Track on separate harvest-task'
|
28
28
|
},
|
29
|
-
'
|
29
|
+
'Many commands output ARIs that can be piped into other commands:' => {
|
30
30
|
'abt tasks asana | grep -i <name of task>' => nil,
|
31
31
|
'abt tasks asana | grep -i <name of task> | abt start' => nil
|
32
32
|
},
|
33
|
-
'Sharing
|
34
|
-
'abt share asana harvest | tr "\n" " "' => 'Print current
|
35
|
-
'abt share asana harvest | tr "\n" " " | pbcopy' => 'Copy
|
36
|
-
'abt start <
|
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
|
'Flags:' => {
|
39
40
|
'abt start harvest -c "comment"' => 'Add command flags after ARIs',
|
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
|
|
data/lib/abt/git_config.rb
CHANGED
@@ -6,21 +6,7 @@ module Abt
|
|
6
6
|
|
7
7
|
class UnsafeNamespaceError < StandardError; end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
def self.local_available?
|
12
|
-
return @local_available if instance_variables.include?(:@local_available)
|
13
|
-
|
14
|
-
@local_available = begin
|
15
|
-
success = false
|
16
|
-
Open3.popen3(LOCAL_CONFIG_AVAILABLE_CHECK_COMMAND) do |_i, _o, _e, thread|
|
17
|
-
success = thread.value.success?
|
18
|
-
end
|
19
|
-
success
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def initialize(namespace: '', scope: 'local')
|
9
|
+
def initialize(scope = 'local', namespace = '')
|
24
10
|
@namespace = namespace
|
25
11
|
|
26
12
|
unless %w[local global].include? scope
|
@@ -30,6 +16,20 @@ module Abt
|
|
30
16
|
@scope = scope
|
31
17
|
end
|
32
18
|
|
19
|
+
def available?
|
20
|
+
unless instance_variables.include?(:available)
|
21
|
+
@available = begin
|
22
|
+
success = false
|
23
|
+
Open3.popen3(availability_check_call) do |_i, _o, _e, thread|
|
24
|
+
success = thread.value.success?
|
25
|
+
end
|
26
|
+
success
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@available
|
31
|
+
end
|
32
|
+
|
33
33
|
def [](key)
|
34
34
|
get(key)
|
35
35
|
end
|
@@ -49,26 +49,6 @@ module Abt
|
|
49
49
|
`git config --#{scope} --get-regexp --name-only ^#{namespace}`.lines.map(&:strip)
|
50
50
|
end
|
51
51
|
|
52
|
-
def local
|
53
|
-
@local ||= begin
|
54
|
-
if scope == 'local'
|
55
|
-
self
|
56
|
-
else
|
57
|
-
self.class.new(namespace: namespace, scope: 'local')
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def global
|
63
|
-
@global ||= begin
|
64
|
-
if scope == 'global'
|
65
|
-
self
|
66
|
-
else
|
67
|
-
self.class.new(namespace: namespace, scope: 'global')
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
52
|
def clear(output: nil)
|
73
53
|
raise UnsafeNamespaceError, 'Keys can only be cleared within a namespace' if namespace.empty?
|
74
54
|
|
@@ -80,8 +60,12 @@ module Abt
|
|
80
60
|
|
81
61
|
private
|
82
62
|
|
63
|
+
def availability_check_call
|
64
|
+
"git config --#{scope} -l"
|
65
|
+
end
|
66
|
+
|
83
67
|
def ensure_scope_available!
|
84
|
-
return if
|
68
|
+
return if available?
|
85
69
|
|
86
70
|
raise StandardError, 'Local configuration is not available outside a git repository'
|
87
71
|
end
|
@@ -3,63 +3,43 @@
|
|
3
3
|
module Abt
|
4
4
|
module Providers
|
5
5
|
module Asana
|
6
|
-
class BaseCommand < Abt::
|
7
|
-
|
6
|
+
class BaseCommand < Abt::BaseCommand
|
7
|
+
extend Forwardable
|
8
8
|
|
9
|
-
|
9
|
+
attr_reader :path, :config
|
10
|
+
|
11
|
+
def_delegators(:@path, :project_gid, :task_gid)
|
12
|
+
|
13
|
+
def initialize(ari:, cli:)
|
10
14
|
super
|
11
15
|
|
12
16
|
@config = Configuration.new(cli: cli)
|
13
17
|
|
14
|
-
|
15
|
-
use_current_path
|
16
|
-
else
|
17
|
-
use_path(path)
|
18
|
-
end
|
18
|
+
@path = ari.path ? Path.new(ari.path) : config.path
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def require_project!
|
24
|
-
|
24
|
+
abort 'No current/specified project. Did you initialize Asana?' if project_gid.nil?
|
25
25
|
end
|
26
26
|
|
27
27
|
def require_task!
|
28
28
|
if project_gid.nil?
|
29
|
-
|
29
|
+
abort 'No current/specified project. Did you initialize Asana and pick a task?'
|
30
30
|
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
def same_args_as_config?
|
35
|
-
project_gid == config.project_gid && task_gid == config.task_gid
|
31
|
+
abort 'No current/specified task. Did you pick an Asana task?' if task_gid.nil?
|
36
32
|
end
|
37
33
|
|
38
34
|
def print_project(project)
|
39
35
|
cli.print_ari('asana', project['gid'], project['name'])
|
40
|
-
|
36
|
+
warn project['permalink_url'] if project.key?('permalink_url') && cli.output.isatty
|
41
37
|
end
|
42
38
|
|
43
39
|
def print_task(project, task)
|
44
40
|
project = { 'gid' => project } if project.is_a?(String)
|
45
41
|
cli.print_ari('asana', "#{project['gid']}/#{task['gid']}", task['name'])
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
def use_current_path
|
50
|
-
@project_gid = config.project_gid
|
51
|
-
@task_gid = config.task_gid
|
52
|
-
end
|
53
|
-
|
54
|
-
def use_path(path)
|
55
|
-
args = path.to_s.split('/')
|
56
|
-
@project_gid = args[0].to_s
|
57
|
-
@project_gid = nil if project_gid.empty?
|
58
|
-
|
59
|
-
return if project_gid.nil?
|
60
|
-
|
61
|
-
@task_gid = args[1].to_s
|
62
|
-
@task_gid = nil if @task_gid.empty?
|
42
|
+
warn task['permalink_url'] if task.key?('permalink_url') && cli.output.isatty
|
63
43
|
end
|
64
44
|
|
65
45
|
def api
|
@@ -17,9 +17,14 @@ module Abt
|
|
17
17
|
require_project!
|
18
18
|
|
19
19
|
task
|
20
|
-
|
20
|
+
warn 'Task created'
|
21
|
+
|
22
|
+
if section
|
23
|
+
move_task
|
24
|
+
warn "Moved to section: #{section['name']}"
|
25
|
+
end
|
21
26
|
|
22
|
-
|
27
|
+
print_task(project, task)
|
23
28
|
end
|
24
29
|
|
25
30
|
private
|
@@ -33,7 +38,6 @@ module Abt
|
|
33
38
|
projects: [project_gid]
|
34
39
|
}
|
35
40
|
}
|
36
|
-
cli.warn 'Creating task'
|
37
41
|
api.post('tasks', Oj.dump(body, mode: :json))
|
38
42
|
end
|
39
43
|
end
|
@@ -53,7 +57,7 @@ module Abt
|
|
53
57
|
end
|
54
58
|
|
55
59
|
def project
|
56
|
-
@project ||= api.get("projects/#{project_gid}")
|
60
|
+
@project ||= api.get("projects/#{project_gid}", opt_fields: 'name')
|
57
61
|
end
|
58
62
|
|
59
63
|
def section
|
@@ -62,10 +66,8 @@ module Abt
|
|
62
66
|
|
63
67
|
def sections
|
64
68
|
@sections ||= begin
|
65
|
-
|
69
|
+
warn 'Fetching sections...'
|
66
70
|
api.get_paged("projects/#{project_gid}/sections", opt_fields: 'name')
|
67
|
-
rescue Abt::HttpError::HttpError
|
68
|
-
[]
|
69
71
|
end
|
70
72
|
end
|
71
73
|
end
|