dude-cli 2.0.4 → 2.1.0.alpha2
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/.github/workflows/verify.yml +32 -0
- data/.rspec +0 -1
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +7 -3
- data/Gemfile +3 -1
- data/Gemfile.lock +48 -9
- data/LICENCE +20 -0
- data/README.md +32 -6
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/bin/dude +2 -2
- data/dude.gemspec +25 -18
- data/lib/dude.rb +9 -9
- data/lib/dude/code_management.rb +10 -0
- data/lib/dude/code_management/github/client.rb +23 -0
- data/lib/dude/code_management/github/create_pull_request.rb +53 -0
- data/lib/dude/commands.rb +24 -17
- data/lib/dude/commands/checkout.rb +4 -2
- data/lib/dude/commands/install.rb +32 -18
- data/lib/dude/commands/move.rb +5 -3
- data/lib/dude/commands/pr.rb +11 -0
- data/lib/dude/commands/pr/create.rb +49 -0
- data/lib/dude/commands/pr/remove.rb +15 -0
- data/lib/dude/commands/start.rb +9 -3
- data/lib/dude/commands/stop.rb +4 -2
- data/lib/dude/commands/tasks.rb +6 -3
- data/lib/dude/commands/track.rb +5 -3
- data/lib/dude/commands/version.rb +3 -1
- data/lib/dude/git.rb +3 -0
- data/lib/dude/git/checkout.rb +20 -1
- data/lib/dude/git/current_branch_name.rb +3 -1
- data/lib/dude/git/remote_name.rb +21 -0
- data/lib/dude/project_management/client.rb +14 -6
- data/lib/dude/project_management/entities/issue.rb +10 -7
- data/lib/dude/project_management/jira.rb +2 -1
- data/lib/dude/project_management/jira/client.rb +14 -7
- data/lib/dude/project_management/jira/fetch_current_task.rb +37 -0
- data/lib/dude/project_management/jira/fetch_current_tasks.rb +48 -0
- data/lib/dude/project_management/jira/get_task_name_by_id.rb +1 -1
- data/lib/dude/project_management/jira/move_task_to_list.rb +15 -13
- data/lib/dude/project_management/trello.rb +5 -3
- data/lib/dude/project_management/trello/client.rb +40 -21
- data/lib/dude/project_management/trello/fetch_current_task.rb +43 -0
- data/lib/dude/project_management/trello/fetch_current_tasks.rb +53 -0
- data/lib/dude/project_management/trello/fetch_lists.rb +24 -0
- data/lib/dude/project_management/trello/get_task_name_by_id.rb +25 -0
- data/lib/dude/project_management/trello/move_task_to_list.rb +55 -0
- data/lib/dude/settings.rb +3 -0
- data/lib/dude/templates/pull_request_template +7 -0
- data/lib/dude/time_trackers/toggl.rb +2 -0
- data/lib/dude/time_trackers/toggl/base.rb +4 -0
- data/lib/dude/time_trackers/toggl/start_time_entry.rb +3 -2
- data/lib/dude/time_trackers/toggl/stop_time_entry.rb +3 -1
- data/lib/dude/version.rb +3 -1
- metadata +77 -21
- data/lib/dude/project_management/entities/board.rb +0 -9
- data/lib/dude/project_management/jira/get_current_tasks.rb +0 -40
- data/lib/dude/project_management/trello/checkout.rb +0 -4
- data/lib/dude/project_management/trello/get_current_tasks.rb +0 -24
- data/lib/dude/project_management/trello/get_lists.rb +0 -15
- data/lib/dude/project_management/trello/move_tasks_to_list.rb +0 -14
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dude
|
4
|
+
module Git
|
5
|
+
class RemoteName
|
6
|
+
def call
|
7
|
+
extract_name push_url
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def push_url
|
13
|
+
`git remote show origin`.split("\n")[1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def extract_name(url)
|
17
|
+
url.scan(%r{(?<=github\.com[/:])(.*)(?=\.git)})[0][0]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './jira/client'
|
4
|
+
require_relative './trello/client'
|
3
5
|
|
4
6
|
module Dude
|
5
7
|
module ProjectManagement
|
@@ -9,16 +11,22 @@ module Dude
|
|
9
11
|
attr_reader :client
|
10
12
|
|
11
13
|
def initialize
|
12
|
-
|
13
|
-
|
14
|
+
tool = settings['PROJECT_MANAGEMENT_TOOL']
|
15
|
+
return unless LIST_OF_AVAILABLE_PROJECT_MANAGEMENT_TOOLS.include? tool
|
16
|
+
|
17
|
+
@client = setup_client(tool)
|
14
18
|
end
|
15
19
|
|
16
20
|
def respond_to_missing?(method_name, include_private = false)
|
17
21
|
client.respond_to_missing?(method_name, include_private)
|
18
22
|
end
|
19
23
|
|
20
|
-
def method_missing(
|
21
|
-
client.send(
|
24
|
+
def method_missing(method, *args, &block)
|
25
|
+
client.send(method, *args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def setup_client(tool)
|
29
|
+
Object.const_get("Dude::ProjectManagement::#{tool.capitalize}::Client").new
|
22
30
|
end
|
23
31
|
end
|
24
32
|
end
|
@@ -1,15 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dude
|
2
4
|
module ProjectManagement
|
3
5
|
module Entities
|
4
6
|
class Issue
|
5
|
-
attr_accessor :id, :title, :description, :status, :assignee
|
7
|
+
attr_accessor :id, :title, :description, :status, :assignee, :url
|
6
8
|
|
7
|
-
def initialize(
|
8
|
-
@id = id
|
9
|
-
@title = title
|
10
|
-
@description = description
|
11
|
-
@status = status
|
12
|
-
@assignee = assignee
|
9
|
+
def initialize(params)
|
10
|
+
@id = params[:id]
|
11
|
+
@title = params[:title]
|
12
|
+
@description = params[:description]
|
13
|
+
@status = params[:status]
|
14
|
+
@assignee = params[:assignee]
|
15
|
+
@url = params[:url]
|
13
16
|
end
|
14
17
|
|
15
18
|
def todo?
|
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'jira-ruby'
|
2
|
-
|
3
|
-
|
4
|
-
|
4
|
+
require_relative './fetch_current_tasks'
|
5
|
+
require_relative './fetch_current_task'
|
6
|
+
require_relative './move_task_to_list'
|
7
|
+
require_relative './get_task_name_by_id'
|
5
8
|
|
6
9
|
module Dude
|
7
10
|
module ProjectManagement
|
@@ -28,12 +31,16 @@ module Dude
|
|
28
31
|
client.respond_to_missing?(method_name, include_private)
|
29
32
|
end
|
30
33
|
|
31
|
-
def method_missing(
|
32
|
-
client.send(
|
34
|
+
def method_missing(method, *args, &block)
|
35
|
+
client.send(method, *args, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch_current_tasks
|
39
|
+
FetchCurrentTasks.new(client).call
|
33
40
|
end
|
34
41
|
|
35
|
-
def
|
36
|
-
|
42
|
+
def fetch_current_task(id)
|
43
|
+
FetchCurrentTask.new(client, id: id).call
|
37
44
|
end
|
38
45
|
|
39
46
|
def move_task_to_list(id, list)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../entities/issue'
|
4
|
+
|
5
|
+
module Dude
|
6
|
+
module ProjectManagement
|
7
|
+
module Jira
|
8
|
+
class FetchCurrentTask
|
9
|
+
include Settings
|
10
|
+
|
11
|
+
def initialize(client, id:)
|
12
|
+
@client = client
|
13
|
+
@id = id
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
create_issue(client.Issue.find(id))
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :client, :id
|
23
|
+
|
24
|
+
def create_issue(issue)
|
25
|
+
Entities::Issue.new(
|
26
|
+
id: issue.key,
|
27
|
+
title: issue.summary,
|
28
|
+
description: issue.description,
|
29
|
+
status: issue.status.name,
|
30
|
+
assignee: issue&.assignee&.displayName,
|
31
|
+
url: "#{settings['ATLASSIAN_URL']}/browse/#{issue.key}"
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../entities/issue'
|
4
|
+
|
5
|
+
module Dude
|
6
|
+
module ProjectManagement
|
7
|
+
module Jira
|
8
|
+
class FetchCurrentTasks
|
9
|
+
include Settings
|
10
|
+
|
11
|
+
def initialize(client)
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
board = client.Board.find(settings['ATLASSIAN_BOARD_ID'])
|
17
|
+
|
18
|
+
all_issues = board_type(board)
|
19
|
+
|
20
|
+
all_issues.map { |issue| create_issue(issue) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :client
|
26
|
+
|
27
|
+
def board_type(board)
|
28
|
+
case board.type
|
29
|
+
when 'kanban' then board.issues
|
30
|
+
when 'simple', 'scrum' then board.sprints(state: 'active').flat_map(&:issues)
|
31
|
+
else raise Dude::ToBeImplementedError
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_issue(issue)
|
36
|
+
Entities::Issue.new(
|
37
|
+
id: issue.key,
|
38
|
+
title: issue.summary,
|
39
|
+
description: issue.description,
|
40
|
+
status: issue.status.name,
|
41
|
+
assignee: issue&.assignee&.displayName,
|
42
|
+
url: "#{settings['ATLASSIAN_URL']}/browse/#{issue.key}"
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Dude
|
4
4
|
module ProjectManagement
|
@@ -14,33 +14,35 @@ module Dude
|
|
14
14
|
|
15
15
|
def call
|
16
16
|
issue = client.Issue.find(id)
|
17
|
-
available_transitions = client.Transition.all(:
|
17
|
+
available_transitions = client.Transition.all(issue: issue)
|
18
|
+
transition_id = generate_transition_id(issue, available_transitions)
|
19
|
+
transition = issue.transitions.build
|
20
|
+
transition.save!(transition: { id: transition_id })
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :client, :id, :list_name
|
18
26
|
|
19
|
-
|
27
|
+
def generate_transition_id(issue, available_transitions)
|
28
|
+
if list_name
|
20
29
|
available_transitions.find { |transition| transition.name == list_name }.id
|
21
30
|
else
|
22
31
|
select_list_for_moving(issue, available_transitions).id
|
23
32
|
end
|
24
|
-
|
25
|
-
transition = issue.transitions.build
|
26
|
-
transition.save!(transition: { id: transition_id })
|
27
33
|
end
|
28
34
|
|
29
|
-
|
30
|
-
|
31
|
-
def select_list_for_moving(issuem, available_transitions)
|
32
|
-
puts "Please, select list for moving:".green.bold
|
35
|
+
def select_list_for_moving(_issue, available_transitions)
|
36
|
+
puts 'Please, select list for moving:'.green.bold
|
33
37
|
|
34
38
|
available_transitions.each_with_index do |ea, index|
|
35
39
|
puts "#{index + 1}: #{ea.name.bold}"
|
36
40
|
end
|
37
41
|
|
38
42
|
print "\nList index: ".bold
|
39
|
-
list_index =
|
43
|
+
list_index = $stdin.gets.chomp
|
40
44
|
available_transitions[list_index.to_i - 1]
|
41
45
|
end
|
42
|
-
|
43
|
-
attr_reader :client, :id, :list_name
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
@@ -1,32 +1,51 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './fetch_current_tasks'
|
4
|
+
require_relative './fetch_current_task'
|
5
|
+
require_relative './move_task_to_list'
|
6
|
+
require_relative './get_task_name_by_id'
|
2
7
|
|
3
8
|
require 'faraday'
|
4
9
|
require 'json'
|
5
10
|
|
6
11
|
module Dude
|
7
|
-
module
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def faraday_client
|
12
|
-
@faraday_client ||= Faraday.new('https://api.trello.com/', {
|
13
|
-
params: {
|
14
|
-
key: "62b20e9eeab2d6e06145c69178521225",
|
15
|
-
token: "6285fc2d2ff6100a5c341838d0e4acfed3601ae503beb973ddccf1cfab088537"
|
16
|
-
}
|
17
|
-
})
|
18
|
-
end
|
12
|
+
module ProjectManagement
|
13
|
+
module Trello
|
14
|
+
class Client
|
15
|
+
include Settings
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
def client
|
18
|
+
@client ||= Faraday.new('https://api.trello.com/', {
|
19
|
+
params: {
|
20
|
+
key: settings['TRELLO_KEY'],
|
21
|
+
token: settings['TRELLO_TOKEN']
|
22
|
+
}
|
23
|
+
})
|
24
|
+
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
26
|
+
def method_missing(method, *args, &block)
|
27
|
+
faraday_client.send(method, *args, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_to_missing?(method_name, include_private = false)
|
31
|
+
client.respond_to_missing?(method_name, include_private)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fetch_current_tasks
|
35
|
+
FetchCurrentTasks.new(client).call
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch_current_task(id)
|
39
|
+
FetchCurrentTask.new(client, id: id).call
|
40
|
+
end
|
41
|
+
|
42
|
+
def move_task_to_list(id, list)
|
43
|
+
MoveTaskToList.new(client, id: id, list_name: list).call
|
44
|
+
end
|
27
45
|
|
28
|
-
|
29
|
-
|
46
|
+
def get_task_name_by_id(id)
|
47
|
+
GetTaskNameById.new(client, id: id).call
|
48
|
+
end
|
30
49
|
end
|
31
50
|
end
|
32
51
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dude
|
4
|
+
module ProjectManagement
|
5
|
+
module Trello
|
6
|
+
class FetchCurrentTask
|
7
|
+
include Settings
|
8
|
+
|
9
|
+
def initialize(client, id:)
|
10
|
+
@client = client
|
11
|
+
@id = id
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
response = client.get("/1/boards/#{settings['ATLASSIAN_BOARD_ID']}/cards/#{id}")
|
16
|
+
create_issue JSON.parse(response.body)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :client, :id
|
22
|
+
|
23
|
+
def create_issue(issue)
|
24
|
+
Entities::Issue.new(
|
25
|
+
id: issue['idShort'],
|
26
|
+
title: issue['name'],
|
27
|
+
description: issue['desc'],
|
28
|
+
status: settings['IN_PROGRESS_LIST_NAME'], # OMG, let's fix this later
|
29
|
+
assignee: members(issue),
|
30
|
+
url: issue['shortUrl']
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def members(issue)
|
35
|
+
people = issue['idMembers'].map do |person|
|
36
|
+
JSON.parse(client.get("/1/members/#{person}", fields: 'fullName').body)['fullName']
|
37
|
+
end
|
38
|
+
people.empty? ? nil : people.join(', ')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './fetch_lists'
|
4
|
+
|
5
|
+
module Dude
|
6
|
+
module ProjectManagement
|
7
|
+
module Trello
|
8
|
+
class FetchCurrentTasks
|
9
|
+
include Settings
|
10
|
+
|
11
|
+
attr_reader :fetch_lists
|
12
|
+
|
13
|
+
def initialize(client, fetch_lists: nil)
|
14
|
+
@client = client
|
15
|
+
|
16
|
+
@fetch_lists = fetch_lists || FetchLists.new(client)
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
lists = fetch_lists.call
|
21
|
+
lists.map { |list| retrieve_list_issues(list) }.flatten
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :client
|
27
|
+
|
28
|
+
def retrieve_list_issues(list)
|
29
|
+
response = client.get("/1/lists/#{list['id']}/cards")
|
30
|
+
body = JSON.parse(response.body)
|
31
|
+
body.map { |issue| create_issue(issue, list) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_issue(issue, current_list)
|
35
|
+
Entities::Issue.new(
|
36
|
+
id: issue['idShort'],
|
37
|
+
title: issue['name'],
|
38
|
+
description: issue['desc'],
|
39
|
+
status: current_list['name'],
|
40
|
+
assignee: members(issue)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def members(issue)
|
45
|
+
people = issue['idMembers'].map do |person|
|
46
|
+
JSON.parse(client.get("/1/members/#{person}", fields: 'fullName').body)['fullName']
|
47
|
+
end
|
48
|
+
people.empty? ? nil : people.join(', ')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|