puma-redeploy 0.1.1 → 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.
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