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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 267952952d37a267fa02cfb82918927cde5127ed27cde56826a87cf87446faf2
4
- data.tar.gz: 9dbc20c994af13f8458cf9668dbccda3f91490d4cff6cab2069eaf4363d90137
3
+ metadata.gz: 90d3b8e87a9471eda0e8c3562f8f9d0be7163f94bcf4e90eb3f05fe4618b0d87
4
+ data.tar.gz: a512b8151eb5a140cb35c15f7ec85744c9e3f8f3bc01ba81e0bc75afa3a98a48
5
5
  SHA512:
6
- metadata.gz: ade65a29e9197e8672ff964c067bedb0d43b973725e420f7d66abe1ab93b00b7dda716ecde26c570d3b81f4d10129dafae0c05c2918159219d413b9d66c4d075
7
- data.tar.gz: 2ad6c9fc136289afa61d737059ff7856cb80bdca7112cfa6ac8eafd8b5baffdddc69f2ddfc1e3a2dec6b332bbded850eb8c9a7886510571a97419dcd31b4f9d2
6
+ metadata.gz: cab9dc3ae7914973a79137af6372e4ca94e5a725f57329da4cdd06babbd437e0024ff263b586c0dbe2394fa191d92ccdb6093c9d9ccca86cc5fc3af339f7ce65
7
+ data.tar.gz: e6dc36d8a6dbae546175f29a372c27ad85d30bccc23350c387f5c57dd487a548323dab6cc8be04748ed3ade3ffe37e93eccb01e9c07054345a3a22a50c3636a3
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.5
1
+ 3.4.1
data/bin/setup CHANGED
@@ -4,4 +4,3 @@ IFS=$'\n\t'
4
4
 
5
5
  gem install bundler --conservative
6
6
  bundle install
7
- ln -s ../../bin/check .git/hooks/pre-commit
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "renuo/cli/app/services/cloudfront_config_service"
4
4
 
5
+ # :nocov:
5
6
  class CreateAwsProject
6
7
  def initialize
7
8
  ensure_aws_setup?
@@ -115,3 +116,4 @@ class CreateAwsProject
115
116
  CLOUDFRONT_COMMANDS
116
117
  end
117
118
  end
119
+ # :nocov:
@@ -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 validate_options(opts)
12
- abort ">> specify a title" unless opts.title
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 create_pr(opts)
16
- puts `gh pr create --assignee @me --title "#{opts.title}" --body "#{pr_body(opts)}" #{opts.draft ? "--draft" : ""}`
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 || opts.redmine_ticket
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} && echo git checkout -b "#{opts.branch}"`
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
- && echo git add . \
47
- && echo git commit -S -m "#{opts.title}" \
48
- && echo git push --set-upstream origin "#{opts.branch}" \
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
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :nocov:
3
4
  module Renuo
4
5
  module Cli
5
- VERSION = "4.7.0"
6
+ VERSION = "4.8.0"
6
7
  NAME = "renuo-cli"
7
8
  end
8
9
  end
10
+ # :nocov:
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 "activeresource", "~> 5.1.0"
26
+ spec.add_dependency "activesupport", "~> 8.0.0"
27
27
  spec.add_dependency "colorize", "~> 0"
28
- spec.add_dependency "commander", "~> 4.0"
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.7.0
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: 2024-12-04 00:00:00.000000000 Z
10
+ date: 2025-01-15 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: activeresource
13
+ name: activesupport
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: 5.1.0
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: 5.1.0
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: '4.0'
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: '4.0'
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.5.18
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
@@ -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