geneva_drive 0.2.0 → 0.3.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/lib/geneva_drive/executor.rb +5 -7
  4. data/lib/geneva_drive/flow_control.rb +20 -5
  5. data/lib/geneva_drive/jobs/housekeeping_job.rb +1 -1
  6. data/lib/geneva_drive/version.rb +1 -1
  7. data/test/dummy/config/environments/development.rb +0 -18
  8. data/test/dummy/config/environments/production.rb +0 -19
  9. data/test/dummy/config/environments/test.rb +0 -14
  10. data/test/dummy/log/test.log +54290 -0
  11. data/test/dummy_install/config/environments/development.rb +0 -18
  12. data/test/dummy_install/config/environments/production.rb +0 -19
  13. data/test/dummy_install/config/environments/test.rb +0 -14
  14. data/test/dummy_install/db/schema.rb +1 -1
  15. data/test/dummy_install/log/test.log +417 -0
  16. data/test/workflow/flow_control_test.rb +6 -5
  17. data/test/workflow/resume_and_skip_test.rb +305 -0
  18. metadata +49 -17
  19. data/app/assets/stylesheets/geneva_drive/application.css +0 -15
  20. data/app/controllers/geneva_drive/application_controller.rb +0 -2
  21. data/app/helpers/geneva_drive/application_helper.rb +0 -2
  22. data/app/jobs/geneva_drive/application_job.rb +0 -2
  23. data/app/mailers/geneva_drive/application_mailer.rb +0 -4
  24. data/app/models/geneva_drive/application_record.rb +0 -3
  25. data/app/views/layouts/geneva_drive/application.html.erb +0 -17
  26. data/test/dummy/app/mailers/application_mailer.rb +0 -4
  27. data/test/dummy/config/storage.yml +0 -34
  28. data/test/dummy_install/app/mailers/application_mailer.rb +0 -4
  29. data/test/dummy_install/config/storage.yml +0 -34
  30. /data/test/dummy_install/db/migrate/{20260126091843_create_geneva_drive_workflows.rb → 20260126164025_create_geneva_drive_workflows.rb} +0 -0
  31. /data/test/dummy_install/db/migrate/{20260126091844_create_geneva_drive_step_executions.rb → 20260126164026_create_geneva_drive_step_executions.rb} +0 -0
  32. /data/test/dummy_install/db/migrate/{20260126091845_add_finished_at_to_geneva_drive_step_executions.rb → 20260126164027_add_finished_at_to_geneva_drive_step_executions.rb} +0 -0
  33. /data/test/dummy_install/db/migrate/{20260126091846_add_error_class_name_to_geneva_drive_step_executions.rb → 20260126164028_add_error_class_name_to_geneva_drive_step_executions.rb} +0 -0
@@ -0,0 +1,305 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class ResumeAndSkipTest < ActiveSupport::TestCase
6
+ include GenevaDrive::TestHelpers
7
+
8
+ # Workflow that fails on a specific step
9
+ class FailingWorkflow < GenevaDrive::Workflow
10
+ step :step_one do
11
+ Thread.current[:step_one_ran] = true
12
+ end
13
+
14
+ step :step_two, on_exception: :pause! do
15
+ Thread.current[:step_two_attempts] ||= 0
16
+ Thread.current[:step_two_attempts] += 1
17
+ raise "Intentional failure" if Thread.current[:should_fail]
18
+ Thread.current[:step_two_completed] = true
19
+ end
20
+
21
+ step :step_three do
22
+ Thread.current[:step_three_ran] = true
23
+ end
24
+ end
25
+
26
+ # Workflow with a waiting step
27
+ class WaitingWorkflow < GenevaDrive::Workflow
28
+ step :step_one do
29
+ Thread.current[:step_one_ran] = true
30
+ end
31
+
32
+ step :step_two, wait: 2.days do
33
+ Thread.current[:step_two_ran] = true
34
+ end
35
+
36
+ step :step_three do
37
+ Thread.current[:step_three_ran] = true
38
+ end
39
+ end
40
+
41
+ setup do
42
+ @user = create_user
43
+ reset_thread_tracking!
44
+ end
45
+
46
+ teardown do
47
+ reset_thread_tracking!
48
+ end
49
+
50
+ def reset_thread_tracking!
51
+ Thread.current[:step_one_ran] = nil
52
+ Thread.current[:step_two_attempts] = nil
53
+ Thread.current[:step_two_completed] = nil
54
+ Thread.current[:step_three_ran] = nil
55
+ Thread.current[:should_fail] = nil
56
+ Thread.current[:step_two_ran] = nil
57
+ end
58
+
59
+ # ===========================================
60
+ # Tests for resume! retrying failed step
61
+ # ===========================================
62
+
63
+ test "resume! retries the failed step instead of skipping to next" do
64
+ workflow = FailingWorkflow.create!(hero: @user)
65
+
66
+ # Execute step_one successfully
67
+ perform_next_step(workflow)
68
+ assert Thread.current[:step_one_ran]
69
+ assert_equal "step_two", workflow.next_step_name
70
+
71
+ # Make step_two fail (exception is re-raised after workflow pauses)
72
+ Thread.current[:should_fail] = true
73
+ assert_raises(RuntimeError) { perform_next_step(workflow) }
74
+
75
+ assert_equal "paused", workflow.state
76
+ assert_equal 1, Thread.current[:step_two_attempts]
77
+ assert_equal "step_two", workflow.next_step_name, "next_step_name should still point to the failed step"
78
+
79
+ # Fix the issue and resume
80
+ Thread.current[:should_fail] = false
81
+ workflow.resume!
82
+
83
+ assert_equal "ready", workflow.state
84
+ assert_equal "step_two", workflow.next_step_name, "resume! should schedule the same step for retry"
85
+
86
+ # Execute the retried step
87
+ perform_next_step(workflow)
88
+
89
+ assert_equal 2, Thread.current[:step_two_attempts], "step_two should have been attempted twice"
90
+ assert Thread.current[:step_two_completed], "step_two should have completed on retry"
91
+ assert_equal "step_three", workflow.next_step_name
92
+ end
93
+
94
+ test "resume! creates new step execution for the failed step" do
95
+ workflow = FailingWorkflow.create!(hero: @user)
96
+
97
+ # Execute step_one
98
+ perform_next_step(workflow)
99
+
100
+ # Make step_two fail
101
+ Thread.current[:should_fail] = true
102
+ assert_raises(RuntimeError) { perform_next_step(workflow) }
103
+
104
+ assert_equal "paused", workflow.state
105
+ failed_execution = workflow.step_executions.where(step_name: "step_two", state: "failed").first
106
+ assert failed_execution, "Should have a failed step_two execution"
107
+
108
+ # Resume
109
+ Thread.current[:should_fail] = false
110
+ workflow.resume!
111
+
112
+ # Should have a new scheduled execution for step_two
113
+ new_execution = workflow.step_executions.where(step_name: "step_two", state: "scheduled").first
114
+ assert new_execution, "Should have a new scheduled step_two execution"
115
+ assert_not_equal failed_execution.id, new_execution.id, "Should be a different execution record"
116
+ end
117
+
118
+ test "next_step_name points to failed step after pause due to exception" do
119
+ workflow = FailingWorkflow.create!(hero: @user)
120
+
121
+ # Execute step_one
122
+ perform_next_step(workflow)
123
+ assert_equal "step_two", workflow.next_step_name
124
+
125
+ # Fail step_two
126
+ Thread.current[:should_fail] = true
127
+ assert_raises(RuntimeError) { perform_next_step(workflow) }
128
+
129
+ assert_equal "paused", workflow.state
130
+ assert_equal "step_two", workflow.next_step_name, "next_step_name should point to the failed step, not the next one"
131
+ end
132
+
133
+ # ===========================================
134
+ # Tests for skip! on paused workflows
135
+ # ===========================================
136
+
137
+ test "skip! on paused workflow advances past the failed step" do
138
+ workflow = FailingWorkflow.create!(hero: @user)
139
+
140
+ # Execute step_one
141
+ perform_next_step(workflow)
142
+
143
+ # Fail step_two
144
+ Thread.current[:should_fail] = true
145
+ assert_raises(RuntimeError) { perform_next_step(workflow) }
146
+
147
+ assert_equal "paused", workflow.state
148
+ assert_equal "step_two", workflow.next_step_name
149
+
150
+ # Skip the failed step
151
+ workflow.skip!
152
+ workflow.reload
153
+
154
+ assert_equal "ready", workflow.state
155
+ assert_equal "step_three", workflow.next_step_name, "skip! should advance to the next step"
156
+ end
157
+
158
+ test "skip! on paused workflow creates execution for the next step" do
159
+ workflow = FailingWorkflow.create!(hero: @user)
160
+
161
+ # Execute step_one
162
+ perform_next_step(workflow)
163
+
164
+ # Fail step_two
165
+ Thread.current[:should_fail] = true
166
+ assert_raises(RuntimeError) { perform_next_step(workflow) }
167
+
168
+ assert_equal "paused", workflow.state
169
+
170
+ # Skip the failed step
171
+ workflow.skip!
172
+ workflow.reload
173
+
174
+ # Should have a scheduled execution for step_three
175
+ step_three_execution = workflow.step_executions.where(step_name: "step_three", state: "scheduled").first
176
+ assert step_three_execution, "Should have a scheduled step_three execution"
177
+ end
178
+
179
+ test "skip! on paused workflow allows continuing execution" do
180
+ workflow = FailingWorkflow.create!(hero: @user)
181
+
182
+ # Execute step_one
183
+ perform_next_step(workflow)
184
+
185
+ # Fail step_two
186
+ Thread.current[:should_fail] = true
187
+ assert_raises(RuntimeError) { perform_next_step(workflow) }
188
+
189
+ # Skip the failed step and continue
190
+ workflow.skip!
191
+ perform_next_step(workflow)
192
+
193
+ assert Thread.current[:step_three_ran], "step_three should have run after skipping step_two"
194
+ assert_nil Thread.current[:step_two_completed], "step_two should not have completed"
195
+ assert_equal "finished", workflow.state
196
+ end
197
+
198
+ # ===========================================
199
+ # Tests for external pause (not due to failure)
200
+ # ===========================================
201
+
202
+ test "resume! after external_pause re-schedules the waiting step" do
203
+ workflow = WaitingWorkflow.create!(hero: @user)
204
+
205
+ # Execute step_one
206
+ perform_next_step(workflow)
207
+ assert_equal "step_two", workflow.next_step_name
208
+
209
+ # Externally pause while waiting for step_two
210
+ workflow.pause!
211
+
212
+ assert_equal "paused", workflow.state
213
+ assert_equal "step_two", workflow.next_step_name
214
+
215
+ # Resume - should re-schedule step_two
216
+ workflow.resume!
217
+
218
+ assert_equal "ready", workflow.state
219
+ assert_equal "step_two", workflow.next_step_name
220
+
221
+ # Verify a new execution was created
222
+ step_two_executions = workflow.step_executions.where(step_name: "step_two")
223
+ assert_equal 2, step_two_executions.count, "Should have original (canceled) and new (scheduled) executions"
224
+ assert step_two_executions.exists?(state: "scheduled")
225
+ end
226
+
227
+ test "skip! on externally paused workflow advances to next step" do
228
+ workflow = WaitingWorkflow.create!(hero: @user)
229
+
230
+ # Execute step_one
231
+ perform_next_step(workflow)
232
+ assert_equal "step_two", workflow.next_step_name
233
+
234
+ # Externally pause
235
+ workflow.pause!
236
+ assert_equal "paused", workflow.state
237
+
238
+ # Skip step_two
239
+ workflow.skip!
240
+ workflow.reload
241
+
242
+ assert_equal "ready", workflow.state
243
+ assert_equal "step_three", workflow.next_step_name
244
+ end
245
+
246
+ # ===========================================
247
+ # Error cases
248
+ # ===========================================
249
+
250
+ test "skip! raises error for finished workflow" do
251
+ workflow = FailingWorkflow.create!(hero: @user)
252
+ speedrun_workflow(workflow)
253
+ assert_equal "finished", workflow.state
254
+
255
+ error = assert_raises(GenevaDrive::InvalidStateError) do
256
+ workflow.skip!
257
+ end
258
+ assert_match(/Cannot skip on a finished workflow/, error.message)
259
+ end
260
+
261
+ test "skip! raises error for canceled workflow" do
262
+ workflow = FailingWorkflow.create!(hero: @user)
263
+ workflow.transition_to!("canceled")
264
+
265
+ error = assert_raises(GenevaDrive::InvalidStateError) do
266
+ workflow.skip!
267
+ end
268
+ assert_match(/Cannot skip on a canceled workflow/, error.message)
269
+ end
270
+
271
+ # ===========================================
272
+ # Integration: multiple retries then skip
273
+ # ===========================================
274
+
275
+ test "can retry multiple times then skip" do
276
+ workflow = FailingWorkflow.create!(hero: @user)
277
+
278
+ # Execute step_one
279
+ perform_next_step(workflow)
280
+
281
+ # Fail step_two multiple times
282
+ Thread.current[:should_fail] = true
283
+
284
+ 3.times do |i|
285
+ assert_raises(RuntimeError) { perform_next_step(workflow) }
286
+ assert_equal "paused", workflow.state
287
+ assert_equal "step_two", workflow.next_step_name
288
+ assert_equal i + 1, Thread.current[:step_two_attempts]
289
+
290
+ workflow.resume! if i < 2 # Resume for first two attempts
291
+ end
292
+
293
+ # After 3 failures, decide to skip
294
+ workflow.skip!
295
+ workflow.reload
296
+
297
+ assert_equal "ready", workflow.state
298
+ assert_equal "step_three", workflow.next_step_name
299
+
300
+ # Complete the workflow
301
+ perform_next_step(workflow)
302
+ assert Thread.current[:step_three_ran]
303
+ assert_equal "finished", workflow.state
304
+ end
305
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geneva_drive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
@@ -10,7 +10,49 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: rails
13
+ name: activerecord
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 7.2.2
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 7.2.2
26
+ - !ruby/object:Gem::Dependency
27
+ name: activejob
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 7.2.2
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 7.2.2
40
+ - !ruby/object:Gem::Dependency
41
+ name: activesupport
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 7.2.2
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 7.2.2
54
+ - !ruby/object:Gem::Dependency
55
+ name: railties
14
56
  requirement: !ruby/object:Gem::Requirement
15
57
  requirements:
16
58
  - - ">="
@@ -38,13 +80,6 @@ files:
38
80
  - MANUAL.md
39
81
  - README.md
40
82
  - Rakefile
41
- - app/assets/stylesheets/geneva_drive/application.css
42
- - app/controllers/geneva_drive/application_controller.rb
43
- - app/helpers/geneva_drive/application_helper.rb
44
- - app/jobs/geneva_drive/application_job.rb
45
- - app/mailers/geneva_drive/application_mailer.rb
46
- - app/models/geneva_drive/application_record.rb
47
- - app/views/layouts/geneva_drive/application.html.erb
48
83
  - bin/md2html
49
84
  - bin/rails
50
85
  - bin/rubocop
@@ -77,7 +112,6 @@ files:
77
112
  - test/dummy/app/controllers/application_controller.rb
78
113
  - test/dummy/app/helpers/application_helper.rb
79
114
  - test/dummy/app/jobs/application_job.rb
80
- - test/dummy/app/mailers/application_mailer.rb
81
115
  - test/dummy/app/models/application_record.rb
82
116
  - test/dummy/app/models/user.rb
83
117
  - test/dummy/app/views/layouts/application.html.erb
@@ -105,7 +139,6 @@ files:
105
139
  - test/dummy/config/locales/en.yml
106
140
  - test/dummy/config/puma.rb
107
141
  - test/dummy/config/routes.rb
108
- - test/dummy/config/storage.yml
109
142
  - test/dummy/db/migrate/20241217000000_create_users.rb
110
143
  - test/dummy/db/migrate/20241217000001_create_geneva_drive_workflows.rb
111
144
  - test/dummy/db/migrate/20241217000002_create_geneva_drive_step_executions.rb
@@ -127,7 +160,6 @@ files:
127
160
  - test/dummy_install/app/controllers/application_controller.rb
128
161
  - test/dummy_install/app/helpers/application_helper.rb
129
162
  - test/dummy_install/app/jobs/application_job.rb
130
- - test/dummy_install/app/mailers/application_mailer.rb
131
163
  - test/dummy_install/app/models/application_record.rb
132
164
  - test/dummy_install/app/models/user.rb
133
165
  - test/dummy_install/app/views/layouts/application.html.erb
@@ -156,12 +188,11 @@ files:
156
188
  - test/dummy_install/config/locales/en.yml
157
189
  - test/dummy_install/config/puma.rb
158
190
  - test/dummy_install/config/routes.rb
159
- - test/dummy_install/config/storage.yml
160
191
  - test/dummy_install/db/migrate/20241217000000_create_users.rb
161
- - test/dummy_install/db/migrate/20260126091843_create_geneva_drive_workflows.rb
162
- - test/dummy_install/db/migrate/20260126091844_create_geneva_drive_step_executions.rb
163
- - test/dummy_install/db/migrate/20260126091845_add_finished_at_to_geneva_drive_step_executions.rb
164
- - test/dummy_install/db/migrate/20260126091846_add_error_class_name_to_geneva_drive_step_executions.rb
192
+ - test/dummy_install/db/migrate/20260126164025_create_geneva_drive_workflows.rb
193
+ - test/dummy_install/db/migrate/20260126164026_create_geneva_drive_step_executions.rb
194
+ - test/dummy_install/db/migrate/20260126164027_add_finished_at_to_geneva_drive_step_executions.rb
195
+ - test/dummy_install/db/migrate/20260126164028_add_error_class_name_to_geneva_drive_step_executions.rb
165
196
  - test/dummy_install/db/schema.rb
166
197
  - test/dummy_install/log/test.log
167
198
  - test/dummy_install/public/400.html
@@ -185,6 +216,7 @@ files:
185
216
  - test/workflow/missing_sti_class_resolution_test.rb
186
217
  - test/workflow/precondition_exception_test.rb
187
218
  - test/workflow/reattempt_on_exception_integration_test.rb
219
+ - test/workflow/resume_and_skip_test.rb
188
220
  - test/workflow/step_definition_source_location_test.rb
189
221
  - test/workflow/workflow_test.rb
190
222
  homepage: https://github.com/julik/geneva_drive
@@ -1,15 +0,0 @@
1
- /*
2
- * This is a manifest file that'll be compiled into application.css, which will include all the files
3
- * listed below.
4
- *
5
- * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
- * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
- *
8
- * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
- * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
- * files in this directory. Styles in this file should be added after the last require_* statement.
11
- * It is generally better to create a new file per style scope.
12
- *
13
- *= require_tree .
14
- *= require_self
15
- */
@@ -1,2 +0,0 @@
1
- class GenevaDrive::ApplicationController < ActionController::Base
2
- end
@@ -1,2 +0,0 @@
1
- module GenevaDrive::ApplicationHelper
2
- end
@@ -1,2 +0,0 @@
1
- class GenevaDrive::ApplicationJob < ActiveJob::Base
2
- end
@@ -1,4 +0,0 @@
1
- class GenevaDrive::ApplicationMailer < ActionMailer::Base
2
- default from: "from@example.com"
3
- layout "mailer"
4
- end
@@ -1,3 +0,0 @@
1
- class GenevaDrive::ApplicationRecord < ActiveRecord::Base
2
- self.abstract_class = true
3
- end
@@ -1,17 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Tmp plugin</title>
5
- <%= csrf_meta_tags %>
6
- <%= csp_meta_tag %>
7
-
8
- <%= yield :head %>
9
-
10
- <%= stylesheet_link_tag "geneva_drive/application", media: "all" %>
11
- </head>
12
- <body>
13
-
14
- <%= yield %>
15
-
16
- </body>
17
- </html>
@@ -1,4 +0,0 @@
1
- class ApplicationMailer < ActionMailer::Base
2
- default from: "from@example.com"
3
- layout "mailer"
4
- end
@@ -1,34 +0,0 @@
1
- test:
2
- service: Disk
3
- root: <%= Rails.root.join("tmp/storage") %>
4
-
5
- local:
6
- service: Disk
7
- root: <%= Rails.root.join("storage") %>
8
-
9
- # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10
- # amazon:
11
- # service: S3
12
- # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13
- # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14
- # region: us-east-1
15
- # bucket: your_own_bucket-<%= Rails.env %>
16
-
17
- # Remember not to checkin your GCS keyfile to a repository
18
- # google:
19
- # service: GCS
20
- # project: your_project
21
- # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22
- # bucket: your_own_bucket-<%= Rails.env %>
23
-
24
- # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25
- # microsoft:
26
- # service: AzureStorage
27
- # storage_account_name: your_account_name
28
- # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29
- # container: your_container_name-<%= Rails.env %>
30
-
31
- # mirror:
32
- # service: Mirror
33
- # primary: local
34
- # mirrors: [ amazon, google, microsoft ]
@@ -1,4 +0,0 @@
1
- class ApplicationMailer < ActionMailer::Base
2
- default from: "from@example.com"
3
- layout "mailer"
4
- end
@@ -1,34 +0,0 @@
1
- test:
2
- service: Disk
3
- root: <%= Rails.root.join("tmp/storage") %>
4
-
5
- local:
6
- service: Disk
7
- root: <%= Rails.root.join("storage") %>
8
-
9
- # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10
- # amazon:
11
- # service: S3
12
- # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13
- # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14
- # region: us-east-1
15
- # bucket: your_own_bucket-<%= Rails.env %>
16
-
17
- # Remember not to checkin your GCS keyfile to a repository
18
- # google:
19
- # service: GCS
20
- # project: your_project
21
- # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22
- # bucket: your_own_bucket-<%= Rails.env %>
23
-
24
- # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25
- # microsoft:
26
- # service: AzureStorage
27
- # storage_account_name: your_account_name
28
- # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29
- # container: your_container_name-<%= Rails.env %>
30
-
31
- # mirror:
32
- # service: Mirror
33
- # primary: local
34
- # mirrors: [ amazon, google, microsoft ]