patches 2.0.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 69347a281680b6b89baafd7d31c2ab4f5256f74a
4
- data.tar.gz: f8896fc3443f64d83ea4c74fbe78aa962a9e58b1
2
+ SHA256:
3
+ metadata.gz: 44dfdb47ded3bbc59e4f3e6e60608368cd63018a2424bcc209800f882444e0d9
4
+ data.tar.gz: 4f197d97c612321564aba0b1e15fbfefa7eb288affdd20fdeb556d6bea8a1de3
5
5
  SHA512:
6
- metadata.gz: 7bd2bf0ce3a97d21fcd20acd5c082d4c03234ae75612fdd495b7199f42c081ea977acf0d91ff4b05468b52ed902b4724fa81e417b71ece81cf8f973749cd9335
7
- data.tar.gz: bd6b3b260c5bb60c9274152d0b7e0a49485f0da5cce5a825846b3bdf54ded029af520bb7457b7f6465e112d207976811ea90b5f28d0876a20575a626b26631ec
6
+ metadata.gz: 5ff6266eee49006debf847b631eebe295b33c9bf3d5f50c528fb19db5e2d137d1158ee89a21fc7f108b623e236a0dacbdaefae444ce3944f3bc951b9420fdc2a
7
+ data.tar.gz: dd761de25dd47a74c9c7123352fda506a38117278ed59c254e4511573a2748172702be682a3fc7e1fe9b54f854052827b428f4c01e563771f222d3322db33cf1
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  /coverage/
11
11
  /test.db
12
12
  /*.gem
13
+ .byebug_history
@@ -5,7 +5,7 @@
5
5
  Add patches to the project Gemfile
6
6
 
7
7
  ```ruby
8
- gem 'patches', '~> 2.0.0'
8
+ gem 'patches', '~> 2.4.0'
9
9
  ```
10
10
 
11
11
  Install the database migration
@@ -22,7 +22,9 @@ bundle exec rake db:migrate
22
22
 
23
23
  ## Configuration
24
24
 
25
- If you would like to run the patches asynchronously, or would like them to notify your hipchat room when they fail or succeed, you need to set up an initializer to set those options.
25
+ If you would like to run the patches asynchronously, or would like them to notify
26
+ your hipchat room or Slack channel when they fail or succeed, you need to set up
27
+ an initializer to set those options.
26
28
 
27
29
  ```Ruby
28
30
  Patches::Config.configure do |config|
@@ -32,11 +34,34 @@ Patches::Config.configure do |config|
32
34
  config.hipchat_options = {
33
35
  api_token: ENV['HIPCHAT_TOKEN'],
34
36
  room: ENV['HIPCHAT_ROOM'],
35
- user: ENV['HIPCHAT_USERNAME'] #maximum of 15 characters
37
+ user: ENV['HIPCHAT_USERNAME'], # maximum of 15 characters
38
+ api_version: 'v1', # optional
36
39
  }
40
+
41
+ config.use_slack = true
42
+ config.slack_options = {
43
+ webhook_url: ENV['SLACK_WEBHOOK_URL'],
44
+ channel: ENV['SLACK_CHANNEL'],
45
+ username: ENV['SLACK_USER']
46
+ }
47
+ end
48
+ ```
49
+
50
+ ### Running patches in parallel for tenants
51
+
52
+ If you are using the Apartment gem, you can run the patches for each tenant in parallel.
53
+ Just set the config ```sidekiq_parallel``` to ```true``` and you're good to go.
54
+
55
+ ```
56
+ Patches::Config.configure do |config|
57
+ config.use_sidekiq = true
58
+ config.sidekiq_parallel = true
37
59
  end
38
60
  ```
39
61
 
62
+ *Note:* Make sure your sidekiq queue is able to process concurrent jobs.
63
+ You can use ```config.sidekiq_options``` to customise it.
64
+
40
65
  ## Creating Patches
41
66
 
42
67
  Generate a patch
@@ -57,6 +82,12 @@ end
57
82
 
58
83
  update the run method and then execute
59
84
 
85
+ Generate patch with specs
86
+
87
+ ```
88
+ bundle exec rails g patches:patch PreferenceUpdate --specs=true
89
+ ```
90
+
60
91
 
61
92
  ```bash
62
93
  bundle exec rake patches:run
@@ -76,8 +107,21 @@ And then in your deploy.rb
76
107
  after 'last_task_you_want_to_run' 'patches:run'
77
108
  ```
78
109
 
79
- If you are using sidekiq and restarting the sidekiq process on the box as a part of the deploy process, please make sure that the patches run task runs after sidekiq restarts, otherwise there is no guarentee the tasks will run.
110
+ If you are using sidekiq and restarting the sidekiq process on the box
111
+ as a part of the deploy process, please make sure that the patches run task runs
112
+ after sidekiq restarts, otherwise there is no guarentee the tasks will run.
113
+
114
+ ## File Download
115
+
116
+ If a patch requires data assets, you could use S3 to store the file.
117
+ If credentials are defined in env vars, as per https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#id1
118
+
119
+ ```
120
+ require 'aws-sdk-s3'
121
+ Aws::S3::Client.new.get_object(bucket: @bucket_name, key: filename, response_target: destination)
122
+ ```
80
123
 
81
124
  ## Multitenant
82
125
 
83
- Patches will detect if `Apartment` gem is installed and if there are any tenants and run the patches for each tenant
126
+ Patches will detect if `Apartment` gem is installed and if there are any tenants
127
+ and run the patches for each tenant
@@ -4,8 +4,11 @@ module Patches
4
4
  desc 'Adds an empty patch'
5
5
  source_root File.expand_path('../templates', __FILE__)
6
6
 
7
+ class_option :specs, type: :boolean, default: false, description: 'Generates a rspec file for the patch'
8
+
7
9
  def generate_patch
8
10
  template "patch.rb.erb", "db/patches/#{file_name}.rb"
11
+ template "patch_spec.rb.erb", "spec/patches/#{file_name}_spec.rb" if options['specs']
9
12
  end
10
13
 
11
14
  private
@@ -0,0 +1,8 @@
1
+ require 'rails_helper'
2
+ require_relative '../../db/patches/<%= file_name %>'
3
+
4
+ describe <%= class_name %> do
5
+ describe '#run' do
6
+ # Specs go here
7
+ end
8
+ end
@@ -20,11 +20,13 @@ module Patches
20
20
  end
21
21
 
22
22
  require "patches/base"
23
+ require "patches/config"
24
+ require "patches/tenant_run_concern"
25
+ require "patches/tenant_worker" if defined?(Sidekiq)
23
26
  require "patches/engine" if defined?(Rails)
24
27
  require "patches/patch"
25
28
  require "patches/pending"
26
29
  require "patches/runner"
27
30
  require "patches/tenant_runner"
28
- require "patches/config"
29
31
  require "patches/notifier"
30
32
  require "patches/worker" if defined?(Sidekiq)
@@ -1,19 +1,58 @@
1
1
  module Patches
2
2
  class Config
3
3
  class << self
4
- class_attribute :use_sidekiq, :sidekiq_queue, :sidekiq_options, :use_hipchat, :hipchat_options
5
-
6
- def sidekiq_queue
7
- @sidekiq_queue ||= 'default'
4
+ def configuration
5
+ @configuration ||= Configuration.new
8
6
  end
9
7
 
10
- def sidekiq_options
11
- @sidekiq_options ||= { retry: false, queue: Patches::Config.sidekiq_queue }
8
+ def configuration=(config)
9
+ @configuration = config
12
10
  end
13
11
 
14
12
  def configure
15
- yield self
13
+ yield configuration
14
+ end
15
+
16
+ class Configuration
17
+ attr_accessor :use_sidekiq, :sidekiq_queue, :sidekiq_options, :use_hipchat,
18
+ :hipchat_options, :sidekiq_parallel, :use_slack, :slack_options
19
+
20
+ def initialize
21
+ @sidekiq_queue = 'default'
22
+ end
23
+
24
+ def sidekiq_options
25
+ @sidekiq_options ||= { retry: false, queue: sidekiq_queue }
26
+ end
27
+
28
+ def hipchat_api_token
29
+ hipchat_options[:api_token]
30
+ end
31
+
32
+ def hipchat_init_options
33
+ hipchat_options.except(:api_token, :room, :user)
34
+ end
35
+
36
+ def hipchat_room
37
+ hipchat_options[:room]
38
+ end
39
+
40
+ def hipchat_user
41
+ hipchat_options[:user]
42
+ end
43
+
44
+ def slack_channel
45
+ slack_options[:channel]
46
+ end
47
+
48
+ def slack_username
49
+ slack_options[:username]
50
+ end
51
+
52
+ def slack_webhook_url
53
+ slack_options[:webhook_url]
54
+ end
16
55
  end
17
56
  end
18
57
  end
19
- end
58
+ end
@@ -1,34 +1,62 @@
1
+ require 'slack-notifier'
2
+
1
3
  class Patches::Notifier
2
4
  class << self
3
5
  def notify_success(patches)
4
6
  send_hipchat_message(success_message(patches), color: 'green')
7
+ send_slack_message(success_message(patches), 'good')
5
8
  end
6
9
 
7
10
  def notify_failure(patch_path, error)
8
11
  send_hipchat_message(failure_message(patch_path, error), color: 'red')
12
+ send_slack_message(failure_message(patch_path, error), 'danger')
9
13
  end
10
14
 
11
15
  def success_message(patches)
12
- message = "#{patches.count} patches succeeded"
16
+ message = "#{environment_prefix}#{patches.count} patches succeeded"
13
17
  append_tenant_message(message)
14
18
  end
15
19
 
16
20
  def failure_message(patch_path, error)
17
- message = "Error applying patch: #{Pathname.new(patch_path).basename} failed with error: #{error}"
21
+ details = "#{Pathname.new(patch_path).basename} failed with error: #{error}"
22
+ message = "#{environment_prefix}Error applying patch: #{details}"
18
23
  append_tenant_message(message)
19
24
  end
20
25
 
26
+ def environment_prefix
27
+ "[#{Rails.env.upcase}] " if defined?(Rails)
28
+ end
29
+
21
30
  def append_tenant_message(message)
22
31
  message = message + " for tenant: #{Apartment::Tenant.current}" if defined?(Apartment)
23
32
  message
24
33
  end
25
34
 
35
+ def send_hipchat_message(message, options)
36
+ return unless defined?(HipChat) && config.use_hipchat
37
+
38
+ client = HipChat::Client.new(config.hipchat_api_token, config.hipchat_init_options)
39
+ room = client[config.hipchat_room]
40
+ room.send(config.hipchat_user, message, options)
41
+ end
42
+
43
+ def send_slack_message(message, color)
44
+ return unless defined?(Slack) && config.use_slack
45
+
46
+ notifier = Slack::Notifier.new(
47
+ config.slack_webhook_url,
48
+ channel: config.slack_channel,
49
+ username: config.slack_username)
50
+
51
+ payload = { attachments: [{ color: color, text: message }] }
52
+
53
+ notifier.post payload
54
+ end
55
+
26
56
  private
27
57
 
28
- def send_hipchat_message(message, options)
29
- if defined?(HipChat) && Patches::Config.use_hipchat
30
- HipChat::Client.new(Patches::Config.hipchat_options[:api_token])[Patches::Config.hipchat_options[:room]].send(Patches::Config.hipchat_options[:user], message, options)
31
- end
58
+ def config
59
+ Patches::Config.configuration
32
60
  end
33
61
  end
34
62
  end
@@ -0,0 +1,8 @@
1
+ module Patches
2
+ module TenantRunConcern
3
+ def run(tenant_name, path = nil)
4
+ Apartment::Tenant.switch(tenant_name)
5
+ Patches::Runner.new(path).perform
6
+ end
7
+ end
8
+ end
@@ -1,4 +1,5 @@
1
1
  class Patches::TenantRunner
2
+ include Patches::TenantRunConcern
2
3
  attr_accessor :path
3
4
 
4
5
  def initialize(path: nil, tenants: nil)
@@ -8,19 +9,22 @@ class Patches::TenantRunner
8
9
 
9
10
  def perform
10
11
  Patches.logger.info("Patches tenant runner for: #{tenants.join(',')}")
11
-
12
12
  tenants.each do |tenant|
13
- Apartment::Tenant.switch(tenant)
14
- runner = build
15
- runner.perform
13
+ if parallel?
14
+ Patches::TenantWorker.perform_async(tenant, path)
15
+ else
16
+ run(tenant, path)
17
+ end
16
18
  end
17
19
  end
18
20
 
19
- def build
20
- Patches::Runner.new(path)
21
- end
22
-
23
21
  def tenants
24
22
  @tenants ||= (Apartment.tenant_names || [])
25
23
  end
24
+
25
+ private
26
+
27
+ def parallel?
28
+ Patches::Config.configuration.sidekiq_parallel
29
+ end
26
30
  end
@@ -0,0 +1,12 @@
1
+ require 'sidekiq'
2
+
3
+ class Patches::TenantWorker
4
+ include Sidekiq::Worker
5
+ include Patches::TenantRunConcern
6
+
7
+ sidekiq_options Patches::Config.configuration.sidekiq_options
8
+
9
+ def perform(tenant_name, path)
10
+ run(tenant_name, path)
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Patches
2
- VERSION = "2.0.0"
2
+ VERSION = "2.4.0"
3
3
  end
@@ -3,7 +3,7 @@ require 'sidekiq'
3
3
  class Patches::Worker
4
4
  include Sidekiq::Worker
5
5
 
6
- sidekiq_options Patches::Config.sidekiq_options
6
+ sidekiq_options Patches::Config.configuration.sidekiq_options
7
7
 
8
8
  def perform(runner)
9
9
  runner.constantize.new.perform
@@ -7,7 +7,7 @@ namespace :patches do
7
7
  runner = Patches::Runner
8
8
  end
9
9
 
10
- if defined?(Sidekiq) && Patches::Config.use_sidekiq
10
+ if defined?(Sidekiq) && Patches::Config.configuration.use_sidekiq
11
11
  Patches::Worker.perform_async(runner)
12
12
  else
13
13
  runner.new.perform
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_dependency "railties", ">= 3.2"
23
+ spec.add_dependency "slack-notifier"
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.8"
25
26
  spec.add_development_dependency "rake", "~> 10.0"
@@ -34,4 +35,7 @@ Gem::Specification.new do |spec|
34
35
  spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4"
35
36
  spec.add_development_dependency "pry"
36
37
  spec.add_development_dependency "sidekiq", "~> 3.4.1"
38
+ spec.add_development_dependency "hipchat"
39
+ spec.add_development_dependency "webmock"
40
+ spec.add_development_dependency "byebug"
37
41
  end
@@ -2,7 +2,9 @@
2
2
  set -e
3
3
 
4
4
  echo '--- setting ruby version'
5
- rbenv local 2.1.5
5
+ cd /var/lib/buildkite-agent/.rbenv/plugins/ruby-build && git pull && cd -
6
+ rbenv install 2.3.7 -s
7
+ rbenv local 2.3.7
6
8
 
7
9
  echo '--- setting up env'
8
10
  REVISION=https://github.com/$BUILDBOX_PROJECT_SLUG/commit/$BUILDBOX_COMMIT
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patches
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John D'Agostino
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-08-06 00:00:00.000000000 Z
11
+ date: 2018-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: slack-notifier
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +220,48 @@ dependencies:
206
220
  - - "~>"
207
221
  - !ruby/object:Gem::Version
208
222
  version: 3.4.1
223
+ - !ruby/object:Gem::Dependency
224
+ name: hipchat
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: webmock
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
251
+ - !ruby/object:Gem::Dependency
252
+ name: byebug
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - ">="
263
+ - !ruby/object:Gem::Version
264
+ version: '0'
209
265
  description: A simple gem for one off tasks for example database patches
210
266
  email:
211
267
  - johnd@jobready.com.au
@@ -228,6 +284,7 @@ files:
228
284
  - lib/generators/patches.rb
229
285
  - lib/generators/patches/patch_generator.rb
230
286
  - lib/generators/patches/templates/patch.rb.erb
287
+ - lib/generators/patches/templates/patch_spec.rb.erb
231
288
  - lib/patches.rb
232
289
  - lib/patches/base.rb
233
290
  - lib/patches/capistrano.rb
@@ -238,7 +295,9 @@ files:
238
295
  - lib/patches/patch.rb
239
296
  - lib/patches/pending.rb
240
297
  - lib/patches/runner.rb
298
+ - lib/patches/tenant_run_concern.rb
241
299
  - lib/patches/tenant_runner.rb
300
+ - lib/patches/tenant_worker.rb
242
301
  - lib/patches/version.rb
243
302
  - lib/patches/worker.rb
244
303
  - lib/tasks/patches.rake
@@ -264,7 +323,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
264
323
  version: '0'
265
324
  requirements: []
266
325
  rubyforge_project:
267
- rubygems_version: 2.4.8
326
+ rubygems_version: 2.7.6
268
327
  signing_key:
269
328
  specification_version: 4
270
329
  summary: A simple gem for one off tasks