ruby-on-railsrubyasset-pipelinesprockets

Rails 5.1.7: Sprockets::Rails::Helper::AssetNotFound


we have a rails 5.1.7 application and all is working fine in the dev environment. but in staging environment or when running server using rails s -e staging, we see this error "Sprockets::Rails::Helper::AssetNotFound".

example, The asset "fontawesome-free-5.1.1-web/css/all.min.css" is not present in the asset pipeline

the folder fontawesome-free-5.1.1-web is located under public/styleheets.

I have tried moving the folder to vendor/stylesheets or assets/stylesheets but I see the same error.

below is my environments/staging.rb

AppName::Application.configure do
  config.cache_classes = true

  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = true
  config.action_view.cache_template_loading = true
  config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

  config.log_level = :debug
  memcached_config = YAML.load_file(Rails.root.join('config/memcached.yml'))
  memcached_hosts = memcached_config['defaults']['servers']
  config.cache_store = :mem_cache_store, *memcached_hosts


  config.action_mailer.delivery_method = :smtp
  config.action_mailer.perform_deliveries = true
  config.action_mailer.raise_delivery_errors = true
  config.i18n.fallbacks = true

  config.active_support.deprecation = :notify

  config.eager_load = true
  config.assets.js_compressor = Uglifier.new(harmony: true)
  config.assets.compile = false
  config.assets.raise_runtime_errors = true
  config.assets.debug = true
end

Any help on what could be going wrong here would be really great, Thanks.


Solution

  • I'm assuming you're doing this:

    <%= stylesheet_link_tag 'fontawesome/all.min.css' %>  
    
    vendor/
    └── assets/                  # <= this one is important
        └── stylesheets/
            └── fontawesome/
                └── all.min.css
    

    Directories in vendor/assets and app/assets are automatically added to assets paths. These are the paths sprockets is using to find assets:

    >> puts Rails.application.config.assets.paths
    ...
    /myapp/app/assets/stylesheets
    /myapp/vendor/assets/stylesheets         # <=
    /usr/local/bundle/gems/coffee-rails-4.2.2/lib/assets/javascripts
    /usr/local/bundle/gems/actioncable-5.2.8.1/lib/assets/compiled
    ...
    

    You have config.assets.compile = false, that means sprockets is done looking around in assets paths and compiling on the fly. It's now expecting precompiled assets in public/assets:

    # assuming sprockets is still serving files (but not compiling them anymore)
    >> Rails.application.config.public_file_server.enabled
    => true
    >> Rails.application.config.assets.compile
    => false
    
    >> helper.asset_path("fontawesome/all.min.css")
    Traceback (most recent call last):
            1: from (irb):5
    Sprockets::Rails::Helper::AssetNotFound (The asset "fontawesome/all.min.css" is not present in the asset pipeline.)
    

    You have to bin/rails assets:precompile:

    # config/initializers/assets.rb
    
    # but make it precompilable first
    Rails.application.config.assets.precompile += ["fontawesome/all.min.css"]
    
    $ bin/rails assets:precompile
    
    I, [2023-09-10T10:04:24.038220 #1]  INFO -- : Writing /myapp/public/assets/fontawesome/all.min-1f62e82d4d0217052a8d48596d631f5c58ee5149386c719419046118e4fa43f3.css
    ...
    

    Should work now:

    >> helper.asset_path("fontawesome/all.min.css")
    => "/assets/fontawesome/all.min-1f62e82d4d0217052a8d48596d631f5c58ee5149386c719419046118e4fa43f3.css"
    
    # check if it is being served
    >> require "open-uri"
    >> URI.open(helper.asset_path("fontawesome/all.min.css", host: "http://localhost:3000")).read
    => "i am font\n"
    

    https://guides.rubyonrails.org/v5.0/configuring.html#configuring-assets


    Update

    To handle files in bulk, there are two options. Add a proc that would dynamically match assets for precompilation:

    # config/initializers/assets.rb
    
    VENDOR_ASSETS = lambda do |logical_path, filename|
      filename.start_with?(::Rails.root.join("vendor/assets/").to_s) &&
        [".css", ".js"].include?(File.extname(logical_path))
    end
    
    Rails.application.config.assets.precompile += [VENDOR_ASSETS]
    

    https://github.com/rails/sprockets-rails/blob/v3.4.2/lib/sprockets/railtie.rb#L88

    A better option is to use manifest.js, which is the default in later rails versions:

    # config/initializers/assets.rb
    
    # don't need this in sprockets v4
    Rails.application.config.assets.precompile += ["manifest.js"]
    
    // app/assets/config/manifest.js
    
    //= link_tree ../images
    //= link_directory ../javascripts .js
    //= link_directory ../stylesheets .css
    //
    // link everything to be precompiled
    //= link_tree ../../../vendor/assets/stylesheets .css
    //= link_tree ../../../vendor/assets/javascripts .js
    //
    // or one file at a time 
    //= link fontawesome/all.min.css
    

    https://github.com/rails/sprockets#directives


    Also, when you precompile assets, that means you want load them directly through a url. If you just use //= require directives, then required files don't need to be precompiled as they are merged into the file that gets precompiled. You just get one big application.css file:

    /* app/assets/stylesheets/application.css */
    
    /*
     *= require_tree .
     *= require_self
     *= require fontawesome/all.min
     */
    

    If you're precompiling locally to test things out, don't forget to bin/rails assets:clobber after you're done, you don't want precompiled assets in development.