deploy_pin 1.5.1 → 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.
- checksums.yaml +4 -4
- data/README.md +41 -17
- data/lib/deploy_pin/collector.rb +18 -17
- data/lib/deploy_pin/database.rb +3 -20
- data/lib/deploy_pin/database_engine.rb +51 -0
- data/lib/deploy_pin/runner.rb +3 -3
- data/lib/deploy_pin/task.rb +24 -6
- data/lib/deploy_pin/version.rb +1 -1
- data/lib/deploy_pin.rb +5 -4
- data/lib/generators/deploy_pin/install/templates/create_deploy_pins.rb +2 -0
- data/lib/generators/deploy_pin/install/templates/deploy_pin.rb +5 -5
- data/lib/generators/deploy_pin/upgrade/USAGE +8 -0
- data/lib/generators/deploy_pin/upgrade/templates/upgrade_deploy_pins.rb +8 -0
- data/lib/generators/deploy_pin/upgrade/upgrade_generator.rb +20 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ced30166491b43cb26d26c837ca1276e9825d3b48631cea70f3095e0a8590d01
|
4
|
+
data.tar.gz: '0538bf5ea753ba70a2daa8e2104939d38d8605f8ae72d284417bcbe249659a4f'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f16f07130b5544236e0a17d0658e2c1b5ec2eb2103a5321c4f11ef391492f87c056b69e8b8498553f686e1523989f6723bec205147760fc2c12062f266c3a27e
|
7
|
+
data.tar.gz: 64424aa72c121603c5ddd23f9e15051fca979b4e3393b1ed942b0319aadd3d441515d24203201148c8c125aed4c6dcb0e43b4c2079466e430f7681a56c5700e8
|
data/README.md
CHANGED
@@ -6,8 +6,6 @@
|
|
6
6
|
|
7
7
|
# DeployPin
|
8
8
|
|
9
|
-

|
10
|
-
|
11
9
|
Deploying applications often involves the need to execute a series of specific commands or tasks either before or after the deployment process. While you might typically turn to migrations for such operations, these tasks aren't always migration-related. Additionally, there are situations where you need to execute code before or after a migration without causing the main thread to block.
|
12
10
|
|
13
11
|
Introducing deploy_pins – your go-to solution for streamlined task management during the deployment process. This Ruby library allows you to seamlessly orchestrate tasks before, after, or independently of migrations, offering the flexibility you need to maintain a smooth and efficient deployment workflow. With deploy_pins, you can take control of your deployment tasks and ensure that your application operates flawlessly in any environment.
|
@@ -178,6 +176,30 @@ end
|
|
178
176
|
|
179
177
|
To use a different formatting value than the default, you need to specify it explicitly in the task, similar to the database timeout configuration.
|
180
178
|
|
179
|
+
## Resumable Tasks
|
180
|
+
|
181
|
+
When working with long-running code that processes a large dataset, it makes sense to store progress in the database to allow resuming the task later. You can do this by using the `DeployPin::Task` instance methods: `#progress` and `#increment_progress(num)`.
|
182
|
+
|
183
|
+
Here is an example of how to use these methods:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
# Some DeployPin task
|
187
|
+
...
|
188
|
+
# === task code goes here ===
|
189
|
+
|
190
|
+
# The progress is 0 by default
|
191
|
+
Users.where(id: progress..).find_each do |user|
|
192
|
+
# Do some work
|
193
|
+
increment_progress(1) # Increment progress by 1 and store it in the database so you can resume the task from this point
|
194
|
+
end
|
195
|
+
```
|
196
|
+
|
197
|
+
This allows your task to resume from where it left off, minimizing the risk of repeating work.
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
```bash
|
202
|
+
|
181
203
|
## Recurring Tasks
|
182
204
|
If you want to generate a recurring task, you can use the `--recurring` option. Make sure to set a correct `--identifier`, which should be a numeric value. Positive and negative numbers are possible here. The identifier affects the order of task execution, allowing you to customize the sequence as desired.
|
183
205
|
|
@@ -192,27 +214,29 @@ rails g deploy_pin:task some_task_title --parallel --recurring --identifier 5
|
|
192
214
|
## DeploymentStateTrack
|
193
215
|
In the initializer
|
194
216
|
```ruby
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
217
|
+
DeployPin.setup do
|
218
|
+
groups %w[I II III post rollback]
|
219
|
+
...
|
220
|
+
deployment_state_transition({
|
221
|
+
ongoing: %w[I III],
|
222
|
+
pending: "rollback", # enters to pending step before "rollback"
|
223
|
+
ttl: 20.second, # memoize the state to avoid Redis spam
|
224
|
+
redis_url: "redis://localhost:6379"
|
225
|
+
})
|
226
|
+
end
|
205
227
|
|
206
|
-
|
207
|
-
|
208
|
-
|
228
|
+
# enabled next methods
|
229
|
+
DeployPin.ongoing_deployment?
|
230
|
+
DeployPin.pending_deployment?
|
209
231
|
```
|
210
232
|
|
211
233
|
Around the deployment
|
212
234
|
```bash
|
213
|
-
|
214
|
-
|
235
|
+
bundle exec rake deploy_pin:run[I, II, III] - # enters to ongoing state before "I" and leaves it after "III" so all tasks in I, II, III have DeployPin.oingoing_deployment? == true
|
236
|
+
bundle exec rake deploy_pin:run[rollback] - # enters "pending state"
|
215
237
|
```
|
238
|
+
## Similar Gems
|
239
|
+
- https://github.com/theSteveMitchell/after_party
|
216
240
|
|
217
241
|
## Contributing
|
218
242
|
Contribution directions go here.
|
data/lib/deploy_pin/collector.rb
CHANGED
@@ -12,50 +12,51 @@ module DeployPin
|
|
12
12
|
# :reek:TooManyStatements
|
13
13
|
def run
|
14
14
|
# cache tasks
|
15
|
-
|
16
|
-
|
17
|
-
DeployPin.task_wrapper.call(task, -> { process(
|
15
|
+
tasks = init_tasks
|
16
|
+
tasks.each_with_index do |task, index|
|
17
|
+
DeployPin.task_wrapper.call(task, -> { process(tasks, task, index) })
|
18
18
|
end
|
19
19
|
end
|
20
20
|
# :reek:TooManyStatements
|
21
21
|
|
22
22
|
def list
|
23
|
-
|
24
|
-
|
23
|
+
tasks = init_tasks
|
24
|
+
tasks.each_with_index do |task, index|
|
25
25
|
DeployPin.list_formatter.call(index, task)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
def executable
|
30
30
|
# cache tasks
|
31
|
-
|
32
|
-
|
33
|
-
task if
|
31
|
+
tasks = init_tasks
|
32
|
+
tasks.map.with_index do |task, index|
|
33
|
+
task if tasks[0..index].none? { |other_task| task.eql?(other_task) }
|
34
34
|
end.compact
|
35
35
|
end
|
36
36
|
|
37
37
|
def tasks_count
|
38
|
-
|
38
|
+
init_tasks.count
|
39
39
|
end
|
40
40
|
|
41
41
|
private
|
42
42
|
|
43
43
|
# :reek:FeatureEnvy
|
44
44
|
# :reek:TooManyStatements
|
45
|
-
def process(
|
45
|
+
def process(tasks, task, index)
|
46
46
|
# run only uniq tasks
|
47
|
-
executable =
|
47
|
+
executable = tasks[0..index].none? { |other_task| task.eql?(other_task) }
|
48
48
|
|
49
|
-
DeployPin.run_formatter.call(index,
|
49
|
+
DeployPin.run_formatter.call(index, tasks.count, task, executable, true)
|
50
|
+
|
51
|
+
task.prepare
|
50
52
|
|
51
53
|
# run if executable
|
52
54
|
if executable
|
53
55
|
duration = execution_duration { run_with_timeout(task) { task.run } }
|
54
|
-
DeployPin.run_formatter.call(index,
|
56
|
+
DeployPin.run_formatter.call(index, tasks.count, task, executable, false, duration)
|
55
57
|
end
|
56
58
|
|
57
|
-
# mark each task as done
|
58
|
-
task.mark unless task.done?
|
59
|
+
task.mark # mark each task as done
|
59
60
|
end
|
60
61
|
# :reek:TooManyStatements
|
61
62
|
# :reek:FeatureEnvy
|
@@ -65,7 +66,7 @@ module DeployPin
|
|
65
66
|
Dir["#{DeployPin.tasks_path}/*.rb"]
|
66
67
|
end
|
67
68
|
|
68
|
-
def
|
69
|
+
def init_tasks
|
69
70
|
[*DeployPin.deployment_tasks_code, *files].map do |file|
|
70
71
|
task = DeployPin::Task.new(file)
|
71
72
|
task.parse
|
@@ -76,7 +77,7 @@ module DeployPin
|
|
76
77
|
end
|
77
78
|
|
78
79
|
def task_criteria
|
79
|
-
@task_criteria ||= DeployPin::TaskCriteria.new(identifiers:
|
80
|
+
@task_criteria ||= DeployPin::TaskCriteria.new(identifiers:)
|
80
81
|
end
|
81
82
|
|
82
83
|
# :reek:UtilityFunction and :reek:ControlParameter
|
data/lib/deploy_pin/database.rb
CHANGED
@@ -2,13 +2,8 @@
|
|
2
2
|
|
3
3
|
module DeployPin
|
4
4
|
module Database
|
5
|
-
PG_TIMEOUT_STATEMENT = 'SET statement_timeout TO %s'
|
6
|
-
MYSQL_TIMEOUT_STATEMENT = 'SET max_execution_time = %s'
|
7
|
-
|
8
5
|
extend self
|
9
6
|
|
10
|
-
def dummy_method; end
|
11
|
-
|
12
7
|
# Run a block under a sql maximum timeout.
|
13
8
|
#
|
14
9
|
# A default timeout will be get from DeployPin.setup
|
@@ -68,23 +63,11 @@ module DeployPin
|
|
68
63
|
|
69
64
|
def set_max_timeout(timeout)
|
70
65
|
timeout_in_milliseconds = timeout.to_f.in_milliseconds.ceil # Make sure is always at least 1. 0 turns this off
|
71
|
-
|
72
|
-
timeout_statement =
|
73
|
-
if postgresql?
|
74
|
-
PG_TIMEOUT_STATEMENT
|
75
|
-
elsif mysql?
|
76
|
-
MYSQL_TIMEOUT_STATEMENT
|
77
|
-
end
|
78
|
-
|
79
|
-
connection.execute timeout_statement % connection.quote(timeout_in_milliseconds)
|
80
|
-
end
|
81
|
-
|
82
|
-
def postgresql?
|
83
|
-
connection.adapter_name =~ /postg/i
|
66
|
+
connection.execute db_engine::TIMEOUT_STATEMENT % connection.quote(timeout_in_milliseconds)
|
84
67
|
end
|
85
68
|
|
86
|
-
def
|
87
|
-
connection.
|
69
|
+
def db_engine
|
70
|
+
@db_engine ||= DeployPin::DatabaseEngine.new(connection).detect
|
88
71
|
end
|
89
72
|
|
90
73
|
def connection
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeployPin
|
4
|
+
# This class is used to detect used database engine and managing specific
|
5
|
+
# differences between them.
|
6
|
+
class DatabaseEngine
|
7
|
+
module PostgreSQL
|
8
|
+
TIMEOUT_STATEMENT = 'SET statement_timeout TO %s'
|
9
|
+
end
|
10
|
+
|
11
|
+
module MySQL
|
12
|
+
TIMEOUT_STATEMENT = 'SET max_execution_time = %s'
|
13
|
+
end
|
14
|
+
|
15
|
+
module MariaDB
|
16
|
+
TIMEOUT_STATEMENT = 'SET SESSION max_statement_time = %s'
|
17
|
+
end
|
18
|
+
|
19
|
+
DB_ENGINES_MAPPING = {
|
20
|
+
mariadb: MariaDB,
|
21
|
+
mysql: MySQL,
|
22
|
+
pg: PostgreSQL
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
def initialize(connection)
|
26
|
+
@connection = connection
|
27
|
+
end
|
28
|
+
|
29
|
+
def detect
|
30
|
+
db_engine_symbol =
|
31
|
+
case connection.adapter_name
|
32
|
+
when /postg/i then :pg
|
33
|
+
when /mysql/i, /trilogy/i then detect_mysql_based_engine
|
34
|
+
end
|
35
|
+
|
36
|
+
DB_ENGINES_MAPPING[db_engine_symbol]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :connection
|
42
|
+
|
43
|
+
def detect_mysql_based_engine
|
44
|
+
mariadb? ? :mariadb : :mysql
|
45
|
+
end
|
46
|
+
|
47
|
+
def mariadb?
|
48
|
+
connection.select_value('SELECT VERSION()') =~ /MariaDB/i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/deploy_pin/runner.rb
CHANGED
@@ -4,17 +4,17 @@
|
|
4
4
|
module DeployPin
|
5
5
|
module Runner
|
6
6
|
def self.run(identifiers:)
|
7
|
-
DeployPin::Collector.new(identifiers:
|
7
|
+
DeployPin::Collector.new(identifiers:).run
|
8
8
|
end
|
9
9
|
|
10
10
|
def self.list(identifiers:)
|
11
|
-
DeployPin::Collector.new(identifiers:
|
11
|
+
DeployPin::Collector.new(identifiers:).list
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.summary(identifiers:)
|
15
15
|
# print summary
|
16
16
|
self.print('======= Summary ========')
|
17
|
-
self.print("Tasks number: #{DeployPin::Collector.new(identifiers:
|
17
|
+
self.print("Tasks number: #{DeployPin::Collector.new(identifiers:).tasks_count}")
|
18
18
|
end
|
19
19
|
|
20
20
|
def self.print(msg)
|
data/lib/deploy_pin/task.rb
CHANGED
@@ -14,6 +14,8 @@ module DeployPin
|
|
14
14
|
:recurring,
|
15
15
|
:explicit_timeout
|
16
16
|
|
17
|
+
delegate :progress, to: :record
|
18
|
+
|
17
19
|
def initialize(file)
|
18
20
|
@file = file
|
19
21
|
@identifier = nil
|
@@ -29,17 +31,34 @@ module DeployPin
|
|
29
31
|
eval(script)
|
30
32
|
end
|
31
33
|
|
34
|
+
def record
|
35
|
+
DeployPin::Record.find_by(uuid: identifier)
|
36
|
+
end
|
37
|
+
|
38
|
+
def prepare
|
39
|
+
return if recurring
|
40
|
+
|
41
|
+
DeployPin::Record.create(uuid: identifier) unless record
|
42
|
+
end
|
43
|
+
|
32
44
|
def mark
|
33
45
|
return if recurring
|
34
46
|
|
35
47
|
# store record in the DB
|
36
|
-
|
48
|
+
record.update(completed_at: Time.current)
|
49
|
+
end
|
50
|
+
|
51
|
+
def increment_progress!(incrementor)
|
52
|
+
raise NotImplementedError, 'Recurring tasks do not support progress yet.' if recurring
|
53
|
+
|
54
|
+
record.increment!(:progress, incrementor)
|
37
55
|
end
|
38
56
|
|
39
57
|
def done?
|
40
58
|
return if recurring
|
59
|
+
return unless record
|
41
60
|
|
42
|
-
|
61
|
+
record.completed_at.present?
|
43
62
|
end
|
44
63
|
|
45
64
|
def under_timeout?
|
@@ -74,9 +93,9 @@ module DeployPin
|
|
74
93
|
|
75
94
|
def details
|
76
95
|
{
|
77
|
-
identifier
|
78
|
-
group
|
79
|
-
title:
|
96
|
+
identifier:,
|
97
|
+
group:,
|
98
|
+
title:
|
80
99
|
}
|
81
100
|
end
|
82
101
|
|
@@ -104,7 +123,6 @@ module DeployPin
|
|
104
123
|
|
105
124
|
protected
|
106
125
|
|
107
|
-
|
108
126
|
def group_index
|
109
127
|
DeployPin.groups.index(group)
|
110
128
|
end
|
data/lib/deploy_pin/version.rb
CHANGED
data/lib/deploy_pin.rb
CHANGED
@@ -8,20 +8,21 @@ require 'deploy_pin/task'
|
|
8
8
|
require 'deploy_pin/task_criteria'
|
9
9
|
require 'deploy_pin/engine'
|
10
10
|
require 'deploy_pin/database'
|
11
|
+
require 'deploy_pin/database_engine'
|
11
12
|
require 'parallel'
|
12
13
|
require 'ruby-progressbar'
|
13
14
|
require 'colorize'
|
14
15
|
|
15
16
|
module DeployPin
|
16
17
|
OPTIONS = %i[
|
17
|
-
|
18
|
+
deployment_state_transition
|
18
19
|
fallback_group
|
19
20
|
groups
|
20
|
-
statement_timeout
|
21
|
-
run_formatter
|
22
21
|
list_formatter
|
22
|
+
run_formatter
|
23
|
+
statement_timeout
|
23
24
|
task_wrapper
|
24
|
-
|
25
|
+
tasks_path
|
25
26
|
].freeze
|
26
27
|
|
27
28
|
DEFAULTS = {
|
@@ -6,11 +6,11 @@ DeployPin.setup do
|
|
6
6
|
fallback_group 'III'
|
7
7
|
statement_timeout 10.minutes
|
8
8
|
deployment_state_transition({
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
ongoing: %w[I III],
|
10
|
+
pending: 'rollback', # enters to pending step before "rollback"
|
11
|
+
ttl: 20.second, # memoize the state to avoid the store spam
|
12
|
+
redis_url: 'redis://localhost:6379'
|
13
|
+
})
|
14
14
|
run_formatter(
|
15
15
|
lambda do |index, task_count, task, executable, start, duration = nil|
|
16
16
|
end_of_msg = if executable
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UpgradeDeployPins < ActiveRecord::Migration[7.0]
|
4
|
+
def change
|
5
|
+
add_column :deploy_pins, :progress, :integer, default: 0, if_not_exists: true
|
6
|
+
add_column :deploy_pins, :completed_at, :datetime, default: nil, if_not_exists: true
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeployPin
|
4
|
+
# This generator is used to generate the migration file for upgrading db schema for 1.7 version support.
|
5
|
+
class UpgradeGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
9
|
+
desc 'Add the upgrade migration for DeployPin.'
|
10
|
+
|
11
|
+
def self.next_migration_number(path)
|
12
|
+
next_migration_number = current_migration_number(path) + 1
|
13
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
14
|
+
end
|
15
|
+
|
16
|
+
def copy_migrations
|
17
|
+
migration_template 'upgrade_deploy_pins.rb', 'db/migrate/upgrade_deploy_pins.rb'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deploy_pin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Viktor Sych
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -235,6 +235,7 @@ files:
|
|
235
235
|
- lib/deploy_pin.rb
|
236
236
|
- lib/deploy_pin/collector.rb
|
237
237
|
- lib/deploy_pin/database.rb
|
238
|
+
- lib/deploy_pin/database_engine.rb
|
238
239
|
- lib/deploy_pin/deployment_state.rb
|
239
240
|
- lib/deploy_pin/engine.rb
|
240
241
|
- lib/deploy_pin/parallel_wrapper.rb
|
@@ -250,6 +251,9 @@ files:
|
|
250
251
|
- lib/generators/deploy_pin/task/task_generator.rb
|
251
252
|
- lib/generators/deploy_pin/task/templates/parallel_task.rb.erb
|
252
253
|
- lib/generators/deploy_pin/task/templates/task.rb.erb
|
254
|
+
- lib/generators/deploy_pin/upgrade/USAGE
|
255
|
+
- lib/generators/deploy_pin/upgrade/templates/upgrade_deploy_pins.rb
|
256
|
+
- lib/generators/deploy_pin/upgrade/upgrade_generator.rb
|
253
257
|
- lib/tasks/deploy_pin_tasks.rake
|
254
258
|
homepage: https://github.com/skcc321/deploy_pin
|
255
259
|
licenses:
|
@@ -264,14 +268,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
264
268
|
requirements:
|
265
269
|
- - ">="
|
266
270
|
- !ruby/object:Gem::Version
|
267
|
-
version: '3.
|
271
|
+
version: '3.1'
|
268
272
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
269
273
|
requirements:
|
270
274
|
- - ">="
|
271
275
|
- !ruby/object:Gem::Version
|
272
276
|
version: '0'
|
273
277
|
requirements: []
|
274
|
-
rubygems_version: 3.5.
|
278
|
+
rubygems_version: 3.5.16
|
275
279
|
signing_key:
|
276
280
|
specification_version: 4
|
277
281
|
summary: pin some task around deployment
|