dude-cli 2.0.7 → 2.1.0.alpha5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rubocop.yml +5 -1
  4. data/Gemfile.lock +36 -15
  5. data/README.md +43 -35
  6. data/dude.gemspec +3 -2
  7. data/lib/dude.rb +12 -2
  8. data/lib/dude/code_management.rb +10 -0
  9. data/lib/dude/code_management/github/client.rb +27 -0
  10. data/lib/dude/code_management/github/create_pull_request.rb +61 -0
  11. data/lib/dude/commands.rb +7 -0
  12. data/lib/dude/commands/health_check.rb +15 -0
  13. data/lib/dude/commands/install.rb +63 -41
  14. data/lib/dude/commands/pr.rb +11 -0
  15. data/lib/dude/commands/pr/create.rb +49 -0
  16. data/lib/dude/commands/pr/remove.rb +15 -0
  17. data/lib/dude/commands/start.rb +10 -8
  18. data/lib/dude/commands/stop.rb +0 -2
  19. data/lib/dude/commands/tasks.rb +0 -2
  20. data/lib/dude/commands/track.rb +2 -4
  21. data/lib/dude/config.rb +18 -0
  22. data/lib/dude/git.rb +1 -0
  23. data/lib/dude/git/remote_name.rb +21 -0
  24. data/lib/dude/health_check.rb +39 -0
  25. data/lib/dude/project_management/client.rb +8 -4
  26. data/lib/dude/project_management/entities/issue.rb +8 -7
  27. data/lib/dude/project_management/jira/client.rb +21 -8
  28. data/lib/dude/project_management/jira/fetch_current_task.rb +37 -0
  29. data/lib/dude/project_management/jira/fetch_current_tasks.rb +3 -4
  30. data/lib/dude/project_management/jira/get_task_name_by_id.rb +0 -2
  31. data/lib/dude/project_management/jira/move_task_to_list.rb +0 -2
  32. data/lib/dude/project_management/{entities/board.rb → trello.rb} +1 -3
  33. data/lib/dude/project_management/trello/client.rb +56 -0
  34. data/lib/dude/project_management/trello/fetch_current_task.rb +41 -0
  35. data/lib/dude/project_management/trello/fetch_current_tasks.rb +51 -0
  36. data/lib/dude/project_management/trello/fetch_lists.rb +22 -0
  37. data/lib/dude/project_management/trello/get_task_name_by_id.rb +23 -0
  38. data/lib/dude/project_management/trello/move_task_to_list.rb +53 -0
  39. data/lib/dude/setup/github.rb +35 -0
  40. data/lib/dude/setup/jira.rb +64 -0
  41. data/lib/dude/setup/toggl.rb +47 -0
  42. data/lib/dude/setup/trello.rb +58 -0
  43. data/lib/dude/templates/duderc_template +32 -0
  44. data/lib/dude/time_trackers/toggl/base.rb +2 -4
  45. data/lib/dude/version.rb +1 -1
  46. metadata +42 -6
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tty-prompt'
4
+ require 'fileutils'
5
+
3
6
  require_relative '../settings'
7
+ require_relative '../setup/jira'
8
+ require_relative '../setup/trello'
9
+ require_relative '../setup/toggl'
10
+ require_relative '../setup/github'
4
11
 
5
12
  module Dude
6
13
  module Commands
@@ -8,51 +15,66 @@ module Dude
8
15
  desc 'Creates .duderc for future configuration'
9
16
 
10
17
  def call
11
- path = File.join(Dir.home, Settings::CONFIG_FILE)
12
- if File.exist?(path)
13
- puts 'Config file already exists'
14
- else
15
- File.open(path, 'w') { |f| f.write(duderc_file_content) }
16
- puts '.duderc created in your HOME directory'
17
- end
18
+ @prompt = TTY::Prompt.new
19
+
20
+ create_file_if_not_exists
21
+
22
+ @current_settings = Dude::Config.configure_with('.duderc.yml')
23
+ @current_settings[:project_management_tool] = setup_project_management_tool # jira, trello
24
+ @current_settings = send("setup_#{current_settings[:project_management_tool]}")
25
+ setup_features.each { send("setup_#{_1}") } # toggl, github
26
+
27
+ save
18
28
  end
19
29
 
20
30
  private
21
31
 
22
- def duderc_file_content
23
- <<~HEREDOC
24
- # Please, don't use quotes and spaces.
25
- # Write all variables using following format: NAME=VALUE
26
- #
27
- # Now jira only (Github, Gitlab, Trello later)
28
- PROJECT_MANAGEMENT_TOOL=jira
29
- ATLASSIAN_EMAIL=
30
- # How to create Atlassian token: https://support.siteimprove.com/hc/en-gb/articles/360004317332-How-to-create-an-API-token-from-your-Atlassian-account
31
- ATLASSIAN_TOKEN=
32
- # URL of your project. Example: https://example.atlassian.net
33
- ATLASSIAN_URL=
34
- # KEY of your project. If your issues have id BT-123 - BT is the key
35
- ATLASSIAN_PROJECT_KEY=
36
- # Just open your atlassian main board and copy id from the url after rapidView=*ID* part.
37
- # Example: https://dealmakerns.atlassian.net/secure/RapidBoard.jspa?rapidView=23&projectKey=DT - 23 is the id
38
- ATLASSIAN_BOARD_ID=
39
-
40
- # Replace it with your project list names. Skip for empty lists
41
- TODO_LIST_NAME=To Do
42
- IN_PROGRESS_LIST_NAME=In Progress
43
- CODE_REVIEW_LIST_NAME=Code Review
44
- TESTING_LIST_NAME=TESTABLE
45
- DONE_LIST_NAME=Done
46
-
47
- # Your Toggl project name
48
- TOGGL_PROJECT_NAME=
49
- # Your Toggl API token can be found at the bottom of the page: https://track.toggl.com/profile
50
- TOGGL_TOKEN=
51
- # Can be copied from url here: https://toggl.com/app/projects/. Example: 123456
52
- TOGGL_WORKSPACE_ID=
53
- # Use the *id* and *title* and specify format for the task titles in Trello or keep it as it is
54
- TOGGL_TASK_FORMAT=[id] title
55
- HEREDOC
32
+ attr_reader :prompt, :current_settings
33
+
34
+ def setup_project_management_tool
35
+ prompt.select(Dude::Config.style_prompt("Select project management tool you're going to use:")) do |menu|
36
+ menu.choice name: 'Jira', value: 'jira'
37
+ menu.choice name: 'Trello', value: 'trello'
38
+ menu.choice name: 'Pivotal Tracker', value: 'pivotal', disabled: '(coming in future)'
39
+ menu.choice name: 'Github', value: 'github', disabled: '(coming in future)'
40
+ end
41
+ end
42
+
43
+ def method_missing(method, *args, &block)
44
+ return super unless method.start_with?('setup_')
45
+
46
+ const_name = method.to_s.split('setup_').last
47
+ Object.const_get("Dude::Setup::#{const_name.capitalize}").new(prompt).call(settings: current_settings)
48
+ end
49
+
50
+ def respond_to_missing?(method_name, include_private = false)
51
+ client.respond_to_missing?(method_name, include_private)
52
+ end
53
+
54
+ def setup_features
55
+ prompt.multi_select(Dude::Config.style_prompt('Select features you want to use:')) do |menu|
56
+ menu.choice 'Toggl time tracking features (Create/stop time entries)', :toggl
57
+ menu.choice 'Github PR creation', :github
58
+ end
59
+ end
60
+
61
+ def save
62
+ File.open('.duderc.yml', 'w') { |file| file.write(current_settings.to_yaml) }
63
+ puts 'Configuration file has been sucessfully updated'.green.bold
64
+ puts 'Your settings are in the .duderc.yml file'.yellow
65
+ puts 'You could change it manually for editing Toggl task format and Github PR template'.yellow
66
+ rescue StandardError => e
67
+ puts "Something went wrong: #{e}"
68
+ end
69
+
70
+ def create_file_if_not_exists
71
+ path = File.join(Dir.pwd, Config::FILE_NAME)
72
+ if File.exist?(path)
73
+ puts 'Config file already exists. All settings will be rewrited'
74
+ else
75
+ FileUtils.cp(File.join(File.dirname(__FILE__), '../templates/duderc_template'), path)
76
+ puts '.duderc created in your HOME directory'
77
+ end
56
78
  end
57
79
  end
58
80
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './pr/create'
4
+ require_relative './pr/remove'
5
+
6
+ module Dude
7
+ module Commands
8
+ module PR
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dude
4
+ module Commands
5
+ module PR
6
+ BASE_BRANCH = 'master'
7
+
8
+ class Create < Dry::CLI::Command
9
+ desc 'Create PR with custom template description'
10
+
11
+ argument :id, required: true, desc: 'The card short ID'
12
+
13
+ def call(id:)
14
+ @id = id
15
+ client = CodeManagement::Github::Client.new
16
+ client.create_pull_request(issue: issue, owner: owner, repo: repo, params: params)
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :id
22
+
23
+ def owner
24
+ repository_name.split('/')[0]
25
+ end
26
+
27
+ def issue
28
+ client = ProjectManagement::Client.new
29
+ client.fetch_current_task(id)
30
+ end
31
+
32
+ def repo
33
+ repository_name.split('/')[1]
34
+ end
35
+
36
+ def params
37
+ {
38
+ head: Git::CurrentBranchName.new.call,
39
+ base: BASE_BRANCH
40
+ }
41
+ end
42
+
43
+ def repository_name
44
+ @repository_name ||= Git::RemoteName.new.call
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dude
4
+ module Commands
5
+ module PR
6
+ class Remove < Dry::CLI::Command
7
+ desc 'Remove'
8
+
9
+ def call(*)
10
+ puts 'To be created later'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,8 +3,6 @@
3
3
  module Dude
4
4
  module Commands
5
5
  class Start < Dry::CLI::Command
6
- include Settings
7
-
8
6
  desc 'Start task (Do checkout, track and move actions)'
9
7
 
10
8
  argument :id, required: true, desc: 'The card short ID'
@@ -12,20 +10,24 @@ module Dude
12
10
  def call(id:)
13
11
  Commands::Move.new.call(id: id, list: selected_list('in_progress'))
14
12
  Commands::Checkout.new.call(id: id)
15
- Commands::Track.new.call(id: id)
13
+ Commands::Track.new.call(id: id) if time_tracking_enabled?
16
14
  end
17
15
 
18
16
  private
19
17
 
20
18
  def selected_list(list)
21
19
  case list
22
- when 'todo' then settings['TODO_LIST_NAME']
23
- when 'in_progress' then settings['IN_PROGRESS_LIST_NAME']
24
- when 'code_review' then settings['CODE_REVIEW_LIST_NAME']
25
- when 'testing' then settings['TESTING_LIST_NAME']
26
- when 'done' then settings['DONE_LIST_NAME']
20
+ when 'todo' then Dude::SETTINGS[:todo_list_name]
21
+ when 'in_progress' then Dude::SETTINGS[:in_progress_list_name]
22
+ when 'code_review' then Dude::SETTINGS[:code_review_list_name]
23
+ when 'testing' then Dude::SETTINGS[:testing_list_name]
24
+ when 'done' then Dude::SETTINGS[:done_list_name]
27
25
  end
28
26
  end
27
+
28
+ def time_tracking_enabled?
29
+ !Dude::SETTINGS.dig(:toggl, :token).nil?
30
+ end
29
31
  end
30
32
  end
31
33
  end
@@ -5,8 +5,6 @@ require_relative '../time_trackers/toggl/stop_time_entry'
5
5
  module Dude
6
6
  module Commands
7
7
  class Stop < Dry::CLI::Command
8
- include Settings
9
-
10
8
  desc 'Stop current time entry in Toggl'
11
9
 
12
10
  def call
@@ -5,8 +5,6 @@ require_relative '../project_management/client'
5
5
  module Dude
6
6
  module Commands
7
7
  class Tasks < Dry::CLI::Command
8
- include Settings
9
-
10
8
  desc "Print tasks as list with ID's and assignees"
11
9
 
12
10
  def call
@@ -5,15 +5,13 @@ require_relative '../time_trackers/toggl/start_time_entry'
5
5
  module Dude
6
6
  module Commands
7
7
  class Track < Dry::CLI::Command
8
- include Settings
9
-
10
8
  desc 'Start time entry in Toggl with issue title and id'
11
9
 
12
10
  argument :id, required: true, desc: 'The card short ID'
13
11
 
14
12
  def call(id:)
15
13
  @id = id
16
- Dude::Toggl::StartTimeEntry.new.call(task_title: task_title, project: settings['TOGGL_PROJECT_NAME'])
14
+ Dude::Toggl::StartTimeEntry.new.call(task_title: task_title, project: Dude::SETTINGS.dig(:toggl, :project_name))
17
15
  end
18
16
 
19
17
  private
@@ -23,7 +21,7 @@ module Dude
23
21
  def task_title
24
22
  client = ProjectManagement::Client.new
25
23
  issue_title = client.get_task_name_by_id(id)
26
- settings['TOGGL_TASK_FORMAT'].sub(/id/, id).sub(/title/, issue_title)
24
+ Dude::SETTINGS.dig(:toggl, :task_format).sub(/{issue_id}/, id).sub(/{issue_title}/, issue_title)
27
25
  end
28
26
  end
29
27
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dude
4
+ class Config
5
+ FILE_NAME = '.duderc.yml'
6
+
7
+ # Configure through yaml file
8
+ def self.configure_with(path_to_yaml_file)
9
+ YAML.safe_load(IO.read(path_to_yaml_file), [Symbol])
10
+ rescue StandardError
11
+ {}
12
+ end
13
+
14
+ def self.style_prompt(text)
15
+ "#{'=>'.green.bold} #{text}"
16
+ end
17
+ end
18
+ end
data/lib/dude/git.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative './git/checkout'
4
4
  require_relative './git/current_branch_name'
5
+ require_relative './git/remote_name'
5
6
 
6
7
  module Git
7
8
  end
@@ -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
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './project_management/jira/client'
4
+ require_relative './project_management/trello/client'
5
+
6
+ module Dude
7
+ class HealthCheck
8
+ def call
9
+ validate(:jira, enabled: Dude::SETTINGS.dig(:jira, :token)) do
10
+ Dude::ProjectManagement::Jira::Client.new.health_check
11
+ end
12
+
13
+ validate(:trello, enabled: Dude::SETTINGS.dig(:trello, :token)) do
14
+ Dude::ProjectManagement::Trello::Client.new.health_check
15
+ end
16
+
17
+ validate(:github, enabled: Dude::SETTINGS.dig(:github, :token)) do
18
+ Dude::CodeManagement::Github::Client.new.health_check
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def validate(check, enabled:)
25
+ prepare_validation(check)
26
+ end_validation(check, enabled ? yield : nil, enabled: enabled)
27
+ end
28
+
29
+ def prepare_validation(check)
30
+ print "#{check.capitalize} status: [#{'WAIT'.yellow}]\r"
31
+ end
32
+
33
+ def end_validation(check, status, enabled: false)
34
+ return puts "#{check.capitalize} status: [#{'DISABLED'.blue}] " unless enabled
35
+
36
+ puts "#{check.capitalize} status: [#{status ? 'OK'.green : 'FAILURE'.red}] "
37
+ end
38
+ end
39
+ end
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './jira/client'
4
+ require_relative './trello/client'
4
5
 
5
6
  module Dude
6
7
  module ProjectManagement
7
8
  class Client
8
- include Settings
9
-
10
9
  attr_reader :client
11
10
 
12
11
  def initialize
13
- return unless LIST_OF_AVAILABLE_PROJECT_MANAGEMENT_TOOLS.include? settings['PROJECT_MANAGEMENT_TOOL']
12
+ tool = Dude::SETTINGS[:project_management_tool]
13
+ return unless LIST_OF_AVAILABLE_PROJECT_MANAGEMENT_TOOLS.include? tool
14
14
 
15
- @client = Dude::ProjectManagement::Jira::Client.new
15
+ @client = setup_client(tool)
16
16
  end
17
17
 
18
18
  def respond_to_missing?(method_name, include_private = false)
@@ -22,6 +22,10 @@ module Dude
22
22
  def method_missing(method, *args, &block)
23
23
  client.send(method, *args, &block)
24
24
  end
25
+
26
+ def setup_client(tool)
27
+ Object.const_get("Dude::ProjectManagement::#{tool.capitalize}::Client").new
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -4,14 +4,15 @@ module Dude
4
4
  module ProjectManagement
5
5
  module Entities
6
6
  class Issue
7
- attr_accessor :id, :title, :description, :status, :assignee
7
+ attr_accessor :id, :title, :description, :status, :assignee, :url
8
8
 
9
- def initialize(id:, title:, description:, status:, assignee: nil)
10
- @id = id
11
- @title = title
12
- @description = description
13
- @status = status
14
- @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]
15
16
  end
16
17
 
17
18
  def todo?
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'jira-ruby'
4
4
  require_relative './fetch_current_tasks'
5
+ require_relative './fetch_current_task'
5
6
  require_relative './move_task_to_list'
6
7
  require_relative './get_task_name_by_id'
7
8
 
@@ -9,21 +10,23 @@ module Dude
9
10
  module ProjectManagement
10
11
  module Jira
11
12
  class Client
12
- include Settings
13
-
14
13
  attr_reader :client, :project
15
14
 
16
- def initialize
17
- options = {
18
- username: settings['ATLASSIAN_EMAIL'],
19
- password: settings['ATLASSIAN_TOKEN'],
20
- site: settings['ATLASSIAN_URL'],
15
+ def options
16
+ {
17
+ username: Dude::SETTINGS.dig(:jira, :email),
18
+ password: Dude::SETTINGS.dig(:jira, :token),
19
+ site: Dude::SETTINGS.dig(:jira, :project, :url),
21
20
  context_path: '',
22
21
  auth_type: :basic
23
22
  }
23
+ end
24
24
 
25
+ def initialize
25
26
  @client = JIRA::Client.new(options)
26
- @project = client.Project.find(settings['ATLASSIAN_PROJECT_KEY'])
27
+ @project = client.Project.find(Dude::SETTINGS.dig(:jira, :project, :key))
28
+ rescue StandardError
29
+ nil
27
30
  end
28
31
 
29
32
  def respond_to_missing?(method_name, include_private = false)
@@ -38,6 +41,10 @@ module Dude
38
41
  FetchCurrentTasks.new(client).call
39
42
  end
40
43
 
44
+ def fetch_current_task(id)
45
+ FetchCurrentTask.new(client, id: id).call
46
+ end
47
+
41
48
  def move_task_to_list(id, list)
42
49
  MoveTaskToList.new(client, id: id, list_name: list).call
43
50
  end
@@ -45,6 +52,12 @@ module Dude
45
52
  def get_task_name_by_id(id)
46
53
  GetTaskNameById.new(client, id: id).call
47
54
  end
55
+
56
+ def health_check
57
+ @project && true
58
+ rescue StandardError
59
+ false
60
+ end
48
61
  end
49
62
  end
50
63
  end