renuo-cli 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status Master](https://semaphoreci.com/api/v1/projects/a6f6bfd7-e48b-4035-858d-fc7cfa190608/1707289/badge.svg)](https://semaphoreci.com/renuo/renuo-cli)
|
2
|
-
[![Coverage Status](https://coveralls.io/repos/renuo/renuo-cli/badge.svg?branch=master&service=github)](https://coveralls.io/github/renuo/renuo-cli?branch=master)
|
3
2
|
[![Code Climate](https://codeclimate.com/github/renuo/renuo-cli/badges/gpa.svg)](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
|
-
|