renuo-cli 4.7.0 → 4.8.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/.ruby-version +1 -1
- data/bin/setup +0 -1
- data/lib/renuo/cli/app/create_aws_project.rb +2 -0
- data/lib/renuo/cli/app/github_pull_request_creator.rb +13 -6
- data/lib/renuo/cli/app/github_replace.rb +6 -6
- data/lib/renuo/cli/version.rb +3 -1
- data/lib/renuo/cli.rb +3 -37
- data/renuo-cli.gemspec +2 -2
- metadata +8 -20
- data/lib/renuo/cli/app/redmine/csv_base_service.rb +0 -56
- data/lib/renuo/cli/app/redmine/issue.rb +0 -25
- data/lib/renuo/cli/app/release_xing.rb +0 -35
- data/lib/renuo/cli/app/toggl/detail.rb +0 -32
- data/lib/renuo/cli/app/toggl/time_entry.rb +0 -32
- data/lib/renuo/cli/app/toggl/user.rb +0 -26
- data/lib/renuo/cli/app/toggl/workspace.rb +0 -19
- data/lib/renuo/cli/app/toggl_redmine_comparator.rb +0 -150
- data/lib/renuo/cli/app/work.rb +0 -96
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90d3b8e87a9471eda0e8c3562f8f9d0be7163f94bcf4e90eb3f05fe4618b0d87
|
4
|
+
data.tar.gz: a512b8151eb5a140cb35c15f7ec85744c9e3f8f3bc01ba81e0bc75afa3a98a48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cab9dc3ae7914973a79137af6372e4ca94e5a725f57329da4cdd06babbd437e0024ff263b586c0dbe2394fa191d92ccdb6093c9d9ccca86cc5fc3af339f7ce65
|
7
|
+
data.tar.gz: e6dc36d8a6dbae546175f29a372c27ad85d30bccc23350c387f5c57dd487a548323dab6cc8be04748ed3ade3ffe37e93eccb01e9c07054345a3a22a50c3636a3
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.4.1
|
data/bin/setup
CHANGED
@@ -2,18 +2,25 @@
|
|
2
2
|
|
3
3
|
class GithubPullRequestCreator
|
4
4
|
def run(opts)
|
5
|
-
validate_options(opts)
|
6
5
|
create_pr(opts)
|
7
6
|
end
|
8
7
|
|
9
8
|
private
|
10
9
|
|
11
|
-
def
|
12
|
-
|
10
|
+
def create_pr(opts)
|
11
|
+
command = [
|
12
|
+
"gh pr create",
|
13
|
+
"--assignee @me",
|
14
|
+
pr_title(opts),
|
15
|
+
"--body \"#{pr_body(opts)}\"",
|
16
|
+
("--draft" if opts.draft)
|
17
|
+
].compact.join(" ")
|
18
|
+
|
19
|
+
puts `#{command}`
|
13
20
|
end
|
14
21
|
|
15
|
-
def
|
16
|
-
|
22
|
+
def pr_title(opts)
|
23
|
+
opts.title ? "--title \"#{opts.title}\"" : "--fill"
|
17
24
|
end
|
18
25
|
|
19
26
|
def pr_body(opts)
|
@@ -25,6 +32,6 @@ class GithubPullRequestCreator
|
|
25
32
|
|
26
33
|
def redmine_ticket_number(opts)
|
27
34
|
current_branch = `git branch --show-current`.strip
|
28
|
-
current_branch.match(/(\d+)/)&.captures&.first
|
35
|
+
opts.redmine_ticket || current_branch.match(/(\d+)/)&.captures&.first
|
29
36
|
end
|
30
37
|
end
|
@@ -6,7 +6,7 @@ class GithubReplace
|
|
6
6
|
validate_options opts
|
7
7
|
|
8
8
|
opts.projects.split(",").each do |repo|
|
9
|
-
base = clone_and_checkout repo
|
9
|
+
base = clone_and_checkout opts, repo
|
10
10
|
find_and_replace args, repo, opts.files.split(",")
|
11
11
|
create_pr opts, repo, base
|
12
12
|
end
|
@@ -26,9 +26,9 @@ class GithubReplace
|
|
26
26
|
abort ">> specify comma-separated files to check" unless opts.files
|
27
27
|
end
|
28
28
|
|
29
|
-
def clone_and_checkout(repo)
|
29
|
+
def clone_and_checkout(opts, repo)
|
30
30
|
puts `cd #{repo} || git clone --depth=1 git@github.com:renuo/#{repo}.git`
|
31
|
-
puts `cd #{repo} &&
|
31
|
+
puts `cd #{repo} && git checkout -b "#{opts.branch}"`
|
32
32
|
`cd #{repo} && git branch -rl origin/main origin/master`.strip
|
33
33
|
end
|
34
34
|
|
@@ -43,9 +43,9 @@ class GithubReplace
|
|
43
43
|
|
44
44
|
def create_pr(opts, repo, base)
|
45
45
|
puts `cd "#{repo}" \
|
46
|
-
&&
|
47
|
-
&&
|
48
|
-
&&
|
46
|
+
&& git add . \
|
47
|
+
&& git commit -S -m "#{opts.title}" \
|
48
|
+
&& git push --set-upstream origin "#{opts.branch}" \
|
49
49
|
&& gh pr create -a @me -b "#{opts.body}" -t "#{opts.title}" -B "#{base}" -H "#{opts.branch}"`
|
50
50
|
end
|
51
51
|
end
|
data/lib/renuo/cli/version.rb
CHANGED
data/lib/renuo/cli.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "renuo/cli/version"
|
4
|
+
require "active_support"
|
5
|
+
require "active_support/core_ext"
|
4
6
|
require "rubygems"
|
5
7
|
require "gems"
|
6
8
|
require "colorize"
|
@@ -16,14 +18,11 @@ require "renuo/cli/app/create_aws_project"
|
|
16
18
|
require "renuo/cli/app/create_heroku_app"
|
17
19
|
require "renuo/cli/app/configure_sentry"
|
18
20
|
require "renuo/cli/app/create_new_logins"
|
19
|
-
require "renuo/cli/app/work"
|
20
21
|
require "renuo/cli/app/release_project"
|
21
22
|
require "renuo/cli/app/fetch_emails"
|
22
23
|
require "renuo/cli/app/heroku_users"
|
23
24
|
require "renuo/cli/app/setup_uptimerobot"
|
24
|
-
require "renuo/cli/app/release_xing"
|
25
25
|
require "renuo/cli/app/configure_semaphore"
|
26
|
-
require "renuo/cli/app/toggl_redmine_comparator"
|
27
26
|
require "renuo/cli/app/renuo_version"
|
28
27
|
require "renuo/cli/app/create_slidev_presentation"
|
29
28
|
require "renuo/cli/app/commit_leaderboard_stage"
|
@@ -142,19 +141,6 @@ module Renuo
|
|
142
141
|
end
|
143
142
|
end
|
144
143
|
|
145
|
-
command "work" do |c|
|
146
|
-
c.syntax = "renuo work"
|
147
|
-
c.summary = "A utility command to start working on a ticket."
|
148
|
-
c.description = "When you start working on a feature, it will jump to the project folder, " \
|
149
|
-
"start a new feature with the ticket number," \
|
150
|
-
'move the ticket in "In progress" state on Redmine and start Toggl with the ' \
|
151
|
-
"ticket number or add the ticket number to the running one if there was none."
|
152
|
-
c.example "renuo work start kinova 1234", "start working on ticket 1234 on kinova project"
|
153
|
-
c.action do |args|
|
154
|
-
Work.new.run(args)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
144
|
command "release" do |c|
|
159
145
|
c.syntax = "renuo release"
|
160
146
|
c.summary = "Release a projects state of develop (on github) to main in one command."
|
@@ -222,16 +208,6 @@ module Renuo
|
|
222
208
|
end
|
223
209
|
end
|
224
210
|
|
225
|
-
command "release-xing" do |c|
|
226
|
-
c.syntax = "renuo release-xing"
|
227
|
-
c.summary = "Release Xing to preview."
|
228
|
-
c.description = "Xing needs to be released manually. " \
|
229
|
-
"You need a VPN connection open before runnign this command."
|
230
|
-
c.action do |_args|
|
231
|
-
ReleaseXing.new.run
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
211
|
command "configure-semaphore" do |c|
|
236
212
|
c.syntax = "renuo configure-semaphore"
|
237
213
|
c.summary = "Adds standard semaphore configuration files to a project and creates the notifications"
|
@@ -242,16 +218,6 @@ module Renuo
|
|
242
218
|
end
|
243
219
|
end
|
244
220
|
|
245
|
-
command "toggl-redmine" do |c|
|
246
|
-
c.syntax = "renuo toggl-redmine"
|
247
|
-
c.summary = "Compares your time entries between Toggl and Redmine"
|
248
|
-
c.description = "This command extracts your time entries in Toggl and in Redmine and checks if they are " \
|
249
|
-
"consistant. It can help you in your time booking to find if you booked something wrong"
|
250
|
-
c.action do |_args|
|
251
|
-
TogglRedmineComparator.call
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
221
|
command "create-slidev-presentation" do |c|
|
256
222
|
c.syntax = "renuo create-slidev-presentation [presentation-name]"
|
257
223
|
c.summary = "Creates a new Slidev presentation with the Renuo theme"
|
@@ -319,7 +285,7 @@ module Renuo
|
|
319
285
|
c.summary = "Creates a PR for the current branch"
|
320
286
|
c.description = "Creates a PR, assigns it to you and automatically inserts the Redmine ticket number into " \
|
321
287
|
"the PR body"
|
322
|
-
c.option "--title <title>", String, "The title of the pull request"
|
288
|
+
c.option "--title <title>", String, "The title of the pull request (optional)"
|
323
289
|
c.option "--redmine-ticket <number>", Integer, "The redmine ticket number (optional)"
|
324
290
|
c.option "--draft", "Mark the PR as draft"
|
325
291
|
c.example "Create a PR with the title 'Implement XYZ'",
|
data/renuo-cli.gemspec
CHANGED
@@ -23,9 +23,9 @@ Gem::Specification.new do |spec|
|
|
23
23
|
|
24
24
|
spec.required_ruby_version = ">= 3.1.0"
|
25
25
|
|
26
|
-
spec.add_dependency "
|
26
|
+
spec.add_dependency "activesupport", "~> 8.0.0"
|
27
27
|
spec.add_dependency "colorize", "~> 0"
|
28
|
-
spec.add_dependency "commander", "~>
|
28
|
+
spec.add_dependency "commander", "~> 5.0"
|
29
29
|
spec.add_dependency "gems", "~> 1.1"
|
30
30
|
spec.add_dependency "redcarpet", "~> 3.0"
|
31
31
|
spec.add_dependency "terminal-table"
|
metadata
CHANGED
@@ -1,29 +1,28 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: renuo-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Renuo AG
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-15 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
13
|
+
name: activesupport
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
16
|
- - "~>"
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
18
|
+
version: 8.0.0
|
20
19
|
type: :runtime
|
21
20
|
prerelease: false
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
23
22
|
requirements:
|
24
23
|
- - "~>"
|
25
24
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
25
|
+
version: 8.0.0
|
27
26
|
- !ruby/object:Gem::Dependency
|
28
27
|
name: colorize
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +43,14 @@ dependencies:
|
|
44
43
|
requirements:
|
45
44
|
- - "~>"
|
46
45
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
46
|
+
version: '5.0'
|
48
47
|
type: :runtime
|
49
48
|
prerelease: false
|
50
49
|
version_requirements: !ruby/object:Gem::Requirement
|
51
50
|
requirements:
|
52
51
|
- - "~>"
|
53
52
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
53
|
+
version: '5.0'
|
55
54
|
- !ruby/object:Gem::Dependency
|
56
55
|
name: gems
|
57
56
|
requirement: !ruby/object:Gem::Requirement
|
@@ -340,10 +339,7 @@ files:
|
|
340
339
|
- lib/renuo/cli/app/local_storage.rb
|
341
340
|
- lib/renuo/cli/app/name_display.rb
|
342
341
|
- lib/renuo/cli/app/package.json
|
343
|
-
- lib/renuo/cli/app/redmine/csv_base_service.rb
|
344
|
-
- lib/renuo/cli/app/redmine/issue.rb
|
345
342
|
- lib/renuo/cli/app/release_project.rb
|
346
|
-
- lib/renuo/cli/app/release_xing.rb
|
347
343
|
- lib/renuo/cli/app/renuo_version.rb
|
348
344
|
- lib/renuo/cli/app/secrets_fetcher.rb
|
349
345
|
- lib/renuo/cli/app/services/cloudfront_config_service.rb
|
@@ -357,15 +353,9 @@ files:
|
|
357
353
|
- lib/renuo/cli/app/templates/semaphore/semaphore.yml.erb
|
358
354
|
- lib/renuo/cli/app/templates/slidev/README.md.erb
|
359
355
|
- lib/renuo/cli/app/templates/slidev/package.json.erb
|
360
|
-
- lib/renuo/cli/app/toggl/detail.rb
|
361
|
-
- lib/renuo/cli/app/toggl/time_entry.rb
|
362
|
-
- lib/renuo/cli/app/toggl/user.rb
|
363
|
-
- lib/renuo/cli/app/toggl/workspace.rb
|
364
|
-
- lib/renuo/cli/app/toggl_redmine_comparator.rb
|
365
356
|
- lib/renuo/cli/app/upgrade_laptop.rb
|
366
357
|
- lib/renuo/cli/app/upgrade_laptop/upgrade_laptop_execution.rb
|
367
358
|
- lib/renuo/cli/app/upgrade_laptop/upgrade_mac_os.rb
|
368
|
-
- lib/renuo/cli/app/work.rb
|
369
359
|
- lib/renuo/cli/version.rb
|
370
360
|
- notification.yml
|
371
361
|
- renuo-cli.gemspec
|
@@ -376,7 +366,6 @@ licenses:
|
|
376
366
|
- MIT
|
377
367
|
metadata:
|
378
368
|
rubygems_mfa_required: 'true'
|
379
|
-
post_install_message:
|
380
369
|
rdoc_options: []
|
381
370
|
require_paths:
|
382
371
|
- lib
|
@@ -391,8 +380,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
391
380
|
- !ruby/object:Gem::Version
|
392
381
|
version: '0'
|
393
382
|
requirements: []
|
394
|
-
rubygems_version: 3.
|
395
|
-
signing_key:
|
383
|
+
rubygems_version: 3.6.2
|
396
384
|
specification_version: 4
|
397
385
|
summary: The Renuo CLI automates some common workflows.
|
398
386
|
test_files: []
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "csv"
|
4
|
-
|
5
|
-
module Redmine
|
6
|
-
class CsvBaseService
|
7
|
-
API_LOCATION = "https://redmine.renuo.ch"
|
8
|
-
|
9
|
-
def initialize(token)
|
10
|
-
@token = token
|
11
|
-
end
|
12
|
-
|
13
|
-
def get
|
14
|
-
http_response = http_request(generate_url)
|
15
|
-
encoded_body = http_response.body.force_encoding("ISO-8859-1").encode("UTF-8")
|
16
|
-
csv = parse_csv(encoded_body)
|
17
|
-
parse_results(csv)
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def generate_url
|
23
|
-
URI("#{API_LOCATION}/time_entries/report.csv?#{query}&key=#{@token}")
|
24
|
-
end
|
25
|
-
|
26
|
-
def http_request(url)
|
27
|
-
req = Net::HTTP::Get.new(url)
|
28
|
-
Net::HTTP.start(url.hostname, url.port, use_ssl: true) do |http|
|
29
|
-
http.request(req)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def query
|
34
|
-
# to be implemented in concrete service
|
35
|
-
end
|
36
|
-
|
37
|
-
def parse_results(_csv)
|
38
|
-
# to be implemented in concrete service
|
39
|
-
end
|
40
|
-
|
41
|
-
def parse_csv(body)
|
42
|
-
separated_csv_entries = CSV.parse(body, col_sep: ",")
|
43
|
-
keys = separated_csv_entries.shift[1..-2]
|
44
|
-
entries = separated_csv_entries.shift[1..-2]
|
45
|
-
keys.zip(entries).to_h
|
46
|
-
rescue CSV::MalformedCSVError
|
47
|
-
raise_bad_data_error
|
48
|
-
end
|
49
|
-
|
50
|
-
def raise_bad_data_error
|
51
|
-
error = "Malformed CSV, please use comma delimiters (Redmine language setting?)"
|
52
|
-
Rails.logger.error error
|
53
|
-
raise Redmine::BadData, error
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "active_resource"
|
4
|
-
|
5
|
-
module Redmine
|
6
|
-
class Issue < ActiveResource::Base
|
7
|
-
STATUSES = {
|
8
|
-
to_start: 9,
|
9
|
-
planned: 15,
|
10
|
-
in_progress: 2,
|
11
|
-
qa: 12
|
12
|
-
}.freeze
|
13
|
-
|
14
|
-
self.site = "https://redmine.renuo.ch"
|
15
|
-
self.include_root_in_json = true
|
16
|
-
|
17
|
-
def self.headers
|
18
|
-
{ "X-Redmine-API-Key" => RenuoCliConfig.redmine_api_key }
|
19
|
-
end
|
20
|
-
|
21
|
-
def html_url
|
22
|
-
"#{self.class.site}issues/#{id}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class ReleaseXing
|
4
|
-
TMP_FOLDER_NAME = "_RENUO_RELEASE_TEMP_#{rand(100_000_000)}".freeze
|
5
|
-
MOVE_TO_TMP_FOLDER = "mkdir -p #{TMP_FOLDER_NAME} && cd #{TMP_FOLDER_NAME}".freeze
|
6
|
-
|
7
|
-
def run
|
8
|
-
checkout_project
|
9
|
-
cmd_in_folder "git remote add -f renuo git@github.com:renuo/xing-campaign.git"
|
10
|
-
cmd_in_folder "git pull"
|
11
|
-
cmd_in_folder "git fetch renuo develop"
|
12
|
-
cmd_in_folder "git subtree pull --prefix public renuo develop --squash"
|
13
|
-
cmd_in_folder "git push"
|
14
|
-
ensure
|
15
|
-
cleanup
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def checkout_project
|
21
|
-
system "#{MOVE_TO_TMP_FOLDER} && git clone git@gitlab.dc.xing.com:renuo-ag/wunsch-arbeitgeber.xing.com.git"
|
22
|
-
end
|
23
|
-
|
24
|
-
def cmd_in_folder(command)
|
25
|
-
system "#{move_and_cd} && #{command}"
|
26
|
-
end
|
27
|
-
|
28
|
-
def move_and_cd
|
29
|
-
"#{MOVE_TO_TMP_FOLDER} && cd wunsch-arbeitgeber.xing.com"
|
30
|
-
end
|
31
|
-
|
32
|
-
def cleanup
|
33
|
-
system("rm -rf #{TMP_FOLDER_NAME}")
|
34
|
-
end
|
35
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "active_resource"
|
4
|
-
|
5
|
-
module Toggl
|
6
|
-
module CustomJsonFormat
|
7
|
-
include ActiveResource::Formats::JsonFormat
|
8
|
-
|
9
|
-
# rubocop:disable Style/ModuleFunction
|
10
|
-
extend self
|
11
|
-
# rubocop:enable Style/ModuleFunction
|
12
|
-
|
13
|
-
def decode(json)
|
14
|
-
ActiveSupport::JSON.decode(json)["data"]
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class Detail < ActiveResource::Base
|
19
|
-
self.format = CustomJsonFormat
|
20
|
-
self.site = "https://toggl.com/reports/api/v2"
|
21
|
-
self.include_root_in_json = true
|
22
|
-
self.include_format_in_path = false
|
23
|
-
|
24
|
-
def self.user
|
25
|
-
RenuoCliConfig.toggl_api_token
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.password
|
29
|
-
"api_token"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "active_resource"
|
4
|
-
|
5
|
-
module Toggl
|
6
|
-
class TimeEntry < ActiveResource::Base
|
7
|
-
self.site = "https://www.toggl.com/api/v8/"
|
8
|
-
self.include_root_in_json = true
|
9
|
-
self.include_format_in_path = false
|
10
|
-
|
11
|
-
def self.user
|
12
|
-
RenuoCliConfig.toggl_api_token
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.password
|
16
|
-
"api_token"
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.current
|
20
|
-
data = get(:current)["data"]
|
21
|
-
data ? new(data) : nil
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.start(params)
|
25
|
-
post(:start, {}, params.to_json)
|
26
|
-
end
|
27
|
-
|
28
|
-
def stop
|
29
|
-
put(:stop)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Toggl
|
4
|
-
class User < 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.custom_method_collection_url(method_name, options)
|
18
|
-
prefix_options, query_options = split_options(options)
|
19
|
-
"#{prefix(prefix_options)}#{method_name}#{format_extension}#{query_string(query_options)}"
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.me
|
23
|
-
new(get(:me)["data"])
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "active_resource"
|
4
|
-
|
5
|
-
module Toggl
|
6
|
-
class Workspace < ActiveResource::Base
|
7
|
-
self.site = "https://www.toggl.com/api/v8/"
|
8
|
-
self.include_root_in_json = true
|
9
|
-
self.include_format_in_path = false
|
10
|
-
|
11
|
-
def self.user
|
12
|
-
RenuoCliConfig.toggl_api_token
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.password
|
16
|
-
"api_token"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,150 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "commander"
|
4
|
-
require "csv"
|
5
|
-
require_relative "environments"
|
6
|
-
require_relative "fetch_emails"
|
7
|
-
require "renuo/cli/app/toggl/workspace"
|
8
|
-
require "renuo/cli/app/toggl/detail"
|
9
|
-
require "renuo/cli/app/toggl/user"
|
10
|
-
require "terminal-table"
|
11
|
-
require "colorize"
|
12
|
-
require "active_support/core_ext/numeric/time"
|
13
|
-
|
14
|
-
# rubocop:disable Metrics/ClassLength
|
15
|
-
class TogglRedmineComparator
|
16
|
-
class << self
|
17
|
-
def call(days_behind = 7)
|
18
|
-
report = {}
|
19
|
-
since_date = days_behind.days.before(Date.yesterday).strftime("%F")
|
20
|
-
until_date = Date.yesterday.strftime("%F")
|
21
|
-
extract_redmine(report, since_date, until_date)
|
22
|
-
extract_toggl(report, since_date, until_date)
|
23
|
-
report = report.sort.reverse.to_h
|
24
|
-
print_table(report)
|
25
|
-
report
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def print_table(report)
|
31
|
-
rows = []
|
32
|
-
report.each do |date, value|
|
33
|
-
rows << colorize_table_row(date, value)
|
34
|
-
rows << :separator
|
35
|
-
end
|
36
|
-
rows.pop
|
37
|
-
table = Terminal::Table.new headings: %w[Day Redmine Toggl].map(&:cyan), rows:,
|
38
|
-
style: { padding_left: 2, padding_right: 2,
|
39
|
-
border_x: "-".blue, border_y: "|".blue, border_i: "+".blue }
|
40
|
-
puts table
|
41
|
-
end
|
42
|
-
|
43
|
-
def colorize_table_row(date, value)
|
44
|
-
printed_day = date.strftime("%F %a")
|
45
|
-
printed_redmine = to_time(value[:redmine])
|
46
|
-
printed_toggl = to_time(value[:toggl])
|
47
|
-
colorize_method = colorization_for_value(value)
|
48
|
-
[printed_day, printed_redmine, printed_toggl].map { |v| v.colorize(colorize_method) }
|
49
|
-
end
|
50
|
-
|
51
|
-
def colorization_for_value(value)
|
52
|
-
if more_toggl?(value)
|
53
|
-
:red
|
54
|
-
elsif more_redmine?(value)
|
55
|
-
:default
|
56
|
-
else
|
57
|
-
:green
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def extract_redmine(report, since_date, _until_date)
|
62
|
-
encoded_body = perform_redmine_call(since_date)
|
63
|
-
csv = convert_redmine_csv(encoded_body)
|
64
|
-
csv.each do |date, entry|
|
65
|
-
report[Date.parse(date)] ||= default_value
|
66
|
-
report[Date.parse(date)][:redmine] = to_seconds(entry) if entry.present?
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def convert_redmine_csv(encoded_body)
|
71
|
-
separated_csv_entries = CSV.parse(encoded_body, col_sep: ",")
|
72
|
-
keys = separated_csv_entries.shift[1..-2]
|
73
|
-
entries = separated_csv_entries.shift[1..-2]
|
74
|
-
keys.zip(entries)
|
75
|
-
end
|
76
|
-
|
77
|
-
def perform_redmine_call(since_date)
|
78
|
-
query = generate_redmine_query(since_date)
|
79
|
-
url = URI("https://redmine.renuo.ch/time_entries/report.csv?#{query}")
|
80
|
-
req = Net::HTTP::Get.new(url)
|
81
|
-
req["X-Redmine-API-Key"] = RenuoCliConfig.redmine_api_key
|
82
|
-
response = Net::HTTP.start(url.hostname, url.port, use_ssl: true) { |http| http.request(req) }
|
83
|
-
response.body.force_encoding("ISO-8859-1").encode("UTF-8")
|
84
|
-
end
|
85
|
-
|
86
|
-
def generate_redmine_query(since_date)
|
87
|
-
URI.encode_www_form(
|
88
|
-
[["utf8", "✓"], ["criteria[]", "user"],
|
89
|
-
["f[]", "spent_on"], ["f[]", "user_id"],
|
90
|
-
["op[spent_on]", ">="], ["op[user_id]", "="],
|
91
|
-
["v[spent_on][]", since_date], ["v[user_id][]", "me"],
|
92
|
-
["f[]", ""],
|
93
|
-
["c[]", "project"], ["c[]", "spent_on"], ["c[]", "user"], ["c[]", "activity"], ["c[]", "issue"],
|
94
|
-
["c[]", "comments"], ["c[]", "hours"], %w[columns day], ["criteria[]", ""]]
|
95
|
-
)
|
96
|
-
end
|
97
|
-
|
98
|
-
def extract_toggl(report, since_date, until_date)
|
99
|
-
user_id = Toggl::User.me.id
|
100
|
-
workspace_ids = Toggl::Workspace.all.map(&:id)
|
101
|
-
|
102
|
-
workspace_ids.each do |workspace_id|
|
103
|
-
time_entries = Toggl::Detail.where(since: since_date, until: until_date,
|
104
|
-
user_agent: "renuo-cli", workspace_id:, user_ids: user_id)
|
105
|
-
parse_toggl_entries(report, time_entries)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def parse_toggl_entries(report, time_entries)
|
110
|
-
time_entries
|
111
|
-
.reject { |te| te.end.nil? }
|
112
|
-
.group_by { |time_entry| Date.parse(time_entry.end) }
|
113
|
-
.each do |date, grouped_time_entries|
|
114
|
-
report[date] ||= default_value
|
115
|
-
report[date][:toggl] += grouped_time_entries.sum(&:dur)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def default_value
|
120
|
-
{ redmine: 0.0, toggl: 0.0 }
|
121
|
-
end
|
122
|
-
|
123
|
-
def to_time(value)
|
124
|
-
sec = value / 1000.0
|
125
|
-
min, _sec = sec.divmod(60.0)
|
126
|
-
hour, min = min.divmod(60.0)
|
127
|
-
format("%<hour>02d:%<min>02d", hour:, min:)
|
128
|
-
end
|
129
|
-
|
130
|
-
def to_seconds(value)
|
131
|
-
hours, minutes = value.to_d.divmod(1.0)
|
132
|
-
(hours * 60 * 60 * 1000) + (minutes * 60 * 60 * 1000)
|
133
|
-
end
|
134
|
-
|
135
|
-
def non_working_day?(value)
|
136
|
-
[value[:redmine], value[:toggl]].all?(&:zero?)
|
137
|
-
end
|
138
|
-
|
139
|
-
BUFFER = 20_000
|
140
|
-
|
141
|
-
def more_toggl?(value)
|
142
|
-
(value[:toggl] - value[:redmine]) > BUFFER
|
143
|
-
end
|
144
|
-
|
145
|
-
def more_redmine?(value)
|
146
|
-
(value[:redmine] - value[:toggl]) > BUFFER
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
# rubocop:enable Metrics/ClassLength
|
data/lib/renuo/cli/app/work.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fileutils"
|
4
|
-
require "renuo/cli/app/redmine/issue"
|
5
|
-
require "renuo/cli/app/toggl/time_entry"
|
6
|
-
|
7
|
-
class Work
|
8
|
-
ACTIONS = %w[start].freeze
|
9
|
-
|
10
|
-
def run(args)
|
11
|
-
ActiveResource::Base.logger = Logger.new($stdout)
|
12
|
-
@action, @project_name, @ticket_number = args
|
13
|
-
validate_action
|
14
|
-
validate_project_name
|
15
|
-
validate_ticket_number
|
16
|
-
start_feature_branch
|
17
|
-
update_redmine_ticket
|
18
|
-
start_toggl
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
# TODO: I want to implement also the stop action.
|
24
|
-
def validate_action
|
25
|
-
abort(">> No action given. It must be start") unless ACTIONS.include? @action
|
26
|
-
end
|
27
|
-
|
28
|
-
def validate_project_name
|
29
|
-
abort(">> No project name given.") unless @project_name
|
30
|
-
end
|
31
|
-
|
32
|
-
def validate_ticket_number
|
33
|
-
abort(">> No ticket number given.") unless @ticket_number
|
34
|
-
issue = Redmine::Issue.find(@ticket_number)
|
35
|
-
open_statuses = Redmine::Issue::STATUSES.values_at(:to_start, :planned, :in_progress, :qa)
|
36
|
-
return if open_statuses.include?(issue.status.id)
|
37
|
-
|
38
|
-
system("open #{issue.html_url}")
|
39
|
-
abort(">> Ticket should be in an open status")
|
40
|
-
end
|
41
|
-
|
42
|
-
def start_feature_branch
|
43
|
-
project_folder = `autojump #{@project_name}`.strip
|
44
|
-
system("cd #{project_folder} && git stash && git checkout develop " \
|
45
|
-
"&& git pull && git flow feature start #{@ticket_number}")
|
46
|
-
end
|
47
|
-
|
48
|
-
def update_redmine_ticket
|
49
|
-
issue = Redmine::Issue.find(@ticket_number)
|
50
|
-
issue.status_id = Redmine::Issue::STATUSES[:in_progress]
|
51
|
-
issue.save
|
52
|
-
system("open #{issue.html_url}")
|
53
|
-
end
|
54
|
-
|
55
|
-
def start_toggl
|
56
|
-
current_time_entry = Toggl::TimeEntry.current
|
57
|
-
if current_time_entry.nil?
|
58
|
-
create_toggl_time_entry
|
59
|
-
elsif current_time_entry.description.nil?
|
60
|
-
update_toggl_time_entry(current_time_entry.id)
|
61
|
-
else
|
62
|
-
existing_toggl(current_time_entry)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def existing_toggl(current_time_entry)
|
67
|
-
say("A timer '#{current_time_entry.description}' was already running.")
|
68
|
-
if current_time_entry.description.to_i == @ticket_number.to_i
|
69
|
-
say("I will keep using it")
|
70
|
-
else
|
71
|
-
say("I stopped it and started a new time entry.")
|
72
|
-
stop_toggl_time_entry(current_time_entry.id)
|
73
|
-
create_toggl_time_entry
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def update_toggl_time_entry(time_entry_id)
|
78
|
-
say("A timer was already running but without a project assigned. I updated the current time entry.")
|
79
|
-
|
80
|
-
time_entry = Toggl::TimeEntry.find(time_entry_id)
|
81
|
-
time_entry.description = @ticket_number.to_s
|
82
|
-
time_entry.tags = [@project_name.to_s]
|
83
|
-
time_entry.created_with = "curl"
|
84
|
-
time_entry.save
|
85
|
-
end
|
86
|
-
|
87
|
-
def create_toggl_time_entry
|
88
|
-
Toggl::TimeEntry.start(time_entry: { description: @ticket_number.to_s,
|
89
|
-
tags: [@project_name.to_s],
|
90
|
-
created_with: "curl" })
|
91
|
-
end
|
92
|
-
|
93
|
-
def stop_toggl_time_entry(time_entry_id)
|
94
|
-
Toggl::TimeEntry.find(time_entry_id).stop
|
95
|
-
end
|
96
|
-
end
|