webpacker_uploader 0.2.1 → 0.6.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/.gitignore +1 -0
- data/.rubocop.yml +197 -10
- data/.yardopts +8 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +55 -41
- data/README.md +56 -15
- data/Rakefile +13 -0
- data/lib/webpacker_uploader.rb +15 -3
- data/lib/webpacker_uploader/configuration.rb +43 -0
- data/lib/webpacker_uploader/instance.rb +44 -10
- data/lib/webpacker_uploader/manifest.rb +3 -3
- data/lib/webpacker_uploader/mime.rb +6 -0
- data/lib/webpacker_uploader/providers/aws.rb +82 -11
- data/lib/webpacker_uploader/version.rb +3 -1
- data/test/configuration_test.rb +87 -0
- data/test/manifest_test.rb +27 -0
- data/test/mime_test.rb +14 -0
- data/test/providers/aws_test.rb +16 -0
- data/test/test_app/config/webpacker.yml +103 -0
- data/test/test_app/public/packs/manifest.json +33 -0
- data/test/test_helper.rb +35 -0
- data/test/webpacker_uploader_test.rb +32 -0
- data/webpacker_uploader.gemspec +7 -9
- metadata +57 -13
- data/.github/workflows/rubocop.yml +0 -35
- data/.github/workflows/ruby.yml +0 -39
- data/bin/console +0 -14
- data/bin/setup +0 -8
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[](https://github.com/tlatsas/webpacker_uploader/actions)
|
4
4
|
[](https://github.com/tlatsas/webpacker_uploader/actions)
|
5
5
|
[](https://rubygems.org/gems/webpacker_uploader)
|
6
|
+
[](https://www.rubydoc.info/gems/webpacker_uploader)
|
6
7
|
|
7
8
|
Webpacker uploader provides an easy way to upload your assets to AWS S3.
|
8
9
|
It knows which files to upload by reading the `manifest.json` file.
|
@@ -15,14 +16,14 @@ S3 + CDN, outside of your Rails application.
|
|
15
16
|
Add this line to your application's Gemfile:
|
16
17
|
|
17
18
|
```ruby
|
18
|
-
gem "webpacker_uploader"
|
19
|
+
gem "webpacker_uploader", require: false
|
19
20
|
```
|
20
21
|
|
21
|
-
|
22
|
+
and run:
|
22
23
|
|
23
24
|
$ bundle install
|
24
25
|
|
25
|
-
|
26
|
+
or:
|
26
27
|
|
27
28
|
$ gem install webpacker_uploader
|
28
29
|
|
@@ -34,6 +35,8 @@ gem "aws-sdk-s3", require: false
|
|
34
35
|
|
35
36
|
## Usage
|
36
37
|
|
38
|
+
Usually, in a Rake task you would add the following to upload the assets to AWS S3:
|
39
|
+
|
37
40
|
```ruby
|
38
41
|
require "webpacker_uploader"
|
39
42
|
require "webpacker_uploader/providers/aws"
|
@@ -89,18 +92,6 @@ provider_options = {
|
|
89
92
|
provider = WebpackerUploader::Providers::Aws.new(provider_options)
|
90
93
|
```
|
91
94
|
|
92
|
-
### Ignore files
|
93
|
-
|
94
|
-
The uploader can be configured to skip certain files based on the file extension.
|
95
|
-
By default `.map` files are excluded. This can be configured through the `ignored_extensions` attribute.
|
96
|
-
In order to upload everything pass an empty array.
|
97
|
-
|
98
|
-
```ruby
|
99
|
-
# skip uploading images
|
100
|
-
WebpackerUploader.ignored_extensions = [".png", ".jpg", ".webp"]
|
101
|
-
WebpackerUploader.upload!(provider)
|
102
|
-
```
|
103
|
-
|
104
95
|
### Prefix remote files
|
105
96
|
|
106
97
|
Uploaded files can be prefixed by setting the `prefix` parameter during upload:
|
@@ -112,6 +103,43 @@ WebpackerUploader.upload!(provider, prefix: "assets")
|
|
112
103
|
This will prefix all remote file paths with `assets` so instead of storing `packs/application-dd6b1cd38bfa093df600.css` it
|
113
104
|
will store `assets/packs/application-dd6b1cd38bfa093df600.css`.
|
114
105
|
|
106
|
+
### Configuration
|
107
|
+
|
108
|
+
WebpackerUploader currently supports the following configuration options:
|
109
|
+
|
110
|
+
| option | description | default value |
|
111
|
+
|----------------------|--------------------------------------------------------------|---------------------------------------|
|
112
|
+
| ignored_extensions | Which files uploader should skip based on the file extension | [] |
|
113
|
+
| logger | The logger to use for logging | ActiveSupport::Logger.new(STDOUT) |
|
114
|
+
| log_output | Log output as we upload files | true |
|
115
|
+
| public_manifest_path | The webpack manifest path | Webpacker.config.public_manifest_path |
|
116
|
+
| public_path | The webpack public output path | Webpacker.config.public_path |
|
117
|
+
|
118
|
+
It can be configured using a block:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
WebpackerUploader.configure do |config|
|
122
|
+
config.ignored_extensions = [".png", ".jpg", ".webp"]
|
123
|
+
config.log_output = false
|
124
|
+
config.public_manifest_path = "path/to/manifest.json"
|
125
|
+
config.public_path = "path/to/public/dir"
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
129
|
+
or directly:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
WebpackerUploader.config.log_output = false
|
133
|
+
```
|
134
|
+
|
135
|
+
The uploader used to skip `.map` files by default. This has changed (see [CHANGELOG.md](https://github.com/tlatsas/webpacker_uploader/blob/main/CHANGELOG.md))
|
136
|
+
and everything is uploaded by default now. To retain the previous functionality use:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
# skip uploading map files
|
140
|
+
WebpackerUploader.config.ignored_extensions = [".map"]
|
141
|
+
```
|
142
|
+
|
115
143
|
## Development
|
116
144
|
|
117
145
|
After checking out the repo, run `bin/setup` to install dependencies.
|
@@ -120,6 +148,19 @@ interactive prompt that will allow you to experiment.
|
|
120
148
|
|
121
149
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
122
150
|
|
151
|
+
## Integration tests
|
152
|
+
|
153
|
+
This gem also provides an integration test suite. It runs using [localstack](https://github.com/localstack/localstack/).
|
154
|
+
To run the integration tests, install docker on your system and spin up a localstack container running the s3 service.
|
155
|
+
|
156
|
+
```shell
|
157
|
+
docker compose -f "integration/docker-compose.yml" up --detach
|
158
|
+
```
|
159
|
+
|
160
|
+
To run the tests, use `rake test:integration`.
|
161
|
+
|
162
|
+
To stop the container once done run: `docker-compose -f "integration/docker-compose.yml" down`
|
163
|
+
|
123
164
|
## Contributing
|
124
165
|
|
125
166
|
Bug reports and pull requests are welcome on GitHub at https://github.com/tlatsas/webpacker_uploader.
|
data/Rakefile
CHANGED
@@ -1,10 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bundler/gem_tasks"
|
2
4
|
require "rake/testtask"
|
3
5
|
|
6
|
+
require "yard"
|
7
|
+
YARD::Rake::YardocTask.new
|
8
|
+
|
4
9
|
Rake::TestTask.new(:test) do |t|
|
5
10
|
t.libs << "test"
|
6
11
|
t.libs << "lib"
|
7
12
|
t.test_files = FileList["test/**/*_test.rb"]
|
8
13
|
end
|
9
14
|
|
15
|
+
namespace :test do
|
16
|
+
desc "Run integration tests using localstack"
|
17
|
+
Rake::TestTask.new(:integration) do |t|
|
18
|
+
t.libs << "integration"
|
19
|
+
t.test_files = FileList["integration/**/*_test.rb"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
10
23
|
task default: :test
|
data/lib/webpacker_uploader.rb
CHANGED
@@ -1,23 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/module"
|
3
4
|
require "active_support/core_ext/object/blank"
|
4
|
-
require "active_support/logger"
|
5
|
-
require "active_support/tagged_logging"
|
6
5
|
|
7
6
|
module WebpackerUploader
|
8
7
|
extend self
|
9
8
|
|
9
|
+
# @private
|
10
10
|
def instance=(instance)
|
11
11
|
@instance = instance
|
12
12
|
end
|
13
13
|
|
14
|
+
# @private
|
14
15
|
def instance
|
15
16
|
@instance ||= WebpackerUploader::Instance.new
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
+
# @!attribute [rw] config
|
20
|
+
# @see Instance#config
|
21
|
+
# @!scope class
|
22
|
+
# @!method configure
|
23
|
+
# @see Instance#configure
|
24
|
+
# @!scope class
|
25
|
+
# @!method upload!
|
26
|
+
# @return [void]
|
27
|
+
# @see Instance#upload!
|
28
|
+
# @!scope class
|
29
|
+
delegate :configure, :config, :upload!, to: :instance
|
19
30
|
end
|
20
31
|
|
32
|
+
require "webpacker_uploader/configuration"
|
21
33
|
require "webpacker_uploader/instance"
|
22
34
|
require "webpacker_uploader/manifest"
|
23
35
|
require "webpacker_uploader/mime"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/logger"
|
4
|
+
require "active_support/tagged_logging"
|
5
|
+
|
6
|
+
# This is the class which holds the configuration options.
|
7
|
+
#
|
8
|
+
# Options are set and retrieved using `WebpackerUploader.config`
|
9
|
+
# and `WebpackerUploader.configure`.
|
10
|
+
class WebpackerUploader::Configuration
|
11
|
+
# @return [Array] the file extentions ignored by the uploader.
|
12
|
+
attr_accessor :ignored_extensions
|
13
|
+
|
14
|
+
# @return [ActiveSupport::Logger] the logger to use.
|
15
|
+
attr_accessor :logger
|
16
|
+
|
17
|
+
# @return [Boolean] whether or not to log operations.
|
18
|
+
attr_accessor :log_output
|
19
|
+
|
20
|
+
# @return [Pathname] the path to manifest.json, defaults to Webpacker public manifest path.
|
21
|
+
attr_reader :public_manifest_path
|
22
|
+
|
23
|
+
# @return [Pathname] the public root path, defaults to Webpacker public root path.
|
24
|
+
attr_reader :public_path
|
25
|
+
|
26
|
+
alias_method :log_output?, :log_output
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@ignored_extensions = []
|
30
|
+
@logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
31
|
+
@log_output = true
|
32
|
+
@public_manifest_path = ::Webpacker.config.public_manifest_path
|
33
|
+
@public_path = ::Webpacker.config.public_path
|
34
|
+
end
|
35
|
+
|
36
|
+
def public_manifest_path=(path)
|
37
|
+
@public_manifest_path = Pathname.new(path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def public_path=(path)
|
41
|
+
@public_path = Pathname.new(path)
|
42
|
+
end
|
43
|
+
end
|
@@ -1,18 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class WebpackerUploader::Instance
|
4
|
-
|
5
|
-
cattr_accessor(:ignored_extensions) { %w[.map] }
|
4
|
+
attr_writer :config
|
6
5
|
|
6
|
+
# @private
|
7
7
|
def manifest
|
8
8
|
@manifest ||= WebpackerUploader::Manifest.new
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# Returns the global WebpackerUploader::Configuration object.
|
12
|
+
# Use this to set and retrieve specific configuration options.
|
13
|
+
#
|
14
|
+
# @return [WebpackerUploader::Configuration]
|
15
|
+
#
|
16
|
+
# @example Disable log output
|
17
|
+
# WebpackerUploader.config.log_output = false
|
18
|
+
#
|
19
|
+
# @example Get the list of excluded file extension
|
20
|
+
# puts WebpackerUploader.config.ignored_extensions
|
21
|
+
#
|
22
|
+
# @see WebpackerUploader::Configuration
|
23
|
+
def config
|
24
|
+
@config ||= WebpackerUploader::Configuration.new
|
25
|
+
end
|
15
26
|
|
27
|
+
# Yields the global configuration to a block. Use this to
|
28
|
+
# configure the base gem features.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# WebpackerUploader.configure do |config|
|
32
|
+
# config.ignored_extensions = [".png", ".jpg", ".webp"]
|
33
|
+
# config.log_output = false
|
34
|
+
# config.public_manifest_path = "path/to/manifest.json"
|
35
|
+
# config.public_path = "path/to/public/dir"
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @see WebpackerUploader::Configuration
|
39
|
+
def configure
|
40
|
+
yield config
|
41
|
+
end
|
42
|
+
|
43
|
+
# Uploads assets using the supplied provider. Currently only AWS S3 is implemented.
|
44
|
+
#
|
45
|
+
# @return [void]
|
46
|
+
# @param provider [WebpackerUploader::Providers::Aws] A provider to use for file uploading.
|
47
|
+
# @param prefix [String] Used to prefix the remote file paths.
|
48
|
+
def upload!(provider, prefix: nil)
|
16
49
|
manifest.assets.each do |name, js_path|
|
17
50
|
path = js_path[1..-1]
|
18
51
|
|
@@ -23,13 +56,14 @@ class WebpackerUploader::Instance
|
|
23
56
|
"#{prefix}/#{path}"
|
24
57
|
end
|
25
58
|
|
26
|
-
file_path =
|
59
|
+
file_path = config.public_path.join(path)
|
27
60
|
|
28
|
-
if name.end_with?(*ignored_extensions)
|
29
|
-
logger.info("Skipping #{file_path}")
|
61
|
+
if name.end_with?(*config.ignored_extensions)
|
62
|
+
config.logger.info("Skipping #{file_path}") if config.log_output?
|
30
63
|
else
|
31
64
|
content_type = WebpackerUploader::Mime.mime_type(path)
|
32
|
-
|
65
|
+
|
66
|
+
config.logger.info("Processing #{file_path} as #{content_type}") if config.log_output?
|
33
67
|
|
34
68
|
provider.upload!(remote_path, file_path, content_type)
|
35
69
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class WebpackerUploader::Manifest
|
3
|
+
class WebpackerUploader::Manifest # @private
|
4
4
|
attr_reader :assets
|
5
5
|
|
6
6
|
def initialize
|
@@ -10,8 +10,8 @@ class WebpackerUploader::Manifest
|
|
10
10
|
private
|
11
11
|
|
12
12
|
def load
|
13
|
-
if
|
14
|
-
JSON.parse(
|
13
|
+
if WebpackerUploader.config.public_manifest_path.exist?
|
14
|
+
JSON.parse(WebpackerUploader.config.public_manifest_path.read).except("entrypoints")
|
15
15
|
else
|
16
16
|
{}
|
17
17
|
end
|
@@ -3,6 +3,12 @@
|
|
3
3
|
require "mime-types"
|
4
4
|
|
5
5
|
module WebpackerUploader::Mime
|
6
|
+
# Returns the mime type for the given file in the filesystem.
|
7
|
+
# If it's unable to detect the mime type, it returns +application/octet-stream+
|
8
|
+
# as a fallback.
|
9
|
+
#
|
10
|
+
# @param file_path [String] A file path in the local filesystem.
|
11
|
+
# @return [String] The file mime type.
|
6
12
|
def mime_type(file_path)
|
7
13
|
fallback = MIME::Types.type_for(file_path).first&.content_type || "application/octet-stream"
|
8
14
|
Rack::Mime.mime_type(File.extname(file_path), fallback)
|
@@ -3,34 +3,105 @@
|
|
3
3
|
require "aws-sdk-s3"
|
4
4
|
|
5
5
|
module WebpackerUploader
|
6
|
+
# Namespace for the upload providers.
|
6
7
|
module Providers
|
8
|
+
# AWS provider uploads files to AWS S3. It uses the +aws-sdk-s3+ gem.
|
7
9
|
class Aws
|
8
|
-
|
9
|
-
|
10
|
-
attr_reader :bucket
|
10
|
+
# AWS provider error class. Raised when the provided AWS client credentials options are wrong.
|
11
|
+
class CredentialsError < StandardError; end
|
11
12
|
|
13
|
+
# @param [Hash] options
|
14
|
+
# * :region (String) The S3 region name.
|
15
|
+
# * :bucket (String) The S3 bucket name.
|
16
|
+
# * :credentials (Hash) credential options for the AWS provider:
|
17
|
+
# * :profile_name (String) use a named profile configured in ~/.aws/credentials
|
18
|
+
# * :instance_profile (Boolean) use an instance profile from an EC2
|
19
|
+
# * :access_key_id (String) the AWS credentials access id.
|
20
|
+
# * :secret_access_key (String) the AWS credentials secret access key.
|
21
|
+
#
|
22
|
+
# @note Any unknown options will be passed directly to the +Aws::S3::Client+ class
|
23
|
+
# during initialization.
|
24
|
+
#
|
25
|
+
# @raise [CredentialsError] if the credential options Hash is not correct.
|
26
|
+
#
|
27
|
+
# @example Initialize using a named profile:
|
28
|
+
#
|
29
|
+
# provider_options = {
|
30
|
+
# credentials: { profile_name: "staging" },
|
31
|
+
# region: "eu-central-1",
|
32
|
+
# bucket: "application-assets-20200929124523451600000001"
|
33
|
+
# }
|
34
|
+
# provider = WebpackerUploader::Providers::Aws.new(provider_options)
|
35
|
+
#
|
36
|
+
# @example Initialize using IAM keys
|
37
|
+
#
|
38
|
+
# provider_options = {
|
39
|
+
# credentials: { access_key_id: "KEY_ID", secret_access_key: "ACCESS_KEY" },
|
40
|
+
# region: "eu-central-1",
|
41
|
+
# bucket: "application-assets-20200929124523451600000001"
|
42
|
+
# }
|
43
|
+
# provider = WebpackerUploader::Providers::Aws.new(provider_options)
|
44
|
+
#
|
45
|
+
# @example Initialize using an EC2 instance profile
|
46
|
+
#
|
47
|
+
# provider_options = {
|
48
|
+
# credentials: { instance_profile: true },
|
49
|
+
# region: "eu-central-1",
|
50
|
+
# bucket: "application-assets-20200929124523451600000001"
|
51
|
+
# }
|
52
|
+
# provider = WebpackerUploader::Providers::Aws.new(provider_options)
|
12
53
|
def initialize(options)
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@
|
54
|
+
@region = options.delete(:region)
|
55
|
+
@bucket_name = options.delete(:bucket)
|
56
|
+
@credentials = credentials(options.delete(:credentials))
|
57
|
+
@aws_options = options
|
58
|
+
@resource = ::Aws::S3::Resource.new(client: client)
|
16
59
|
end
|
17
60
|
|
61
|
+
# Uploads a file to AWS S3.
|
62
|
+
#
|
63
|
+
# @param object_key [String] Is the remote path name for the S3 object.
|
64
|
+
# @param file [Pathname] Path of the local file.
|
65
|
+
# @param content_type [String] The content type that will be set to the S3 object.
|
66
|
+
# @return [void]
|
18
67
|
def upload!(object_key, file, content_type = "")
|
19
|
-
object = @bucket.object(object_key)
|
68
|
+
object = @resource.bucket(@bucket_name).object(object_key)
|
20
69
|
object.upload_file(file, content_type: content_type)
|
21
70
|
end
|
22
71
|
|
23
72
|
private
|
24
|
-
|
25
73
|
def credentials(options)
|
26
|
-
if options
|
27
|
-
|
74
|
+
if options.key?(:profile_name)
|
75
|
+
{ profile: options[:profile_name] }
|
28
76
|
elsif options.key?(:instance_profile) && options[:instance_profile]
|
29
77
|
::Aws::InstanceProfileCredentials.new
|
30
|
-
|
78
|
+
elsif options.key?(:access_key_id) && options.key?(:secret_access_key)
|
31
79
|
::Aws::Credentials.new(options[:access_key_id], options[:secret_access_key])
|
80
|
+
else
|
81
|
+
raise CredentialsError, "Wrong AWS provider credentials options."
|
32
82
|
end
|
33
83
|
end
|
84
|
+
|
85
|
+
def profile?
|
86
|
+
@credentials.is_a?(Hash) && @credentials.key?(:profile)
|
87
|
+
end
|
88
|
+
|
89
|
+
def credentials_object?
|
90
|
+
!profile?
|
91
|
+
end
|
92
|
+
|
93
|
+
def client
|
94
|
+
::Aws::S3::Client.new(client_options)
|
95
|
+
end
|
96
|
+
|
97
|
+
def client_options
|
98
|
+
opts = {}
|
99
|
+
opts.merge!(@aws_options)
|
100
|
+
opts[:region] = @region
|
101
|
+
opts.merge!(@credentials) if profile?
|
102
|
+
opts[:credentials] = @credentials if credentials_object?
|
103
|
+
opts
|
104
|
+
end
|
34
105
|
end
|
35
106
|
end
|
36
107
|
end
|