abt-cli 0.0.14 → 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/bin/abt +1 -1
- data/lib/abt.rb +4 -3
- 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 +89 -54
- data/lib/abt/cli/arguments_parser.rb +48 -0
- data/lib/abt/cli/{dialogs.rb → prompt.rb} +38 -18
- data/lib/abt/docs.rb +35 -28
- data/lib/abt/docs/cli.rb +42 -11
- data/lib/abt/docs/markdown.rb +38 -11
- data/lib/abt/git_config.rb +26 -31
- data/lib/abt/providers/asana/base_command.rb +17 -37
- data/lib/abt/providers/asana/commands/add.rb +15 -13
- data/lib/abt/providers/asana/commands/{branch-name.rb → branch_name.rb} +12 -7
- data/lib/abt/providers/asana/commands/clear.rb +19 -6
- data/lib/abt/providers/asana/commands/current.rb +22 -37
- data/lib/abt/providers/asana/commands/finalize.rb +6 -6
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +12 -7
- data/lib/abt/providers/asana/commands/init.rb +11 -11
- data/lib/abt/providers/asana/commands/pick.rb +30 -17
- data/lib/abt/providers/asana/commands/projects.rb +4 -4
- data/lib/abt/providers/asana/commands/share.rb +5 -9
- data/lib/abt/providers/asana/commands/start.rb +27 -19
- data/lib/abt/providers/asana/commands/tasks.rb +7 -6
- data/lib/abt/providers/asana/configuration.rb +23 -37
- 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 +18 -44
- data/lib/abt/providers/devops/commands/boards.rb +7 -5
- data/lib/abt/providers/devops/commands/{branch-name.rb → branch_name.rb} +10 -6
- data/lib/abt/providers/devops/commands/clear.rb +19 -6
- data/lib/abt/providers/devops/commands/current.rb +17 -41
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +12 -4
- data/lib/abt/providers/devops/commands/init.rb +20 -20
- data/lib/abt/providers/devops/commands/pick.rb +18 -18
- data/lib/abt/providers/devops/commands/share.rb +6 -7
- data/lib/abt/providers/devops/commands/work-items.rb +4 -4
- data/lib/abt/providers/devops/configuration.rb +20 -57
- data/lib/abt/providers/devops/path.rb +50 -0
- data/lib/abt/providers/git/commands/branch.rb +28 -28
- data/lib/abt/providers/harvest/base_command.rb +18 -36
- data/lib/abt/providers/harvest/commands/clear.rb +19 -6
- data/lib/abt/providers/harvest/commands/current.rb +27 -34
- data/lib/abt/providers/harvest/commands/init.rb +10 -11
- data/lib/abt/providers/harvest/commands/pick.rb +16 -9
- data/lib/abt/providers/harvest/commands/projects.rb +4 -4
- data/lib/abt/providers/harvest/commands/share.rb +7 -11
- data/lib/abt/providers/harvest/commands/start.rb +6 -42
- data/lib/abt/providers/harvest/commands/stop.rb +10 -10
- data/lib/abt/providers/harvest/commands/tasks.rb +7 -4
- data/lib/abt/providers/harvest/commands/track.rb +66 -21
- data/lib/abt/providers/harvest/configuration.rb +23 -38
- data/lib/abt/providers/harvest/path.rb +36 -0
- data/lib/abt/version.rb +1 -1
- metadata +12 -9
- data/lib/abt/cli/io.rb +0 -23
- data/lib/abt/providers/asana/commands/clear_global.rb +0 -24
- data/lib/abt/providers/devops/commands/clear_global.rb +0 -24
- data/lib/abt/providers/harvest/commands/clear_global.rb +0 -24
@@ -5,17 +5,30 @@ module Abt
|
|
5
5
|
module Devops
|
6
6
|
module Commands
|
7
7
|
class Clear < BaseCommand
|
8
|
-
def self.
|
9
|
-
'clear devops'
|
8
|
+
def self.usage
|
9
|
+
'abt clear devops'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
|
-
'Clear DevOps
|
13
|
+
'Clear DevOps configuration'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
16
|
+
def self.flags
|
17
|
+
[
|
18
|
+
['-g', '--global', 'Clear global instead of local DevOp configuration (credentials etc.)'],
|
19
|
+
['-a', '--all', 'Clear all DevOp configuration']
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform
|
24
|
+
if flags[:global] && flags[:all]
|
25
|
+
abort('Flags --global and --all cannot be used at the same time')
|
26
|
+
end
|
27
|
+
|
28
|
+
config.clear_local unless flags[:global]
|
29
|
+
config.clear_global if flags[:global] || flags[:all]
|
30
|
+
|
31
|
+
warn 'Configuration cleared'
|
19
32
|
end
|
20
33
|
end
|
21
34
|
end
|
@@ -5,72 +5,48 @@ module Abt
|
|
5
5
|
module Devops
|
6
6
|
module Commands
|
7
7
|
class Current < BaseCommand
|
8
|
-
def self.
|
9
|
-
'current devops[:<organization-name>/<project-name>/<board-id>[/<work-item-id>]]'
|
8
|
+
def self.usage
|
9
|
+
'abt current devops[:<organization-name>/<project-name>/<board-id>[/<work-item-id>]]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Get or set DevOps configuration for current git repository'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
|
+
abort 'Must be run inside a git repository' unless config.local_available?
|
18
|
+
|
17
19
|
require_board!
|
20
|
+
ensure_valid_configuration!
|
18
21
|
|
19
|
-
if
|
20
|
-
|
21
|
-
|
22
|
-
cli.warn 'Updating configuration'
|
23
|
-
update_configuration
|
22
|
+
if path != config.path && config.local_available?
|
23
|
+
config.path = path
|
24
|
+
warn 'Configuration updated'
|
24
25
|
end
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
26
|
|
29
|
-
|
30
|
-
if work_item_id.nil?
|
31
|
-
print_board(organization_name, project_name, board)
|
32
|
-
else
|
33
|
-
print_work_item(organization_name, project_name, board, work_item)
|
34
|
-
end
|
27
|
+
print_configuration
|
35
28
|
end
|
36
29
|
|
37
|
-
|
38
|
-
ensure_board_is_valid!
|
30
|
+
private
|
39
31
|
|
32
|
+
def print_configuration
|
40
33
|
if work_item_id.nil?
|
41
|
-
update_board_config
|
42
|
-
config.work_item_id = nil
|
43
|
-
|
44
34
|
print_board(organization_name, project_name, board)
|
45
35
|
else
|
46
|
-
ensure_work_item_is_valid!
|
47
|
-
|
48
|
-
update_board_config
|
49
|
-
config.work_item_id = work_item_id
|
50
|
-
|
51
36
|
print_work_item(organization_name, project_name, board, work_item)
|
52
37
|
end
|
53
38
|
end
|
54
39
|
|
55
|
-
def
|
56
|
-
config.organization_name = organization_name
|
57
|
-
config.project_name = project_name
|
58
|
-
config.board_id = board_id
|
59
|
-
end
|
60
|
-
|
61
|
-
def ensure_board_is_valid!
|
40
|
+
def ensure_valid_configuration!
|
62
41
|
if board.nil?
|
63
|
-
|
42
|
+
abort 'Board could not be found, ensure that settings for organization, project, and board are correct'
|
64
43
|
end
|
65
|
-
|
66
|
-
|
67
|
-
def ensure_work_item_is_valid!
|
68
|
-
cli.abort "No such work item: ##{work_item_id}" if work_item.nil?
|
44
|
+
abort "No such work item: ##{work_item_id}" if work_item_id && work_item.nil?
|
69
45
|
end
|
70
46
|
|
71
47
|
def board
|
72
48
|
@board ||= begin
|
73
|
-
|
49
|
+
warn 'Fetching board...'
|
74
50
|
api.get("work/boards/#{board_id}")
|
75
51
|
rescue HttpError::NotFoundError
|
76
52
|
nil
|
@@ -79,7 +55,7 @@ module Abt
|
|
79
55
|
|
80
56
|
def work_item
|
81
57
|
@work_item ||= begin
|
82
|
-
|
58
|
+
warn 'Fetching work item...'
|
83
59
|
work_item = api.get_paged('wit/workitems', ids: work_item_id)[0]
|
84
60
|
sanitize_work_item(work_item)
|
85
61
|
rescue HttpError::NotFoundError
|
@@ -5,15 +5,15 @@ module Abt
|
|
5
5
|
module Devops
|
6
6
|
module Commands
|
7
7
|
class HarvestTimeEntryData < BaseCommand
|
8
|
-
def self.
|
9
|
-
'harvest-time-entry-data devops[:<organization-name>/<project-name>/<board-id>/<work-item-id>]'
|
8
|
+
def self.usage
|
9
|
+
'abt harvest-time-entry-data devops[:<organization-name>/<project-name>/<board-id>/<work-item-id>]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Print Harvest time entry data for DevOps work item as json. Used by harvest start script.'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
17
|
require_work_item!
|
18
18
|
|
19
19
|
body = {
|
@@ -25,7 +25,15 @@ module Abt
|
|
25
25
|
}
|
26
26
|
}
|
27
27
|
|
28
|
-
|
28
|
+
puts Oj.dump(body, mode: :json)
|
29
|
+
rescue HttpError::NotFoundError
|
30
|
+
args = [organization_name, project_name, board_id, work_item_id].compact
|
31
|
+
|
32
|
+
error_message = [
|
33
|
+
'Unable to find work item for configuration:',
|
34
|
+
"devops:#{args.join('/')}"
|
35
|
+
].join("\n")
|
36
|
+
abort error_message
|
29
37
|
end
|
30
38
|
|
31
39
|
private
|
@@ -8,24 +8,20 @@ module Abt
|
|
8
8
|
AZURE_DEV_URL_REGEX = %r{^https://dev\.azure\.com/(?<organization>[^/]+)/(?<project>[^/]+)}.freeze
|
9
9
|
VS_URL_REGEX = %r{^https://(?<organization>[^.]+)\.visualstudio\.com/(?<project>[^/]+)}.freeze
|
10
10
|
|
11
|
-
def self.
|
12
|
-
'init devops'
|
11
|
+
def self.usage
|
12
|
+
'abt init devops'
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.description
|
16
16
|
'Pick DevOps board for current git repository'
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
19
|
+
def perform
|
20
|
+
abort 'Must be run inside a git repository' unless config.local_available?
|
21
21
|
|
22
|
-
|
23
|
-
@project_name = config.project_name = project_name_from_url
|
24
|
-
|
25
|
-
board = cli.prompt_choice 'Select a project work board', boards
|
26
|
-
|
27
|
-
config.board_id = board['id']
|
22
|
+
board = cli.prompt.choice 'Select a project work board', boards
|
28
23
|
|
24
|
+
config.path = Path.from_ids(organization_name, project_name, board['id'])
|
29
25
|
print_board(organization_name, project_name, board)
|
30
26
|
end
|
31
27
|
|
@@ -35,24 +31,28 @@ module Abt
|
|
35
31
|
@boards ||= api.get_paged('work/boards')
|
36
32
|
end
|
37
33
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
def project_name
|
35
|
+
@project_name ||= begin
|
36
|
+
if (match = AZURE_DEV_URL_REGEX.match(project_url)) ||
|
37
|
+
(match = VS_URL_REGEX.match(project_url))
|
38
|
+
match[:project]
|
39
|
+
end
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
def organization_name
|
44
|
+
@organization_name ||= begin
|
45
|
+
if (match = AZURE_DEV_URL_REGEX.match(project_url)) ||
|
46
|
+
(match = VS_URL_REGEX.match(project_url))
|
47
|
+
match[:organization]
|
48
|
+
end
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
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
|
'',
|
@@ -61,7 +61,7 @@ module Abt
|
|
61
61
|
|
62
62
|
break url if AZURE_DEV_URL_REGEX =~ url || VS_URL_REGEX =~ url
|
63
63
|
|
64
|
-
|
64
|
+
warn 'Invalid URL'
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -5,48 +5,48 @@ module Abt
|
|
5
5
|
module Devops
|
6
6
|
module Commands
|
7
7
|
class Pick < BaseCommand
|
8
|
-
def self.
|
9
|
-
'pick devops[:<organization-name>/<project-name>/<board-id>]'
|
8
|
+
def self.usage
|
9
|
+
'abt pick devops[:<organization-name>/<project-name>/<board-id>]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Pick work item for current git repository'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def self.flags
|
17
|
+
[
|
18
|
+
['-d', '--dry-run', 'Keep existing configuration']
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
def perform
|
23
|
+
abort 'Must be run inside a git repository' unless config.local_available?
|
18
24
|
require_board!
|
19
25
|
|
20
|
-
|
26
|
+
warn "#{project_name} - #{board['name']}"
|
21
27
|
|
22
28
|
work_item = select_work_item
|
29
|
+
print_work_item(organization_name, project_name, board, work_item)
|
23
30
|
|
24
|
-
|
31
|
+
return if flags[:"dry-run"]
|
25
32
|
|
26
|
-
|
33
|
+
config.path = Path.from_ids(organization_name, project_name, board_id, work_item['id'])
|
27
34
|
end
|
28
35
|
|
29
36
|
private
|
30
37
|
|
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
|
-
|
38
38
|
def select_work_item
|
39
39
|
loop do
|
40
|
-
column = cli.
|
41
|
-
|
40
|
+
column = cli.prompt.choice 'Which column?', columns
|
41
|
+
warn 'Fetching work items...'
|
42
42
|
work_items = work_items_in_column(column)
|
43
43
|
|
44
44
|
if work_items.length.zero?
|
45
|
-
|
45
|
+
warn 'Section is empty'
|
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
|
@@ -5,19 +5,18 @@ module Abt
|
|
5
5
|
module Devops
|
6
6
|
module Commands
|
7
7
|
class Share < BaseCommand
|
8
|
-
def self.
|
9
|
-
'share devops[:<organization-name>/<project-name>/<board-id>[/<work-item-id>]]'
|
8
|
+
def self.usage
|
9
|
+
'abt share devops[:<organization-name>/<project-name>/<board-id>[/<work-item-id>]]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
|
-
'Print DevOps
|
13
|
+
'Print DevOps ARI'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def perform
|
17
|
+
require_board!
|
18
18
|
|
19
|
-
|
20
|
-
cli.print_provider_command('devops', args.join('/'))
|
19
|
+
cli.print_ari('devops', path)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
@@ -5,15 +5,15 @@ module Abt
|
|
5
5
|
module Devops
|
6
6
|
module Commands
|
7
7
|
class WorkItems < BaseCommand
|
8
|
-
def self.
|
9
|
-
'work-items devops'
|
8
|
+
def self.usage
|
9
|
+
'abt work-items devops'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'List all work items on board - useful for piping into grep etc.'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
17
|
require_board!
|
18
18
|
|
19
19
|
work_items.each do |work_item|
|
@@ -25,7 +25,7 @@ module Abt
|
|
25
25
|
|
26
26
|
def work_items
|
27
27
|
@work_items ||= begin
|
28
|
-
|
28
|
+
warn 'Fetching work items...'
|
29
29
|
api.work_item_query(
|
30
30
|
<<~WIQL
|
31
31
|
SELECT [System.Id]
|
@@ -8,77 +8,34 @@ module Abt
|
|
8
8
|
|
9
9
|
def initialize(cli:)
|
10
10
|
@cli = cli
|
11
|
-
@git = GitConfig.new(namespace: 'abt.devops')
|
12
11
|
end
|
13
12
|
|
14
13
|
def local_available?
|
15
|
-
|
14
|
+
git.available?
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
19
|
-
local_available?
|
17
|
+
def path
|
18
|
+
Path.new(local_available? && git['path'] || '')
|
20
19
|
end
|
21
20
|
|
22
|
-
def
|
23
|
-
|
21
|
+
def path=(new_path)
|
22
|
+
git['path'] = new_path
|
24
23
|
end
|
25
24
|
|
26
|
-
def
|
27
|
-
|
25
|
+
def clear_local(verbose: true)
|
26
|
+
git.clear(output: verbose ? cli.err_output : nil)
|
28
27
|
end
|
29
28
|
|
30
|
-
def
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
def organization_name=(value)
|
35
|
-
return if organization_name == value
|
36
|
-
|
37
|
-
clear_local
|
38
|
-
git['organizationName'] = value unless value.nil?
|
39
|
-
end
|
40
|
-
|
41
|
-
def project_name=(value)
|
42
|
-
return if project_name == value
|
43
|
-
|
44
|
-
git['projectName'] = value unless value.nil?
|
45
|
-
git['boardId'] = nil
|
46
|
-
git['workItemId'] = nil
|
47
|
-
end
|
48
|
-
|
49
|
-
def board_id=(value)
|
50
|
-
return if board_id == value
|
51
|
-
|
52
|
-
git['boardId'] = value unless value.nil?
|
53
|
-
git['workItemId'] = nil
|
54
|
-
end
|
55
|
-
|
56
|
-
def work_item_id=(value)
|
57
|
-
git['workItemId'] = value
|
58
|
-
end
|
59
|
-
|
60
|
-
def clear_local
|
61
|
-
cli.abort 'No local configuration was found' unless local_available?
|
62
|
-
|
63
|
-
git['organizationName'] = nil
|
64
|
-
git['projectName'] = nil
|
65
|
-
git['boardId'] = nil
|
66
|
-
git['workItemId'] = nil
|
67
|
-
end
|
68
|
-
|
69
|
-
def clear_global
|
70
|
-
git.global.keys.each do |key|
|
71
|
-
cli.puts 'Deleting configuration: ' + key
|
72
|
-
git.global[key] = nil
|
73
|
-
end
|
29
|
+
def clear_global(verbose: true)
|
30
|
+
git_global.clear(output: verbose ? cli.err_output : nil)
|
74
31
|
end
|
75
32
|
|
76
33
|
def username_for_organization(organization_name)
|
77
34
|
username_key = "organizations.#{organization_name}.username"
|
78
35
|
|
79
|
-
return
|
36
|
+
return git_global[username_key] unless git_global[username_key].nil?
|
80
37
|
|
81
|
-
|
38
|
+
git_global[username_key] = cli.prompt.text([
|
82
39
|
"Please provide your username for the DevOps organization (#{organization_name}).",
|
83
40
|
'',
|
84
41
|
'Enter username'
|
@@ -88,9 +45,9 @@ module Abt
|
|
88
45
|
def access_token_for_organization(organization_name)
|
89
46
|
access_token_key = "organizations.#{organization_name}.accessToken"
|
90
47
|
|
91
|
-
return
|
48
|
+
return git_global[access_token_key] unless git_global[access_token_key].nil?
|
92
49
|
|
93
|
-
|
50
|
+
git_global[access_token_key] = cli.prompt.text([
|
94
51
|
"Please provide your personal access token for the DevOps organization (#{organization_name}).",
|
95
52
|
'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
53
|
'',
|
@@ -103,7 +60,13 @@ module Abt
|
|
103
60
|
|
104
61
|
private
|
105
62
|
|
106
|
-
|
63
|
+
def git
|
64
|
+
@git ||= GitConfig.new('local', 'abt.devops')
|
65
|
+
end
|
66
|
+
|
67
|
+
def git_global
|
68
|
+
@git_global ||= GitConfig.new('global', 'abt.devops')
|
69
|
+
end
|
107
70
|
end
|
108
71
|
end
|
109
72
|
end
|