dude-cli 2.0.5 → 2.1.0.alpha3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/verify.yml +32 -0
  3. data/.rspec +0 -1
  4. data/.rubocop.yml +13 -0
  5. data/CHANGELOG.md +7 -3
  6. data/Gemfile +3 -1
  7. data/Gemfile.lock +62 -9
  8. data/LICENCE +20 -0
  9. data/README.md +60 -35
  10. data/Rakefile +5 -3
  11. data/bin/console +4 -3
  12. data/bin/dude +2 -2
  13. data/demo/dude.gif +0 -0
  14. data/demo/wizard.gif +0 -0
  15. data/dude.gemspec +26 -18
  16. data/lib/dude.rb +18 -7
  17. data/lib/dude/code_management.rb +10 -0
  18. data/lib/dude/code_management/github/client.rb +27 -0
  19. data/lib/dude/code_management/github/create_pull_request.rb +51 -0
  20. data/lib/dude/commands.rb +26 -17
  21. data/lib/dude/commands/checkout.rb +4 -2
  22. data/lib/dude/commands/health_check.rb +15 -0
  23. data/lib/dude/commands/install.rb +67 -43
  24. data/lib/dude/commands/move.rb +5 -3
  25. data/lib/dude/commands/pr.rb +11 -0
  26. data/lib/dude/commands/pr/create.rb +49 -0
  27. data/lib/dude/commands/pr/remove.rb +15 -0
  28. data/lib/dude/commands/start.rb +14 -10
  29. data/lib/dude/commands/stop.rb +4 -4
  30. data/lib/dude/commands/tasks.rb +6 -5
  31. data/lib/dude/commands/track.rb +7 -7
  32. data/lib/dude/commands/version.rb +3 -1
  33. data/lib/dude/config.rb +18 -0
  34. data/lib/dude/git.rb +3 -0
  35. data/lib/dude/git/checkout.rb +20 -1
  36. data/lib/dude/git/current_branch_name.rb +3 -1
  37. data/lib/dude/git/remote_name.rb +21 -0
  38. data/lib/dude/health_check.rb +39 -0
  39. data/lib/dude/project_management/client.rb +14 -7
  40. data/lib/dude/project_management/entities/issue.rb +10 -7
  41. data/lib/dude/project_management/jira.rb +2 -1
  42. data/lib/dude/project_management/jira/client.rb +35 -18
  43. data/lib/dude/project_management/jira/fetch_current_task.rb +35 -0
  44. data/lib/dude/project_management/jira/fetch_current_tasks.rb +46 -0
  45. data/lib/dude/project_management/jira/get_task_name_by_id.rb +1 -3
  46. data/lib/dude/project_management/jira/move_task_to_list.rb +15 -15
  47. data/lib/dude/project_management/{entities/board.rb → trello.rb} +3 -4
  48. data/lib/dude/project_management/trello/client.rb +56 -0
  49. data/lib/dude/project_management/trello/fetch_current_task.rb +41 -0
  50. data/lib/dude/project_management/trello/fetch_current_tasks.rb +51 -0
  51. data/lib/dude/project_management/trello/fetch_lists.rb +22 -0
  52. data/lib/dude/project_management/trello/get_task_name_by_id.rb +23 -0
  53. data/lib/dude/project_management/trello/move_task_to_list.rb +53 -0
  54. data/lib/dude/settings.rb +3 -0
  55. data/lib/dude/setup/github.rb +35 -0
  56. data/lib/dude/setup/jira.rb +64 -0
  57. data/lib/dude/setup/toggl.rb +47 -0
  58. data/lib/dude/setup/trello.rb +58 -0
  59. data/lib/dude/templates/duderc_template +32 -0
  60. data/lib/dude/time_trackers/toggl.rb +2 -0
  61. data/lib/dude/time_trackers/toggl/base.rb +4 -4
  62. data/lib/dude/time_trackers/toggl/start_time_entry.rb +3 -2
  63. data/lib/dude/time_trackers/toggl/stop_time_entry.rb +3 -1
  64. data/lib/dude/version.rb +3 -1
  65. metadata +99 -14
  66. data/lib/dude/project_management/jira/get_current_tasks.rb +0 -40
data/lib/dude.rb CHANGED
@@ -1,12 +1,23 @@
1
- require "colorize"
1
+ # frozen_string_literal: true
2
2
 
3
- require "dude/settings"
4
- require "dude/version"
5
- require "dude/commands"
6
- require "dude/git"
3
+ require 'colorize'
4
+
5
+ begin
6
+ require 'pry'
7
+ rescue LoadError
8
+ nil
9
+ end
10
+
11
+ require_relative './dude/settings'
12
+ require_relative './dude/version'
13
+ require_relative './dude/commands'
14
+ require_relative './dude/git'
15
+ require_relative './dude/code_management'
16
+ require_relative './dude/config'
7
17
 
8
18
  module Dude
9
- class ToBeImplementedError < StandardError; end
19
+ SETTINGS = Dude::Config.configure_with('.duderc.yml')
20
+ LIST_OF_AVAILABLE_PROJECT_MANAGEMENT_TOOLS = %w[jira trello].freeze
10
21
 
11
- LIST_OF_AVAILABLE_PROJECT_MANAGEMENT_TOOLS = %w[jira]
22
+ class ToBeImplementedError < StandardError; end
12
23
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './code_management/github/client'
4
+
5
+ module Dude
6
+ module CodeManagement
7
+ module Github
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './create_pull_request'
4
+
5
+ module Dude
6
+ module CodeManagement
7
+ module Github
8
+ class Client
9
+ def client
10
+ @client ||= Faraday.new('https://api.github.com/', {
11
+ headers: { Authorization: "token #{Dude::SETTINGS.dig(:github, :token)}" }
12
+ })
13
+ end
14
+
15
+ def create_pull_request(issue:, owner:, repo:, params:)
16
+ CreatePullRequest.new.call(client, issue: issue, owner: owner, repo: repo, params: params)
17
+ end
18
+
19
+ def health_check
20
+ client.get('https://api.github.com/user').status == 200
21
+ rescue StandardError
22
+ false
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Dude
6
+ module CodeManagement
7
+ module Github
8
+ class CreatePullRequest
9
+ def call(client, issue:, owner:, repo:, params:)
10
+ @issue = issue
11
+ @owner = owner
12
+ @repo = repo
13
+ @params = params
14
+
15
+ response = client.post("https://api.github.com/repos/#{owner}/#{repo}/pulls", body.to_json)
16
+ res = JSON.parse(response.body)
17
+ url = res['html_url']
18
+ puts "Pull request has been created: #{url}"
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :issue, :owner, :repo, :params
24
+
25
+ def body
26
+ {
27
+ title: params[:title] || template['title'],
28
+ body: params[:body] || template['body'],
29
+ head: params[:head],
30
+ base: params[:base]
31
+ }
32
+ end
33
+
34
+ def template
35
+ file = YAML.load_file(File.join(File.dirname(__FILE__), '../../templates/pull_request_template'))
36
+ file.tap do |template|
37
+ template['title'] = fill_variables(template['title'])
38
+ template['body'] = fill_variables(template['body'])
39
+ end
40
+ end
41
+
42
+ def fill_variables(text)
43
+ text
44
+ .then { _1.gsub('{issue_id}', issue.id) }.chomp
45
+ .then { _1.gsub('{issue_url}', issue.url) }
46
+ .then { _1.gsub('{issue_title}', issue.title) }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
data/lib/dude/commands.rb CHANGED
@@ -1,24 +1,33 @@
1
- require "dry/cli"
2
- require_relative "./commands/version"
3
- require_relative "./commands/tasks"
4
- require_relative "./commands/move"
5
- require_relative "./commands/checkout"
6
- require_relative "./commands/start"
7
- require_relative "./commands/track"
8
- require_relative "./commands/stop"
9
- require_relative "./commands/install"
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/cli'
4
+ require_relative './commands/version'
5
+ require_relative './commands/tasks'
6
+ require_relative './commands/move'
7
+ require_relative './commands/checkout'
8
+ require_relative './commands/start'
9
+ require_relative './commands/track'
10
+ require_relative './commands/stop'
11
+ require_relative './commands/install'
12
+ require_relative './commands/pr'
13
+ require_relative './commands/health_check'
10
14
 
11
15
  module Dude
12
16
  module Commands
13
17
  extend Dry::CLI::Registry
14
18
 
15
- register "install", Dude::Commands::Install, aliases: ["install"]
16
- register "version", Dude::Commands::Version, aliases: ["v", "-v", "--version"]
17
- register "tasks", Dude::Commands::Tasks, aliases: ["t", "-t", "--tasks"]
18
- register "move", Dude::Commands::Move, aliases: ["m", "-m", "--move"]
19
- register "checkout", Dude::Commands::Checkout, aliases: ["co"]
20
- register "track", Dude::Commands::Track, aliases: ["tr"]
21
- register "stop", Dude::Commands::Stop
22
- register "start", Dude::Commands::Start, aliases: ["st"]
19
+ register 'install', Dude::Commands::Install, aliases: ['install']
20
+ register 'version', Dude::Commands::Version, aliases: ['v', '-v', '--version']
21
+ register 'tasks', Dude::Commands::Tasks, aliases: ['t', '-t', '--tasks']
22
+ register 'move', Dude::Commands::Move, aliases: ['m', '-m', '--move']
23
+ register 'checkout', Dude::Commands::Checkout, aliases: ['co']
24
+ register 'track', Dude::Commands::Track, aliases: ['tr']
25
+ register 'stop', Dude::Commands::Stop
26
+ register 'start', Dude::Commands::Start, aliases: ['st']
27
+ register 'healthcheck', Dude::Commands::HealthCheck
28
+
29
+ register 'pr' do |prefix|
30
+ prefix.register 'create', Dude::Commands::PR::Create
31
+ end
23
32
  end
24
33
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dude
2
4
  module Commands
3
5
  class Checkout < Dry::CLI::Command
4
- desc "Checkout to branch named as current issue"
6
+ desc 'Checkout to branch named as current issue'
5
7
 
6
- argument :id, required: true, desc: "The card short ID"
8
+ argument :id, required: true, desc: 'The card short ID'
7
9
 
8
10
  def call(id:)
9
11
  client = ProjectManagement::Client.new
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../health_check'
4
+
5
+ module Dude
6
+ module Commands
7
+ class HealthCheck < Dry::CLI::Command
8
+ desc 'Run healthcheck for enabled integrations'
9
+
10
+ def call
11
+ Dude::HealthCheck.new.call
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,56 +1,80 @@
1
- require 'dude/settings'
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-prompt'
4
+ require 'fileutils'
5
+
6
+ require_relative '../settings'
7
+ require_relative '../setup/jira'
8
+ require_relative '../setup/trello'
9
+ require_relative '../setup/toggl'
10
+ require_relative '../setup/github'
2
11
 
3
12
  module Dude
4
13
  module Commands
5
14
  class Install < Dry::CLI::Command
6
- desc "Creates .duderc for future configuration"
15
+ desc 'Creates .duderc for future configuration'
7
16
 
8
17
  def call
9
- path = File.join(Dir.home, Settings::CONFIG_FILE)
10
- if File.exist?(path)
11
- puts "Config file already exists"
12
- else
13
- File.open(path, 'w') {|f| f.write(duderc_file_content) }
14
- puts ".duderc created in your HOME directory"
15
- 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
16
28
  end
17
29
 
18
30
  private
19
31
 
20
- def duderc_file_content
21
- <<~HEREDOC
22
- # Please, don't use quotes and spaces.
23
- # Write all variables using following format: NAME=VALUE
24
- #
25
- # Now jira only (Github, Gitlab, Trello later)
26
- PROJECT_MANAGEMENT_TOOL=jira
27
- ATLASSIAN_EMAIL=
28
- # How to create Atlassian token: https://support.siteimprove.com/hc/en-gb/articles/360004317332-How-to-create-an-API-token-from-your-Atlassian-account
29
- ATLASSIAN_TOKEN=
30
- # URL of your project. Example: https://example.atlassian.net
31
- ATLASSIAN_URL=
32
- # KEY of your project. If your issues have id BT-123 - BT is the key
33
- ATLASSIAN_PROJECT_KEY=
34
- # Just open your atlassian main board and copy id from the url after rapidView=*ID* part.
35
- # Example: https://dealmakerns.atlassian.net/secure/RapidBoard.jspa?rapidView=23&projectKey=DT - 23 is the id
36
- ATLASSIAN_BOARD_ID=
37
-
38
- # Replace it with your project list names. Skip for empty lists
39
- TODO_LIST_NAME=To Do
40
- IN_PROGRESS_LIST_NAME=In Progress
41
- CODE_REVIEW_LIST_NAME=Code Review
42
- TESTING_LIST_NAME=TESTABLE
43
- DONE_LIST_NAME=Done
44
-
45
- # Your Toggl project name
46
- TOGGL_PROJECT_NAME=
47
- # Your Toggl API token can be found at the bottom of the page: https://track.toggl.com/profile
48
- TOGGL_TOKEN=
49
- # Can be copied from url here: https://toggl.com/app/projects/. Example: 123456
50
- TOGGL_WORKSPACE_ID=
51
- # Use the *id* and *title* and specify format for the task titles in Trello or keep it as it is
52
- TOGGL_TASK_FORMAT=[id] title
53
- 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
54
78
  end
55
79
  end
56
80
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dude
2
4
  module Commands
3
5
  class Move < Dry::CLI::Command
4
- desc "Move task between board columns"
6
+ desc 'Move task between board columns'
5
7
 
6
- argument :id, required: true, desc: "The card short ID"
7
- option :list, desc: "List name for moving card"
8
+ argument :id, required: true, desc: 'The card short ID'
9
+ option :list, desc: 'List name for moving card'
8
10
 
9
11
  def call(id:, **options)
10
12
  client = ProjectManagement::Client.new
@@ -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 '123'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,29 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dude
2
4
  module Commands
3
5
  class Start < Dry::CLI::Command
4
- include Settings
5
-
6
- desc "Start task (Do checkout, track and move actions)"
6
+ desc 'Start task (Do checkout, track and move actions)'
7
7
 
8
- argument :id, required: true, desc: "The card short ID"
8
+ argument :id, required: true, desc: 'The card short ID'
9
9
 
10
10
  def call(id:)
11
11
  Commands::Move.new.call(id: id, list: selected_list('in_progress'))
12
12
  Commands::Checkout.new.call(id: id)
13
- Commands::Track.new.call(id: id)
13
+ Commands::Track.new.call(id: id) if time_tracking_enabled?
14
14
  end
15
15
 
16
16
  private
17
17
 
18
18
  def selected_list(list)
19
19
  case list
20
- when 'todo' then settings['TODO_LIST_NAME']
21
- when 'in_progress' then settings['IN_PROGRESS_LIST_NAME']
22
- when 'code_review' then settings['CODE_REVIEW_LIST_NAME']
23
- when 'testing' then settings['TESTING_LIST_NAME']
24
- 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]
25
25
  end
26
26
  end
27
+
28
+ def time_tracking_enabled?
29
+ !Dude::SETTINGS.dig(:toggl, :token).nil?
30
+ end
27
31
  end
28
32
  end
29
33
  end