renuo-cli 1.6.0 → 1.7.0
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/.rspec +1 -1
- data/.rubocop.yml +1 -0
- data/README.md +0 -1
- data/Rakefile +0 -3
- data/lib/renuo/cli.rb +29 -3
- data/lib/renuo/cli/app/configure_semaphore.rb +1 -1
- data/lib/renuo/cli/app/redmine/csv_base_service.rb +54 -0
- data/lib/renuo/cli/app/redmine/issue.rb +23 -0
- data/lib/renuo/cli/app/services/renuo_cli_config.rb +29 -0
- data/lib/renuo/cli/app/templates/semaphore-deploy.yml.erb +1 -1
- data/lib/renuo/cli/app/templates/semaphore.yml.erb +11 -0
- data/lib/renuo/cli/app/toggl/detail.rb +30 -0
- data/lib/renuo/cli/app/toggl/time_entry.rb +30 -0
- data/lib/renuo/cli/app/toggl/user.rb +24 -0
- data/lib/renuo/cli/app/toggl/workspace.rb +17 -0
- data/lib/renuo/cli/app/toggl_redmine_comparator.rb +145 -0
- data/lib/renuo/cli/app/work.rb +94 -0
- data/lib/renuo/cli/version.rb +1 -1
- data/renuo-cli.gemspec +4 -2
- metadata +55 -20
- data/.coveralls.yml +0 -1
- data/.travis.yml +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b56cf8eda01fe283362ccc2975af7a8b41caf506387c3bf4b9d4028b08315ba2
|
4
|
+
data.tar.gz: c86c1b56f7a1495c2e84e8dcbbcff1189ab3d6fd621195d07ac3303601eaff5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcab1fcac348715b901b6ce0c928e891331fb7c1dfaa84e4736b13cace7fbeddd6c75554e44d4575ca1ab04e8889fb14a1f78902f54d7e7fa245c379f06d2b36
|
7
|
+
data.tar.gz: 006d062c2c0d8a04d65abb0a45c181db41d6afa397fc784a1736819bc50eddab9a3330a639bdb2dbe10a409a3a93c9c209877d3d3e99db65a2ef93cf4e6424fc
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
[](https://semaphoreci.com/renuo/renuo-cli)
|
2
|
-
[](https://coveralls.io/github/renuo/renuo-cli?branch=master)
|
3
2
|
[](https://codeclimate.com/github/renuo/renuo-cli)
|
4
3
|
|
5
4
|
# Renuo::Cli
|
data/Rakefile
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rspec/core/rake_task'
|
3
|
-
require 'coveralls/rake/task'
|
4
3
|
require 'cucumber/rake/task'
|
5
4
|
|
6
5
|
RSpec::Core::RakeTask.new(:spec)
|
7
6
|
Cucumber::Rake::Task.new
|
8
|
-
Coveralls::RakeTask.new
|
9
7
|
|
10
|
-
task test_with_coveralls: [:spec, :cucumber, 'coveralls:push']
|
11
8
|
task default: %i[spec cucumber]
|
data/lib/renuo/cli.rb
CHANGED
@@ -2,6 +2,7 @@ require 'renuo/cli/version'
|
|
2
2
|
require 'rubygems'
|
3
3
|
require 'gems'
|
4
4
|
require 'colorize'
|
5
|
+
require 'renuo/cli/app/services/renuo_cli_config'
|
5
6
|
require 'renuo/cli/app/name_display'
|
6
7
|
require 'renuo/cli/app/local_storage'
|
7
8
|
require 'renuo/cli/app/list_large_git_files'
|
@@ -11,12 +12,14 @@ require 'renuo/cli/app/create_aws_project'
|
|
11
12
|
require 'renuo/cli/app/create_heroku_app'
|
12
13
|
require 'renuo/cli/app/configure_sentry'
|
13
14
|
require 'renuo/cli/app/create_new_logins'
|
14
|
-
require 'renuo/cli/app/
|
15
|
-
require 'renuo/cli/app/
|
16
|
-
require 'renuo/cli/app/
|
15
|
+
require 'renuo/cli/app/work'
|
16
|
+
require 'renuo/cli/app/release_project'
|
17
|
+
require 'renuo/cli/app/fetch_emails'
|
18
|
+
require 'renuo/cli/app/heroku_users'
|
17
19
|
require 'renuo/cli/app/setup_uptimerobot'
|
18
20
|
require 'renuo/cli/app/release_xing'
|
19
21
|
require 'renuo/cli/app/configure_semaphore'
|
22
|
+
require 'renuo/cli/app/toggl_redmine_comparator'
|
20
23
|
|
21
24
|
module Renuo
|
22
25
|
class CLI
|
@@ -129,6 +132,19 @@ module Renuo
|
|
129
132
|
end
|
130
133
|
end
|
131
134
|
|
135
|
+
command 'work' do |c|
|
136
|
+
c.syntax = 'renuo work'
|
137
|
+
c.summary = 'A utility command to start working on a ticket.'
|
138
|
+
c.description = 'When you start working on a feature, it will jump to the project folder, '\
|
139
|
+
'start a new feature with the ticket number,'\
|
140
|
+
'move the ticket in "In progress" state on Redmine and start Toggl with the '\
|
141
|
+
'ticket number or add the ticket number to the running one if there was none.'
|
142
|
+
c.example 'renuo work start kinova 1234', 'start working on ticket 1234 on kinova project'
|
143
|
+
c.action do|args|
|
144
|
+
Work.new.run(args)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
132
148
|
command 'release' do |c|
|
133
149
|
c.syntax = 'renuo release'
|
134
150
|
c.summary = 'Release a projects state of develop (on github) to master in one command.'
|
@@ -214,6 +230,16 @@ module Renuo
|
|
214
230
|
ConfigureSemaphore.new.call
|
215
231
|
end
|
216
232
|
end
|
233
|
+
|
234
|
+
command 'toggl-redmine' do |c|
|
235
|
+
c.syntax = 'renuo toggl-redmine'
|
236
|
+
c.summary = 'Compares your time entries between Toggl and Redmine'
|
237
|
+
c.description = 'This command extracts your time entries in Toggl and in Redmine and checks if they are'\
|
238
|
+
' consistant. It can help you in your time booking to find if you booked something wrong'
|
239
|
+
c.action do |args|
|
240
|
+
TogglRedmineComparator.call
|
241
|
+
end
|
242
|
+
end
|
217
243
|
end
|
218
244
|
end
|
219
245
|
end
|
@@ -22,7 +22,7 @@ class ConfigureSemaphore
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def semaphore_cli_installed?
|
25
|
-
semaphore_cli_installed = `sem context`.strip == '* renuo_semaphoreci_com'
|
25
|
+
semaphore_cli_installed = `sem context | grep '*'`.strip == '* renuo_semaphoreci_com'
|
26
26
|
warn('You need to install and configure Semaphore CLI to run this command.') unless semaphore_cli_installed
|
27
27
|
semaphore_cli_installed
|
28
28
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module Redmine
|
4
|
+
class CsvBaseService
|
5
|
+
API_LOCATION = 'https://redmine.renuo.ch'.freeze
|
6
|
+
|
7
|
+
def initialize(token)
|
8
|
+
@token = token
|
9
|
+
end
|
10
|
+
|
11
|
+
def get
|
12
|
+
http_response = http_request(generate_url)
|
13
|
+
encoded_body = http_response.body.force_encoding('ISO-8859-1').encode('UTF-8')
|
14
|
+
csv = parse_csv(encoded_body)
|
15
|
+
parse_results(csv)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def generate_url
|
21
|
+
URI("#{API_LOCATION}/time_entries/report.csv?#{query}&key=#{@token}")
|
22
|
+
end
|
23
|
+
|
24
|
+
def http_request(url)
|
25
|
+
req = Net::HTTP::Get.new(url)
|
26
|
+
Net::HTTP.start(url.hostname, url.port, use_ssl: true) do |http|
|
27
|
+
http.request(req)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def query
|
32
|
+
# to be implemented in concrete service
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_results(_csv)
|
36
|
+
# to be implemented in concrete service
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_csv(body)
|
40
|
+
separated_csv_entries = CSV.parse(body, col_sep: ',')
|
41
|
+
keys = separated_csv_entries.shift[1..-2]
|
42
|
+
entries = separated_csv_entries.shift[1..-2]
|
43
|
+
Hash[keys.zip(entries)]
|
44
|
+
rescue CSV::MalformedCSVError
|
45
|
+
raise_bad_data_error
|
46
|
+
end
|
47
|
+
|
48
|
+
def raise_bad_data_error
|
49
|
+
error = 'Malformed CSV, please use comma delimiters (Redmine language setting?)'
|
50
|
+
Rails.logger.error error
|
51
|
+
raise Redmine::BadData, error
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module Redmine
|
4
|
+
class Issue < ActiveResource::Base
|
5
|
+
STATUSES = {
|
6
|
+
to_start: 9,
|
7
|
+
planned: 15,
|
8
|
+
in_progress: 2,
|
9
|
+
qa: 12
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
self.site = 'https://redmine.renuo.ch'
|
13
|
+
self.include_root_in_json = true
|
14
|
+
|
15
|
+
def self.headers
|
16
|
+
{ 'X-Redmine-API-Key' => RenuoCliConfig.redmine_api_key }
|
17
|
+
end
|
18
|
+
|
19
|
+
def html_url
|
20
|
+
"#{self.class.site}issues/#{id}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class RenuoCliConfig
|
4
|
+
CONFIG_FILE_PATH = "#{File.expand_path('~')}/.renuo_cli".freeze
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def redmine_api_key
|
8
|
+
get_config_value('REDMINE_API_KEY', 'https://redmine.renuo.ch/my/account')
|
9
|
+
end
|
10
|
+
|
11
|
+
def toggl_api_token
|
12
|
+
get_config_value('TOGGL_API_TOKEN', 'https://toggl.com/app/profile')
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def get_config_value(name, open_url)
|
18
|
+
FileUtils.touch(CONFIG_FILE_PATH)
|
19
|
+
config = YAML.load_file(CONFIG_FILE_PATH, fallback: {})
|
20
|
+
value = config[name]
|
21
|
+
return value if value
|
22
|
+
system("open #{open_url}")
|
23
|
+
value = ask("You haven't set your #{name}, yet. Please provide one:")
|
24
|
+
config[name] = value
|
25
|
+
File.write(CONFIG_FILE_PATH, config.to_yaml)
|
26
|
+
value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -7,8 +7,11 @@ agent:
|
|
7
7
|
auto_cancel:
|
8
8
|
running:
|
9
9
|
when: "true"
|
10
|
+
|
10
11
|
blocks:
|
11
12
|
- name: cache
|
13
|
+
execution_time_limit:
|
14
|
+
minutes: 10
|
12
15
|
dependencies: []
|
13
16
|
task:
|
14
17
|
secrets:
|
@@ -23,6 +26,8 @@ blocks:
|
|
23
26
|
- bin/yarn install --cache-folder ~/.cache/yarn
|
24
27
|
- cache store
|
25
28
|
- name: tests
|
29
|
+
execution_time_limit:
|
30
|
+
minutes: 10
|
26
31
|
dependencies: ['cache']
|
27
32
|
task:
|
28
33
|
secrets:
|
@@ -37,6 +42,12 @@ blocks:
|
|
37
42
|
- checkout
|
38
43
|
- cache restore
|
39
44
|
- bundle install --deployment --path vendor/bundle
|
45
|
+
- sem-service start postgres
|
46
|
+
- bundle exec rails db:create db:schema:load
|
47
|
+
jobs:
|
48
|
+
- name: tests
|
49
|
+
commands:
|
50
|
+
- bin/check
|
40
51
|
promotions:
|
41
52
|
- name: develop
|
42
53
|
pipeline_file: develop-deploy.yml
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module Toggl
|
4
|
+
module CustomJsonFormat
|
5
|
+
include ActiveResource::Formats::JsonFormat
|
6
|
+
|
7
|
+
# rubocop:disable Style/ModuleFunction
|
8
|
+
extend self
|
9
|
+
# rubocop:enable Style/ModuleFunction
|
10
|
+
|
11
|
+
def decode(json)
|
12
|
+
ActiveSupport::JSON.decode(json)['data']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Detail < ActiveResource::Base
|
17
|
+
self.format = CustomJsonFormat
|
18
|
+
self.site = 'https://toggl.com/reports/api/v2'
|
19
|
+
self.include_root_in_json = true
|
20
|
+
self.include_format_in_path = false
|
21
|
+
|
22
|
+
def self.user
|
23
|
+
RenuoCliConfig.toggl_api_token
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.password
|
27
|
+
'api_token'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module Toggl
|
4
|
+
class TimeEntry < ActiveResource::Base
|
5
|
+
self.site = 'https://www.toggl.com/api/v8/'
|
6
|
+
self.include_root_in_json = true
|
7
|
+
self.include_format_in_path = false
|
8
|
+
|
9
|
+
def self.user
|
10
|
+
RenuoCliConfig.toggl_api_token
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.password
|
14
|
+
'api_token'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.current
|
18
|
+
data = get(:current)['data']
|
19
|
+
data ? new(data) : nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.start(params)
|
23
|
+
post(:start, {}, params.to_json)
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop
|
27
|
+
put(:stop)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Toggl
|
2
|
+
class User < ActiveResource::Base
|
3
|
+
self.site = 'https://www.toggl.com/api/v8/'
|
4
|
+
self.include_root_in_json = true
|
5
|
+
self.include_format_in_path = false
|
6
|
+
|
7
|
+
def self.user
|
8
|
+
RenuoCliConfig.toggl_api_token
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.password
|
12
|
+
'api_token'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.custom_method_collection_url(method_name, options)
|
16
|
+
prefix_options, query_options = split_options(options)
|
17
|
+
"#{prefix(prefix_options)}#{method_name}#{format_extension}#{query_string(query_options)}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.me
|
21
|
+
new(get(:me)['data'])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module Toggl
|
4
|
+
class Workspace < ActiveResource::Base
|
5
|
+
self.site = 'https://www.toggl.com/api/v8/'
|
6
|
+
self.include_root_in_json = true
|
7
|
+
self.include_format_in_path = false
|
8
|
+
|
9
|
+
def self.user
|
10
|
+
RenuoCliConfig.toggl_api_token
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.password
|
14
|
+
'api_token'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'commander'
|
2
|
+
require 'csv'
|
3
|
+
require_relative './environments'
|
4
|
+
require_relative './fetch_emails'
|
5
|
+
require 'renuo/cli/app/toggl/workspace'
|
6
|
+
require 'renuo/cli/app/toggl/detail'
|
7
|
+
require 'renuo/cli/app/toggl/user'
|
8
|
+
require 'terminal-table'
|
9
|
+
require 'colorize'
|
10
|
+
|
11
|
+
class TogglRedmineComparator
|
12
|
+
class << self
|
13
|
+
def call(days_behind = 7)
|
14
|
+
report = {}
|
15
|
+
since_date = days_behind.days.before(Date.yesterday).strftime('%F')
|
16
|
+
until_date = Date.yesterday.strftime('%F')
|
17
|
+
extract_redmine(report, since_date, until_date)
|
18
|
+
extract_toggl(report, since_date, until_date)
|
19
|
+
report = report.sort.reverse.to_h
|
20
|
+
print_table(report)
|
21
|
+
report
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def print_table(report)
|
27
|
+
rows = []
|
28
|
+
report.each do |date, value|
|
29
|
+
rows << colorize_table_row(date, value)
|
30
|
+
rows << :separator
|
31
|
+
end
|
32
|
+
rows.pop
|
33
|
+
table = Terminal::Table.new headings: %w[Day Redmine Toggl].map(&:cyan), rows: rows,
|
34
|
+
style: { padding_left: 2, padding_right: 2,
|
35
|
+
border_x: '-'.blue, border_y: '|'.blue, border_i: '+'.blue }
|
36
|
+
puts table
|
37
|
+
end
|
38
|
+
|
39
|
+
def colorize_table_row(date, value)
|
40
|
+
printed_day = date.strftime('%F %a')
|
41
|
+
printed_redmine = to_time(value[:redmine])
|
42
|
+
printed_toggl = to_time(value[:toggl])
|
43
|
+
colorize_method = colorization_for_value(value)
|
44
|
+
[printed_day, printed_redmine, printed_toggl].map { |v| v.colorize(colorize_method) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def colorization_for_value(value)
|
48
|
+
if more_toggl?(value)
|
49
|
+
:red
|
50
|
+
elsif more_redmine?(value)
|
51
|
+
:default
|
52
|
+
else
|
53
|
+
:green
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def extract_redmine(report, since_date, _until_date)
|
58
|
+
encoded_body = perform_redmine_call(since_date)
|
59
|
+
csv = convert_redmine_csv(encoded_body)
|
60
|
+
csv.each do |date, entry|
|
61
|
+
report[Date.parse(date)] ||= default_value
|
62
|
+
report[Date.parse(date)][:redmine] = to_seconds(entry) if entry.present?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def convert_redmine_csv(encoded_body)
|
67
|
+
separated_csv_entries = CSV.parse(encoded_body, col_sep: ',')
|
68
|
+
keys = separated_csv_entries.shift[1..-2]
|
69
|
+
entries = separated_csv_entries.shift[1..-2]
|
70
|
+
keys.zip(entries)
|
71
|
+
end
|
72
|
+
|
73
|
+
def perform_redmine_call(since_date)
|
74
|
+
query = generate_redmine_query(since_date)
|
75
|
+
url = URI("https://redmine.renuo.ch/time_entries/report.csv?#{query}")
|
76
|
+
req = Net::HTTP::Get.new(url)
|
77
|
+
req['X-Redmine-API-Key'] = RenuoCliConfig.redmine_api_key
|
78
|
+
response = Net::HTTP.start(url.hostname, url.port, use_ssl: true) { |http| http.request(req) }
|
79
|
+
response.body.force_encoding('ISO-8859-1').encode('UTF-8')
|
80
|
+
end
|
81
|
+
|
82
|
+
def generate_redmine_query(since_date)
|
83
|
+
URI.encode_www_form(
|
84
|
+
[['utf8', '✓'], ['criteria[]', 'user'],
|
85
|
+
['f[]', 'spent_on'], ['f[]', 'user_id'],
|
86
|
+
['op[spent_on]', '>='], ['op[user_id]', '='],
|
87
|
+
['v[spent_on][]', since_date], ['v[user_id][]', 'me'],
|
88
|
+
['f[]', ''],
|
89
|
+
['c[]', 'project'], ['c[]', 'spent_on'], ['c[]', 'user'], ['c[]', 'activity'], ['c[]', 'issue'],
|
90
|
+
['c[]', 'comments'], ['c[]', 'hours'], %w[columns day], ['criteria[]', '']]
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def extract_toggl(report, since_date, until_date)
|
95
|
+
user_id = Toggl::User.me.id
|
96
|
+
workspace_ids = Toggl::Workspace.all.map(&:id)
|
97
|
+
|
98
|
+
workspace_ids.each do |workspace_id|
|
99
|
+
time_entries = Toggl::Detail.where(since: since_date, until: until_date,
|
100
|
+
user_agent: 'renuo-cli', workspace_id: workspace_id, user_ids: user_id)
|
101
|
+
parse_toggl_entries(report, time_entries)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def parse_toggl_entries(report, time_entries)
|
106
|
+
time_entries
|
107
|
+
.reject { |te| te.end.nil? }
|
108
|
+
.group_by { |time_entry| Date.parse(time_entry.end) }
|
109
|
+
.each do |date, grouped_time_entries|
|
110
|
+
report[date] ||= default_value
|
111
|
+
report[date][:toggl] += grouped_time_entries.sum(&:dur)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def default_value
|
116
|
+
{ redmine: 0.0, toggl: 0.0 }
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_time(value)
|
120
|
+
sec = value / 1000.0
|
121
|
+
min, _sec = sec.divmod(60.0)
|
122
|
+
hour, min = min.divmod(60.0)
|
123
|
+
format('%02d:%02d', hour, min)
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_seconds(value)
|
127
|
+
hours, minutes = value.to_d.divmod(1.0)
|
128
|
+
(hours * 60 * 60 * 1000) + (minutes * 60 * 60 * 1000)
|
129
|
+
end
|
130
|
+
|
131
|
+
def non_working_day?(value)
|
132
|
+
[value[:redmine], value[:toggl]].all? { |v| v == 0.0 }
|
133
|
+
end
|
134
|
+
|
135
|
+
BUFFER = 20_000
|
136
|
+
|
137
|
+
def more_toggl?(value)
|
138
|
+
(value[:toggl] - value[:redmine]) > BUFFER
|
139
|
+
end
|
140
|
+
|
141
|
+
def more_redmine?(value)
|
142
|
+
(value[:redmine] - value[:toggl]) > BUFFER
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'renuo/cli/app/redmine/issue'
|
3
|
+
require 'renuo/cli/app/toggl/time_entry'
|
4
|
+
|
5
|
+
class Work
|
6
|
+
ACTIONS = %w[start].freeze
|
7
|
+
|
8
|
+
def run(args)
|
9
|
+
ActiveResource::Base.logger = Logger.new(STDOUT)
|
10
|
+
@action, @project_name, @ticket_number = args
|
11
|
+
validate_action
|
12
|
+
validate_project_name
|
13
|
+
validate_ticket_number
|
14
|
+
start_feature_branch
|
15
|
+
update_redmine_ticket
|
16
|
+
start_toggl
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# TODO: I want to implement also the stop action.
|
22
|
+
def validate_action
|
23
|
+
abort('>> No action given. It must be start') unless ACTIONS.include? @action
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate_project_name
|
27
|
+
abort('>> No project name given.') unless @project_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_ticket_number
|
31
|
+
abort('>> No ticket number given.') unless @ticket_number
|
32
|
+
issue = Redmine::Issue.find(@ticket_number)
|
33
|
+
open_statuses = Redmine::Issue::STATUSES.values_at(:to_start, :planned, :in_progress, :qa)
|
34
|
+
return if open_statuses.include?(issue.status.id)
|
35
|
+
|
36
|
+
system("open #{issue.html_url}")
|
37
|
+
abort('>> Ticket should be in an open status')
|
38
|
+
end
|
39
|
+
|
40
|
+
def start_feature_branch
|
41
|
+
project_folder = `autojump #{@project_name}`.strip
|
42
|
+
system("cd #{project_folder} && git stash && git checkout develop "\
|
43
|
+
"&& git pull && git flow feature start #{@ticket_number}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def update_redmine_ticket
|
47
|
+
issue = Redmine::Issue.find(@ticket_number)
|
48
|
+
issue.status_id = Redmine::Issue::STATUSES[:in_progress]
|
49
|
+
issue.save
|
50
|
+
system("open #{issue.html_url}")
|
51
|
+
end
|
52
|
+
|
53
|
+
def start_toggl
|
54
|
+
current_time_entry = Toggl::TimeEntry.current
|
55
|
+
if current_time_entry.nil?
|
56
|
+
create_toggl_time_entry
|
57
|
+
elsif current_time_entry.description.nil?
|
58
|
+
update_toggl_time_entry(current_time_entry.id)
|
59
|
+
else
|
60
|
+
existing_toggl(current_time_entry)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def existing_toggl(current_time_entry)
|
65
|
+
say("A timer '#{current_time_entry.description}' was already running.")
|
66
|
+
if current_time_entry.description.to_i == @ticket_number.to_i
|
67
|
+
say('I will keep using it')
|
68
|
+
else
|
69
|
+
say('I stopped it and started a new time entry.')
|
70
|
+
stop_toggl_time_entry(current_time_entry.id)
|
71
|
+
create_toggl_time_entry
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def update_toggl_time_entry(time_entry_id)
|
76
|
+
say('A timer was already running but without a project assigned. I updated the current time entry.')
|
77
|
+
|
78
|
+
time_entry = Toggl::TimeEntry.find(time_entry_id)
|
79
|
+
time_entry.description = @ticket_number.to_s
|
80
|
+
time_entry.tags = [@project_name.to_s]
|
81
|
+
time_entry.created_with = 'curl'
|
82
|
+
time_entry.save
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_toggl_time_entry
|
86
|
+
Toggl::TimeEntry.start(time_entry: { description: @ticket_number.to_s,
|
87
|
+
tags: [@project_name.to_s],
|
88
|
+
created_with: 'curl' })
|
89
|
+
end
|
90
|
+
|
91
|
+
def stop_toggl_time_entry(time_entry_id)
|
92
|
+
Toggl::TimeEntry.find(time_entry_id).stop
|
93
|
+
end
|
94
|
+
end
|
data/lib/renuo/cli/version.rb
CHANGED
data/renuo-cli.gemspec
CHANGED
@@ -17,14 +17,15 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables << 'renuo'
|
18
18
|
spec.require_paths = ['lib']
|
19
19
|
|
20
|
+
spec.add_dependency 'activeresource', '~> 5.1.0'
|
20
21
|
spec.add_dependency 'colorize', '~> 0'
|
21
22
|
spec.add_dependency 'commander', '~> 4.0'
|
22
23
|
spec.add_dependency 'gems', '~> 1.1'
|
23
24
|
spec.add_dependency 'redcarpet', '~> 3.0'
|
25
|
+
spec.add_dependency 'terminal-table'
|
24
26
|
|
25
27
|
spec.add_development_dependency 'aruba', '~> 0.14.5'
|
26
28
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
27
|
-
spec.add_development_dependency 'coveralls', '~> 0.8.9'
|
28
29
|
spec.add_development_dependency 'cucumber', '~> 3.1'
|
29
30
|
spec.add_development_dependency 'dotenv', '~> 2.7.2'
|
30
31
|
spec.add_development_dependency 'mdl', '~> 0.4.0'
|
@@ -32,7 +33,8 @@ Gem::Specification.new do |spec|
|
|
32
33
|
spec.add_development_dependency 'rake', '~> 10.0'
|
33
34
|
spec.add_development_dependency 'rspec', '~> 3.5'
|
34
35
|
spec.add_development_dependency 'rubocop', '0.55.0'
|
35
|
-
spec.add_development_dependency 'simplecov'
|
36
|
+
spec.add_development_dependency 'simplecov'
|
37
|
+
spec.add_development_dependency 'simplecov-console'
|
36
38
|
spec.add_development_dependency 'vcr', '~> 4.0.0'
|
37
39
|
spec.add_development_dependency 'webmock', '~> 3.5.1'
|
38
40
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: renuo-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Renuo AG
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activeresource
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.1.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: colorize
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,47 +81,47 @@ dependencies:
|
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '3.0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: terminal-table
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - ">="
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0
|
76
|
-
type: :
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
98
|
+
name: aruba
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
101
|
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
103
|
+
version: 0.14.5
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
110
|
+
version: 0.14.5
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
112
|
+
name: bundler
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0
|
117
|
+
version: '2.0'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0
|
124
|
+
version: '2.0'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: cucumber
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -210,16 +224,30 @@ dependencies:
|
|
210
224
|
name: simplecov
|
211
225
|
requirement: !ruby/object:Gem::Requirement
|
212
226
|
requirements:
|
213
|
-
- -
|
227
|
+
- - ">="
|
214
228
|
- !ruby/object:Gem::Version
|
215
|
-
version: 0
|
229
|
+
version: '0'
|
216
230
|
type: :development
|
217
231
|
prerelease: false
|
218
232
|
version_requirements: !ruby/object:Gem::Requirement
|
219
233
|
requirements:
|
220
|
-
- -
|
234
|
+
- - ">="
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '0'
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: simplecov-console
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - ">="
|
221
242
|
- !ruby/object:Gem::Version
|
222
|
-
version: 0
|
243
|
+
version: '0'
|
244
|
+
type: :development
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - ">="
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '0'
|
223
251
|
- !ruby/object:Gem::Dependency
|
224
252
|
name: vcr
|
225
253
|
requirement: !ruby/object:Gem::Requirement
|
@@ -257,14 +285,12 @@ executables:
|
|
257
285
|
extensions: []
|
258
286
|
extra_rdoc_files: []
|
259
287
|
files:
|
260
|
-
- ".coveralls.yml"
|
261
288
|
- ".editorconfig"
|
262
289
|
- ".gitignore"
|
263
290
|
- ".mdlrc"
|
264
291
|
- ".rspec"
|
265
292
|
- ".rubocop.yml"
|
266
293
|
- ".ruby-version"
|
267
|
-
- ".travis.yml"
|
268
294
|
- CODE_OF_CONDUCT.md
|
269
295
|
- Gemfile
|
270
296
|
- LICENSE.txt
|
@@ -289,17 +315,26 @@ files:
|
|
289
315
|
- lib/renuo/cli/app/list_large_git_files.rb
|
290
316
|
- lib/renuo/cli/app/local_storage.rb
|
291
317
|
- lib/renuo/cli/app/name_display.rb
|
318
|
+
- lib/renuo/cli/app/redmine/csv_base_service.rb
|
319
|
+
- lib/renuo/cli/app/redmine/issue.rb
|
292
320
|
- lib/renuo/cli/app/release_project.rb
|
293
321
|
- lib/renuo/cli/app/release_xing.rb
|
294
322
|
- lib/renuo/cli/app/services/cloudfront_config_service.rb
|
295
323
|
- lib/renuo/cli/app/services/markdown_parser_service.rb
|
324
|
+
- lib/renuo/cli/app/services/renuo_cli_config.rb
|
296
325
|
- lib/renuo/cli/app/setup_uptimerobot.rb
|
297
326
|
- lib/renuo/cli/app/templates/semaphore-deploy.yml.erb
|
298
327
|
- lib/renuo/cli/app/templates/semaphore.yml.erb
|
328
|
+
- lib/renuo/cli/app/toggl/detail.rb
|
329
|
+
- lib/renuo/cli/app/toggl/time_entry.rb
|
330
|
+
- lib/renuo/cli/app/toggl/user.rb
|
331
|
+
- lib/renuo/cli/app/toggl/workspace.rb
|
332
|
+
- lib/renuo/cli/app/toggl_redmine_comparator.rb
|
299
333
|
- lib/renuo/cli/app/upgrade_laptop.rb
|
300
334
|
- lib/renuo/cli/app/upgrade_laptop/run_command.rb
|
301
335
|
- lib/renuo/cli/app/upgrade_laptop/upgrade_laptop_execution.rb
|
302
336
|
- lib/renuo/cli/app/upgrade_laptop/upgrade_mac_os.rb
|
337
|
+
- lib/renuo/cli/app/work.rb
|
303
338
|
- lib/renuo/cli/version.rb
|
304
339
|
- renuo-cli.gemspec
|
305
340
|
- vcr_cassettes/successful_uptimerobot_calls.yml
|
data/.coveralls.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
|