61 votes

La vitesse d'actifs:précompiler avec Rails 3.1/3.2 Capistrano de déploiement

Mes déploiements sont lents, ils prennent au moins 3 minutes. La lenteur de Capistrano au cours de déployer est actif:précompiler. Cela prend probablement 99% du total de l'heure du déploiement. Comment puis-je accélérer les choses? Dois-je précompiler mon actif sur ma machine locale et les ajouter à mon repo git?

Edit: Ajout d' config.assets.initialize_on_precompile = false de ma demande.rb fichier a chuté de la précompiler temps avec la moitié d'une minute, mais il est encore lent.

84voto

tommasop Points 5692

L'idée est que si vous ne changez pas vos biens que vous n'avez pas besoin de recompiler à chaque fois:

C'est la solution que Ben Curtis proposer pour un déploiement avec git:

 namespace :deploy do
      namespace :assets do
        task :precompile, :roles => :web, :except => { :no_release => true } do
          from = source.next_revision(current_revision)
          if releases.length <= 1 || capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
            run %Q{cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile}
          else
            logger.info "Skipping asset pre-compilation because there were no asset changes"
          end
      end
    end
  end

Voici une autre approche basée sur l'âge des actifs (https://gist.github.com/2784462) :

set :max_asset_age, 2 ## Set asset age in minutes to test modified date against.

after "deploy:finalize_update", "deploy:assets:determine_modified_assets", "deploy:assets:conditionally_precompile"

namespace :deploy do
  namespace :assets do

    desc "Figure out modified assets."
    task :determine_modified_assets, :roles => assets_role, :except => { :no_release => true } do
      set :updated_assets, capture("find #{latest_release}/app/assets -type d -name .git -prune -o -mmin -#{max_asset_age} -type f -print", :except => { :no_release => true }).split
    end

    desc "Remove callback for asset precompiling unless assets were updated in most recent git commit."
    task :conditionally_precompile, :roles => assets_role, :except => { :no_release => true } do
      if(updated_assets.empty?)
        callback = callbacks[:after].find{|c| c.source == "deploy:assets:precompile" }
        callbacks[:after].delete(callback)
        logger.info("Skipping asset precompiling, no updated assets.")
      else
        logger.info("#{updated_assets.length} updated assets. Will precompile.")
      end
    end

  end
end

Si vous préférez précompiler vos actifs localement, vous pouvez utiliser cette tâche:

namespace :deploy do
  namespace :assets do
    desc 'Run the precompile task locally and rsync with shared'
    task :precompile, :roles => :web, :except => { :no_release => true } do
      from = source.next_revision(current_revision)
      if releases.length <= 1 || capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
        %x{bundle exec rake assets:precompile}
        %x{rsync --recursive --times --rsh=ssh --compress --human-readable --progress public/assets #{user}@#{host}:#{shared_path}}
        %x{bundle exec rake assets:clean}
      else
        logger.info 'Skipping asset pre-compilation because there were no asset changes'
      end
    end
  end
end 

Une autre approche intéressante, peut être que de l'aide d'un git crochet. Par exemple, vous pouvez ajouter ce code à l' .git/hooks/pre-commit qui vérifie si il y a des différences dans les actifs des fichiers et finalement précompilation et de les ajouter à l'actuel commettre.

#!/bin/bash

# source rvm and .rvmrc if present
[ -s "$HOME/.rvm/scripts/rvm" ] && . "$HOME/.rvm/scripts/rvm"
[ -s "$PWD/.rvmrc" ] && . "$PWD/.rvmrc"

# precompile assets if any have been updated
if git diff-index --name-only HEAD | egrep '^app/assets' >/dev/null ; then
  echo 'Precompiling assets...'
  rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets
  git add public/assets/*
fi

Si vous décidez d'utiliser cette approche, vous aurez probablement besoin de changer votre config/environments/development.rb ajout de:

config.assets.prefix = '/assets_dev'

De sorte que, bien qu'en développement, vous ne servez pas les précompilés actifs.

46voto

nathan.f77 Points 4941

Je viens d'écrire un bijou à résoudre ce problème à l'intérieur des Rails, appelé turbo-pignons-rails3. Il accélère votre assets:precompile en recompilant seulement les fichiers modifiés, et seulement de compiler une fois pour générer l'ensemble des actifs. Il fonctionne hors de la boîte pour Capistrano, depuis votre répertoire actif est partagé entre les versions.

C'est beaucoup plus à l'épreuve des balles que les solutions qui utilisent git log, depuis mon patch analyse les sources de vos actifs, même s'ils viennent d'un bijou. Par exemple, si vous mettez à jour jquery-rails, un changement va être détectés application.js, et seulement application.js sera recompilé.

A noter que je suis aussi essayer d'obtenir ce patch fusionnées dans les Rails 4.0.0, et, éventuellement, des Rails 3.2.9 (voir https://github.com/rails/sprockets-rails/pull/21). Mais pour l'instant, ce serait génial si vous pouviez m'aider à tester les turbo-pignons-rails3 gem, et laissez-moi savoir si vous avez des problèmes.

4voto

user668478 Points 91

tommasop la solution ne fonctionne pas lorsqu'il est activé en cache-copie, ma version modifiée:

task :precompile, :roles => :web, :except => { :no_release => true } do
  from = source.next_revision(current_revision)
  if capture("cd #{shared_path}/cached-copy && git diff #{from}.. --stat | grep 'app/assets' | wc -l").to_i > 0
    run %Q{cd #{latest_release} && #{rake} RAILS_ENV=#{Rubber.env} #{asset_env} assets:precompile:primary}
  else
    logger.info "Skipping asset pre-compilation because there were no asset changes"
  end
end

3voto

Vimal Points 391

Vous pouvez enregistrer votre serveur effort pour la pré-compilation des actifs en procédant de la même (pré-compilation des actifs) sur votre système local. Et il suffit de le déplacer vers le serveur.

from = source.next_revision(current_revision) rescue nil      
if from.nil? || capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
  ln_assets    
  run_locally "rake assets:precompile"
  run_locally "cd public; tar -zcvf assets.tar.gz assets"
  top.upload "public/assets.tar.gz", "#{shared_path}", :via => :scp
  run "cd #{shared_path}; tar -zxvf assets.tar.gz"
  run_locally "rm public/assets.tar.gz"    
else
  run "ln -s #{shared_path}/assets #{latest_release}/public/assets"
  logger.info "Skipping asset pre-compilation because there were no asset changes"
end

2voto

pinguin666 Points 316

La solution que Ben Curtis proposer ne fonctionne pas pour moi, parce que je ne copie pas le .git dossier lors du déploiement (lent et inutile) :

set :scm, :git
set :deploy_via, :remote_cache
set :copy_exclude, ['.git']

Je suis en utilisant le fragment de code suivant, sans en load 'deploy/assets'

task :assets, :roles => :app do
  run <<-EOF
    cd #{release_path} &&
    rm -rf public/assets &&
    mkdir -p #{shared_path}/assets &&
    ln -s #{shared_path}/assets public/assets &&
    export FROM=`[ -f #{current_path}/REVISION ] && (cat #{current_path}/REVISION | perl -pe 's/$/../')` &&
    export TO=`cat #{release_path}/REVISION` &&
    echo ${FROM}${TO} &&
    cd #{shared_path}/cached-copy &&
    git log ${FROM}${TO} -- app/assets vendor/assets | wc -l | egrep '^0$' ||
    (
      echo "Recompiling assets" &&
      cd #{release_path} &&
      source .rvmrc &&
      RAILS_ENV=production bundle exec rake assets:precompile --trace
    )
  EOF
end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X