about 10 years ago

TL;DR; 我不確定有沒有人對這個主題有興趣。不過我承認我是蠻神經病才這樣幹的...

[警告] 這樣作有危險。而且我並不會回答讀者為何你的專案跑不起來的問題。(這是一個超級拼裝車的架構)

起因

我們公司內部的幾個 Project,因為現在一開始都會作 Landing Page,所以 Asset 略顯壅腫。在以往的 Deploy 流程中,我們都是使用 Capistrano 這套工具,策略是到機器上再 compile asset,然後重起整個 project。

這樣的想法是不要卡住 developer 端 deploy 的流暢度。開個新窗放著讓它跑就可以。

只是在機器上 project 越大,compile 越跑越慢。我想我已經是非常會 tune aseet pipeline 的開發者了,但是還是對這樣的情形越來越沒輒。

所以我決定改變策略,改成在本機跑,再 Deploy,測試效率,於是找到這套 Gem https://github.com/spagalloco/capistrano-local-precompile。可以無痛掛上去在本機編完,rsync,再清掉 asset。

Rails 4

在 Rails4 的 project 真的非常有效。因為我的開發機非常的暴力,加上 Rails 4 Sprocket 底層又大改效能提升不少。所以在本機編完效率再 deploy 效率是在遠端環境的非常多倍。

Rails 3

在本機上編和在遠端編速度其實差不多 :/

Pros & Cons

在本機端編,好處是本機機器暴力可以編的很快。但是在遠端編,因為遠端有程式版本可比對,可以做到如果 Asset 不變動,就不重編

所以這個 Gem 在 Rails 3 的 project 上就顯得十分雞肋。因為如果改到本地端編,若無法十分迅速的編譯,反而會比原先更痛苦...

瘋狂的 Idea :把 Sprocket2 拆到 Rails 3 project 上跑...

Rails 4 用的是 Sprocket2。我已經見識到 Rails 4 編 Asset 是多麼變態了。要是可以拆到 Rails 3 上跑應該會很過癮。

不過這是一個很危險的想法,因為 Asset Pipeline 出了名的...嗯...正常人不會想要去解裡面的問題。(這是一個很長的故事)

當然,我不是第一個有這樣想法的人。很快的我就在網路上找到了這篇 :
https://discussion.heroku.com/t/using-the-rails-4-asset-pipeline-in-rails-3-apps-for-faster-deploys/205

但是這個解法是沒有用的。因為 Sprocket Team 雖然釋出了 backport。但是,在跟 saas-rails 一起跑得時候,不會動....( sass-rails 3.2.6 使用的 resolver,跟 sprocket2 放在一起的時候,resolver 會出錯...)

而這並不是一個 bug。也不能責怪 sass-rails team。(因為...另外一個故事)

總之,要解決這個問題只能靠自己。

解法

我先寫解法,再講過程

焊接方法:

1.(更改 Gemfile 裡面的整組 Asset Gem)

Gemfile
group :assets do
  gem 'sprockets', '2.2.2.backport2'
  gem 'sprockets-rails', :require => 'sprockets/railtie', :git => "git@github.com:logdown/sprockets-rails.git"

  gem 'sass-rails',   '~> 4.0.1', :git => "git@github.com:logdown/sass-rails.git"
  gem "compass-rails", "~> 1.1.2", :git => "git@github.com:logdown/compass-rails.git"
 
  gem 'coffee-rails', '~> 3.2.1'

  gem 'uglifier', '>= 1.0.3'
end

2. 變更 config/enviorments/production.rb

config/enviorments/production.rb
  # Disable Rails's static asset server (Apache or nginx will already do this)

  config.serve_static_assets = true

  # Compress JavaScripts and CSS

  config.assets.compress = true


  # Generate digests for assets URLs

  config.assets.digest = true
  
  config.assets.js_compressor = :uglifier
  config.assets.css_compressor = :sass


3. 更改 config/deploy.rb

請先掛上 capistrano-local-precompile 這個 Gem 在 deploy 流程裡。
再加掛一個這樣的指令 override 掉 precompile 的參數:

config/deploy.rb
set :precompile_cmd, "bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile"

解說

Gemfile
  gem 'sprockets', '2.2.2.backport2'
  gem 'sprockets-rails', :require => 'sprockets/railtie', :git => "git@github.com:logdown/sprockets-rails.git"

我對這件事情的假設是:sprocket / sass-rails / compass 的三方架構是獨立在 Rails 外的。所以 asset 裡的架構再怎樣變應該都跟 Rails 版本無關。但鎖版本的原因應該是 railtie 版本架構的原因。(長期研究過這三方的架構與恩怨情仇..)

所以理當硬焊可以 work,而不用去改接口 API(因為 Rails 4.0.0 穩定了)。但問題就在於要改哪些參數..

1. 改 sprocket-rails,讓它支援 sprocket 2.2 +

https://github.com/logdown/sprockets-rails/commit/7f6d8d7241ee8f7cff151461d8afe5bd64c6aa92

sprockets-railsgem 'sprockets-rails', '2.0.0.backport1' 這個版本是爛的。但我發現 master 是好的。不過因為 sprocket-rails 只吃 sporocket 2.8 以上版本。所以要欺騙他去吃 sprockets 2.2 的版本。

2. 使用最新版的 sass-rails,但是跑在 rails 3 版本下

https://github.com/logdown/sass-rails/commit/8e23667f86e3b3669835957bb6604ab06eb9256b

Gemfile
 gem 'sass-rails',   '~> 4.0.1', :git => "git@github.com:logdown/sass-rails.git"

sprockets 改動了架構,所以才會在 3.2.6 的 sass-rails 有問題。我「猜」最新版的 sass-rails 應該是沒有問題的,所以硬是把 sass-rails 4.0.1 掛在 Rails 3 下面跑。

3. 更改 compass-rails 的 strategy,讓它誤以為自己跑在 Rails 4 下。

https://github.com/logdown/compass-rails/commit/67779b4f388ff4d56f7683c18c4e24d2ad2ecf78

在上一步的 hack,成功的讓 project 跑起來了,但是新的問題又產生了:只要牽涉到 compass mixin 的地方都會讀不到。根據以前多年與 compass mounting 問題纏鬥的經驗。compass 作者為了對付不負責任的 sprocket 行為,採取的策略是每一個版本寫一個 Resolver Strategy 去應對(因為用 if/else condition 顯然是閃不掉)。

所以我「猜」關鍵點應該也在這,就看要怎麼改。不過運氣好,我猜對了而且也只要改一行就能動。

4. 蓋掉 precompile 參數

config/deploy.rb
set :precompile_cmd, "bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile"

本來到了解開 compass-rails 這一步之後,我認為已經完成了。沒想到 deploy 到 production 才是另一個挑戰的開始...

真正的問題在於 Rails 3 與 Rails 4 的編譯行為與機制不同。

在 Rails 3 會有個預設的 asset group

  group :asset do 
    ....
  end

但 Rails 4 取消掉了。

在 Rails 3 設計的原始原因是要在 production 用最小的 gemset 可以編 asset。但是在 Rails4 被拿掉了。任何環境都可以鞭。但問題這台拼裝車要用哪個策略呢?

而在 Rails 4 + Sprocket 2。直接在 local 執行 "bundle exec rake assets:precompile" 是沒問題的,會編出正確的 asset。但是在 Rails 3 + Sprocket 2 是不行的。這個指令只會編出一個「串接」在一起的 asset,無壓縮,也不會針對 webfonts 的 import 特別處理(放在第一行)。

deploy 到遠端就悲劇了。(結果是會出現一個有 debug 訊息、無壓縮且隨意亂合成的 css )

所以要加上這一行...

5. 編輯 config/production.rb 的參數

config/enviorments/production.rb
  # Disable Rails's static asset server (Apache or nginx will already do this)

  config.serve_static_assets = true

  # Compress JavaScripts and CSS

  config.assets.compress = true

  # Generate digests for assets URLs

  config.assets.digest = true
  
  config.assets.js_compressor = :uglifier
  config.assets.css_compressor = :sass

參數要調過。

到這一步就可以動了。

後續注意事項

https://github.com/rails/sprockets-rails#changes-from-rails-3x

Sprockets 2 架構有不少更改的地方。deploy 上去有一些以前的 import 和 url 的寫法,可能會失效或者 decrapted 掉,所以要自己 debug 閃掉這些地方。

再次強調,這樣焊接是有風險的事。請自行斟酌風險...

特別是這樣的 debug 要 deploy 測試非常多遍。

← MySQL with utf8mb4 再來 3 招實用Asset Pipeline 加速術 →
 
comments powered by Disqus