abt-cli 0.0.11 → 0.0.12
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/dialogs.rb +28 -9
- data/lib/abt/providers/asana/base_command.rb +11 -0
- data/lib/abt/providers/asana/commands/add.rb +75 -0
- data/lib/abt/providers/asana/commands/current.rb +3 -3
- data/lib/abt/providers/asana/commands/finalize.rb +1 -1
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +1 -0
- data/lib/abt/providers/asana/commands/pick.rb +1 -0
- data/lib/abt/providers/asana/commands/share.rb +3 -3
- data/lib/abt/providers/asana/commands/start.rb +1 -1
- data/lib/abt/providers/asana/commands/tasks.rb +2 -0
- data/lib/abt/providers/devops/api.rb +12 -0
- data/lib/abt/providers/devops/base_command.rb +16 -0
- data/lib/abt/providers/devops/commands/boards.rb +34 -0
- data/lib/abt/providers/devops/commands/current.rb +3 -7
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +2 -0
- data/lib/abt/providers/devops/commands/pick.rb +17 -15
- data/lib/abt/providers/devops/commands/share.rb +4 -10
- data/lib/abt/providers/devops/commands/work-items.rb +46 -0
- data/lib/abt/providers/harvest/base_command.rb +11 -0
- data/lib/abt/providers/harvest/commands/current.rb +3 -3
- data/lib/abt/providers/harvest/commands/pick.rb +1 -0
- data/lib/abt/providers/harvest/commands/start.rb +1 -3
- data/lib/abt/providers/harvest/commands/tasks.rb +2 -0
- data/lib/abt/providers/harvest/commands/track.rb +1 -2
- data/lib/abt/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f1631b9069ca5094ba301eb3ccc5cf42da8e48a85b2678e8499cea1e9e4551c
|
4
|
+
data.tar.gz: cd9dc57e8a6276de5a2bca61e59fb537bc8887901615d7ec86f2c4a59145f76c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdca57213f85db7a3604ac1a5ed50a16a2918fded5b3b3e090e52aeb184414345d7609f08ef5145f205cf3fb9f1576f9e0aaa6b25a2e41684cfd506bed19e851
|
7
|
+
data.tar.gz: d7eb95a22516aa0f894262e0a1b52ee659736b88775936040a87db03260711e12dbc6764674799b5d2f8bdb86cb14495f01440a0703d300759c3482db17a810a
|
data/lib/abt/cli/dialogs.rb
CHANGED
@@ -24,18 +24,18 @@ module Abt
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
def prompt_choice(text, options,
|
27
|
+
def prompt_choice(text, options, nil_option = false)
|
28
28
|
warn "#{text}:"
|
29
29
|
|
30
30
|
if options.length.zero?
|
31
|
-
abort 'No available options' unless
|
31
|
+
abort 'No available options' unless nil_option
|
32
32
|
|
33
33
|
warn 'No available options'
|
34
34
|
return nil
|
35
35
|
end
|
36
36
|
|
37
37
|
print_options(options)
|
38
|
-
select_options(options,
|
38
|
+
select_options(options, nil_option)
|
39
39
|
end
|
40
40
|
|
41
41
|
private
|
@@ -46,11 +46,11 @@ module Abt
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def select_options(options,
|
49
|
+
def select_options(options, nil_option)
|
50
50
|
loop do
|
51
|
-
number = read_option_number(options.length,
|
51
|
+
number = read_option_number(options.length, nil_option)
|
52
52
|
if number.nil?
|
53
|
-
return nil if
|
53
|
+
return nil if nil_option
|
54
54
|
|
55
55
|
next
|
56
56
|
end
|
@@ -62,12 +62,12 @@ module Abt
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
def read_option_number(options_length,
|
66
|
-
err_output.print "(1-#{options_length}#{
|
65
|
+
def read_option_number(options_length, nil_option)
|
66
|
+
err_output.print "(1-#{options_length}#{nil_option_string(nil_option)}): "
|
67
67
|
|
68
68
|
input = read_user_input
|
69
69
|
|
70
|
-
return nil if
|
70
|
+
return nil if nil_option && input == nil_option_character(nil_option)
|
71
71
|
|
72
72
|
option_number = input.to_i
|
73
73
|
if option_number <= 0 || option_number > options_length
|
@@ -78,6 +78,25 @@ module Abt
|
|
78
78
|
option_number
|
79
79
|
end
|
80
80
|
|
81
|
+
def nil_option_string(nil_option)
|
82
|
+
return '' unless nil_option
|
83
|
+
|
84
|
+
", #{nil_option_character(nil_option)}: #{nil_option_description(nil_option)}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def nil_option_character(nil_option)
|
88
|
+
return 'q' if nil_option == true
|
89
|
+
|
90
|
+
nil_option[0]
|
91
|
+
end
|
92
|
+
|
93
|
+
def nil_option_description(nil_option)
|
94
|
+
return 'back' if nil_option == true
|
95
|
+
return nil_option if nil_option.is_a?(String)
|
96
|
+
|
97
|
+
nil_option[1]
|
98
|
+
end
|
99
|
+
|
81
100
|
def read_user_input
|
82
101
|
open('/dev/tty', &:gets).strip
|
83
102
|
end
|
@@ -20,6 +20,17 @@ module Abt
|
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
+
def require_project!
|
24
|
+
cli.abort 'No current/specified project. Did you initialize Asana?' if project_gid.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def require_task!
|
28
|
+
if project_gid.nil?
|
29
|
+
cli.abort 'No current/specified project. Did you initialize Asana and pick a task?'
|
30
|
+
end
|
31
|
+
cli.abort 'No current/specified task. Did you pick an Asana task?' if task_gid.nil?
|
32
|
+
end
|
33
|
+
|
23
34
|
def same_args_as_config?
|
24
35
|
project_gid == config.project_gid && task_gid == config.task_gid
|
25
36
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
module Providers
|
5
|
+
module Asana
|
6
|
+
module Commands
|
7
|
+
class Add < BaseCommand
|
8
|
+
def self.command
|
9
|
+
'add asana[:<project-gid>]'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.description
|
13
|
+
'Create a new task for the current/specified Asana project'
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
require_project!
|
18
|
+
|
19
|
+
task
|
20
|
+
print_task(project, task)
|
21
|
+
|
22
|
+
move_task if section
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def task
|
28
|
+
@task ||= begin
|
29
|
+
body = {
|
30
|
+
data: {
|
31
|
+
name: name,
|
32
|
+
notes: notes,
|
33
|
+
projects: [project_gid]
|
34
|
+
}
|
35
|
+
}
|
36
|
+
cli.warn 'Creating task'
|
37
|
+
api.post('tasks', Oj.dump(body, mode: :json))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def move_task
|
42
|
+
body = { data: { task: task['gid'] } }
|
43
|
+
body_json = Oj.dump(body, mode: :json)
|
44
|
+
api.post("sections/#{section['gid']}/addTask", body_json)
|
45
|
+
end
|
46
|
+
|
47
|
+
def name
|
48
|
+
@name ||= cli.prompt 'Enter task description'
|
49
|
+
end
|
50
|
+
|
51
|
+
def notes
|
52
|
+
@notes ||= cli.prompt 'Enter task notes'
|
53
|
+
end
|
54
|
+
|
55
|
+
def project
|
56
|
+
@project ||= api.get("projects/#{project_gid}")
|
57
|
+
end
|
58
|
+
|
59
|
+
def section
|
60
|
+
@section ||= cli.prompt_choice 'Add to section?', sections, ['q', 'Don\'t add to section']
|
61
|
+
end
|
62
|
+
|
63
|
+
def sections
|
64
|
+
@sections ||= begin
|
65
|
+
cli.warn 'Fetching sections...'
|
66
|
+
api.get_paged("projects/#{project_gid}/sections", opt_fields: 'name')
|
67
|
+
rescue Abt::HttpError::HttpError
|
68
|
+
[]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -14,6 +14,8 @@ module Abt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def call
|
17
|
+
require_project!
|
18
|
+
|
17
19
|
if same_args_as_config? || !config.local_available?
|
18
20
|
show_current_configuration
|
19
21
|
else
|
@@ -25,9 +27,7 @@ module Abt
|
|
25
27
|
private
|
26
28
|
|
27
29
|
def show_current_configuration
|
28
|
-
if
|
29
|
-
cli.warn 'No project selected'
|
30
|
-
elsif task_gid.nil?
|
30
|
+
if task_gid.nil?
|
31
31
|
print_project(project)
|
32
32
|
else
|
33
33
|
print_task(project, task)
|
@@ -17,7 +17,7 @@ module Abt
|
|
17
17
|
unless config.local_available?
|
18
18
|
cli.abort 'This is a no-op for tasks outside the current project'
|
19
19
|
end
|
20
|
-
|
20
|
+
require_task!
|
21
21
|
print_task(project_gid, task)
|
22
22
|
|
23
23
|
if task_already_in_finalized_section?
|
@@ -14,9 +14,9 @@ module Abt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def call
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
require_project!
|
18
|
+
|
19
|
+
if task_gid.nil?
|
20
20
|
cli.print_provider_command('asana', project_gid)
|
21
21
|
else
|
22
22
|
cli.print_provider_command('asana', "#{project_gid}/#{task_gid}")
|
@@ -28,6 +28,18 @@ module Abt
|
|
28
28
|
# TODO: Loop if necessary
|
29
29
|
end
|
30
30
|
|
31
|
+
def work_item_query(wiql)
|
32
|
+
response = post('wit/wiql', Oj.dump({ query: wiql }, mode: :json))
|
33
|
+
ids = response['workItems'].map { |work_item| work_item['id'] }
|
34
|
+
|
35
|
+
work_items = []
|
36
|
+
ids.each_slice(200) do |page_ids|
|
37
|
+
work_items += get_paged('wit/workitems', ids: page_ids.join(','))
|
38
|
+
end
|
39
|
+
|
40
|
+
work_items
|
41
|
+
end
|
42
|
+
|
31
43
|
def request(*args)
|
32
44
|
response = connection.public_send(*args)
|
33
45
|
|
@@ -21,6 +21,22 @@ module Abt
|
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
+
def require_board!
|
25
|
+
return if organization_name && project_name && board_id
|
26
|
+
|
27
|
+
cli.abort 'No current/specified board. Did you initialize DevOps?'
|
28
|
+
end
|
29
|
+
|
30
|
+
def require_work_item!
|
31
|
+
unless organization_name && project_name && board_id
|
32
|
+
cli.abort 'No current/specified board. Did you initialize DevOps and pick a work item?'
|
33
|
+
end
|
34
|
+
|
35
|
+
return if work_item_id
|
36
|
+
|
37
|
+
cli.abort 'No current/specified work item. Did you pick a DevOps work item?'
|
38
|
+
end
|
39
|
+
|
24
40
|
def sanitize_work_item(work_item)
|
25
41
|
return nil if work_item.nil?
|
26
42
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
module Providers
|
5
|
+
module Devops
|
6
|
+
module Commands
|
7
|
+
class Boards < BaseCommand
|
8
|
+
def self.command
|
9
|
+
'boards devops'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.description
|
13
|
+
'List all boards - useful for piping into grep etc'
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
cli.abort 'No organization selected. Did you initialize DevOps?' if organization_name.nil?
|
18
|
+
cli.abort 'No project selected. Did you initialize DevOps?' if project_name.nil?
|
19
|
+
|
20
|
+
boards.map do |board|
|
21
|
+
print_board(organization_name, project_name, board)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def boards
|
28
|
+
@boards ||= api.get_paged('work/boards')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -14,6 +14,8 @@ module Abt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def call
|
17
|
+
require_board!
|
18
|
+
|
17
19
|
if same_args_as_config? || !config.local_available?
|
18
20
|
show_current_configuration
|
19
21
|
else
|
@@ -25,13 +27,7 @@ module Abt
|
|
25
27
|
private
|
26
28
|
|
27
29
|
def show_current_configuration
|
28
|
-
if
|
29
|
-
cli.warn 'No organization selected'
|
30
|
-
elsif project_name.nil?
|
31
|
-
cli.warn 'No project selected'
|
32
|
-
elsif board_id.nil?
|
33
|
-
cli.warn 'No board selected'
|
34
|
-
elsif work_item_id.nil?
|
30
|
+
if work_item_id.nil?
|
35
31
|
print_board(organization_name, project_name, board)
|
36
32
|
else
|
37
33
|
print_work_item(organization_name, project_name, board, work_item)
|
@@ -15,22 +15,26 @@ module Abt
|
|
15
15
|
|
16
16
|
def call
|
17
17
|
cli.abort 'Must be run inside a git repository' unless config.local_available?
|
18
|
+
require_board!
|
18
19
|
|
19
20
|
cli.warn "#{project_name} - #{board['name']}"
|
20
21
|
|
21
22
|
work_item = select_work_item
|
22
23
|
|
23
|
-
|
24
|
-
config.organization_name = organization_name
|
25
|
-
config.project_name = project_name
|
26
|
-
config.board_id = board_id
|
27
|
-
config.work_item_id = work_item['id']
|
24
|
+
update_config!(work_item)
|
28
25
|
|
29
26
|
print_work_item(organization_name, project_name, board, work_item)
|
30
27
|
end
|
31
28
|
|
32
29
|
private
|
33
30
|
|
31
|
+
def update_config!(work_item)
|
32
|
+
config.organization_name = organization_name
|
33
|
+
config.project_name = project_name
|
34
|
+
config.board_id = board_id
|
35
|
+
config.work_item_id = work_item['id']
|
36
|
+
end
|
37
|
+
|
34
38
|
def select_work_item
|
35
39
|
loop do
|
36
40
|
column = cli.prompt_choice 'Which column?', columns
|
@@ -48,16 +52,14 @@ module Abt
|
|
48
52
|
end
|
49
53
|
|
50
54
|
def work_items_in_column(column)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
ids = response['workItems'].map { |work_item| work_item['id'] }
|
60
|
-
work_items = api.get_paged('wit/workitems', ids: ids.join(','))
|
55
|
+
work_items = api.work_item_query(
|
56
|
+
<<~WIQL
|
57
|
+
SELECT [System.Id]
|
58
|
+
FROM WorkItems
|
59
|
+
WHERE [System.BoardColumn] = '#{column['name']}'
|
60
|
+
ORDER BY [Microsoft.VSTS.Common.BacklogPriority] ASC
|
61
|
+
WIQL
|
62
|
+
)
|
61
63
|
|
62
64
|
work_items.map { |work_item| sanitize_work_item(work_item) }
|
63
65
|
end
|
@@ -14,16 +14,10 @@ module Abt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def call
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
elsif board_id.nil?
|
22
|
-
cli.warn 'No board selected'
|
23
|
-
else
|
24
|
-
args = [organization_name, project_name, board_id, work_item_id].compact
|
25
|
-
cli.print_provider_command('devops', args.join('/'))
|
26
|
-
end
|
17
|
+
require_work_item!
|
18
|
+
|
19
|
+
args = [organization_name, project_name, board_id, work_item_id].compact
|
20
|
+
cli.print_provider_command('devops', args.join('/'))
|
27
21
|
end
|
28
22
|
end
|
29
23
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
module Providers
|
5
|
+
module Devops
|
6
|
+
module Commands
|
7
|
+
class WorkItems < BaseCommand
|
8
|
+
def self.command
|
9
|
+
'work-items devops'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.description
|
13
|
+
'List all work items on board - useful for piping into grep etc.'
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
require_board!
|
18
|
+
|
19
|
+
work_items.each do |work_item|
|
20
|
+
print_work_item(organization_name, project_name, board, work_item)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def work_items
|
27
|
+
@work_items ||= begin
|
28
|
+
cli.warn 'Fetching work items...'
|
29
|
+
api.work_item_query(
|
30
|
+
<<~WIQL
|
31
|
+
SELECT [System.Id]
|
32
|
+
FROM WorkItems
|
33
|
+
ORDER BY [System.Title] ASC
|
34
|
+
WIQL
|
35
|
+
).map { |work_item| sanitize_work_item(work_item) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def board
|
40
|
+
@board ||= api.get("work/boards/#{board_id}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -20,6 +20,17 @@ module Abt
|
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
+
def require_project!
|
24
|
+
cli.abort 'No current/specified project. Did you initialize Harvest?' if project_id.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def require_task!
|
28
|
+
if project_id.nil?
|
29
|
+
cli.abort 'No current/specified project. Did you initialize Harvest and pick a task?'
|
30
|
+
end
|
31
|
+
cli.abort 'No current/specified task. Did you pick a Harvest task?' if task_id.nil?
|
32
|
+
end
|
33
|
+
|
23
34
|
def same_args_as_config?
|
24
35
|
project_id == config.project_id && task_id == config.task_id
|
25
36
|
end
|
@@ -14,6 +14,8 @@ module Abt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def call
|
17
|
+
require_project!
|
18
|
+
|
17
19
|
if same_args_as_config? || !config.local_available?
|
18
20
|
show_current_configuration
|
19
21
|
else
|
@@ -25,9 +27,7 @@ module Abt
|
|
25
27
|
private
|
26
28
|
|
27
29
|
def show_current_configuration
|
28
|
-
if
|
29
|
-
cli.warn 'No project selected'
|
30
|
-
elsif task_id.nil?
|
30
|
+
if task_id.nil?
|
31
31
|
print_project(project)
|
32
32
|
else
|
33
33
|
print_task(project, task)
|
@@ -37,9 +37,7 @@ module Abt
|
|
37
37
|
output = StringIO.new
|
38
38
|
Abt::Cli.new(argv: ['track'], output: output, input: input).perform
|
39
39
|
|
40
|
-
|
41
|
-
cli.abort 'No task provided' if output_str.empty?
|
42
|
-
output_str
|
40
|
+
output.string.strip
|
43
41
|
end
|
44
42
|
|
45
43
|
def maybe_override_current_task
|
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.12
|
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-01
|
11
|
+
date: 2021-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-inflector
|
@@ -88,6 +88,7 @@ files:
|
|
88
88
|
- "./lib/abt/providers/asana.rb"
|
89
89
|
- "./lib/abt/providers/asana/api.rb"
|
90
90
|
- "./lib/abt/providers/asana/base_command.rb"
|
91
|
+
- "./lib/abt/providers/asana/commands/add.rb"
|
91
92
|
- "./lib/abt/providers/asana/commands/clear.rb"
|
92
93
|
- "./lib/abt/providers/asana/commands/clear_global.rb"
|
93
94
|
- "./lib/abt/providers/asana/commands/current.rb"
|
@@ -103,6 +104,7 @@ files:
|
|
103
104
|
- "./lib/abt/providers/devops.rb"
|
104
105
|
- "./lib/abt/providers/devops/api.rb"
|
105
106
|
- "./lib/abt/providers/devops/base_command.rb"
|
107
|
+
- "./lib/abt/providers/devops/commands/boards.rb"
|
106
108
|
- "./lib/abt/providers/devops/commands/clear.rb"
|
107
109
|
- "./lib/abt/providers/devops/commands/clear_global.rb"
|
108
110
|
- "./lib/abt/providers/devops/commands/current.rb"
|
@@ -110,6 +112,7 @@ files:
|
|
110
112
|
- "./lib/abt/providers/devops/commands/init.rb"
|
111
113
|
- "./lib/abt/providers/devops/commands/pick.rb"
|
112
114
|
- "./lib/abt/providers/devops/commands/share.rb"
|
115
|
+
- "./lib/abt/providers/devops/commands/work-items.rb"
|
113
116
|
- "./lib/abt/providers/devops/configuration.rb"
|
114
117
|
- "./lib/abt/providers/harvest.rb"
|
115
118
|
- "./lib/abt/providers/harvest/api.rb"
|