puma-redeploy 0.1.1 → 0.3.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
2
  SHA256:
3
- metadata.gz: 71217e13019896c5b071db53e1b149ddb34d5dd5cf98eb5cd2da7f189c335f6a
4
- data.tar.gz: 32b87f0dd6e54ca89bc0be6032d60c37fb6e88f6fd58eae15e25cb4bc13aebad
3
+ metadata.gz: 9ed0d1a89c2ce26afe253893f3a716e346edb7b7bfe109c0acf6c1cc2599db23
4
+ data.tar.gz: 39a7a76965ca098d9dcfb0586d5d96f3c47326fafd4aa22b3a6a9f68d7358e4b
5
5
  SHA512:
6
- metadata.gz: bcf4d8732453d835c898595bff21c84a31d8b1f5b30f5cf9a46f1f6c33d3e5dfdf4fcbcc9416a698caa70acb17b4fa319144e3a8e0f3f85af944e5d914ba1ab8
7
- data.tar.gz: e7bbea1217f1eaa8f1bab4d3d0d4d9a798f477d42a28fe79b682251b8e3dd0f5525e4a63aea3dfec880e20c182060dbc81e4b72c05eea53e539f29bcc84ad6d7
6
+ metadata.gz: dfd1f53e8d88ea177310c7797428f924ac4b865dcce699a6f299be86510a6d1b702b86fbc934cba9c727f3df6a2834194c6ecfde4b113b7a0ebf845b264989fc
7
+ data.tar.gz: c9bd80080b88819fbd3a03ec49cd8649fa80dbc6b10d15d2a2f063b97ed74aabb276aac02df48a6a1744033af0240be17ca95ef6b85a88626ff65f306e638575
@@ -1,4 +1,4 @@
1
- name: Build and publish gem to github package
1
+ name: Build and Publish Gem
2
2
 
3
3
  on:
4
4
  workflow_dispatch:
data/.gitignore CHANGED
@@ -11,3 +11,5 @@
11
11
  .rspec_status
12
12
  Gemfile.lock
13
13
  .DS_Store
14
+ archive.1.0.0.zip
15
+ watch.me
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.7
2
+ TargetRubyVersion: 3.1
3
3
  DisplayCopNames: true
4
4
  SuggestExtensions: false
5
5
  NewCops: enable
@@ -20,3 +20,18 @@ Metrics/MethodLength:
20
20
 
21
21
  RSpec/NestedGroups:
22
22
  Max: 4
23
+
24
+ RSpec/MultipleMemoizedHelpers:
25
+ Max: 12
26
+
27
+ RSpec/ExampleLength:
28
+ Max: 6
29
+
30
+ RSpec/ExpectInHook:
31
+ Enabled: false
32
+
33
+ RSpec/StubbedMock:
34
+ Enabled: false
35
+
36
+ RSpec/MessageSpies:
37
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,12 +1,28 @@
1
-
2
1
  # Changelog
3
2
 
4
- ## v0.1.1 (NEXT)
3
+ ## v0.3.x (NEXT)
4
+
5
+
6
+ ## v0.3.0
7
+
8
+ **Breaking Changes:**
9
+ - Rename load_archive to archive-loader
10
+ - Add options flags to archive-loader
11
+
12
+ **Fixes and enhancements:**
13
+
14
+ ## v0.2.1
5
15
 
6
16
  **Features:**
7
17
 
8
- - Your contribution here
18
+ - Add support for S3 handler
19
+
20
+ **Fixes and enhancements:**
21
+
22
+ - Refactor file handler to use new base handler class
23
+
24
+ ## v0.1.1
9
25
 
10
26
  **Fixes and enhancements:**
11
27
 
12
- - Add CHANGELOG.md
28
+ - Initial File based handler support
data/README.md CHANGED
@@ -4,14 +4,17 @@ The puma-redeploy gem is a puma plugin that allows you to redeploy a new version
4
4
 
5
5
  Key Points:
6
6
  * Encourages the separation of the build process from deployment
7
+ * Runtime container does not include application code
7
8
  * Leverages Puma [phased-restart](https://github.com/puma/puma/blob/master/docs/restart.md#phased-restart) to ensure uptime deploy
8
9
  * Deploys in seconds
10
+ * Pluggable handlers to detect redeploy (File, S3, Artifactory, etc..)
9
11
 
12
+ **Note** - Currently there are File and S3 handlers for loading archives.
10
13
 
11
14
  ![image](https://user-images.githubusercontent.com/121275/219976698-80575b17-17b7-4861-8c10-675f3f615e25.png)
12
15
 
13
16
 
14
- Example application can be found [here](https://github.com/tbeauvais/sinatra-api-base)
17
+ Example application can be found [here](https://github.com/tbeauvais/puma-redeploy-test-app)
15
18
 
16
19
 
17
20
  ## Installation
@@ -38,18 +41,41 @@ Update your `config/puma.rb` config file with the following
38
41
  # Add the puma-redeploy plugin
39
42
  plugin :redeploy
40
43
 
41
- # specify the redeploy watch file
42
- redeploy_watch_file './watch_me'
44
+ # Specify the redeploy watch file from an environment variable. This can a file system location or S3 URL. For example `/app/pkg/watch.me` or `s3://puma-test-app-archives/watch.me`.
45
+ redeploy_watch_file ENV['WATCH_FILE']
46
+
43
47
 
44
48
  # Specify the number of seconds between checking watch file. Defaults to 30.
45
49
  redeploy_watch_delay 15
46
50
 
47
51
  ```
48
52
 
49
- The watch file must contain the path to the current archive.
50
- For example:
53
+ The watch file must contain the path to the current archive. This can be a file path or S3 URL.
54
+
55
+ For example when using a file:
56
+ ```
57
+ /app/pkg/test_app_0.0.3.zip
58
+ ```
59
+
60
+ For example when using S3:
61
+ ```
62
+ s3://puma-test-app-archives/test_app_0.0.3.zip
63
+ ```
64
+
65
+ ### Archive Loader
66
+ The `archive-loader` is a cli used to fetch and deploy the application archive prior to starting the puma server. This is useful when the application code does not exist in the runtime container.
67
+
68
+ ```shell
69
+ archive-loader
70
+ Usage: archive-loader [options]. Used to load the archive prior to starting the puma app server.
71
+ -a, --app-dir=DIR [Required] Location of application directory within the container.
72
+ -w, --watch=WATCH [Required] Location of watch file (file or s3 location).
73
+ -h, --help Prints this help
51
74
  ```
52
- /sinatra_test/pkg/my_application_0.0.1.zip
75
+
76
+ For example this will fetch and unzip the application archive and then start puma.
77
+ ```shell
78
+ archive-loader /app /app/pkg/watch.me && bundle exec puma -C config/puma.rb
53
79
  ```
54
80
 
55
81
  ## Development
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'puma-redeploy'
6
+ require 'logger'
7
+ require 'optparse'
8
+
9
+ def deploy_archive(app_dir, watch_file, logger)
10
+ handler = Puma::Redeploy::DeployerFactory.create(target: app_dir, watch_file:,
11
+ logger:)
12
+ handler.deploy(source: handler.archive_file)
13
+ end
14
+
15
+ def option_parser(opts)
16
+ OptionParser.new do |o|
17
+ o.banner = 'Usage: archive-loader [options]. Used to load the archive prior to starting the puma app server.'
18
+
19
+ o.on '-a', '--app-dir=DIR', '[Required] Location of application directory within the container.' do |arg|
20
+ opts[:app_dir] = arg
21
+ end
22
+
23
+ o.on '-w', '--watch=WATCH', '[Required] Location of watch file (file or s3 location).' do |arg|
24
+ opts[:watch] = arg
25
+ end
26
+
27
+ o.on('-h', '--help', 'Prints this help') do
28
+ puts o
29
+ exit
30
+ end
31
+ end
32
+ end
33
+
34
+ def logger
35
+ Logger.new($stdout)
36
+ end
37
+
38
+ ops = {}
39
+ parser = option_parser(ops)
40
+ parser.parse!(ARGV)
41
+
42
+ unless ops[:app_dir] && ops[:watch]
43
+ puts parser.help
44
+ exit 1
45
+ end
46
+
47
+ deploy_archive(ops[:app_dir], ops[:watch], logger)
@@ -13,8 +13,8 @@ Puma::Plugin.create do
13
13
  in_background do
14
14
  logger = launcher.options[:redeploy_logger] || Logger.new($stdout)
15
15
  watch_file = launcher.options[:redeploy_watch_file]
16
- handler = Puma::Redeploy::DeployerFactory.create(target: launcher.restart_dir, watch_file: watch_file,
17
- logger: logger)
16
+ handler = Puma::Redeploy::DeployerFactory.create(target: launcher.restart_dir, watch_file:,
17
+ logger:)
18
18
 
19
19
  delay = launcher.options[:redeploy_watch_delay] || 30
20
20
  monitor_loop(handler, delay, launcher, logger)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Puma
6
+ module Redeploy
7
+ # base redeploy handler
8
+ class BaseHandler
9
+ extend Forwardable
10
+ attr_reader :watch_file
11
+
12
+ def initialize(watch_file:, deployer:, logger:)
13
+ @watch_file = watch_file
14
+ @logger = logger
15
+ @deployer = deployer
16
+ end
17
+
18
+ def needs_redeploy?
19
+ return false unless (mtime = touched_at) != @touched_at
20
+
21
+ @touched_at = mtime
22
+ true
23
+ end
24
+
25
+ def_delegators :@deployer, :deploy
26
+
27
+ private
28
+
29
+ attr_accessor :logger
30
+ end
31
+ end
32
+ end
@@ -2,11 +2,19 @@
2
2
 
3
3
  module Puma
4
4
  module Redeploy
5
- # Creates instance if deployer implementation
5
+ # Creates instance of the redeploy handler based on the watch file location
6
6
  class DeployerFactory
7
7
  def self.create(target:, watch_file:, logger:)
8
- file_deployer = Puma::Redeploy::FileDeployer.new(target: target, logger: logger)
9
- Puma::Redeploy::FileHandler.new(watch_file: watch_file, deployer: file_deployer, logger: logger)
8
+ file_deployer = Puma::Redeploy::ZipDeployer.new(target:, logger:)
9
+ if watch_file.start_with?('s3')
10
+ Puma::Redeploy::S3Handler.new(watch_file:, deployer: file_deployer, logger:, s3_client:)
11
+ else
12
+ Puma::Redeploy::FileHandler.new(watch_file:, deployer: file_deployer, logger:)
13
+ end
14
+ end
15
+
16
+ def self.s3_client
17
+ Aws::S3::Client.new
10
18
  end
11
19
  end
12
20
  end
@@ -5,33 +5,18 @@ require 'forwardable'
5
5
  module Puma
6
6
  module Redeploy
7
7
  # file based redeploy handler
8
- class FileHandler
9
- extend Forwardable
10
- attr_reader :watch_file
11
-
8
+ class FileHandler < BaseHandler
12
9
  def initialize(watch_file:, deployer:, logger:)
13
- @watch_file = watch_file
14
- @logger = logger
10
+ super
15
11
  @touched_at = touched_at
16
- @deployer = deployer
17
- end
18
-
19
- def_delegators :@deployer, :deploy
20
- def needs_redeploy?
21
- return false unless (mtime = touched_at) != @touched_at
22
-
23
- @touched_at = mtime
24
- true
25
12
  end
26
13
 
27
14
  def archive_file
28
- File.read(watch_file)&.strip
15
+ File.read(watch_file).strip
29
16
  end
30
17
 
31
18
  private
32
19
 
33
- attr_accessor :logger
34
-
35
20
  def touched_at
36
21
  if File.exist?(watch_file)
37
22
  File.mtime(watch_file)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'aws-sdk-s3'
5
+
6
+ module Puma
7
+ module Redeploy
8
+ # S3 based redeploy handler
9
+ class S3Handler < BaseHandler
10
+ attr_reader :bucket_name, :object_key, :s3_client
11
+
12
+ def initialize(watch_file:, deployer:, logger:, s3_client:)
13
+ super(watch_file:, deployer:, logger:)
14
+ @s3_client = s3_client
15
+ @bucket_name, @object_key = s3_object_reference(watch_file)
16
+ @touched_at = touched_at
17
+ end
18
+
19
+ def archive_file
20
+ s3_url = read_watch_object
21
+ archive_bucket_name, archive_object_key = s3_object_reference(s3_url)
22
+
23
+ response = s3_client.get_object(bucket: archive_bucket_name, key: archive_object_key)
24
+
25
+ file_name = local_file_path(archive_object_key)
26
+ # Write the object data to a local file
27
+ File.binwrite(file_name, response.body.read)
28
+ file_name
29
+ end
30
+
31
+ private
32
+
33
+ def s3_object_reference(watch_file)
34
+ s3_uri = URI.parse(watch_file)
35
+ [s3_uri.host, s3_uri.path[1..]]
36
+ end
37
+
38
+ def local_file_path(object_key)
39
+ File.basename(object_key)
40
+ end
41
+
42
+ def read_watch_object
43
+ # Get the object data from S3
44
+ object = s3_client.get_object(bucket: bucket_name, key: object_key)
45
+
46
+ object.body.read.strip
47
+ end
48
+
49
+ def touched_at
50
+ head_object = s3_client.head_object(bucket: bucket_name, key: object_key)
51
+
52
+ if head_object
53
+ head_object.last_modified
54
+ else
55
+ logger.info "The S3 object #{bucket_name}/#{object_key} does not exist"
56
+ 0
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Puma
4
4
  module Redeploy
5
- VERSION = '0.1.1'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
@@ -5,7 +5,7 @@ require 'open3'
5
5
  module Puma
6
6
  module Redeploy
7
7
  # Deploys zip archive
8
- class FileDeployer
8
+ class ZipDeployer
9
9
  def initialize(target:, logger:)
10
10
  @target = target
11
11
  @logger = logger
data/lib/puma_redeploy.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Dir.glob("#{__dir__}/puma/redeploy/**/*.rb").sort.each { |file| require file }
3
+ Dir.glob("#{__dir__}/puma/redeploy/**/*.rb").each { |file| require file }
4
4
 
5
5
  module Puma
6
6
  module Redeploy
@@ -14,9 +14,9 @@ Gem::Specification.new do |spec|
14
14
  'will be pulled and the app will be reloaded.'
15
15
  spec.homepage = 'https://github.com/tbeauvais/puma-redeploy'
16
16
  spec.license = 'MIT'
17
- spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
17
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.1.0')
18
18
 
19
- spec.executables = ['load_archive']
19
+ spec.executables = ['archive-loader']
20
20
 
21
21
  spec.metadata['homepage_uri'] = spec.homepage
22
22
  spec.metadata['source_code_uri'] = 'https://github.com/tbeauvais/puma-redeploy'
@@ -30,6 +30,8 @@ Gem::Specification.new do |spec|
30
30
  spec.bindir = 'bin'
31
31
  spec.require_paths = ['lib']
32
32
 
33
+ spec.add_runtime_dependency 'aws-sdk-s3', '~> 1.120.0'
34
+
33
35
  spec.add_development_dependency 'rake', '~> 13.0.6'
34
36
  spec.add_development_dependency 'rspec', '~> 3.12.0'
35
37
  spec.add_development_dependency 'rubocop', '~> 1.43'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma-redeploy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - tbeauvais
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-14 00:00:00.000000000 Z
11
+ date: 2023-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-s3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.120.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.120.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rake
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -128,7 +142,7 @@ description: Puma plugin to redeploy and reload app from a remote artifact. This
128
142
  email:
129
143
  - tbeauvais1@gmail.com
130
144
  executables:
131
- - load_archive
145
+ - archive-loader
132
146
  extensions: []
133
147
  extra_rdoc_files: []
134
148
  files:
@@ -144,14 +158,16 @@ files:
144
158
  - LICENSE.txt
145
159
  - README.md
146
160
  - Rakefile
147
- - bin/load_archive
161
+ - bin/archive-loader
148
162
  - lib/puma-redeploy.rb
149
163
  - lib/puma/plugin/redeploy.rb
164
+ - lib/puma/redeploy/base_handler.rb
150
165
  - lib/puma/redeploy/deployer_factory.rb
151
166
  - lib/puma/redeploy/dsl.rb
152
- - lib/puma/redeploy/file_deployer.rb
153
167
  - lib/puma/redeploy/file_handler.rb
168
+ - lib/puma/redeploy/s3_handler.rb
154
169
  - lib/puma/redeploy/version.rb
170
+ - lib/puma/redeploy/zip_deployer.rb
155
171
  - lib/puma_redeploy.rb
156
172
  - puma-redeploy.gemspec
157
173
  homepage: https://github.com/tbeauvais/puma-redeploy
@@ -170,7 +186,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
170
186
  requirements:
171
187
  - - ">="
172
188
  - !ruby/object:Gem::Version
173
- version: 2.7.0
189
+ version: 3.1.0
174
190
  required_rubygems_version: !ruby/object:Gem::Requirement
175
191
  requirements:
176
192
  - - ">="
data/bin/load_archive DELETED
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # frozen_string_literal: true
4
-
5
- require 'puma-redeploy'
6
- require 'logger'
7
-
8
- def deploy_archive(app_dir, watch_file, logger)
9
- handler = Puma::Redeploy::DeployerFactory.create(target: app_dir, watch_file: watch_file,
10
- logger: logger)
11
- handler.deploy(source: handler.archive_file)
12
- end
13
-
14
- logger = Logger.new($stdout)
15
-
16
- if ARGV.size != 2
17
- logger.error 'You must specify the application directory and watch file'
18
- logger.info 'load_archive <app directory> <watch file>'
19
- logger.info 'e.g. load_archive /app /build/pkg/watch.me'
20
- end
21
-
22
- app_dir = ARGV[0]
23
- watch_file = ARGV[1]
24
-
25
- if File.exist?(watch_file) && Dir.exist?(app_dir)
26
- deploy_archive(app_dir, watch_file, logger)
27
- else
28
- logger.error "watch_file #{watch_file} does not exist" unless File.exist?(watch_file)
29
- logger.error "app_dir #{app_dir} does not exist" unless Dir.exist?(app_dir)
30
- end