secvault 2.7.1 → 3.0.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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +389 -34
  3. data/lib/secvault/version.rb +1 -1
  4. data/lib/secvault.rb +102 -198
  5. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f3f850252ea738c99ee2fd7bb7c2e9a2688082abc78245f0ac1ae4f262c9f57
4
- data.tar.gz: 95c9d1b54bdf4fdeaf490e7bbf4bd1bb13411d50ce9a11291f89848049258636
3
+ metadata.gz: 7e9d55bf6f12233064ea7ced4e9b40c84f8f3a6ce38d6b5b79d5f0e8f4a7214a
4
+ data.tar.gz: 7ade516b0c550c0b3c98f04fc39ff1079d5c46436da6a7fd6153bdc1d9eac4fb
5
5
  SHA512:
6
- metadata.gz: a2399e023247f056496230dc96da152b5c9fcdd1db98b5f7359a5406f9ce45dc190ce0c93cd5dc4736abb16e7396043d78482b0879785faa60353fe0a1d773dd
7
- data.tar.gz: e3deef79404b1c382c2e6167e6cb2b2981f511b6e9058fdf4fc484d8b9fce0f041551121631165f60f2220ee1d3be0ef444fb12296dac803a58afb4800b18ce0
6
+ metadata.gz: b2939f3acd3e9525d000b7195fd9967a4e8f80c04800e4cbac9da774625bc1cae7b26e1f0320d4d57d53abbf1bdb53a3899977deb9625880fd84458c7c302277
7
+ data.tar.gz: 84de3b9f8ecd84e820668d3ac2bd7fedeb7946e2707f88e00cd7219f258d597417b025a72b889dc93bf6e408c2686b2c444b90e1d2eaee856f1040d9f5b8d63a
data/README.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # Secvault
2
2
 
3
- Restores Rails `secrets.yml` functionality for environment-specific secrets management using YAML files with ERB templating.
3
+ Secvault restores the classic Rails `secrets.yml` functionality using simple, plain YAML files for environment-specific secrets management. Compatible with all modern Rails versions (7.1+, 7.2+, 8.0+).
4
4
 
5
- ## Rails Version Support
5
+ [![Gem Version](https://img.shields.io/gem/v/secvault.svg)](https://rubygems.org/gems/secvault)
6
6
 
7
- - **Rails 7.2+**: Automatic setup (drop-in replacement for removed functionality)
8
- - **Rails 7.1**: Manual setup required
9
- - **Rails 8.0+**: Full compatibility
7
+ ## Why Secvault?
8
+
9
+ - **Drop-in replacement** for Rails 7.2+'s removed `secrets.yml` functionality
10
+ - **Universal compatibility** across Rails 7.1+, 7.2+, and 8.0+
11
+ - **ERB templating** support for environment variables
12
+ - **Multi-file support** with deep merging capabilities
13
+ - **Shared sections** for common configuration across environments
14
+ - **Simple YAML** - no complex credential management required
10
15
 
11
16
  ## Installation
12
17
 
@@ -15,87 +20,437 @@ Restores Rails `secrets.yml` functionality for environment-specific secrets mana
15
20
  gem 'secvault'
16
21
  ```
17
22
 
23
+ ```bash
24
+ bundle install
25
+ ```
26
+
18
27
  ## Quick Start
19
28
 
29
+ ### 1. Simple Setup
30
+
31
+ Create `config/initializers/secvault.rb`:
32
+
33
+ ```ruby
34
+ # The simplest setup - works across all Rails versions
35
+ Secvault.start!
36
+ ```
37
+
20
38
  Create `config/secrets.yml`:
21
39
 
22
40
  ```yaml
41
+ shared:
42
+ app_name: "My Rails App"
43
+ timeout: 30
44
+
23
45
  development:
46
+ secret_key_base: "dev_secret_key_here"
24
47
  api_key: "dev_key_123"
25
48
  database_url: "postgresql://localhost/myapp_dev"
49
+ debug: true
50
+
51
+ test:
52
+ secret_key_base: "test_secret_key_here"
53
+ api_key: "test_key_123"
26
54
 
27
55
  production:
56
+ secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
28
57
  api_key: <%= ENV['API_KEY'] %>
29
58
  database_url: <%= ENV['DATABASE_URL'] %>
59
+ debug: false
30
60
  ```
31
61
 
32
- Access secrets in your app:
62
+ ### 2. Access Your Secrets
33
63
 
34
64
  ```ruby
65
+ # In your Rails application
35
66
  Rails.application.secrets.api_key
67
+ Rails.application.secrets.app_name
36
68
  Rails.application.secrets.database_url
69
+
70
+ # Nested secrets work too
71
+ Rails.application.secrets.database.host
72
+ Rails.application.secrets.features.analytics
37
73
  ```
38
74
 
39
- ## Multi-File Configuration
75
+ ## Setup Methods
76
+
77
+ ### Unified start! Method
40
78
 
41
- Load and merge multiple secrets files:
79
+ Secvault now uses a single, simplified `start!` method for all use cases:
80
+
81
+ ```ruby
82
+ # Simplest setup - loads config/secrets.yml with Rails integration
83
+ Secvault.start!
84
+
85
+ # Custom single file
86
+ Secvault.start!(files: ['custom.yml'])
87
+
88
+ # Multiple files with hot reload
89
+ Secvault.start!(
90
+ files: ['config/secrets.yml', 'config/secrets.local.yml'],
91
+ hot_reload: true
92
+ )
93
+
94
+ # All available options
95
+ Secvault.start!(
96
+ files: ['config/secrets.yml'], # Default: ['config/secrets.yml']
97
+ integrate_with_rails: true, # Default: true
98
+ set_secret_key_base: true, # Default: true
99
+ hot_reload: true, # Default: true in development
100
+ logger: true # Default: true except production
101
+ )
102
+ ```
103
+
104
+ ### Advanced Usage
105
+
106
+ ```ruby
107
+ # Load without Rails integration (standalone mode)
108
+ Secvault.start!(integrate_with_rails: false)
109
+ # Access via: Secvault.secrets.your_key
110
+
111
+ # Multi-file with hot reload for development
112
+ Secvault.start!(
113
+ files: [
114
+ 'config/secrets.yml',
115
+ 'config/secrets.oauth.yml',
116
+ 'config/secrets.local.yml' # Git-ignored local overrides
117
+ ],
118
+ hot_reload: true,
119
+ logger: true
120
+ )
121
+ ```
122
+
123
+
124
+ ## Advanced Features
125
+
126
+ ### ERB Templating
127
+
128
+ Secvault supports full ERB templating for dynamic configuration:
129
+
130
+ ```yaml
131
+ production:
132
+ secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
133
+ api_key: <%= ENV['API_KEY'] %>
134
+ pool_size: <%= ENV.fetch('DB_POOL', '5').to_i %>
135
+
136
+ # Complex expressions
137
+ features:
138
+ enabled: <%= ENV.fetch('FEATURES_ON', 'false') == 'true' %>
139
+ analytics: <%= Rails.env.production? && ENV['ANALYTICS'] != 'false' %>
140
+
141
+ # Arrays and complex data structures
142
+ allowed_hosts: <%= ENV.fetch('ALLOWED_HOSTS', 'localhost').split(',') %>
143
+
144
+ # Conditional values
145
+ redis_url: <%=
146
+ if ENV['REDIS_URL']
147
+ ENV['REDIS_URL']
148
+ else
149
+ "redis://localhost:6379/#{Rails.env}"
150
+ end
151
+ %>
152
+ ```
153
+
154
+ ### Shared Sections
155
+
156
+ Define common secrets that apply to all environments:
157
+
158
+ ```yaml
159
+ shared:
160
+ app_name: "MyApp"
161
+ version: "2.1.0"
162
+ timeout: 30
163
+ features:
164
+ analytics: true
165
+
166
+ development:
167
+ secret_key_base: "dev_secret"
168
+ features:
169
+ debug: true # Merges with shared.features
170
+
171
+ production:
172
+ secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
173
+ features:
174
+ analytics: false # Overrides shared.features.analytics
175
+ ```
176
+
177
+ ### Multi-File Configuration
178
+
179
+ Organize your secrets across multiple files for better maintainability:
42
180
 
43
181
  ```ruby
44
- # config/initializers/secvault.rb
45
182
  Secvault.setup_multi_file!([
46
- 'config/secrets.yml',
47
- 'config/secrets.oauth.yml',
48
- 'config/secrets.local.yml'
183
+ 'config/secrets.yml', # Base secrets
184
+ 'config/secrets.oauth.yml', # OAuth provider settings
185
+ 'config/secrets.database.yml', # Database configurations
186
+ 'config/secrets.local.yml' # Local overrides (git-ignored)
49
187
  ])
50
188
  ```
51
189
 
52
- Files are merged in order with deep merge support for nested hashes.
190
+ **File merging behavior:**
191
+ - Files are processed in order
192
+ - Later files override earlier ones
193
+ - Deep merging for nested hashes
194
+ - Shared sections are merged first, then environment-specific
195
+
196
+ ### Hot Reload (Development)
197
+
198
+ Secvault provides hot reload functionality for development:
199
+
200
+ ```ruby
201
+ # Enable hot reload when starting Secvault
202
+ Secvault.start!(hot_reload: true) # Default: true in development
203
+
204
+ # Then reload secrets without restarting Rails
205
+ reload_secrets!
206
+
207
+ # Or via Rails.application
208
+ Rails.application.reload_secrets!
209
+ ```
210
+
211
+ Hot reload is automatically enabled in development mode and provides instant feedback when you change your secrets files.
53
212
 
54
213
  ## Manual API
55
214
 
215
+ For advanced use cases, you can use the lower-level API:
216
+
56
217
  ```ruby
57
218
  # Parse specific files
58
- secrets = Rails::Secrets.parse(['config/secrets.yml'], env: Rails.env)
219
+ secrets = Rails::Secrets.parse(['config/secrets.yml'], env: 'production')
220
+
221
+ # Load from default location
222
+ secrets = Rails::Secrets.load(env: 'development')
223
+
224
+ # Check if Secvault is active
225
+ Secvault.active? # => true/false
226
+
227
+ # Check if integrated with Rails
228
+ Secvault.rails_integrated? # => true/false
229
+
230
+ # Access loaded secrets directly
231
+ Secvault.secrets.api_key # Available after Secvault.start!
232
+ ```
59
233
 
60
- # Load default config/secrets.yml
61
- secrets = Rails::Secrets.load(env: 'production')
234
+ ## Rails Version Compatibility
62
235
 
63
- # Check if active
64
- Secvault.active? # => true/false
236
+ | Rails Version | Support Level | Notes |
237
+ |---------------|---------------|-------|
238
+ | **Rails 7.1+** | ✅ Full compatibility | Manual setup required |
239
+ | **Rails 7.2+** | ✅ Drop-in replacement | Automatic setup works |
240
+ | **Rails 8.0+** | ✅ Full compatibility | Future-proof |
241
+
242
+ ### Rails 7.2+ Notes
243
+ Rails 7.2 removed the built-in `secrets.yml` functionality. Secvault provides a complete replacement with the same API.
244
+
245
+ ### Rails 7.1 Notes
246
+ Rails 7.1 still has `secrets.yml` support but shows deprecation warnings. Secvault provides a consistent experience across Rails versions.
247
+
248
+ ## Migration Guide
249
+
250
+ ### From Previous Secvault Versions
251
+
252
+ **BREAKING CHANGE**: The API has been simplified. Update your initializers:
253
+
254
+ ```ruby
255
+ # Old API (no longer supported):
256
+ Secvault.setup!
257
+ Secvault.setup_multi_file!(['file1.yml', 'file2.yml'])
258
+ Secvault.integrate_with_rails!
259
+
260
+ # New unified API:
261
+ Secvault.start! # Simple case
262
+ Secvault.start!(files: ['file1.yml', 'file2.yml']) # Multi-file case
263
+ Secvault.start!(integrate_with_rails: false) # Standalone mode
65
264
  ```
66
265
 
67
- ## Rails 7.1 Integration
266
+ ### From Rails < 7.2 Built-in Secrets
267
+
268
+ 1. **Add Secvault to your Gemfile**:
269
+ ```ruby
270
+ gem 'secvault'
271
+ ```
272
+
273
+ 2. **Create initializer**:
274
+ ```ruby
275
+ # config/initializers/secvault.rb
276
+ Secvault.start!
277
+ ```
278
+
279
+ 3. **Your existing `config/secrets.yml` works as-is** - no changes needed!
280
+
281
+ ### From Rails Credentials
282
+
283
+ 1. **Extract your credentials to YAML**:
284
+ ```bash
285
+ # Export existing credentials
286
+ rails credentials:show > config/secrets.yml
287
+ ```
288
+
289
+ 2. **Format as environment-specific YAML**:
290
+ ```yaml
291
+ development:
292
+ secret_key_base: "your_dev_secret"
293
+ # ... other secrets
294
+
295
+ production:
296
+ secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
297
+ # ... other secrets
298
+ ```
299
+
300
+ 3. **Set up Secvault**:
301
+ ```ruby
302
+ # config/initializers/secvault.rb
303
+ Secvault.start!
304
+ ```
305
+
306
+ ## Configuration Examples
68
307
 
69
- For Rails 7.1 with existing secrets functionality:
308
+ ### Basic Application
309
+
310
+ ```yaml
311
+ # config/secrets.yml
312
+ shared:
313
+ app_name: "MyApp"
314
+
315
+ development:
316
+ secret_key_base: "long_random_string_for_dev"
317
+ database_url: "postgresql://localhost/myapp_dev"
318
+
319
+ test:
320
+ secret_key_base: "long_random_string_for_test"
321
+ database_url: "postgresql://localhost/myapp_test"
322
+
323
+ production:
324
+ secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
325
+ database_url: <%= ENV['DATABASE_URL'] %>
326
+ ```
327
+
328
+ ### Multi-Service Application
70
329
 
71
330
  ```ruby
72
331
  # config/initializers/secvault.rb
73
- Secvault.setup_backward_compatibility_with_older_rails!
332
+ Secvault.start!(
333
+ files: [
334
+ 'config/secrets.yml',
335
+ 'config/secrets.oauth.yml',
336
+ 'config/secrets.external_apis.yml',
337
+ 'config/secrets.local.yml' # Git-ignored
338
+ ],
339
+ hot_reload: true
340
+ )
74
341
  ```
75
342
 
76
- ## ERB Templating
343
+ ```yaml
344
+ # config/secrets.yml (base)
345
+ shared:
346
+ app_name: "MyApp"
347
+ timeout: 30
348
+
349
+ development:
350
+ secret_key_base: "dev_secret"
351
+ debug: true
77
352
 
78
- Supports full ERB templating for environment variables:
353
+ production:
354
+ secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
355
+ debug: false
356
+ ```
79
357
 
80
358
  ```yaml
359
+ # config/secrets.oauth.yml
360
+ shared:
361
+ oauth:
362
+ google:
363
+ scope: "email profile"
364
+
365
+ development:
366
+ oauth:
367
+ google:
368
+ client_id: "dev_google_client_id"
369
+ client_secret: "dev_google_client_secret"
370
+
81
371
  production:
82
- api_key: <%= ENV['API_KEY'] %>
83
- pool_size: <%= ENV.fetch('DB_POOL', '5').to_i %>
84
- features:
85
- enabled: <%= ENV.fetch('FEATURES_ON', 'false') == 'true' %>
86
- hosts: <%= ENV.fetch('ALLOWED_HOSTS', 'localhost').split(',') %>
372
+ oauth:
373
+ google:
374
+ client_id: <%= ENV['GOOGLE_CLIENT_ID'] %>
375
+ client_secret: <%= ENV['GOOGLE_CLIENT_SECRET'] %>
87
376
  ```
88
377
 
89
- ## Development Tools
378
+ ## Troubleshooting
90
379
 
91
- Reload secrets in development:
380
+ ### Common Issues
92
381
 
382
+ **1. "No secrets.yml file found"**
383
+ ```bash
384
+ # Create the file
385
+ mkdir -p config
386
+ touch config/secrets.yml
387
+ ```
388
+
389
+ **2. "undefined method `secrets' for Rails.application"**
93
390
  ```ruby
94
- # Available after setup_multi_file!
95
- reload_secrets!
96
- Rails.application.reload_secrets!
391
+ # Make sure Secvault is set up in an initializer
392
+ # config/initializers/secvault.rb
393
+ Secvault.start!
394
+ ```
395
+
396
+ **3. "Secrets not loading in tests"**
397
+ ```ruby
398
+ # In your test helper or rails_helper.rb
399
+ Secvault.start! if defined?(Secvault)
400
+ ```
401
+
402
+ **4. "Environment variables not working"**
403
+ ```yaml
404
+ # Make sure you're using ERB syntax
405
+ production:
406
+ api_key: <%= ENV['API_KEY'] %> # ✅ Correct
407
+ api_key: $API_KEY # ❌ Wrong
97
408
  ```
98
409
 
410
+ ### Debug Mode
411
+
412
+ ```ruby
413
+ # Enable detailed logging (development/test only)
414
+ Secvault.start!(files: ['config/secrets.yml'], logger: true)
415
+
416
+ # Check if Secvault is working
417
+ Secvault.active? # Should return true
418
+ Secvault.rails_integrated? # Should return true
419
+ Rails.application.secrets # Should show your secrets
420
+ ```
421
+
422
+ ## API Reference
423
+
424
+ ### Setup Methods
425
+
426
+ - `Secvault.start!(files: [], integrate_with_rails: true, set_secret_key_base: true, hot_reload: auto, logger: auto)` - Main and only setup method
427
+
428
+ ### Status Methods
429
+
430
+ - `Secvault.active?` - Check if secrets are loaded
431
+ - `Secvault.rails_integrated?` - Check if Rails integration is active
432
+ - `Secvault.secrets` - Access loaded secrets directly
433
+
434
+ ### Rails API Compatibility
435
+
436
+ - `Rails::Secrets.parse(files, env:)` - Parse specific files
437
+ - `Rails::Secrets.load(env:)` - Load from default config/secrets.yml
438
+ - `Rails.application.secrets` - Access secrets (same as classic Rails)
439
+
440
+ ### Legacy Aliases
441
+
442
+ - `Secvault.setup_backward_compatibility_with_older_rails!` (alias for `setup!`)
443
+ - `Secvault.setup_rails_71_integration!` (alias for `setup!`)
444
+ - `Secvault.setup_multi_files!` (alias for `setup_multi_file!`)
445
+
446
+ ## Contributing
447
+
448
+ 1. Fork it
449
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
450
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
451
+ 4. Push to the branch (`git push origin my-new-feature`)
452
+ 5. Create new Pull Request
453
+
99
454
  ## License
100
455
 
101
- MIT
456
+ MIT License. See [LICENSE](LICENSE) for details.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Secvault
4
- VERSION = "2.7.1"
4
+ VERSION = "3.0.0"
5
5
  end
data/lib/secvault.rb CHANGED
@@ -15,7 +15,7 @@ loader.setup
15
15
  #
16
16
  # Secvault restores the classic Rails secrets.yml functionality using simple,
17
17
  # plain YAML files for environment-specific secrets management. Works consistently
18
- # across all Rails versions with automatic deprecation warning suppression.
18
+ # across all Rails versions.
19
19
  #
20
20
  # ## Rails Version Support:
21
21
  # - Rails 7.1+: Full compatibility with automatic setup
@@ -26,18 +26,20 @@ loader.setup
26
26
  # Add this to an initializer:
27
27
  #
28
28
  # # config/initializers/secvault.rb
29
- # Secvault.setup!
29
+ # Secvault.start!
30
30
  #
31
31
  # ## Usage:
32
32
  # Rails.application.secrets.api_key
33
33
  # Rails.application.secrets.oauth_settings[:google_client_id]
34
+ # Secvault.secrets.your_key # Direct access
34
35
  # Rails::Secrets.load(env: 'development') # Load default config/secrets.yml
35
36
  # Rails::Secrets.parse(['custom.yml'], env: Rails.env) # Parse custom files
36
37
  #
37
38
  # ## Getting Started:
38
39
  # 1. Create config/secrets.yml with your secrets
39
- # 2. Use Rails.application.secrets.your_secret in your app
40
- # 3. For production, use environment variables with ERB syntax
40
+ # 2. Call Secvault.start! in an initializer
41
+ # 3. Use Rails.application.secrets.your_secret in your app
42
+ # 4. For production, use environment variables with ERB syntax
41
43
  #
42
44
  # @see https://github.com/unnitallman/secvault
43
45
  module Secvault
@@ -70,125 +72,78 @@ module Secvault
70
72
  require "secvault/rails_secrets"
71
73
  end
72
74
 
73
- # Set up Secvault for all Rails versions
74
- # This provides a universal way to integrate Secvault into Rails apps
75
- # with consistent behavior across all Rails versions.
75
+ # Start Secvault with simplified, unified API
76
+ # This is the main entry point for all Secvault functionality
76
77
  #
77
- # Usage in an initializer:
78
- # Secvault.setup!
79
- # Secvault.setup!(suppress_warnings: false)
78
+ # Usage examples:
79
+ # Secvault.start! # Simple: config/secrets.yml + Rails integration
80
+ # Secvault.start!(files: ['custom.yml']) # Custom single file
81
+ # Secvault.start!(files: ['base.yml', 'local.yml']) # Multiple files
82
+ # Secvault.start!(integrate_with_rails: false) # Load only, no Rails integration
83
+ # Secvault.start!(hot_reload: true) # Enable hot reload in development
80
84
  #
81
- # This will:
82
- # 1. Set up Rails::Secrets with Secvault implementation
83
- # 2. Replace Rails.application.secrets with Secvault-powered functionality
84
- # 3. Load secrets from config/secrets.yml automatically
85
- # 4. Suppress Rails deprecation warnings about secrets (default: true)
86
- # 5. Set Rails.application.config.secret_key_base from secrets (default: true)
87
- def setup!(suppress_warnings: true, set_secret_key_base: true)
88
- # Override native Rails::Secrets
89
- Rails.send(:remove_const, :Secrets) if defined?(Rails::Secrets)
90
- Rails.const_set(:Secrets, Secvault::RailsSecrets)
91
-
92
- # Set up Rails.application.secrets replacement
93
- Rails.application.config.after_initialize do
94
- # Suppress Rails deprecation warnings about secrets if requested
95
- suppress_secrets_deprecation_warning! if suppress_warnings
96
-
97
- secrets_path = Rails.root.join("config/secrets.yml")
98
-
99
- if secrets_path.exist?
100
- # Load secrets using Secvault
101
- loaded_secrets = Rails::Secrets.parse([secrets_path], env: Rails.env)
102
-
103
- # Create ActiveSupport::OrderedOptions object for compatibility
104
- secrets_object = ActiveSupport::OrderedOptions.new
105
- secrets_object.merge!(loaded_secrets)
106
-
107
- # Replace Rails.application.secrets
108
- Rails.application.define_singleton_method(:secrets) do
109
- secrets_object
110
- end
111
-
112
- # Set secret_key_base in Rails config to avoid accessing it from secrets
113
- if set_secret_key_base && loaded_secrets.key?("secret_key_base")
114
- Rails.application.config.secret_key_base = loaded_secrets["secret_key_base"]
115
- unless Rails.env.production?
116
- Rails.logger&.info "[Secvault] Set Rails.application.config.secret_key_base from secrets.yml"
117
- end
118
- end
119
-
120
- # Log integration success (except in production)
121
- unless Rails.env.production?
122
- Rails.logger&.info "[Secvault] Integration complete. Loaded #{loaded_secrets.keys.size} secret keys."
123
- end
124
- else
125
- Rails.logger&.warn "[Secvault] No secrets.yml file found at #{secrets_path}"
126
- end
127
- end
128
- end
129
-
130
- # Set up multi-file secrets loading with a clean API
131
- # Just pass an array of file paths and Secvault handles the rest
132
- #
133
- # Usage in an initializer:
134
- # Secvault.setup_multi_file!([
135
- # 'config/secrets.yml',
136
- # 'config/secrets.oauth.yml',
137
- # 'config/secrets.local.yml'
138
- # ])
85
+ # Access secrets:
86
+ # Rails.application.secrets.your_key # When integrate_rails: true (default)
87
+ # Secvault.secrets.your_key # Direct access (always available)
139
88
  #
140
89
  # Options:
141
- # - files: Array of file paths (String or Pathname)
142
- # - reload_method: Add a reload helper method (default: true in development)
143
- # - logger: Enable/disable logging (default: true except in production)
144
- # - suppress_warnings: Suppress Rails deprecation warnings about secrets (default: true)
90
+ # - files: Array of file paths (String or Pathname). Defaults to ['config/secrets.yml']
91
+ # - integrate_with_rails: Integrate with Rails.application.secrets (default: true)
145
92
  # - set_secret_key_base: Set Rails.application.config.secret_key_base from secrets (default: true)
146
- def setup_multi_file!(files, reload_method: Rails.env.development?, logger: !Rails.env.production?,
147
- suppress_warnings: true, set_secret_key_base: true)
148
- # Ensure Secvault integration is active
149
- setup!(suppress_warnings: suppress_warnings, set_secret_key_base: set_secret_key_base) unless active?
150
-
151
- # Convert strings to Pathname objects and resolve relative to Rails.root
152
- file_paths = Array(files).map do |file|
93
+ # - hot_reload: Add reload_secrets! methods for development (default: true in development)
94
+ # - logger: Enable logging (default: true except production)
95
+ def start!(files: [], integrate_with_rails: true, set_secret_key_base: true,
96
+ hot_reload: (defined?(Rails) && Rails.env.respond_to?(:development?) ? Rails.env.development? : false),
97
+ logger: (defined?(Rails) && Rails.env.respond_to?(:production?) ? !Rails.env.production? : true))
98
+
99
+ # Default to config/secrets.yml if no files specified
100
+ files_to_load = files.empty? ? ["config/secrets.yml"] : Array(files)
101
+
102
+ # Convert to Pathname objects and resolve relative to Rails.root
103
+ file_paths = files_to_load.map do |file|
153
104
  file.is_a?(Pathname) ? file : Rails.root.join(file)
154
105
  end
155
-
156
- # Set up the multi-file loading
157
- Rails.application.config.after_initialize do
158
- load_multi_file_secrets!(file_paths, logger: logger, suppress_warnings: suppress_warnings,
159
- set_secret_key_base: set_secret_key_base)
106
+
107
+ # Load secrets into Secvault.secrets
108
+ load_secrets!(file_paths, logger: logger)
109
+
110
+ # Integrate with Rails if requested
111
+ if integrate_with_rails
112
+ setup_rails_integration!(file_paths, set_secret_key_base: set_secret_key_base, logger: logger)
160
113
  end
161
-
162
- # Add reload helper in development
163
- return unless reload_method
164
-
165
- add_reload_helper!(file_paths)
114
+
115
+ # Add hot reload functionality if requested
116
+ if hot_reload
117
+ add_hot_reload!(file_paths)
118
+ end
119
+
120
+ true
121
+ rescue => e
122
+ Rails.logger&.error "[Secvault] Failed to start: #{e.message}" if defined?(Rails) && logger
123
+ false
166
124
  end
167
125
 
168
- # Load secrets into Secvault.secrets only (no Rails integration)
169
- def load_secrets_only!(files, logger: !Rails.env.production?)
170
- # Convert strings to Pathname objects and resolve relative to Rails.root
171
- file_paths = Array(files).map do |file|
172
- file.is_a?(Pathname) ? file : Rails.root.join(file)
173
- end
126
+ private
174
127
 
128
+ # Load secrets into Secvault.secrets (internal storage)
129
+ def load_secrets!(file_paths, logger: (defined?(Rails) && Rails.env.respond_to?(:production?) ? !Rails.env.production? : true))
175
130
  existing_files = file_paths.select(&:exist?)
176
-
131
+
177
132
  if existing_files.any?
178
- # Load and merge all secrets files using Secvault's parser directly
133
+ # Load and merge all secrets files
179
134
  merged_secrets = Secvault::Secrets.parse(existing_files, env: Rails.env)
180
-
181
- # Store in Secvault.secrets (ActiveSupport::OrderedOptions for compatibility)
135
+
136
+ # Store in internal storage with ActiveSupport::OrderedOptions for compatibility
182
137
  @@loaded_secrets = ActiveSupport::OrderedOptions.new
183
138
  @@loaded_secrets.merge!(merged_secrets)
184
-
139
+
185
140
  # Log successful loading
186
141
  if logger
187
142
  file_names = existing_files.map(&:basename)
188
143
  Rails.logger&.info "[Secvault] Loaded #{existing_files.size} files: #{file_names.join(", ")}"
189
144
  Rails.logger&.info "[Secvault] Parsed #{merged_secrets.keys.size} secret keys for #{Rails.env}"
190
145
  end
191
-
146
+
192
147
  true
193
148
  else
194
149
  Rails.logger&.warn "[Secvault] No secrets files found" if logger
@@ -196,116 +151,65 @@ module Secvault
196
151
  false
197
152
  end
198
153
  end
199
-
200
- # Load secrets from multiple files and merge them (with Rails integration)
201
- def load_multi_file_secrets!(file_paths, logger: !Rails.env.production?, suppress_warnings: true,
202
- set_secret_key_base: true)
203
- existing_files = file_paths.select(&:exist?)
204
-
205
- if existing_files.any?
206
- # Suppress Rails deprecation warnings about secrets if requested
207
- suppress_secrets_deprecation_warning! if suppress_warnings
208
-
209
- # Load and merge all secrets files
210
- merged_secrets = Rails::Secrets.parse(existing_files, env: Rails.env)
211
-
212
- # Create ActiveSupport::OrderedOptions object for Rails compatibility
213
- secrets_object = ActiveSupport::OrderedOptions.new
214
- secrets_object.merge!(merged_secrets)
215
-
216
- # Replace Rails.application.secrets
217
- Rails.application.define_singleton_method(:secrets) { secrets_object }
218
-
219
- # Set secret_key_base in Rails config to avoid accessing it from secrets
220
- if set_secret_key_base && merged_secrets.key?("secret_key_base")
221
- Rails.application.config.secret_key_base = merged_secrets["secret_key_base"]
222
- Rails.logger&.info "[Secvault Multi-File] Set Rails.application.config.secret_key_base from secrets" if logger
223
- end
224
-
225
- # Log successful loading
226
- if logger
227
- file_names = existing_files.map(&:basename)
228
- Rails.logger&.info "[Secvault Multi-File] Loaded #{existing_files.size} files: #{file_names.join(", ")}"
229
- Rails.logger&.info "[Secvault Multi-File] Merged #{merged_secrets.keys.size} secret keys for #{Rails.env}"
154
+
155
+ # Set up Rails integration
156
+ def setup_rails_integration!(file_paths, set_secret_key_base: true, logger: (defined?(Rails) && Rails.env.respond_to?(:production?) ? !Rails.env.production? : true))
157
+ # Override native Rails::Secrets with Secvault implementation
158
+ Rails.send(:remove_const, :Secrets) if defined?(Rails::Secrets)
159
+ Rails.const_set(:Secrets, Secvault::RailsSecrets)
160
+
161
+ # Set up Rails.application.secrets replacement in after_initialize
162
+ Rails.application.config.after_initialize do
163
+ if @@loaded_secrets && !@@loaded_secrets.empty?
164
+ # Replace Rails.application.secrets with our loaded secrets
165
+ Rails.application.define_singleton_method(:secrets) do
166
+ @@loaded_secrets
167
+ end
168
+
169
+ # Set secret_key_base in Rails config to avoid accessing it from secrets
170
+ if set_secret_key_base && @@loaded_secrets.key?(:secret_key_base)
171
+ Rails.application.config.secret_key_base = @@loaded_secrets[:secret_key_base]
172
+ Rails.logger&.info "[Secvault] Set Rails.application.config.secret_key_base from secrets" if logger
173
+ end
174
+
175
+ # Log integration success (except in production)
176
+ if logger
177
+ Rails.logger&.info "[Secvault] Rails integration complete. #{@@loaded_secrets.keys.size} secret keys available."
178
+ end
179
+ else
180
+ Rails.logger&.warn "[Secvault] No secrets loaded for Rails integration" if logger
230
181
  end
231
-
232
- merged_secrets
233
- else
234
- Rails.logger&.warn "[Secvault Multi-File] No secrets files found" if logger
235
- {}
236
182
  end
237
183
  end
238
-
239
- # Add reload helper method for development
240
- def add_reload_helper!(file_paths)
184
+
185
+ # Add hot reload functionality for development
186
+ def add_hot_reload!(file_paths)
241
187
  # Define reload method on Rails.application
242
188
  Rails.application.define_singleton_method(:reload_secrets!) do
243
- Secvault.load_multi_file_secrets!(file_paths, logger: true)
244
- puts "🔄 Reloaded secrets from #{file_paths.size} files"
189
+ # Reload secrets
190
+ Secvault.send(:load_secrets!, file_paths, logger: true)
191
+
192
+ # Re-apply Rails integration if needed
193
+ if Secvault.rails_integrated? && @@loaded_secrets
194
+ Rails.application.define_singleton_method(:secrets) do
195
+ @@loaded_secrets
196
+ end
197
+ end
198
+
199
+ puts "🔄 Hot reloaded secrets from #{file_paths.size} files"
245
200
  true
246
201
  end
247
-
202
+
248
203
  # Also make it available as a top-level method
249
204
  Object.define_method(:reload_secrets!) do
250
205
  Rails.application.reload_secrets!
251
206
  end
207
+
208
+ Rails.logger&.info "[Secvault] Hot reload enabled. Use reload_secrets! to refresh secrets." unless (defined?(Rails) && Rails.env.respond_to?(:production?) && Rails.env.production?)
252
209
  end
210
+
211
+ public
253
212
 
254
- # Start Secvault and load secrets (without Rails integration)
255
- #
256
- # Usage:
257
- # Secvault.start! # Uses config/secrets.yml only
258
- # Secvault.start!(files: []) # Same as above
259
- # Secvault.start!(files: ['path/to/secrets.yml']) # Custom single file
260
- # Secvault.start!(files: ['gem.yml', 'app.yml']) # Multiple files
261
- #
262
- # Access loaded secrets via: Secvault.secrets.your_key
263
- # To integrate with Rails.application.secrets, call: Secvault.integrate_with_rails!
264
- #
265
- # Options:
266
- # - files: Array of file paths (String or Pathname). Defaults to ['config/secrets.yml']
267
- # - logger: Enable logging (default: true except production)
268
- def start!(files: [], logger: !Rails.env.production?)
269
- # Default to host app's config/secrets.yml if no files specified
270
- files_to_load = files.empty? ? ["config/secrets.yml"] : files
271
-
272
- # Load secrets into Secvault.secrets (completely independent of Rails)
273
- load_secrets_only!(files_to_load, logger: logger)
274
-
275
- true
276
- rescue => e
277
- Rails.logger&.error "[Secvault] Failed to start: #{e.message}" if defined?(Rails)
278
- false
279
- end
280
-
281
- # Integrate loaded secrets with Rails.application.secrets
282
- def integrate_with_rails!
283
- return false unless @@loaded_secrets
284
-
285
- begin
286
- # Set up Rails::Secrets to use Secvault's parser (only when integrating)
287
- unless rails_integrated?
288
- Rails.send(:remove_const, :Secrets) if defined?(Rails::Secrets)
289
- Rails.const_set(:Secrets, Secvault::RailsSecrets)
290
- end
291
-
292
- # Replace Rails.application.secrets with Secvault's loaded secrets
293
- Rails.application.define_singleton_method(:secrets) do
294
- Secvault.secrets
295
- end
296
-
297
- Rails.logger&.info "[Secvault] Integrated with Rails.application.secrets" unless Rails.env.production?
298
- true
299
- rescue => e
300
- Rails.logger&.error "[Secvault] Failed to integrate with Rails: #{e.message}" if defined?(Rails)
301
- false
302
- end
303
- end
304
-
305
- # Backward compatibility aliases
306
- alias_method :setup_backward_compatibility_with_older_rails!, :setup! # Legacy name
307
- alias_method :setup_rails_71_integration!, :setup! # Legacy name
308
- alias_method :setup_multi_files!, :setup_multi_file! # Alternative name
309
213
  end
310
214
 
311
215
  Secvault.install! if defined?(Rails)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secvault
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Unnikrishnan KP