dude-cli 2.1.0.alpha2 → 2.1.0.alpha3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +15 -1
  3. data/README.md +43 -44
  4. data/demo/dude.gif +0 -0
  5. data/demo/wizard.gif +0 -0
  6. data/dude.gemspec +1 -0
  7. data/lib/dude.rb +10 -2
  8. data/lib/dude/code_management/github/client.rb +7 -3
  9. data/lib/dude/code_management/github/create_pull_request.rb +0 -2
  10. data/lib/dude/commands.rb +2 -0
  11. data/lib/dude/commands/health_check.rb +15 -0
  12. data/lib/dude/commands/install.rb +63 -53
  13. data/lib/dude/commands/start.rb +6 -8
  14. data/lib/dude/commands/stop.rb +0 -2
  15. data/lib/dude/commands/tasks.rb +0 -2
  16. data/lib/dude/commands/track.rb +2 -4
  17. data/lib/dude/config.rb +18 -0
  18. data/lib/dude/health_check.rb +39 -0
  19. data/lib/dude/project_management/client.rb +1 -3
  20. data/lib/dude/project_management/jira/client.rb +21 -11
  21. data/lib/dude/project_management/jira/fetch_current_task.rb +1 -3
  22. data/lib/dude/project_management/jira/fetch_current_tasks.rb +2 -4
  23. data/lib/dude/project_management/jira/get_task_name_by_id.rb +0 -2
  24. data/lib/dude/project_management/jira/move_task_to_list.rb +0 -2
  25. data/lib/dude/project_management/trello/client.rb +9 -5
  26. data/lib/dude/project_management/trello/fetch_current_task.rb +2 -4
  27. data/lib/dude/project_management/trello/fetch_current_tasks.rb +0 -2
  28. data/lib/dude/project_management/trello/fetch_lists.rb +1 -3
  29. data/lib/dude/project_management/trello/get_task_name_by_id.rb +1 -3
  30. data/lib/dude/project_management/trello/move_task_to_list.rb +1 -3
  31. data/lib/dude/setup/github.rb +35 -0
  32. data/lib/dude/setup/jira.rb +64 -0
  33. data/lib/dude/setup/toggl.rb +47 -0
  34. data/lib/dude/setup/trello.rb +58 -0
  35. data/lib/dude/templates/duderc_template +32 -0
  36. data/lib/dude/time_trackers/toggl/base.rb +2 -4
  37. data/lib/dude/version.rb +1 -1
  38. metadata +26 -3
  39. data/lib/dude/templates/pull_request_template +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6031769a356f360e4c2dcc232c0018aec9b00353908c4158e3fe3e7c030f8a6d
4
- data.tar.gz: 5e8697a8d8606d30029996965b347c00e025de94570366804fe9e2f29285a295
3
+ metadata.gz: 5a37ec9e7b502cf8da43c6e20431f452321dc55fd6ac0be38444e80cf0796fd0
4
+ data.tar.gz: fce3bf1daa29bc55f29cc4b96ccfc92ebcf694890b18153c3c4f162f5b641749
5
5
  SHA512:
6
- metadata.gz: 14ec971a8a01102bbca10813e4e3603d0a8bb132f7055f463016532cc9a28e170fdff23bb99749c0616cd800033d252a1a0eaac3800f093f7329b42dab3e60af
7
- data.tar.gz: 8ff94ed54537ea464c7f747d5a572ae98807081ad71ae8e7aa9ac2976c4d002c06af9004a6f8b48d787d87439df6517923a7a4ab113d340a23b45f0899dfb494
6
+ metadata.gz: b9e295659a902a6d904bab212191e1a19ff8a9b584a717008b493b9ec77c0583207033989cb65ab4afda97ff1653c20ff781bf44595e23f543446cd16acb2656
7
+ data.tar.gz: 0a1f084399039789b96b6bef5e24189ad4d7e4cd96cf57ef229f5ae1e31c2c877521dd3b0f81e9eaa62d4c2085f6d09e107ecc14a5ce201a8ec9d70b7f51f710
data/Gemfile.lock CHANGED
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dude-cli (2.1.0.alpha2)
4
+ dude-cli (2.1.0.alpha3)
5
5
  colorize (~> 0.8.1)
6
6
  dry-cli (~> 0.6)
7
7
  faraday (~> 1.1)
8
8
  jira-ruby (~> 2.1)
9
+ tty-prompt (~> 0.23.1)
9
10
 
10
11
  GEM
11
12
  remote: https://rubygems.org/
@@ -53,6 +54,8 @@ GEM
53
54
  parallel (1.20.1)
54
55
  parser (3.0.1.1)
55
56
  ast (~> 2.4.1)
57
+ pastel (0.8.0)
58
+ tty-color (~> 0.5)
56
59
  pry (0.14.1)
57
60
  coderay (~> 1.1)
58
61
  method_source (~> 1.0)
@@ -94,9 +97,20 @@ GEM
94
97
  simplecov (~> 0.8)
95
98
  simplecov-html (0.12.3)
96
99
  simplecov_json_formatter (0.1.2)
100
+ tty-color (0.6.0)
101
+ tty-cursor (0.7.1)
102
+ tty-prompt (0.23.1)
103
+ pastel (~> 0.8)
104
+ tty-reader (~> 0.8)
105
+ tty-reader (0.9.0)
106
+ tty-cursor (~> 0.7)
107
+ tty-screen (~> 0.8)
108
+ wisper (~> 2.0)
109
+ tty-screen (0.8.1)
97
110
  tzinfo (2.0.4)
98
111
  concurrent-ruby (~> 1.0)
99
112
  unicode-display_width (2.0.0)
113
+ wisper (2.0.1)
100
114
  zeitwerk (2.4.2)
101
115
 
102
116
  PLATFORMS
data/README.md CHANGED
@@ -8,7 +8,9 @@
8
8
 
9
9
  A daily assistant in the hard work of a programmer
10
10
 
11
- This program helps to combine such services as [Jira](https://atlassian.net), [Toggl](https://toggl.com) and replace most routine activities with one simple CLI utility.
11
+ This program helps to combine such services as [Jira](https://atlassian.net), [Trello](https://trello.com), [Toggl](https://toggl.com), etc. and replace most routine activities with one simple CLI utility.
12
+
13
+ ![Dude](/demo/dude.gif)
12
14
 
13
15
  ## Installation
14
16
 
@@ -26,54 +28,49 @@ Or install it yourself as:
26
28
 
27
29
  $ gem install dude-cli
28
30
 
29
- After that create .duderc file in your HOME directory by command:
31
+ After that create .duderc.yml file in your work project directory by command:
30
32
 
31
33
  $ dude install
32
34
 
33
- And configure all variables in this file
34
-
35
- `PROJECT_MANAGEMENT_TOOL=jira|trello` - Project management (Now only Jira and Trello supported)
36
-
37
- ##### Jira setup
38
-
39
- `ATLASSIAN_EMAIL` - Your Jira email
40
-
41
- `ATLASSIAN_TOKEN` - How to create Atlassian token: https://support.siteimprove.com/hc/en-gb/articles/360004317332-How-to-create-an-API-token-from-your-Atlassian-account
42
-
43
- `ATLASSIAN_URL` - URL of your project. Example: https://example.atlassian.net
35
+ It will offer you a step by step instruction how to setup dude:
44
36
 
45
- `ATLASSIAN_PROJECT_KEY` - KEY of your project. If your issues have id BT-123 - BT is the key
37
+ ![Setup Wizard](/demo/wizard.gif)
46
38
 
47
- `ATLASSIAN_BOARD_ID`:
48
- Just open your atlassian main board and copy id from the url after rapidView=*ID* part.
39
+ You always could edit this file manually and setup some stuff like Toggl time entry name or Github PR template
49
40
 
50
- Example: https://dealmakerns.atlassian.net/secure/RapidBoard.jspa?rapidView=23&projectKey=DT - 23 is the id
41
+ Default template could be found here: [lib/dude/templates/duderc_template](/lib/dude/templates/duderc_template)
51
42
 
52
- ##### Trello setup
53
- You could generate your key and token here: https://trello.com/app-key
54
-
55
- `TRELLO_KEY`
56
-
57
- `TRELLO_TOKEN`
43
+ ### Additional configuration variables:
58
44
 
59
45
  #### Replace it with your project list names. Skip for empty lists
60
46
 
61
- ```
62
- TODO_LIST_NAME=To Do
63
- IN_PROGRESS_LIST_NAME=In Progress
64
- CODE_REVIEW_LIST_NAME=Code Review
65
- TESTING_LIST_NAME=TESTABLE
66
- DONE_LIST_NAME=Done
47
+ ```yaml
48
+ :todo_list_name: To Do
49
+ :in_progress_list_name: In Progress
50
+ :code_review_list_name: Code Review
51
+ :testing_list_name: TESTABLE
52
+ :done_list_name: Done
67
53
  ```
68
54
 
69
- `TOGGL_PROJECT_NAME` - Your Toggl project name
55
+ #### Use the *{issue_id}* and *{issue_title}* and specify format for the task titles in Toggl or keep it as it is
70
56
 
71
- `TOGGL_TOKEN` - Your Toggl API token can be found at the bottom of the page: https://track.toggl.com/profile
72
-
73
- `TOGGL_WORKSPACE_ID` - Can be copied from url here: https://toggl.com/app/projects/. Example: 123456
57
+ ```yaml
58
+ :toggl:
59
+ :task_format: [{issue_id}] {issue_title}
60
+ ```
74
61
 
75
- #### Use the *id* and *title* and specify format for the task titles in Toggl or keep it as it is
76
- `TOGGL_TASK_FORMAT=[id] title`
62
+ #### Github PR template looks like this (Available variables: *{issue_id}*, *{issue_title}*, *{issue_url}*)
63
+
64
+ ```yaml
65
+ :github:
66
+ :pr_template:
67
+ :title: "[{issue_id}] {issue_title}\n"
68
+ :body: |
69
+ ## Story
70
+ [**\[{issue_id}\] {issue_title}**]({issue_url})
71
+ ## Description
72
+ Example description of the issue
73
+ ```
77
74
 
78
75
  ## Usage
79
76
 
@@ -88,14 +85,16 @@ alias dude="rvm 2.7.2 do dude"
88
85
 
89
86
  | Command | Required parameters | Optional parameters | Description |
90
87
  |:-------------:|:-------------------|:-------------------|:--------------------------------------------------------------------------------------|
91
- | dude install | - | - | Create .duderc file in your home directory |
92
- | dude checkout | ISSUE_ID | - | Checkout to branch with name "ID-issue-title" |
93
- | dude track | ISSUE_ID | - | Start time entry in Toggl with issue project, title and id |
94
- | dude tasks | - | - | Show all issues in current project (For current sprint) |
95
- | dude stop | - | - | Stop current time entry in Toggl |
96
- | dude start | ISSUE_ID | - | Do `checkout`, `track` and `move` actions |
97
- | dude move | ISSUE_ID | --list=NAME | Move issue to another column (Will provide options if called without --list parameter) |
98
- | dude version | - | - | Display gem version |
88
+ | dude install | - | - | Create .duderc file in your home directory |
89
+ | dude checkout | ISSUE_ID | - | Checkout to branch with name "ID-issue-title" |
90
+ | dude track | ISSUE_ID | - | Start time entry in Toggl with issue project, title and id |
91
+ | dude tasks | - | - | Show all issues in current project (For current sprint) |
92
+ | dude stop | - | - | Stop current time entry in Toggl |
93
+ | dude start | ISSUE_ID | - | Do `checkout`, `track` and `move` actions |
94
+ | dude move | ISSUE_ID | --list=NAME | Move issue to another column (Will provide options if called without --list parameter) |
95
+ | dude pr create | | | Creates PR in Github using template |
96
+ | dude version | - | - | Display gem version |
97
+ | dude healthcheck | - | - | Check configuration of all dependencies |
99
98
 
100
99
  You also can use `dude help` for short description of every command.
101
100
 
@@ -109,4 +108,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
109
108
 
110
109
  ## Changelog
111
110
 
112
- https://github.com/npupko/dude/blob/master/CHANGELOG.md
111
+ [CHANGELOG.md](/CHANGELOG.md)
data/demo/dude.gif ADDED
Binary file
data/demo/wizard.gif ADDED
Binary file
data/dude.gemspec CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_runtime_dependency 'dry-cli', '~> 0.6'
33
33
  spec.add_runtime_dependency 'faraday', '~> 1.1'
34
34
  spec.add_runtime_dependency 'jira-ruby', '~> 2.1'
35
+ spec.add_runtime_dependency 'tty-prompt', '~> 0.23.1'
35
36
 
36
37
  spec.add_development_dependency 'pry', '~> 0.14.0'
37
38
  spec.add_development_dependency 'rake', '~> 13.0'
data/lib/dude.rb CHANGED
@@ -2,14 +2,22 @@
2
2
 
3
3
  require 'colorize'
4
4
 
5
+ begin
6
+ require 'pry'
7
+ rescue LoadError
8
+ nil
9
+ end
10
+
5
11
  require_relative './dude/settings'
6
12
  require_relative './dude/version'
7
13
  require_relative './dude/commands'
8
14
  require_relative './dude/git'
9
15
  require_relative './dude/code_management'
16
+ require_relative './dude/config'
10
17
 
11
18
  module Dude
12
- class ToBeImplementedError < StandardError; end
13
-
19
+ SETTINGS = Dude::Config.configure_with('.duderc.yml')
14
20
  LIST_OF_AVAILABLE_PROJECT_MANAGEMENT_TOOLS = %w[jira trello].freeze
21
+
22
+ class ToBeImplementedError < StandardError; end
15
23
  end
@@ -6,17 +6,21 @@ module Dude
6
6
  module CodeManagement
7
7
  module Github
8
8
  class Client
9
- include Settings
10
-
11
9
  def client
12
10
  @client ||= Faraday.new('https://api.github.com/', {
13
- headers: { Authorization: "token #{settings['GITHUB_TOKEN']}" }
11
+ headers: { Authorization: "token #{Dude::SETTINGS.dig(:github, :token)}" }
14
12
  })
15
13
  end
16
14
 
17
15
  def create_pull_request(issue:, owner:, repo:, params:)
18
16
  CreatePullRequest.new.call(client, issue: issue, owner: owner, repo: repo, params: params)
19
17
  end
18
+
19
+ def health_check
20
+ client.get('https://api.github.com/user').status == 200
21
+ rescue StandardError
22
+ false
23
+ end
20
24
  end
21
25
  end
22
26
  end
@@ -6,8 +6,6 @@ module Dude
6
6
  module CodeManagement
7
7
  module Github
8
8
  class CreatePullRequest
9
- include Settings
10
-
11
9
  def call(client, issue:, owner:, repo:, params:)
12
10
  @issue = issue
13
11
  @owner = owner
data/lib/dude/commands.rb CHANGED
@@ -10,6 +10,7 @@ require_relative './commands/track'
10
10
  require_relative './commands/stop'
11
11
  require_relative './commands/install'
12
12
  require_relative './commands/pr'
13
+ require_relative './commands/health_check'
13
14
 
14
15
  module Dude
15
16
  module Commands
@@ -23,6 +24,7 @@ module Dude
23
24
  register 'track', Dude::Commands::Track, aliases: ['tr']
24
25
  register 'stop', Dude::Commands::Stop
25
26
  register 'start', Dude::Commands::Start, aliases: ['st']
27
+ register 'healthcheck', Dude::Commands::HealthCheck
26
28
 
27
29
  register 'pr' do |prefix|
28
30
  prefix.register 'create', Dude::Commands::PR::Create
@@ -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,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,63 +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
- # Replace it with your project list names. Skip for empty lists
28
- TODO_LIST_NAME=To Do
29
- IN_PROGRESS_LIST_NAME=In Progress
30
- CODE_REVIEW_LIST_NAME=Code Review
31
- TESTING_LIST_NAME=TESTABLE
32
- DONE_LIST_NAME=Done
33
-
34
- # Your Toggl project name
35
- TOGGL_PROJECT_NAME=
36
- # Your Toggl API token can be found at the bottom of the page: https://track.toggl.com/profile
37
- TOGGL_TOKEN=
38
- # Can be copied from url here: https://toggl.com/app/projects/. Example: 123456
39
- TOGGL_WORKSPACE_ID=
40
- # Use the *id* and *title* and specify format for the task titles in Trello or keep it as it is
41
- TOGGL_TASK_FORMAT=[id] title
42
-
43
- # Now jira/trello only (Github, Gitlab)
44
- # Choose one and uncomment section for Jira or Trello
45
-
46
- # [TRELLO setup start]
47
- # # https://trello.com/app-key
48
- # PROJECT_MANAGEMENT_TOOL=trello
49
- # TRELLO_KEY=
50
- # TRELLO_TOKEN=
51
- # [TRELLO setup end]
52
-
53
- # [JIRA setup start]
54
- PROJECT_MANAGEMENT_TOOL=jira
55
- ATLASSIAN_EMAIL=
56
- # How to create Atlassian token: https://support.siteimprove.com/hc/en-gb/articles/360004317332-How-to-create-an-API-token-from-your-Atlassian-account
57
- ATLASSIAN_TOKEN=
58
- # URL of your project. Example: https://example.atlassian.net
59
- ATLASSIAN_URL=
60
- # KEY of your project. If your issues have id BT-123 - BT is the key
61
- ATLASSIAN_PROJECT_KEY=
62
- # Just open your atlassian main board and copy id from the url after rapidView=*ID* part.
63
- # Example: https://dealmakerns.atlassian.net/secure/RapidBoard.jspa?rapidView=23&projectKey=DT - 23 is the id
64
- ATLASSIAN_BOARD_ID=
65
- # [JIRA setup end]
66
-
67
- 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
68
78
  end
69
79
  end
70
80
  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'
@@ -19,16 +17,16 @@ module Dude
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
29
27
 
30
28
  def time_tracking_enabled?
31
- !settings['TOGGL_TOKEN'].nil?
29
+ !Dude::SETTINGS.dig(:toggl, :token).nil?
32
30
  end
33
31
  end
34
32
  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
@@ -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
@@ -6,12 +6,10 @@ require_relative './trello/client'
6
6
  module Dude
7
7
  module ProjectManagement
8
8
  class Client
9
- include Settings
10
-
11
9
  attr_reader :client
12
10
 
13
11
  def initialize
14
- tool = settings['PROJECT_MANAGEMENT_TOOL']
12
+ tool = Dude::SETTINGS[:project_management_tool]
15
13
  return unless LIST_OF_AVAILABLE_PROJECT_MANAGEMENT_TOOLS.include? tool
16
14
 
17
15
  @client = setup_client(tool)
@@ -10,21 +10,25 @@ module Dude
10
10
  module ProjectManagement
11
11
  module Jira
12
12
  class Client
13
- include Settings
14
-
15
13
  attr_reader :client, :project
16
14
 
17
- def initialize
18
- options = {
19
- username: settings['ATLASSIAN_EMAIL'],
20
- password: settings['ATLASSIAN_TOKEN'],
21
- site: settings['ATLASSIAN_URL'],
22
- context_path: '',
23
- auth_type: :basic
24
- }
15
+ class << self
16
+ def options
17
+ {
18
+ username: Dude::SETTINGS.dig(:jira, :email),
19
+ password: Dude::SETTINGS.dig(:jira, :token),
20
+ site: Dude::SETTINGS.dig(:jira, :project, :url),
21
+ context_path: '',
22
+ auth_type: :basic
23
+ }
24
+ end
25
+ end
25
26
 
27
+ def initialize
26
28
  @client = JIRA::Client.new(options)
27
- @project = client.Project.find(settings['ATLASSIAN_PROJECT_KEY'])
29
+ @project = client.Project.find(Dude::SETTINGS.dig(:jira, :project, :key))
30
+ rescue StandardError
31
+ nil
28
32
  end
29
33
 
30
34
  def respond_to_missing?(method_name, include_private = false)
@@ -50,6 +54,12 @@ module Dude
50
54
  def get_task_name_by_id(id)
51
55
  GetTaskNameById.new(client, id: id).call
52
56
  end
57
+
58
+ def health_check
59
+ @project && true
60
+ rescue StandardError
61
+ false
62
+ end
53
63
  end
54
64
  end
55
65
  end
@@ -6,8 +6,6 @@ module Dude
6
6
  module ProjectManagement
7
7
  module Jira
8
8
  class FetchCurrentTask
9
- include Settings
10
-
11
9
  def initialize(client, id:)
12
10
  @client = client
13
11
  @id = id
@@ -28,7 +26,7 @@ module Dude
28
26
  description: issue.description,
29
27
  status: issue.status.name,
30
28
  assignee: issue&.assignee&.displayName,
31
- url: "#{settings['ATLASSIAN_URL']}/browse/#{issue.key}"
29
+ url: "#{Dude::SETTINGS.dig(:dig, :project, :url)}/browse/#{issue.key}"
32
30
  )
33
31
  end
34
32
  end
@@ -6,14 +6,12 @@ module Dude
6
6
  module ProjectManagement
7
7
  module Jira
8
8
  class FetchCurrentTasks
9
- include Settings
10
-
11
9
  def initialize(client)
12
10
  @client = client
13
11
  end
14
12
 
15
13
  def call
16
- board = client.Board.find(settings['ATLASSIAN_BOARD_ID'])
14
+ board = client.Board.find(Dude::SETTINGS.dig(:jira, :board_id))
17
15
 
18
16
  all_issues = board_type(board)
19
17
 
@@ -39,7 +37,7 @@ module Dude
39
37
  description: issue.description,
40
38
  status: issue.status.name,
41
39
  assignee: issue&.assignee&.displayName,
42
- url: "#{settings['ATLASSIAN_URL']}/browse/#{issue.key}"
40
+ url: "#{Dude::SETTINGS.dig(:jira, :project, :url)}/browse/#{issue.key}"
43
41
  )
44
42
  end
45
43
  end
@@ -4,8 +4,6 @@ module Dude
4
4
  module ProjectManagement
5
5
  module Jira
6
6
  class GetTaskNameById
7
- include Settings
8
-
9
7
  def initialize(client, id:)
10
8
  @client = client
11
9
  @id = id
@@ -4,8 +4,6 @@ module Dude
4
4
  module ProjectManagement
5
5
  module Jira
6
6
  class MoveTaskToList
7
- include Settings
8
-
9
7
  def initialize(client, id:, list_name:)
10
8
  @client = client
11
9
  @id = id
@@ -12,13 +12,11 @@ module Dude
12
12
  module ProjectManagement
13
13
  module Trello
14
14
  class Client
15
- include Settings
16
-
17
15
  def client
18
16
  @client ||= Faraday.new('https://api.trello.com/', {
19
17
  params: {
20
- key: settings['TRELLO_KEY'],
21
- token: settings['TRELLO_TOKEN']
18
+ key: Dude::SETTINGS.dig(:trello, :key),
19
+ token: Dude::SETTINGS.dig(:trello, :token)
22
20
  }
23
21
  })
24
22
  end
@@ -28,7 +26,7 @@ module Dude
28
26
  end
29
27
 
30
28
  def respond_to_missing?(method_name, include_private = false)
31
- client.respond_to_missing?(method_name, include_private)
29
+ client.respond_to?(method_name, include_private)
32
30
  end
33
31
 
34
32
  def fetch_current_tasks
@@ -46,6 +44,12 @@ module Dude
46
44
  def get_task_name_by_id(id)
47
45
  GetTaskNameById.new(client, id: id).call
48
46
  end
47
+
48
+ def health_check
49
+ client.get("/1/tokens/#{Dude::SETTINGS.dig(:trello, :token)}").status == 200
50
+ rescue StandardError
51
+ false
52
+ end
49
53
  end
50
54
  end
51
55
  end
@@ -4,15 +4,13 @@ module Dude
4
4
  module ProjectManagement
5
5
  module Trello
6
6
  class FetchCurrentTask
7
- include Settings
8
-
9
7
  def initialize(client, id:)
10
8
  @client = client
11
9
  @id = id
12
10
  end
13
11
 
14
12
  def call
15
- response = client.get("/1/boards/#{settings['ATLASSIAN_BOARD_ID']}/cards/#{id}")
13
+ response = client.get("/1/boards/#{Dude::SETTINGS.dig(:jira, :board_id)}/cards/#{id}")
16
14
  create_issue JSON.parse(response.body)
17
15
  end
18
16
 
@@ -25,7 +23,7 @@ module Dude
25
23
  id: issue['idShort'],
26
24
  title: issue['name'],
27
25
  description: issue['desc'],
28
- status: settings['IN_PROGRESS_LIST_NAME'], # OMG, let's fix this later
26
+ status: Dude::SETTINGS[:in_progress_list_name], # OMG, let's fix this later
29
27
  assignee: members(issue),
30
28
  url: issue['shortUrl']
31
29
  )
@@ -6,8 +6,6 @@ module Dude
6
6
  module ProjectManagement
7
7
  module Trello
8
8
  class FetchCurrentTasks
9
- include Settings
10
-
11
9
  attr_reader :fetch_lists
12
10
 
13
11
  def initialize(client, fetch_lists: nil)
@@ -4,14 +4,12 @@ module Dude
4
4
  module ProjectManagement
5
5
  module Trello
6
6
  class FetchLists
7
- include Settings
8
-
9
7
  def initialize(client)
10
8
  @client = client
11
9
  end
12
10
 
13
11
  def call
14
- response = client.get("/1/board/#{settings['ATLASSIAN_BOARD_ID']}/lists", { fields: 'name' })
12
+ response = client.get("/1/board/#{Dude::SETTINGS.dig(:jira, :board_id)}/lists", { fields: 'name' })
15
13
  JSON.parse(response.body)
16
14
  end
17
15
 
@@ -4,15 +4,13 @@ module Dude
4
4
  module ProjectManagement
5
5
  module Trello
6
6
  class GetTaskNameById
7
- include Settings
8
-
9
7
  def initialize(client, id:)
10
8
  @client = client
11
9
  @id = id
12
10
  end
13
11
 
14
12
  def call
15
- response = client.get("/1/boards/#{settings['ATLASSIAN_BOARD_ID']}/cards/#{id}")
13
+ response = client.get("/1/boards/#{Dude::SETTINGS.dig(:jira, :board_id)}/cards/#{id}")
16
14
  JSON.parse(response.body)['name']
17
15
  end
18
16
 
@@ -6,8 +6,6 @@ module Dude
6
6
  module ProjectManagement
7
7
  module Trello
8
8
  class MoveTaskToList
9
- include Settings
10
-
11
9
  def initialize(client, id:, list_name:)
12
10
  @client = client
13
11
  @id = id
@@ -15,7 +13,7 @@ module Dude
15
13
  end
16
14
 
17
15
  def call
18
- response = client.get("/1/boards/#{settings['ATLASSIAN_BOARD_ID']}/cards/#{id}", { fields: 'id' })
16
+ response = client.get("/1/boards/#{Dude::SETTINGS.dig(:jira, :board_id)}/cards/#{id}", { fields: 'id' })
19
17
  card_id = JSON.parse(response.body)['id']
20
18
  client.put("/1/cards/#{card_id}", { idList: list_id })
21
19
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dude
4
+ module Setup
5
+ class Github
6
+ def initialize(prompt)
7
+ @prompt = prompt
8
+ end
9
+
10
+ def call(settings:)
11
+ settings[:github][:token] = setup_token
12
+ settings
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :prompt
18
+
19
+ def setup_token
20
+ puts <<~HEREDOC
21
+ You need to create personal token
22
+
23
+ #{'1.'.bold} Log in to https://github.com/settings/tokens
24
+ #{'2.'.bold} Copy the token and paste it below
25
+ HEREDOC
26
+
27
+ if prompt.yes?(Dude::Config.style_prompt('Open Github token creation page in your browser?'))
28
+ `open https://github.com/settings/tokens`
29
+ end
30
+
31
+ prompt.ask(Dude::Config.style_prompt('Github token:'), required: true)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dude
4
+ module Setup
5
+ class Jira
6
+ def initialize(prompt)
7
+ @prompt = prompt
8
+ end
9
+
10
+ # rubocop:disable Metrics/AbcSize
11
+ def call(settings:)
12
+ settings[:jira][:email] = setup_email
13
+ settings[:jira][:token] = setup_token
14
+ settings[:jira][:project][:url] = setup_project_url
15
+ settings[:jira][:project][:key] = setup_project_key
16
+ settings[:jira][:board_id] = setup_board_id
17
+ settings
18
+ end
19
+ # rubocop:enable Metrics/AbcSize
20
+
21
+ private
22
+
23
+ attr_reader :prompt
24
+
25
+ def setup_email
26
+ prompt.ask(Dude::Config.style_prompt('Jira user email:'), required: true)
27
+ end
28
+
29
+ def setup_token
30
+ puts <<~HEREDOC
31
+ You need to create personal token
32
+
33
+ #{'1.'.bold} Log in to https://id.atlassian.com/manage/api-tokens
34
+ #{'2.'.bold} Click 'Create API token.'
35
+ #{'3.'.bold} From the dialog that appears, enter a memorable and concise 'Label' for your token and click 'Create.'
36
+ #{'4.'.bold} Use 'Copy to clipboard' and paste the token below
37
+ HEREDOC
38
+
39
+ if prompt.yes?(Dude::Config.style_prompt('Open Atlassian token creation page in your browser?'))
40
+ `open https://id.atlassian.com/manage-profile/security/api-tokens`
41
+ end
42
+
43
+ prompt.ask(Dude::Config.style_prompt('Jira token:'), required: true)
44
+ end
45
+
46
+ def setup_project_url
47
+ prompt.ask(Dude::Config.style_prompt('URL of your project (Example: https://example.atlassian.net):'), {
48
+ required: true
49
+ })
50
+ end
51
+
52
+ def setup_project_key
53
+ prompt.ask(Dude::Config.style_prompt('KEY of your project (If your issues have id BT-123 - BT is the key):'), {
54
+ required: true
55
+ })
56
+ end
57
+
58
+ def setup_board_id
59
+ puts 'Just open your atlassian main board and copy id from the url after rapidView=ID part.'
60
+ prompt.ask(Dude::Config.style_prompt('Board ID:'), required: true)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dude
4
+ module Setup
5
+ class Toggl
6
+ def initialize(prompt)
7
+ @prompt = prompt
8
+ end
9
+
10
+ def call(settings:)
11
+ settings[:toggl][:token] = setup_token
12
+ settings[:toggl][:project_name] = setup_project_name
13
+ settings[:toggl][:workspace_id] = setup_workspace_id
14
+ settings
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :prompt
20
+
21
+ def setup_token
22
+ puts <<~HEREDOC
23
+ You need to create personal token
24
+
25
+ #{'1.'.bold} Log in to https://trello.com/app-key
26
+ #{'2.'.bold} Your Toggl API token can be found at the bottom of the page
27
+ #{'3.'.bold} Press --Click to Reveal-- and paste token below
28
+ HEREDOC
29
+
30
+ if prompt.yes?(Dude::Config.style_prompt('Open Toggl profile page in your browser?'))
31
+ `open https://track.toggl.com/profile`
32
+ end
33
+
34
+ prompt.ask(Dude::Config.style_prompt('Toggl token:'), required: true)
35
+ end
36
+
37
+ def setup_project_name
38
+ prompt.ask(Dude::Config.style_prompt('Your Toggl project name:'), required: true)
39
+ end
40
+
41
+ def setup_workspace_id
42
+ puts 'Can be copied from url here: https://toggl.com/app/projects/ (Example: 123456)'
43
+ prompt.ask(Dude::Config.style_prompt('Workspace ID:'), required: true)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dude
4
+ module Setup
5
+ class Trello
6
+ def initialize(prompt)
7
+ @prompt = prompt
8
+ end
9
+
10
+ def call(settings:)
11
+ settings[:trello][:key] = setup_key
12
+ settings[:trello][:token] = setup_token
13
+ settings[:trello][:board_id] = setup_board_id
14
+ settings
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :prompt
20
+
21
+ def setup_email
22
+ prompt.ask(Dude::Config.style_prompt('Jira user email:'), required: true)
23
+ end
24
+
25
+ def setup_key
26
+ puts <<~HEREDOC
27
+ You need to create personal token
28
+
29
+ #{'1.'.bold} Log in to https://trello.com/app-key
30
+ #{'2.'.bold} Copy KEY and paste it below
31
+ HEREDOC
32
+
33
+ if prompt.yes?(Dude::Config.style_prompt('Open Trello token creation page in your browser?'))
34
+ `open https://trello.com/app-key`
35
+ end
36
+
37
+ prompt.ask(Dude::Config.style_prompt('Trello key:'), required: true)
38
+ end
39
+
40
+ def setup_token
41
+ puts <<~HEREDOC
42
+ You need to create personal token
43
+
44
+ #{'1.'.bold} Log in to https://trello.com/app-key
45
+ #{'2.'.bold} From the dialog that appears click 'Allow'
46
+ #{'3.'.bold} Copy created token to clipboard and paste the it below
47
+ HEREDOC
48
+
49
+ prompt.ask(Dude::Config.style_prompt('Trello token:'), required: true)
50
+ end
51
+
52
+ def setup_board_id
53
+ puts 'Just open your Trello main board and copy id from the url (Example: 123aBcdE)'
54
+ prompt.ask(Dude::Config.style_prompt('Board ID:'), required: true)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,32 @@
1
+ ---
2
+ :project_management_tool: jira
3
+ :jira:
4
+ :email:
5
+ :token:
6
+ :project:
7
+ :url:
8
+ :key:
9
+ :board_id:
10
+ :trello:
11
+ :board_id:
12
+ :key:
13
+ :token:
14
+ :todo_list_name: To Do
15
+ :in_progress_list_name: In Progress
16
+ :code_review_list_name: Code Review
17
+ :testing_list_name: TESTABLE
18
+ :done_list_name: Done
19
+ :toggl:
20
+ :project_name:
21
+ :token:
22
+ :workspace_id:
23
+ :task_format: "[{issue_id}] {issue_title}"
24
+ :github:
25
+ :token:
26
+ :pr_template:
27
+ :title: "[{issue_id}] {issue_title}\n"
28
+ :body: |
29
+ ## Story
30
+ [**\[{issue_id}\] {issue_title}**]({issue_url})
31
+ ## Description
32
+ Example description of the issue
@@ -5,17 +5,15 @@ require 'faraday'
5
5
  module Dude
6
6
  module Toggl
7
7
  class Base
8
- include Settings
9
-
10
8
  def toggl_api
11
9
  Faraday.new('https://api.track.toggl.com') do |conn|
12
- conn.basic_auth settings['TOGGL_TOKEN'], 'api_token'
10
+ conn.basic_auth Dude::SETTINGS.dig(:toggl, :token), 'api_token'
13
11
  conn.headers['Content-Type'] = 'application/json'
14
12
  end
15
13
  end
16
14
 
17
15
  def projects_response
18
- toggl_api.get("/api/v8/workspaces/#{settings['TOGGL_WORKSPACE_ID']}/projects")
16
+ toggl_api.get("/api/v8/workspaces/#{Dude::SETTINGS.dig(:toggl, :workspace_id)}/projects")
19
17
  end
20
18
  end
21
19
  end
data/lib/dude/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dude
4
- VERSION = '2.1.0.alpha2'
4
+ VERSION = '2.1.0.alpha3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dude-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0.alpha2
4
+ version: 2.1.0.alpha3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Pupko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-01 00:00:00.000000000 Z
11
+ date: 2021-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: tty-prompt
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.23.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.23.1
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: pry
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -176,6 +190,8 @@ files:
176
190
  - bin/console
177
191
  - bin/dude
178
192
  - bin/setup
193
+ - demo/dude.gif
194
+ - demo/wizard.gif
179
195
  - dude.gemspec
180
196
  - lib/dude.rb
181
197
  - lib/dude/code_management.rb
@@ -183,6 +199,7 @@ files:
183
199
  - lib/dude/code_management/github/create_pull_request.rb
184
200
  - lib/dude/commands.rb
185
201
  - lib/dude/commands/checkout.rb
202
+ - lib/dude/commands/health_check.rb
186
203
  - lib/dude/commands/install.rb
187
204
  - lib/dude/commands/move.rb
188
205
  - lib/dude/commands/pr.rb
@@ -193,10 +210,12 @@ files:
193
210
  - lib/dude/commands/tasks.rb
194
211
  - lib/dude/commands/track.rb
195
212
  - lib/dude/commands/version.rb
213
+ - lib/dude/config.rb
196
214
  - lib/dude/git.rb
197
215
  - lib/dude/git/checkout.rb
198
216
  - lib/dude/git/current_branch_name.rb
199
217
  - lib/dude/git/remote_name.rb
218
+ - lib/dude/health_check.rb
200
219
  - lib/dude/project_management/client.rb
201
220
  - lib/dude/project_management/entities/issue.rb
202
221
  - lib/dude/project_management/jira.rb
@@ -213,7 +232,11 @@ files:
213
232
  - lib/dude/project_management/trello/get_task_name_by_id.rb
214
233
  - lib/dude/project_management/trello/move_task_to_list.rb
215
234
  - lib/dude/settings.rb
216
- - lib/dude/templates/pull_request_template
235
+ - lib/dude/setup/github.rb
236
+ - lib/dude/setup/jira.rb
237
+ - lib/dude/setup/toggl.rb
238
+ - lib/dude/setup/trello.rb
239
+ - lib/dude/templates/duderc_template
217
240
  - lib/dude/time_trackers/toggl.rb
218
241
  - lib/dude/time_trackers/toggl/base.rb
219
242
  - lib/dude/time_trackers/toggl/start_time_entry.rb
@@ -1,7 +0,0 @@
1
- title: >
2
- [{issue_id}] {issue_title}
3
- body: |
4
- ## Story
5
- [**\[{issue_id}\] {issue_title}**]({issue_url})
6
- ## Description
7
- Example description of the issue