abt-cli 0.0.26 → 0.0.31
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/abt.rb +0 -4
- data/lib/abt/cli.rb +5 -1
- data/lib/abt/directory_config.rb +28 -10
- data/lib/abt/docs.rb +10 -6
- data/lib/abt/providers/asana.rb +1 -0
- data/lib/abt/providers/asana/base_command.rb +33 -3
- data/lib/abt/providers/asana/commands/add.rb +0 -4
- data/lib/abt/providers/asana/commands/branch_name.rb +0 -13
- data/lib/abt/providers/asana/commands/clear.rb +1 -1
- data/lib/abt/providers/asana/commands/current.rb +0 -18
- data/lib/abt/providers/asana/commands/finalize.rb +2 -4
- data/lib/abt/providers/asana/commands/pick.rb +11 -41
- data/lib/abt/providers/asana/commands/tasks.rb +2 -7
- data/lib/abt/providers/asana/commands/write_config.rb +73 -0
- data/lib/abt/providers/asana/configuration.rb +1 -1
- data/lib/abt/providers/asana/path.rb +2 -2
- data/lib/abt/providers/asana/services/project_picker.rb +54 -0
- data/lib/abt/providers/asana/services/task_picker.rb +83 -0
- data/lib/abt/providers/devops.rb +1 -0
- data/lib/abt/providers/devops/api.rb +27 -20
- data/lib/abt/providers/devops/base_command.rb +42 -25
- data/lib/abt/providers/devops/commands/branch_name.rb +8 -16
- data/lib/abt/providers/devops/commands/clear.rb +1 -1
- data/lib/abt/providers/devops/commands/current.rb +2 -21
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +8 -16
- data/lib/abt/providers/devops/commands/pick.rb +11 -60
- data/lib/abt/providers/devops/commands/work_items.rb +3 -7
- data/lib/abt/providers/devops/commands/write_config.rb +47 -0
- data/lib/abt/providers/devops/configuration.rb +1 -1
- data/lib/abt/providers/devops/path.rb +24 -8
- data/lib/abt/providers/devops/services/board_picker.rb +69 -0
- data/lib/abt/providers/devops/services/project_picker.rb +73 -0
- data/lib/abt/providers/devops/services/work_item_picker.rb +99 -0
- data/lib/abt/providers/harvest.rb +1 -0
- data/lib/abt/providers/harvest/base_command.rb +45 -3
- data/lib/abt/providers/harvest/commands/clear.rb +1 -1
- data/lib/abt/providers/harvest/commands/current.rb +0 -28
- data/lib/abt/providers/harvest/commands/pick.rb +12 -27
- data/lib/abt/providers/harvest/commands/projects.rb +2 -9
- data/lib/abt/providers/harvest/commands/tasks.rb +2 -19
- data/lib/abt/providers/harvest/commands/track.rb +72 -39
- data/lib/abt/providers/harvest/commands/write_config.rb +41 -0
- data/lib/abt/providers/harvest/configuration.rb +1 -1
- data/lib/abt/providers/harvest/harvest_helpers.rb +25 -0
- data/lib/abt/providers/harvest/path.rb +1 -1
- data/lib/abt/providers/harvest/services/project_picker.rb +53 -0
- data/lib/abt/providers/harvest/services/task_picker.rb +50 -0
- data/lib/abt/version.rb +1 -1
- metadata +13 -6
- data/lib/abt/providers/asana/commands/init.rb +0 -42
- data/lib/abt/providers/devops/commands/boards.rb +0 -34
- data/lib/abt/providers/devops/commands/init.rb +0 -79
- data/lib/abt/providers/harvest/commands/init.rb +0 -53
@@ -15,7 +15,7 @@ module Abt
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def path
|
18
|
-
Path.new(local_available? && git["path"] ||
|
18
|
+
Path.new(local_available? && git["path"] || cli.directory_config.dig("harvest", "path") || "")
|
19
19
|
end
|
20
20
|
|
21
21
|
def path=(new_path)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
module Providers
|
5
|
+
module Harvest
|
6
|
+
class HarvestHelpers
|
7
|
+
class << self
|
8
|
+
HOURS_REGEX = /(?<hours>\d+)/.freeze
|
9
|
+
MINUTES_REGEX = /(?<minutes>[0-5][0-9])/.freeze
|
10
|
+
SECONDS_REGEX = /(?<seconds>[0-5][0-9])/.freeze
|
11
|
+
TIME_REGEX = /^#{HOURS_REGEX}:#{MINUTES_REGEX}(?::#{SECONDS_REGEX})?$/.freeze
|
12
|
+
|
13
|
+
def decimal_hours_from_string(hh_mm_ss)
|
14
|
+
match = TIME_REGEX.match(hh_mm_ss)
|
15
|
+
raise Abt::Cli::Abort, "Invalid time: #{hh_mm_ss}, supported formats are: HH:MM, HH:MM:SS" if match.nil?
|
16
|
+
|
17
|
+
match[:hours].to_i +
|
18
|
+
match[:minutes].to_i / 60.0 +
|
19
|
+
match[:seconds].to_i / 60.0 / 60.0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
module Providers
|
5
|
+
module Harvest
|
6
|
+
module Services
|
7
|
+
class ProjectPicker
|
8
|
+
class Result
|
9
|
+
attr_reader :project, :path
|
10
|
+
|
11
|
+
def initialize(project:, path:)
|
12
|
+
@project = project
|
13
|
+
@path = path
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.call(**args)
|
18
|
+
new(**args).call
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :cli, :project_assignments
|
22
|
+
|
23
|
+
def initialize(cli:, project_assignments:)
|
24
|
+
@cli = cli
|
25
|
+
@project_assignments = project_assignments
|
26
|
+
end
|
27
|
+
|
28
|
+
def call
|
29
|
+
project = cli.prompt.search("Select a project", searchable_projects)["project"]
|
30
|
+
|
31
|
+
path = Path.from_ids(project_id: project["id"])
|
32
|
+
|
33
|
+
Result.new(project: project, path: path)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def searchable_projects
|
39
|
+
@searchable_projects ||= project_assignments.map do |project_assignment|
|
40
|
+
client = project_assignment["client"]
|
41
|
+
project = project_assignment["project"]
|
42
|
+
|
43
|
+
project_assignment.merge(
|
44
|
+
"name" => "#{client['name']} > #{project['name']}",
|
45
|
+
"project" => project
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
module Providers
|
5
|
+
module Harvest
|
6
|
+
module Services
|
7
|
+
class TaskPicker
|
8
|
+
class Result
|
9
|
+
attr_reader :task, :path
|
10
|
+
|
11
|
+
def initialize(task:, path:)
|
12
|
+
@task = task
|
13
|
+
@path = path
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.call(**args)
|
18
|
+
new(**args).call
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :cli, :path, :project_assignment
|
22
|
+
|
23
|
+
def initialize(cli:, path:, project_assignment:)
|
24
|
+
@cli = cli
|
25
|
+
@path = path
|
26
|
+
@project_assignment = project_assignment
|
27
|
+
end
|
28
|
+
|
29
|
+
def call
|
30
|
+
task = cli.prompt.choice("Select a task from #{project['name']}", tasks)
|
31
|
+
|
32
|
+
path_with_task = Path.from_ids(project_id: path.project_id, task_id: task["id"])
|
33
|
+
|
34
|
+
Result.new(task: task, path: path_with_task)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def project
|
40
|
+
project_assignment["project"]
|
41
|
+
end
|
42
|
+
|
43
|
+
def tasks
|
44
|
+
@tasks ||= project_assignment["task_assignments"].map { |ta| ta["task"] }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
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.31
|
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-
|
11
|
+
date: 2021-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-inflector
|
@@ -105,28 +105,32 @@ files:
|
|
105
105
|
- "./lib/abt/providers/asana/commands/current.rb"
|
106
106
|
- "./lib/abt/providers/asana/commands/finalize.rb"
|
107
107
|
- "./lib/abt/providers/asana/commands/harvest_time_entry_data.rb"
|
108
|
-
- "./lib/abt/providers/asana/commands/init.rb"
|
109
108
|
- "./lib/abt/providers/asana/commands/pick.rb"
|
110
109
|
- "./lib/abt/providers/asana/commands/projects.rb"
|
111
110
|
- "./lib/abt/providers/asana/commands/share.rb"
|
112
111
|
- "./lib/abt/providers/asana/commands/start.rb"
|
113
112
|
- "./lib/abt/providers/asana/commands/tasks.rb"
|
113
|
+
- "./lib/abt/providers/asana/commands/write_config.rb"
|
114
114
|
- "./lib/abt/providers/asana/configuration.rb"
|
115
115
|
- "./lib/abt/providers/asana/path.rb"
|
116
|
+
- "./lib/abt/providers/asana/services/project_picker.rb"
|
117
|
+
- "./lib/abt/providers/asana/services/task_picker.rb"
|
116
118
|
- "./lib/abt/providers/devops.rb"
|
117
119
|
- "./lib/abt/providers/devops/api.rb"
|
118
120
|
- "./lib/abt/providers/devops/base_command.rb"
|
119
|
-
- "./lib/abt/providers/devops/commands/boards.rb"
|
120
121
|
- "./lib/abt/providers/devops/commands/branch_name.rb"
|
121
122
|
- "./lib/abt/providers/devops/commands/clear.rb"
|
122
123
|
- "./lib/abt/providers/devops/commands/current.rb"
|
123
124
|
- "./lib/abt/providers/devops/commands/harvest_time_entry_data.rb"
|
124
|
-
- "./lib/abt/providers/devops/commands/init.rb"
|
125
125
|
- "./lib/abt/providers/devops/commands/pick.rb"
|
126
126
|
- "./lib/abt/providers/devops/commands/share.rb"
|
127
127
|
- "./lib/abt/providers/devops/commands/work_items.rb"
|
128
|
+
- "./lib/abt/providers/devops/commands/write_config.rb"
|
128
129
|
- "./lib/abt/providers/devops/configuration.rb"
|
129
130
|
- "./lib/abt/providers/devops/path.rb"
|
131
|
+
- "./lib/abt/providers/devops/services/board_picker.rb"
|
132
|
+
- "./lib/abt/providers/devops/services/project_picker.rb"
|
133
|
+
- "./lib/abt/providers/devops/services/work_item_picker.rb"
|
130
134
|
- "./lib/abt/providers/git.rb"
|
131
135
|
- "./lib/abt/providers/git/commands/branch.rb"
|
132
136
|
- "./lib/abt/providers/harvest.rb"
|
@@ -134,7 +138,6 @@ files:
|
|
134
138
|
- "./lib/abt/providers/harvest/base_command.rb"
|
135
139
|
- "./lib/abt/providers/harvest/commands/clear.rb"
|
136
140
|
- "./lib/abt/providers/harvest/commands/current.rb"
|
137
|
-
- "./lib/abt/providers/harvest/commands/init.rb"
|
138
141
|
- "./lib/abt/providers/harvest/commands/pick.rb"
|
139
142
|
- "./lib/abt/providers/harvest/commands/projects.rb"
|
140
143
|
- "./lib/abt/providers/harvest/commands/share.rb"
|
@@ -142,8 +145,12 @@ files:
|
|
142
145
|
- "./lib/abt/providers/harvest/commands/stop.rb"
|
143
146
|
- "./lib/abt/providers/harvest/commands/tasks.rb"
|
144
147
|
- "./lib/abt/providers/harvest/commands/track.rb"
|
148
|
+
- "./lib/abt/providers/harvest/commands/write_config.rb"
|
145
149
|
- "./lib/abt/providers/harvest/configuration.rb"
|
150
|
+
- "./lib/abt/providers/harvest/harvest_helpers.rb"
|
146
151
|
- "./lib/abt/providers/harvest/path.rb"
|
152
|
+
- "./lib/abt/providers/harvest/services/project_picker.rb"
|
153
|
+
- "./lib/abt/providers/harvest/services/task_picker.rb"
|
147
154
|
- "./lib/abt/version.rb"
|
148
155
|
- bin/abt
|
149
156
|
homepage: https://github.com/abtion/abt
|
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Abt
|
4
|
-
module Providers
|
5
|
-
module Asana
|
6
|
-
module Commands
|
7
|
-
class Init < BaseCommand
|
8
|
-
def self.usage
|
9
|
-
"abt init asana"
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.description
|
13
|
-
"Pick Asana project for current git repository"
|
14
|
-
end
|
15
|
-
|
16
|
-
def perform
|
17
|
-
require_local_config!
|
18
|
-
|
19
|
-
projects # Load projects up front to make it obvious that searches are instant
|
20
|
-
project = cli.prompt.search("Select a project", projects)
|
21
|
-
|
22
|
-
config.path = Path.from_ids(project_gid: project["gid"])
|
23
|
-
|
24
|
-
print_project(project)
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def projects
|
30
|
-
@projects ||= begin
|
31
|
-
warn("Fetching projects...")
|
32
|
-
api.get_paged("projects",
|
33
|
-
workspace: config.workspace_gid,
|
34
|
-
archived: false,
|
35
|
-
opt_fields: "name,permalink_url")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,34 +0,0 @@
|
|
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.usage
|
9
|
-
"abt boards devops"
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.description
|
13
|
-
"List all boards - useful for piping into grep etc"
|
14
|
-
end
|
15
|
-
|
16
|
-
def perform
|
17
|
-
abort("No organization selected. Did you initialize DevOps?") if organization_name.nil?
|
18
|
-
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
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Abt
|
4
|
-
module Providers
|
5
|
-
module Devops
|
6
|
-
module Commands
|
7
|
-
class Init < BaseCommand
|
8
|
-
AZURE_DEV_URL_REGEX = %r{^https://dev\.azure\.com/(?<organization>[^/]+)/(?<project>[^/]+)}.freeze
|
9
|
-
VS_URL_REGEX = %r{^https://(?<organization>[^.]+)\.visualstudio\.com/(?<project>[^/]+)}.freeze
|
10
|
-
|
11
|
-
def self.usage
|
12
|
-
"abt init devops"
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.description
|
16
|
-
"Pick DevOps board for current git repository"
|
17
|
-
end
|
18
|
-
|
19
|
-
def perform
|
20
|
-
require_local_config!
|
21
|
-
board = cli.prompt.choice("Select a project work board", boards)
|
22
|
-
|
23
|
-
config.path = Path.from_ids(
|
24
|
-
organization_name: organization_name,
|
25
|
-
project_name: project_name,
|
26
|
-
board_id: board["id"]
|
27
|
-
)
|
28
|
-
print_board(organization_name, project_name, board)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def boards
|
34
|
-
@boards ||= api.get_paged("work/boards")
|
35
|
-
end
|
36
|
-
|
37
|
-
def project_name
|
38
|
-
@project_name ||= begin
|
39
|
-
if (match = AZURE_DEV_URL_REGEX.match(project_url)) ||
|
40
|
-
(match = VS_URL_REGEX.match(project_url))
|
41
|
-
match[:project]
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def organization_name
|
47
|
-
@organization_name ||= begin
|
48
|
-
if (match = AZURE_DEV_URL_REGEX.match(project_url)) ||
|
49
|
-
(match = VS_URL_REGEX.match(project_url))
|
50
|
-
match[:organization]
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def project_url
|
56
|
-
@project_url ||= begin
|
57
|
-
loop do
|
58
|
-
url = cli.prompt.text(project_url_prompt_text)
|
59
|
-
|
60
|
-
break url if AZURE_DEV_URL_REGEX =~ url || VS_URL_REGEX =~ url
|
61
|
-
|
62
|
-
warn("Invalid URL")
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def project_url_prompt_text
|
68
|
-
<<~TXT
|
69
|
-
Please provide the URL for the devops project
|
70
|
-
For instance https://{organization}.visualstudio.com/{project} or https://dev.azure.com/{organization}/{project}
|
71
|
-
|
72
|
-
Enter URL
|
73
|
-
TXT
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Abt
|
4
|
-
module Providers
|
5
|
-
module Harvest
|
6
|
-
module Commands
|
7
|
-
class Init < BaseCommand
|
8
|
-
def self.usage
|
9
|
-
"abt init harvest"
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.description
|
13
|
-
"Pick Harvest project for current git repository"
|
14
|
-
end
|
15
|
-
|
16
|
-
def perform
|
17
|
-
require_local_config!
|
18
|
-
projects # Load projects up front to make it obvious that searches are instant
|
19
|
-
project = cli.prompt.search("Select a project", searchable_projects)["project"]
|
20
|
-
|
21
|
-
config.path = Path.from_ids(project_id: project["id"])
|
22
|
-
|
23
|
-
print_project(project)
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def searchable_projects
|
29
|
-
@searchable_projects ||= projects.map do |project|
|
30
|
-
{
|
31
|
-
"name" => "#{project['client']['name']} > #{project['name']}",
|
32
|
-
"project" => project
|
33
|
-
}
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def projects
|
38
|
-
@projects ||= begin
|
39
|
-
warn("Fetching projects...")
|
40
|
-
project_assignments.map do |project_assignment|
|
41
|
-
project_assignment["project"].merge("client" => project_assignment["client"])
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def project_assignments
|
47
|
-
@project_assignments ||= api.get_paged("users/me/project_assignments")
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|