modulorails 1.5.2.pre.2 → 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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/release-modulorails/SKILL.md +150 -0
  3. data/.rubocop.yml +2 -1
  4. data/CHANGELOG.md +55 -8
  5. data/Gemfile.lock +75 -26
  6. data/README.md +69 -9
  7. data/lib/generators/modulorails/claude_code/claude_code_generator.rb +64 -0
  8. data/lib/generators/modulorails/claude_code/templates/.gitlab-ci.yml.tt +120 -0
  9. data/lib/generators/modulorails/claude_code/templates/.modulorails-gitlab-ci +6 -0
  10. data/lib/generators/modulorails/claude_code/templates/bin/init-firewall.sh.tt +118 -0
  11. data/lib/generators/modulorails/docker/compose/compose_generator.rb +7 -6
  12. data/lib/generators/modulorails/docker/config/config_generator.rb +11 -5
  13. data/lib/generators/modulorails/docker/config/templates/config/database.yml.tt +7 -2
  14. data/lib/generators/modulorails/docker/devcontainer/devcontainer_generator.rb +52 -0
  15. data/lib/generators/modulorails/docker/devcontainer/templates/devcontainer/Dockerfile.tt +53 -0
  16. data/lib/generators/modulorails/docker/devcontainer/templates/devcontainer/compose.yml.tt +97 -0
  17. data/lib/generators/modulorails/docker/devcontainer/templates/devcontainer/devcontainer.json.tt +80 -0
  18. data/lib/generators/modulorails/docker/docker_generator.rb +7 -0
  19. data/lib/generators/modulorails/docker/dockerfile/dockerfile_generator.rb +15 -11
  20. data/lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/{rails/Dockerfile.prod.tt → Dockerfile.prod.tt} +31 -12
  21. data/lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/dockerignore.tt +120 -0
  22. data/lib/generators/modulorails/docker/entrypoint/entrypoint_generator.rb +11 -5
  23. data/lib/generators/modulorails/docker/entrypoint/templates/entrypoints/docker-entrypoint.sh.tt +5 -0
  24. data/lib/generators/modulorails/githooks/githooks_generator.rb +5 -3
  25. data/lib/generators/modulorails/githooks/templates/dc.sh +30 -0
  26. data/lib/generators/modulorails/githooks/templates/dcr.sh +47 -0
  27. data/lib/generators/modulorails/githooks/templates/post-rewrite.sh +1 -1
  28. data/lib/generators/modulorails/githooks/templates/pre-merge-commit.sh +1 -1
  29. data/lib/generators/modulorails/githooks/templates/refresh_generations.sh +17 -9
  30. data/lib/generators/modulorails/gitlabci/gitlabci_generator.rb +7 -1
  31. data/lib/generators/modulorails/gitlabci/templates/.gitlab-ci.yml.tt +15 -13
  32. data/lib/generators/modulorails/gitlabci/templates/bin/test.sh.tt +36 -0
  33. data/lib/generators/modulorails/gitlabci/templates/config/deploy/production.yaml.tt +4 -4
  34. data/lib/generators/modulorails/gitlabci/templates/config/deploy/review.yaml.tt +4 -4
  35. data/lib/generators/modulorails/gitlabci/templates/config/deploy/staging.yaml.tt +7 -7
  36. data/lib/generators/modulorails/moduloproject/moduloproject_generator.rb +8 -3
  37. data/lib/generators/modulorails/moduloproject/templates/config/environments/production.rb.tt +21 -51
  38. data/lib/generators/modulorails/rubocop/rubocop_generator.rb +8 -3
  39. data/lib/generators/modulorails/rubocop/templates/rubocop.yml.tt +7 -1
  40. data/lib/generators/modulorails/self_update/self_update_generator.rb +4 -0
  41. data/lib/generators/modulorails/sidekiq/sidekiq_generator.rb +95 -38
  42. data/lib/generators/modulorails/sidekiq/templates/config/initializers/sidekiq.rb.tt +4 -4
  43. data/lib/modulorails/configuration.rb +17 -7
  44. data/lib/modulorails/data.rb +41 -12
  45. data/lib/modulorails/generators/base.rb +1 -1
  46. data/lib/modulorails/railtie.rb +4 -1
  47. data/lib/modulorails/version.rb +1 -1
  48. data/lib/modulorails.rb +15 -5
  49. metadata +17 -10
  50. data/lib/generators/modulorails/docker/compose/templates/docker-compose.yml.tt +0 -81
  51. data/lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/modulotech/Dockerfile.prod.tt +0 -66
  52. data/lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/modulotech/Dockerfile.tt +0 -30
  53. data/lib/generators/modulorails/docker/entrypoint/templates/entrypoints/webpack-entrypoint.sh.tt +0 -7
  54. data/lib/generators/modulorails/githooks/templates/dockeruby.rb +0 -124
  55. data/lib/generators/modulorails/sidekiq/templates/entrypoints/sidekiq-entrypoint.sh.tt +0 -7
@@ -1,18 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails/generators'
3
+ require 'modulorails/generators/base'
4
4
 
5
- class Modulorails::RubocopGenerator < Rails::Generators::Base
5
+ class Modulorails::RubocopGenerator < Modulorails::Generators::Base
6
+
7
+ VERSION = 1
6
8
 
7
9
  source_root File.expand_path('templates', __dir__)
8
10
  desc 'This generator creates a configuration for Rubocop'
9
11
 
10
- def create_config_files
12
+ protected
13
+
14
+ def create_config
11
15
  rubocop_config_path = Rails.root.join('.rubocop.yml')
12
16
  gitlab_config_path = Rails.root.join('.gitlab-ci.yml')
13
17
 
14
18
  template 'rubocop.yml', rubocop_config_path, force: true
15
19
 
20
+ return unless File.exist?(gitlab_config_path)
16
21
  return if File.read(gitlab_config_path).match?(/\s+extends:\s+.lint\s*$/)
17
22
 
18
23
  append_file gitlab_config_path do
@@ -10,7 +10,8 @@
10
10
  # See https://docs.rubocop.org/rubocop/configuration
11
11
 
12
12
  # Enabling Rails-specific cops.
13
- require: rubocop-rails
13
+ plugins:
14
+ - rubocop-rails
14
15
 
15
16
  inherit_mode:
16
17
  merge:
@@ -102,6 +103,7 @@ Metrics/CyclomaticComplexity:
102
103
  # Commonly used screens these days easily fit more than 80 characters.
103
104
  Layout/LineLength:
104
105
  Max: 120
106
+ AllowedPatterns: ['\A\s*#']
105
107
 
106
108
  # Too short methods lead to extraction of single-use methods, which can make
107
109
  # the code easier to read (by naming things), but can also clutter the class
@@ -235,6 +237,10 @@ Metrics/BlockLength:
235
237
  Exclude:
236
238
  - 'app/admin/**/*'
237
239
  - 'lib/tasks/**/*'
240
+ # Allow big blocks for Concerns
241
+ AllowedMethods:
242
+ - class_methods
243
+ - included
238
244
 
239
245
  # Checks if empty lines around the bodies of classes match the configuration.
240
246
  Layout/EmptyLinesAroundClassBody:
@@ -12,6 +12,10 @@ class Modulorails::SelfUpdateGenerator < Rails::Generators::Base
12
12
  LATEST_VERSION_URL = 'https://rubygems.org/api/v1/versions/modulorails.json'
13
13
 
14
14
  def create_config_file
15
+ Modulorails.deprecator.warn(<<~MESSAGE)
16
+ Modulorails::SelfUpdateGenerator is deprecated and will be removed in version 2.0.
17
+ MESSAGE
18
+
15
19
  # Get the last published version
16
20
  versions = HTTParty.get(LATEST_VERSION_URL).parsed_response
17
21
  last_published_version = versions&.reject { |version| Gem::Version.new(version['number']).prerelease? }&.first
@@ -8,12 +8,15 @@ class Modulorails::SidekiqGenerator < Rails::Generators::Base
8
8
 
9
9
  desc 'This generator adds Sidekiq to the project'
10
10
 
11
+ def deprecation_warning
12
+ Modulorails.deprecator.warn(<<~MESSAGE)
13
+ Modulorails::SidekiqGenerator is deprecated and will be removed in version 2.0.
14
+ Use Moduloproject 3.0 (available later) to initialize new projects with Sidekiq configuration.
15
+ MESSAGE
16
+ end
17
+
11
18
  def add_to_docker_compose
12
- if Rails.root.join('compose.yml').exist?
13
- add_to_docker_compose_yml_file(Rails.root.join('compose.yml'))
14
- elsif Rails.root.join('docker-compose.yml').exist?
15
- add_to_docker_compose_yml_file(Rails.root.join('docker-compose.yml'))
16
- end
19
+ add_to_docker_compose_yml_file(Rails.root.join('.devcontainer/compose.yml'))
17
20
  end
18
21
 
19
22
  def add_to_deploy_files
@@ -26,12 +29,15 @@ class Modulorails::SidekiqGenerator < Rails::Generators::Base
26
29
  gemfile_path = Rails.root.join('Gemfile')
27
30
 
28
31
  # Add gem redis unless already present
29
- append_to_file(gemfile_path, "gem 'redis'\n") unless File.read(gemfile_path).match?(/^\s*gem ['"]redis['"]/)
32
+ append_to_file(gemfile_path, "\ngem 'redis'\n") unless File.read(gemfile_path).match?(/^\s*gem ['"]redis['"]/)
30
33
 
31
34
  # Add gem sidekiq unless already present
32
- return if File.read(gemfile_path).match?(/^\s*gem ['"]sidekiq['"]/)
35
+ append_to_file(gemfile_path, "\ngem 'sidekiq'\n") unless File.read(gemfile_path).match?(/^\s*gem ['"]sidekiq['"]/)
36
+
37
+ # Add gem sidekiq-datadog-error-tracking unless already present
38
+ return if File.read(gemfile_path).match?(/^\s*gem ['"]sidekiq-datadog-error-tracking['"]/)
33
39
 
34
- append_to_file(gemfile_path, "gem 'sidekiq'\n")
40
+ append_to_file(gemfile_path, "\ngem 'sidekiq-datadog-error-tracking'\n")
35
41
  end
36
42
 
37
43
  def add_to_config
@@ -46,20 +52,19 @@ class Modulorails::SidekiqGenerator < Rails::Generators::Base
46
52
 
47
53
  def add_routes
48
54
  routes_path = Rails.root.join('config/routes.rb')
49
-
50
55
  return if File.read(routes_path).match?(%r{require ['"]sidekiq/web["']})
51
56
 
52
- inject_into_file routes_path, after: "Rails.application.routes.draw do\n" do
53
- <<-RUBY
54
- require 'sidekiq/web'
55
- mount Sidekiq::Web => '/sidekiq'
56
-
57
- RUBY
57
+ authentication_type = Modulorails.data.authentication_type
58
+ if respond_to?("add_#{authentication_type}_authenticated_route")
59
+ send("add_#{authentication_type}_authenticated_route", routes_path)
60
+ else
61
+ add_unauthenticated_route(routes_path)
58
62
  end
59
63
  end
60
64
 
61
65
  def add_health_check
62
66
  file_path = Rails.root.join('config/initializers/health_check.rb')
67
+ return unless File.exist?(file_path)
63
68
 
64
69
  return if File.read(file_path).match?(/add_custom_check\s*\(?\s*['"]sidekiq-queues['"]\s*\)?/)
65
70
 
@@ -73,10 +78,10 @@ class Modulorails::SidekiqGenerator < Rails::Generators::Base
73
78
  # No queues, means no jobs, ok!
74
79
  next '' if queues.empty?
75
80
 
76
- enqueued_jobs_count = queues.each.map { |queue| queue.count }.sum
81
+ global_latency = queues.each.map { |queue| queue.latency }.sum
77
82
 
78
- # Less than 200 enqueued jobs, ok!
79
- enqueued_jobs_count < 200 ? '' : "\#{enqueued_jobs_count} are currently enqueued."
83
+ # Global latency is less than 5 minutes, ok!
84
+ global_latency < 5.minutes ? '' : "Global latency (\#{global_latency}) is too high."
80
85
  end
81
86
 
82
87
  # Add one or more custom checks that return a blank string if ok, or an error message if there is an error
@@ -90,9 +95,9 @@ class Modulorails::SidekiqGenerator < Rails::Generators::Base
90
95
  end
91
96
  end
92
97
 
93
- def add_entrypoint
94
- template 'entrypoints/sidekiq-entrypoint.sh', 'bin/sidekiq-entrypoint'
95
- chmod 'bin/sidekiq-entrypoint', 0o755
98
+ def remove_entrypoint
99
+ remove_file 'entrypoints/sidekiq-entrypoint.sh'
100
+ remove_file 'bin/sidekiq-entrypoint'
96
101
  end
97
102
 
98
103
  private
@@ -100,31 +105,43 @@ class Modulorails::SidekiqGenerator < Rails::Generators::Base
100
105
  def add_to_docker_compose_yml_file(file_path)
101
106
  @image_name ||= Modulorails.data.name.parameterize
102
107
 
103
- # Create docker-compose.yml unless present
104
- invoke(Modulorails::DockerGenerator, []) unless File.exist?(file_path)
108
+ unless File.exist?(file_path)
109
+ puts("Compose file not found at #{file_path}. Ignoring.")
110
+ return
111
+ end
105
112
 
106
- return if File.read(file_path).match?(/^ {2}sidekiq:$/)
113
+ # Update existing Sidekiq service
114
+ if File.read(file_path).match?(/^ {2}sidekiq:$/)
115
+ pattern = /^(\s*)entrypoint:(\s*).\/(bin\/sidekiq-entrypoint|entrypoints\/sidekiq-entrypoint\.sh)/
116
+ gsub_file file_path, pattern, '\1command:\2./bin/bundle exec sidekiq'
117
+ return
118
+ end
107
119
 
120
+ add_sidekiq_service(file_path)
121
+ end
122
+
123
+ def add_sidekiq_service(file_path)
108
124
  insert_into_file file_path, after: /^services:/ do
125
+ # Using `<<-` Heredoc syntax to preserve YAML indentation
109
126
  <<-YAML
110
127
 
111
128
  sidekiq:
112
129
  image: modulotechgroup/#{@image_name}:dev
113
130
  build:
114
- context: .
115
- dockerfile: Dockerfile
131
+ context: ..
132
+ dockerfile: .devcontainer/Dockerfile
116
133
  depends_on:
117
134
  - database
118
135
  - redis
119
136
  volumes:
120
- - .:/app
137
+ - ..:/rails
121
138
  environment:
122
139
  RAILS_ENV: development
123
140
  URL: http://app:3000
124
- #{@image_name.upcase}_DATABASE_HOST: database
125
- #{@image_name.upcase}_DATABASE_NAME: #{@image_name}
126
- REDIS_URL: redis://redis:6379/1
127
- entrypoint: ./bin/sidekiq-entrypoint
141
+ env_file:
142
+ - path: .env
143
+ required: false
144
+ command: ./bin/bundle exec sidekiq
128
145
  stdin_open: true
129
146
  tty: true
130
147
  YAML
@@ -146,8 +163,9 @@ class Modulorails::SidekiqGenerator < Rails::Generators::Base
146
163
  end
147
164
 
148
165
  def add_to_deploy_file(file_path)
149
- # Do nothing if file does not exists or Sidekiq is already enabled
150
- return if !File.exist?(file_path) || File.read(file_path).match?(/^ {2}sidekiq:$/)
166
+ # Do nothing if file does not exist or Sidekiq is already enabled
167
+ return unless File.exist?(file_path)
168
+ return if File.read(file_path).match?(/^sidekiq:\n {2}enabled: true$/m)
151
169
 
152
170
  # Add sidekiq to deploy file
153
171
  insert_into_file file_path do
@@ -158,17 +176,56 @@ class Modulorails::SidekiqGenerator < Rails::Generators::Base
158
176
  resources:
159
177
  requests:
160
178
  cpu: 100m
161
- memory: 512Mi
179
+ memory: 1024Mi
162
180
  limits:
163
- cpu: 100m
164
- memory: 512Mi
181
+ memory: 1024Mi
165
182
  autoscaling:
166
183
  enabled: true
167
- minReplicas: 1
168
- maxReplicas: 10
184
+ minReplicas: #{file_path.to_s.match?('production.y') ? 2 : 1}
185
+ maxReplicas: #{file_path.to_s.match?('production.y') ? 10 : 2}
169
186
  targetCPUUtilizationPercentage: 80
187
+ command: ["./bin/bundle", "exec", "sidekiq"]
170
188
  YAML
171
189
  end
172
190
  end
173
191
 
192
+ RAILS_AUTHENTICATION_TEMPLATE = <<~RUBY.freeze
193
+ require 'sidekiq/web'
194
+ constraints lambda { |request| Session.find_by(id: request.cookie_jar.signed[:session_id])&.user&.role == 'admin' } do
195
+ mount Sidekiq::Web => '/admin/sidekiq'
196
+ end
197
+
198
+
199
+ RUBY
200
+
201
+ def add_devise_authenticated_route(routes_path)
202
+ template = <<~RUBY
203
+ require 'sidekiq/web'
204
+ authenticate :user, lambda { |u| u.respond_to?('role') && u.role == 'admin' } do
205
+ mount Sidekiq::Web => '/admin/sidekiq'
206
+ end
207
+
208
+ RUBY
209
+ puts("Injecting #{template} into #{routes_path}: update the authentication block.")
210
+ inject_into_file routes_path, template, after: "Rails.application.routes.draw do\n"
211
+ end
212
+
213
+ def add_rails_authenticated_route(routes_path)
214
+ template = RAILS_AUTHENTICATION_TEMPLATE
215
+ puts("Injecting #{template} into #{routes_path}: update the authentication block.")
216
+ inject_into_file routes_path, template, after: "Rails.application.routes.draw do\n"
217
+ end
218
+
219
+ def add_unauthenticated_route(routes_path)
220
+ template = <<~RUBY
221
+ require 'sidekiq/web'
222
+ mount Sidekiq::Web => '/admin/sidekiq'
223
+
224
+ RUBY
225
+ puts("Injecting #{template} into #{routes_path}: you should add authentication to the route.")
226
+ puts("If you do not have authentication, execute `rails generate authentication` and replace \
227
+ the route by #{RAILS_AUTHENTICATION_TEMPLATE}.")
228
+ inject_into_file routes_path, template, after: "Rails.application.routes.draw do\n"
229
+ end
230
+
174
231
  end
@@ -1,9 +1,9 @@
1
- redis_url = Rails.application.config_for('cable')['url']
2
-
3
1
  Sidekiq.configure_server do |config|
4
- config.redis = { url: redis_url }
2
+ config.redis = { url: REDIS_URL }
3
+ config.logger.formatter = Sidekiq::Logger::Formatters::Datadog.new
4
+ config.error_handlers << Sidekiq::Datadog::Error::Tracking
5
5
  end
6
6
 
7
7
  Sidekiq.configure_client do |config|
8
- config.redis = { url: redis_url }
8
+ config.redis = { url: REDIS_URL }
9
9
  end
@@ -16,14 +16,8 @@ module Modulorails
16
16
  # config.project_manager 'pm@modulotech.fr'
17
17
  # config.endpoint "intranet's endpoint"
18
18
  # config.api_key "intranet's api key"
19
- # config.production_url "production.app.com"
20
- # config.staging_url "staging.app.com"
21
- # config.review_base_url "review.app.com"
22
19
  # end
23
- %i[
24
- name main_developer project_manager endpoint api_key no_auto_update production_url staging_url
25
- review_base_url
26
- ].each do |field|
20
+ %i[name main_developer project_manager endpoint api_key].each do |field|
27
21
  define_method(field) do |value=nil|
28
22
  # No value means we want to get the field
29
23
  return send("_#{field}") unless value
@@ -33,6 +27,22 @@ module Modulorails
33
27
  end
34
28
  end
35
29
 
30
+ # Deprecated configuration options - will be removed in 2.0
31
+ %i[no_auto_update production_url staging_url review_base_url].each do |field|
32
+ define_method(field) do |value=nil|
33
+ # No value means we want to get the field
34
+ return send("_#{field}") unless value
35
+
36
+ # Warn only when setting the value (i.e., in the initializer)
37
+ Modulorails.deprecator.warn(
38
+ "Modulorails configuration `#{field}` is deprecated and will be removed in version 2.0."
39
+ )
40
+
41
+ # Else we want to set the field
42
+ send("_#{field}=", value)
43
+ end
44
+ end
45
+
36
46
  end
37
47
 
38
48
  end
@@ -13,7 +13,7 @@ module Modulorails
13
13
  ATTRIBUTE_KEYS = %i[
14
14
  name main_developer project_manager repository type rails_name ruby_version rails_version
15
15
  bundler_version modulorails_version adapter db_version adapter_version webpacker_version
16
- importmap_version jsbundling_version
16
+ importmap_version js_engine
17
17
  production_url staging_url review_base_url
18
18
  environment_name
19
19
  ].freeze
@@ -48,6 +48,10 @@ module Modulorails
48
48
  }
49
49
  end
50
50
 
51
+ def authentication_type
52
+ @authentication_type ||= authentication_data
53
+ end
54
+
51
55
  private
52
56
 
53
57
  def initialize_from_constants
@@ -104,20 +108,16 @@ module Modulorails
104
108
 
105
109
  def initialize_from_database
106
110
  # Get the database connection to identify the database used by the application
107
- # or return nil if the database does not exist
108
- db_connection = begin
109
- ActiveRecord::Base.connection
110
- rescue ActiveRecord::NoDatabaseError, ActiveRecord::ConnectionNotEstablished => e
111
- warn("[Modulorails] Error: #{e.message}")
112
- nil
113
- end
111
+ db_connection = ActiveRecord::Base.connection || return
114
112
 
115
113
  # The name of the ActiveRecord adapter; it gives the name of the database system too
116
- @adapter = db_connection&.adapter_name&.downcase
114
+ @adapter = db_connection.adapter_name.downcase
117
115
 
118
116
  # The version of the database engine; this request works only on MySQL and PostgreSQL
119
117
  # It should not be a problem since those are the sole database engines used at Modulotech
120
- @db_version = db_connection&.select_value('SELECT version()')
118
+ @db_version = db_connection.select_value('SELECT version()')
119
+ rescue ActiveRecord::NoDatabaseError, ActiveRecord::ConnectionNotEstablished => e
120
+ warn("[Modulorails] Error: #{e.message}")
121
121
  end
122
122
 
123
123
  def initialize_from_gem_specs
@@ -134,14 +134,28 @@ module Modulorails
134
134
  # The version of the ActiveRecord adapter
135
135
  @adapter_version = gem_version(loaded_specs[@adapter])
136
136
 
137
+ initialize_from_js_specs(loaded_specs)
138
+ end
139
+
140
+ def initialize_from_js_specs(loaded_specs)
137
141
  # The version of the webpacker gem - might be nil
138
142
  @webpacker_version = gem_version(loaded_specs['webpacker'])
139
143
 
140
144
  # The version of the importmap-rails gem - might be nil
141
145
  @importmap_version = gem_version(loaded_specs['importmap-rails'])
142
146
 
143
- # The version of the jsbundling-rails gem - might be nil
144
- @jsbundling_version = gem_version(loaded_specs['jsbundling-rails'])
147
+ @js_engine =
148
+ if @webpacker_version
149
+ :webpacker
150
+ elsif @importmap_version
151
+ :importmap
152
+ else
153
+ find_js_engine
154
+ end
155
+ end
156
+
157
+ def find_js_engine
158
+ File.exist?(Rails.root.join('bun.config.js')) ? :bun : :unknown
145
159
  end
146
160
 
147
161
  def gem_version(spec)
@@ -152,6 +166,8 @@ module Modulorails
152
166
  # Theorically, origin is the main repository of the project and git is the sole VCS we use
153
167
  # at Modulotech
154
168
  @repository = Git.open(::Rails.root).config('remote.origin.url')
169
+ rescue ArgumentError, Git::GitExecuteError => e
170
+ warn("[Modulorails] Error: #{e.message}")
155
171
  end
156
172
 
157
173
  def to_project_data_params
@@ -182,6 +198,19 @@ module Modulorails
182
198
  }
183
199
  end
184
200
 
201
+ def authentication_data
202
+ loaded_specs = Gem.loaded_specs
203
+
204
+ # The version of the devise gem - might be nil
205
+ devise_version = gem_version(loaded_specs['devise'])
206
+
207
+ if devise_version
208
+ :devise
209
+ elsif File.exist?(Rails.root.join('app/controllers/concerns/authentication.rb'))
210
+ :rails
211
+ end
212
+ end
213
+
185
214
  end
186
215
 
187
216
  end
@@ -72,7 +72,7 @@ module Modulorails
72
72
  'version' => v
73
73
  }
74
74
 
75
- create_file(file, config.to_yaml)
75
+ create_file(file, config.to_yaml, force: true)
76
76
 
77
77
  say "Add #{file} to git"
78
78
  git add: file
@@ -32,7 +32,7 @@ module Modulorails
32
32
  end
33
33
 
34
34
  initializer "modulorails.deprecator" do |app|
35
- app.deprecators[:modulorails] = Modulorails.deprecator
35
+ app.deprecators[:modulorails] = Modulorails.deprecator if app.respond_to?(:deprecators)
36
36
  end
37
37
 
38
38
  # Sending data after the initialization ensures we can access
@@ -62,6 +62,9 @@ module Modulorails
62
62
  # Add/update Bundler-audit config
63
63
  Modulorails.generate_bundleraudit_template
64
64
 
65
+ # Add/update Claude Code config
66
+ Modulorails.configure_claude_code
67
+
65
68
  # Gem's self-update if a new version was released
66
69
  Modulorails.self_update
67
70
  end
@@ -1,6 +1,6 @@
1
1
  module Modulorails
2
2
 
3
- VERSION = '1.5.2.pre.2'.freeze
3
+ VERSION = '1.7.0'.freeze
4
4
 
5
5
  # Useful to compare the current Ruby version
6
6
  COMPARABLE_RUBY_VERSION = Gem::Version.new(RUBY_VERSION)
data/lib/modulorails.rb CHANGED
@@ -9,6 +9,7 @@ require 'generators/modulorails/self_update/self_update_generator'
9
9
  require 'generators/modulorails/rubocop/rubocop_generator'
10
10
  require 'generators/modulorails/bundleraudit/bundleraudit_generator'
11
11
  require 'generators/modulorails/githooks/githooks_generator'
12
+ require 'generators/modulorails/claude_code/claude_code_generator'
12
13
  require 'httparty'
13
14
  require 'modulorails/error_data'
14
15
  require 'modulorails/success_data'
@@ -97,14 +98,14 @@ module Modulorails
97
98
  # went wrong with an `errors` field. We do not want to raise since the gem's user is not
98
99
  # (necessarily) responsible for the error but we still need to display it somewhere to warn
99
100
  # the user something went wrong.
100
- Rails.logger.debug { "[Modulorails] Error: #{response['errors'].join(', ')}" } if response.code == 400
101
+ Rails.logger&.debug { "[Modulorails] Error: #{response['errors'].join(', ')}" } if response.code == 400
101
102
 
102
103
  # Return the response to allow users to do some more
103
104
  response
104
105
  rescue StandardError => e
105
106
  # Still need to notify the user
106
- Rails.logger.debug { "[Modulorails] Error: Could not post to #{configuration.endpoint}" }
107
- Rails.logger.debug e.message
107
+ Rails.logger&.debug { "[Modulorails] Error: Could not post to #{configuration.endpoint}" }
108
+ Rails.logger&.debug e.message
108
109
  nil
109
110
  end
110
111
  end
@@ -134,7 +135,7 @@ module Modulorails
134
135
 
135
136
  Modulorails::SelfUpdateGenerator.new([], {}, {}).invoke_all
136
137
  rescue StandardError => e
137
- Rails.logger.debug { "[Modulorails] An error occured: #{e.class} - #{e.message}" }
138
+ Rails.logger&.debug { "[Modulorails] An error occured: #{e.class} - #{e.message}" }
138
139
  end
139
140
 
140
141
  # @author Matthieu 'ciappa_m' Ciappara
@@ -147,7 +148,8 @@ module Modulorails
147
148
 
148
149
  # @author Matthieu 'ciappa_m' Ciappara
149
150
  #
150
- # Generate a rubocop configuration.
151
+ # Generate a rubocop configuration unless it was already done.
152
+ # The check is done using a 'keepfile'.
151
153
  def generate_rubocop_template
152
154
  Modulorails::RubocopGenerator.new([], {}, {}).invoke_all
153
155
  end
@@ -166,6 +168,14 @@ module Modulorails
166
168
  Modulorails::GithooksGenerator.new([], {}, {}).invoke_all
167
169
  end
168
170
 
171
+ # @author Matthieu 'ciappa_m' Ciappara
172
+ #
173
+ # Configure devcontainer for Claude code unless it was already done.
174
+ # The check is done using a 'keepfile'.
175
+ def configure_claude_code
176
+ Modulorails::ClaudeCodeGenerator.new([], {}, {}).invoke_all
177
+ end
178
+
169
179
  def deprecator
170
180
  @deprecator ||= ActiveSupport::Deprecation.new('2.0', 'Modulorails')
171
181
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modulorails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.2.pre.2
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthieu Ciappara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-06 00:00:00.000000000 Z
11
+ date: 2026-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler-audit
@@ -140,6 +140,7 @@ executables: []
140
140
  extensions: []
141
141
  extra_rdoc_files: []
142
142
  files:
143
+ - ".claude/skills/release-modulorails/SKILL.md"
143
144
  - ".dockerignore"
144
145
  - ".gitignore"
145
146
  - ".rspec"
@@ -171,29 +172,36 @@ files:
171
172
  - gemfiles/rails_61.gemfile
172
173
  - gemfiles/rails_70.gemfile
173
174
  - lib/generators/modulorails/bundleraudit/bundleraudit_generator.rb
175
+ - lib/generators/modulorails/claude_code/claude_code_generator.rb
176
+ - lib/generators/modulorails/claude_code/templates/.gitlab-ci.yml.tt
177
+ - lib/generators/modulorails/claude_code/templates/.modulorails-gitlab-ci
178
+ - lib/generators/modulorails/claude_code/templates/bin/init-firewall.sh.tt
174
179
  - lib/generators/modulorails/docker/compose/compose_generator.rb
175
- - lib/generators/modulorails/docker/compose/templates/docker-compose.yml.tt
176
180
  - lib/generators/modulorails/docker/config/config_generator.rb
177
181
  - lib/generators/modulorails/docker/config/templates/config/cable.yml.tt
178
182
  - lib/generators/modulorails/docker/config/templates/config/database.yml.tt
179
183
  - lib/generators/modulorails/docker/config/templates/config/initializers/0_redis.rb
180
184
  - lib/generators/modulorails/docker/config/templates/config/puma.rb
185
+ - lib/generators/modulorails/docker/devcontainer/devcontainer_generator.rb
186
+ - lib/generators/modulorails/docker/devcontainer/templates/devcontainer/Dockerfile.tt
187
+ - lib/generators/modulorails/docker/devcontainer/templates/devcontainer/compose.yml.tt
188
+ - lib/generators/modulorails/docker/devcontainer/templates/devcontainer/devcontainer.json.tt
181
189
  - lib/generators/modulorails/docker/docker_generator.rb
182
190
  - lib/generators/modulorails/docker/dockerfile/dockerfile_generator.rb
183
- - lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/modulotech/Dockerfile.prod.tt
184
- - lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/modulotech/Dockerfile.tt
185
- - lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/rails/Dockerfile.prod.tt
191
+ - lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/Dockerfile.prod.tt
192
+ - lib/generators/modulorails/docker/dockerfile/templates/dockerfiles/dockerignore.tt
186
193
  - lib/generators/modulorails/docker/entrypoint/entrypoint_generator.rb
187
194
  - lib/generators/modulorails/docker/entrypoint/templates/entrypoints/docker-entrypoint.sh.tt
188
- - lib/generators/modulorails/docker/entrypoint/templates/entrypoints/webpack-entrypoint.sh.tt
189
195
  - lib/generators/modulorails/githooks/githooks_generator.rb
190
- - lib/generators/modulorails/githooks/templates/dockeruby.rb
196
+ - lib/generators/modulorails/githooks/templates/dc.sh
197
+ - lib/generators/modulorails/githooks/templates/dcr.sh
191
198
  - lib/generators/modulorails/githooks/templates/post-rewrite.sh
192
199
  - lib/generators/modulorails/githooks/templates/pre-merge-commit.sh
193
200
  - lib/generators/modulorails/githooks/templates/refresh_generations.sh
194
201
  - lib/generators/modulorails/gitlabci/gitlabci_generator.rb
195
202
  - lib/generators/modulorails/gitlabci/templates/.gitlab-ci.yml.tt
196
203
  - lib/generators/modulorails/gitlabci/templates/.modulorails-gitlab-ci
204
+ - lib/generators/modulorails/gitlabci/templates/bin/test.sh.tt
197
205
  - lib/generators/modulorails/gitlabci/templates/config/deploy/production.yaml.tt
198
206
  - lib/generators/modulorails/gitlabci/templates/config/deploy/review.yaml.tt
199
207
  - lib/generators/modulorails/gitlabci/templates/config/deploy/staging.yaml.tt
@@ -210,7 +218,6 @@ files:
210
218
  - lib/generators/modulorails/service/templates/service.rb.tt
211
219
  - lib/generators/modulorails/sidekiq/sidekiq_generator.rb
212
220
  - lib/generators/modulorails/sidekiq/templates/config/initializers/sidekiq.rb.tt
213
- - lib/generators/modulorails/sidekiq/templates/entrypoints/sidekiq-entrypoint.sh.tt
214
221
  - lib/modulorails.rb
215
222
  - lib/modulorails/configuration.rb
216
223
  - lib/modulorails/data.rb
@@ -251,7 +258,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
251
258
  - !ruby/object:Gem::Version
252
259
  version: '0'
253
260
  requirements: []
254
- rubygems_version: 3.5.3
261
+ rubygems_version: 3.4.19
255
262
  signing_key:
256
263
  specification_version: 4
257
264
  summary: Common base for Ruby on Rails projects at Modulotech