Skip to content

Commit

Permalink
Merge pull request #530 from rails/schneems/double-render
Browse files Browse the repository at this point in the history
Raise error when two assets produce same path
  • Loading branch information
schneems committed Mar 12, 2018
2 parents efbfa49 + b32b5ec commit 983a791
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 3 deletions.
22 changes: 21 additions & 1 deletion lib/sprockets/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@
require 'sprockets/uri_tar'

module Sprockets

class DoubleLinkError < Sprockets::Error
def initialize(parent_filename:, logical_path:, last_filename:, filename:)
message = String.new
message << "Multiple files with the same output path cannot be linked (#{logical_path.inspect})\n"
message << "In #{parent_filename.inspect} these files were linked:\n"
message << " - #{last_filename}\n"
message << " - #{filename}\n"
super(message)
end
end

# `Base` class for `Environment` and `CachedEnvironment`.
class Base
include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils, SourceMapUtils
Expand Down Expand Up @@ -73,14 +85,22 @@ def find_asset(*args)
def find_all_linked_assets(*args)
return to_enum(__method__, *args) unless block_given?

asset = find_asset(*args)
parent_asset = asset = find_asset(*args)
return unless asset

yield asset
stack = asset.links.to_a
linked_paths = {}

while uri = stack.shift
yield asset = load(uri)
if linked_paths[asset.logical_path]
raise DoubleLinkError.new(parent_filename: parent_asset.filename,
last_filename: linked_paths[asset.logical_path],
logical_path: asset.logical_path,
filename: asset.filename )
end
linked_paths[asset.logical_path] = asset.filename
stack = asset.links.to_a + stack
end

Expand Down
5 changes: 5 additions & 0 deletions lib/sprockets/manifest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,12 @@ def compile(*args)
filenames = []
concurrent_exporters = []

assets_to_export = Concurrent::Array.new
find(*args) do |asset|
assets_to_export << asset
end

assets_to_export.each do |asset|
mtime = Time.now.iso8601
files[asset.digest_path] = {
'logical_path' => asset.logical_path,
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/double/link_directory_manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//= link_directory ./stylesheets .css
1 change: 1 addition & 0 deletions test/fixtures/double/stylesheets/a.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
body {color: red;}
1 change: 1 addition & 0 deletions test/fixtures/double/stylesheets/a.css.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
body {color: blue;}
13 changes: 12 additions & 1 deletion test/test_manifest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@ def setup

def teardown
FileUtils.remove_entry_secure(@dir) if File.exist?(@dir)
assert Dir["#{@dir}/*"].empty?
assert Dir["#{@dir}/*"].empty?, "Expected #{Dir["#{@dir}/*"]} to be empty but it was not"
end

test 'double rendering with link_directory raises an error' do
config_manifest = 'link_directory_manifest.js'

@env.append_path(fixture_path('double'))
output_manifest_json = File.join(@dir, "manifest.json")
manifest = Sprockets::Manifest.new(@env, @dir, output_manifest_json)
assert_raises(Sprockets::DoubleLinkError) do
manifest.compile(config_manifest)
end
end

test "specify full manifest filename" do
Expand Down
3 changes: 2 additions & 1 deletion test/test_path_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def test_entries
"context",
"default",
"directives",
"double",
"encoding",
"errors",
"index-assets",
Expand All @@ -44,7 +45,7 @@ def test_entries
"source-maps",
"symlink"
], entries(File.expand_path("../fixtures", __FILE__))

[ ['a', 'b'], ['a', 'b', '.', '..'] ].each do |dir_contents|
Dir.stub :entries, dir_contents do
assert_equal ['a', 'b'], entries(Dir.tmpdir)
Expand Down

4 comments on commit 983a791

@Fudoshiki
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app/assets/config/manifest.js

//= link_tree ../images
//= link_tree ../stylesheets/ .css
//= link_directory ../javascripts .js

app/assets/images

app.svg

app/assets/stylesheets/app.sass

.app
  background-image: image-url(app)
$ rails s
=> Booting Puma
=> Rails 6.0.0.alpha application starting in development 
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 3.11.3 (ruby 2.6.0-p-1), codename: Love Song
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
Started GET "/" for 127.0.0.1 at 2018-03-13 15:50:23 +0500
MONGODB | Topology type 'unknown' initializing.
MONGODB | Server /tmp/mongodb-27017.sock initializing.
MONGODB | Topology type 'unknown' changed to type 'single'.
MONGODB | Server description for /tmp/mongodb-27017.sock changed from 'unknown' to 'standalone'.
MONGODB | There was a change in the members of the 'single' topology.
MONGODB | /tmp/mongodb-27017.sock | honshu_development.find | STARTED | {"find"=>"users", "filter"=>{"d_at"=>nil, "_id"=>BSON::ObjectId('5aa26f53263ecf3fda22c1a2')}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}}
MONGODB | /tmp/mongodb-27017.sock | honshu_development.find | SUCCEEDED | 0.014193s
MONGODB | /tmp/mongodb-27017.sock | honshu_development.update | STARTED | {"update"=>"users", "ordered"=>true, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}, "updates"=>[{"q"=>{"_id"=>BSON::ObjectId('5aa26f53263ecf3fda22c1a2')}, "u"=>{"$set"=>{"last_sign_in_at"=>2018-03-12 18:54:04 UTC...
MONGODB | /tmp/mongodb-27017.sock | honshu_development.update | SUCCEEDED | 0.030688s
Processing by UsersController#index as HTML
MONGODB | /tmp/mongodb-27017.sock | honshu_development.listCollections | STARTED | {"listCollections"=>1, "cursor"=>{}, "filter"=>{"name"=>{"$not"=>/system\.|\$/}}, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}}
MONGODB | /tmp/mongodb-27017.sock | honshu_development.listCollections | SUCCEEDED | 0.024314s
MONGODB | /tmp/mongodb-27017.sock | honshu_development.count | STARTED | {"count"=>"trackers", "query"=>{"d_at"=>nil}, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}}
MONGODB | /tmp/mongodb-27017.sock | honshu_development.count | SUCCEEDED | 0.00074s
MONGODB | /tmp/mongodb-27017.sock | honshu_development.listCollections | STARTED | {"listCollections"=>1, "cursor"=>{}, "filter"=>{"name"=>{"$not"=>/system\.|\$/}}, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}}
MONGODB | /tmp/mongodb-27017.sock | honshu_development.listCollections | SUCCEEDED | 0.001078s
MONGODB | /tmp/mongodb-27017.sock | honshu_development.listCollections | STARTED | {"listCollections"=>1, "cursor"=>{}, "filter"=>{"name"=>{"$not"=>/system\.|\$/}}, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}}
MONGODB | /tmp/mongodb-27017.sock | honshu_development.listCollections | SUCCEEDED | 0.000797s
  Rendering users/index.slim within layouts/application
  Rendered users/index.slim within layouts/application (17.0ms)
Completed 401 Unauthorized in 3164ms


  
ActionView::Template::Error (Multiple files with the same output path cannot be linked ("app.svg")
In "/Users/fudoshiki/Repository/Fudoshiki/Honshu/app/assets/config/manifest.js" these files were linked:
  - /Users/fudoshiki/Repository/Fudoshiki/Honshu/app/assets/images/app.svg
  - /Users/fudoshiki/Repository/Fudoshiki/Honshu/app/assets/images/app.svg
):
Started GET "/favicon.ico" for 127.0.0.1 at 2018-03-13 15:50:26 +0500
  
ActionController::RoutingError (No route matches [GET] "/favicon.ico"):
  
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:65:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/web-console-c9be53edca48/lib/web_console/middleware.rb:137:in `call_app'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/web-console-c9be53edca48/lib/web_console/middleware.rb:30:in `block in call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/web-console-c9be53edca48/lib/web_console/middleware.rb:20:in `catch'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/web-console-c9be53edca48/lib/web_console/middleware.rb:20:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/railties/lib/rails/rack/logger.rb:38:in `call_app'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/railties/lib/rails/rack/logger.rb:26:in `block in call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/activesupport/lib/active_support/tagged_logging.rb:71:in `block in tagged'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/activesupport/lib/active_support/tagged_logging.rb:28:in `tagged'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/activesupport/lib/active_support/tagged_logging.rb:71:in `tagged'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/railties/lib/rails/rack/logger.rb:26:in `call'
sprockets-rails (3.2.1) lib/sprockets/rails/quiet_assets.rb:13:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/actionpack/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/actionpack/lib/action_dispatch/middleware/request_id.rb:27:in `call'
rack (2.0.4) lib/rack/method_override.rb:22:in `call'
rack (2.0.4) lib/rack/runtime.rb:22:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/actionpack/lib/action_dispatch/middleware/executor.rb:14:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/actionpack/lib/action_dispatch/middleware/static.rb:127:in `call'
rack (2.0.4) lib/rack/sendfile.rb:111:in `call'
/Users/fudoshiki/.rvm/gems/ruby-head@Honshu/bundler/gems/rails-3c9edcffb60b/railties/lib/rails/engine.rb:524:in `call'
puma (3.11.3) lib/puma/configuration.rb:225:in `call'
puma (3.11.3) lib/puma/server.rb:624:in `handle_request'
puma (3.11.3) lib/puma/server.rb:438:in `process_client'
puma (3.11.3) lib/puma/server.rb:302:in `block in run'
puma (3.11.3) lib/puma/thread_pool.rb:120:in `block in spawn_thread'
Started GET "/" for 127.0.0.1 at 2018-03-13 15:57:42 +0500
MONGODB | /tmp/mongodb-27017.sock | honshu_development.find | STARTED | {"find"=>"users", "filter"=>{"d_at"=>nil, "_id"=>BSON::ObjectId('5aa26f53263ecf3fda22c1a2')}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}}
MONGODB | /tmp/mongodb-27017.sock | honshu_development.find | SUCCEEDED | 0.000634s
MONGODB | /tmp/mongodb-27017.sock | honshu_development.update | STARTED | {"update"=>"users", "ordered"=>true, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}, "updates"=>[{"q"=>{"_id"=>BSON::ObjectId('5aa26f53263ecf3fda22c1a2')}, "u"=>{"$set"=>{"last_sign_in_at"=>2018-03-13 10:50:23 UTC...
MONGODB | /tmp/mongodb-27017.sock | honshu_development.update | SUCCEEDED | 0.00052s
Processing by UsersController#index as HTML
MONGODB | /tmp/mongodb-27017.sock | honshu_development.count | STARTED | {"count"=>"trackers", "query"=>{"d_at"=>nil}, "lsid"=>{"id"=><BSON::Binary:0x70295343045860 type=uuid data=0xb11be4252eb14511...>}}
MONGODB | /tmp/mongodb-27017.sock | honshu_development.count | SUCCEEDED | 0.000455s
  Rendering users/index.slim within layouts/application
  Rendered users/index.slim within layouts/application (5.2ms)
Completed 401 Unauthorized in 59ms


  
ActionView::Template::Error (Multiple files with the same output path cannot be linked ("app.svg")
In "/Users/fudoshiki/Repository/Fudoshiki/Honshu/app/assets/config/manifest.js" these files were linked:
  - /Users/fudoshiki/Repository/Fudoshiki/Honshu/app/assets/images/app.svg
  - /Users/fudoshiki/Repository/Fudoshiki/Honshu/app/assets/images/app.svg
):

@schneems
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the report! I was able to reproduce. I think the fix is going to be checking if both source files are identical and not raising in that case. There might be a performance penalty here for linking the same file twice. It would be nice to investigate that and skip duplicate work (if it's not already being skipped).

@Fudoshiki
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works now

@schneems
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reverted the raise behavior, i'm working on adding it back in with a fix on #549

Please sign in to comment.