guard / guard-livereload

Guard::LiveReload automatically reload your browser when 'view' files are modified.
https://rubygems.org/gems/guard-livereload
MIT License
2.11k stars 147 forks source link

Compass-generated CSS files don't trigger reload #105

Closed deisner closed 9 years ago

deisner commented 10 years ago

I'm using

guard (2.6.0)
guard-compass (1.1.0)
guard-livereload (2.1.2)

When I make a change to a sass file (.scss), guard detects the change, runs compass via the guard-compass plugin-in, and updates the.css files. However, those CSS files aren't causing Chrome to actually reload the page. If I manually touch the .css file, the reload does happen.

After some digging around, I think I know what's going on. Commit e20657 changed guard-livereload so that it uses run_on_modifications rather than run_on_changes. When compass is run, the original .css file is apparently deleted and then regenerated. I can tell this is so because ls -i shows the .css file has a new inode. You can also see it in the guard debugging output -- note that guard is trying to call the run_on_changes hook:

13:18:17 - DEBUG - Stop interactor
13:18:17 - DEBUG - Trying to run Guard::Compass#run_on_modifications with ["sass/variables/_colors.scss", "sass/variables/_colors.scss"]
13:18:17 - DEBUG - Trying to run Guard::Compass#run_on_changes with ["sass/variables/_colors.scss", "sass/variables/_colors.scss"]
13:18:17 - DEBUG - Hook :run_on_change_begin executed for Guard::Compass
overwrite css/calce-omega.styles.css
identical css/calce-omega.normalize.css
unchanged sass/calce-omega.hacks.scss
overwrite css/calce-omega.no-query.css
13:18:19 - DEBUG - Hook :run_on_change_end executed for Guard::Compass
13:18:19 - DEBUG - Start interactor
13:18:19 - DEBUG - Stop interactor
13:18:19 - DEBUG - Trying to run Guard::LiveReload#run_on_additions with ["css/calce-omega.no-query.css", "css/calce-omega.styles.css"]
13:18:19 - DEBUG - Trying to run Guard::LiveReload#run_on_changes with ["css/calce-omega.no-query.css", "css/calce-omega.styles.css"]
13:18:19 - DEBUG - Trying to run Guard::LiveReload#run_on_change with ["css/calce-omega.no-query.css", "css/calce-omega.styles.css"]
13:18:19 - DEBUG - Start interactor

When I touch the .css file (i.e. $ touch css/calce-omega.styles.css), the mtime is updated and then guard-livereload runs (via the run_on_modifications hook) and Chrome reloads the page. Here's the guard debugging output:

13:37:19 - DEBUG - Stop interactor
13:37:19 - DEBUG - Hook :run_on_modifications_begin executed for Guard::LiveReload
13:37:19 - INFO - Reloading browser: css/calce-omega.styles.css
13:37:19 - DEBUG - {:command=>"reload", :path=>"/path_to/calce_omega/css/calce-omega.styles.css", :liveCSS=>true}
13:37:19 - DEBUG - Hook :run_on_modifications_end executed for Guard::LiveReload
13:37:19 - DEBUG - Start interactor

I'm not entirely sure why the change was made, but I think this is not the expected behavior. I'm kind of new to this, though, so I may be missing something.

thibaudgg commented 10 years ago

What about watching directly the scss file with guard-livereload?

watch(%r{sass/(.+).scss}) { |m| "css/#{m[1]}.css" }

deisner commented 10 years ago

Thibaud, that works -- thanks for your help. However, I think of this as a workaround. If I tell livereload to watch a path, it seems like livereload should tell the browser when that file has changed, whether I edit the file in place or delete it and replace it with a new version (which is what compass appears to be doing).

To put it another way, if:

  1. I tell livereload to watch a file at path foo/bar.baz, and
  2. at time T the file at path foo/bar.baz has one sequence of bytes, and
  3. at a later time T' the file at path foo/bar.baz has a new sequence of bytes,

then livereload should tell the browser to reload. To me, that behavior is in the spirit of the guard API, too: run_on_changes looks like the right method to use.

But as I said, I'm still getting up to speed. If this is a won't-fix, that's OK. I'm working on a Drupal site, and the Guardfile I use was automatically generated by drush omega-wizard, a command-line tool provided (the omega-wizard command, not drush) by the Omega Drupal theme: http://drupalcode.org/project/omega.git/blob/a6c85ed04d103402badc4c1135394eacbd88ec92:/omega/starterkits/default/Guardfile

If this is the new way guard-livereload works, I'll open a bug report with them to update the Guardfile they generate.

thibaudgg commented 10 years ago

@e2 is working on a PR for guard/listen that should fix VIM edition and would allow https://github.com/guard/guard-livereload/commit/e20657df1d2f6c05b9ddb5a98177f8fc8c1cdfbe to be reverted.

deisner commented 10 years ago

OK, cool. I'll use your workaround until then. Thanks again for your help.

e2 commented 10 years ago

Yes, this should be fixed by the next linux/vim/inotify changes.

Here's why you're getting the problem, @deisner:

  1. 'touch' changes attributes, which trigger a 'modified' event, which runs run_on_modifications (as expected)
  2. until now, editing a file with vim caused file to be renamed/moved, which triggered an 'added' event, which tries to call run_on_additions, which falls back to run_on_changes, which isn't handled by newer versions of plugins (run_on_modifications is preferred since run_on_changes causes multiple callbacks).

This happens because until now, the listen gem was unable to detect moving and renaming a file (which is what most editors do) as a 'modified' event, which would call run_on_modifications - which is what updated guard plugins normally expect (e.g. guard-livereload).

So after this new patch, everything should work perfectly.

diqidoq commented 10 years ago

Any news on this? We can still reproduce this issue on latest

guard-livereload 2.2.0
guard 2.0 

regardless of using vim or gedit or PHPstorm. It doesn't matter. Browser connects successfully. Files get updated with guard and compass successfully and message in terminal confirms that. Only the browser-refresh confirmation message is still missing. Refresh of Browser has to be made manually. All changes are occurring fine, but no auto-refresh from guard-livereload gets fired.

One additional info: Only Gedit allows to "re-save" an already saved document. On this second re-save the Browser refresh happens successfully. Funny thou ...

diqidoq commented 10 years ago

I can confirm that adding "scss" to the watching "match" in the "livereload do" function is a usable work-around for Drupal Omega subtheme for now, but if I understand this issue thread here correctly, this could be left out after fix mentioned from @thibaudgg again? Is this correct? Otherwise, is there already an issue opened on Drupal.org, @deisner? We should inform the Omega 4 theme maintainer about it (@fubhy).

e2 commented 10 years ago

@diqidoq - first, make sure you're using the latest versions of guard and listen gems. Then, run with the following environment variable: LISTEN_GEM_DEBUGGING=1 - that should show if listen is correctly responding to changes.

After that, we can work out what's going on and where the fix is necessary.

diqidoq commented 10 years ago

@e2: thanks for chiming in. We are using guard version 2.6.1 and listen 2.7 with ruby 2.1.2 installed via rvm locally on working root (project/sites/all/themes/THEME). Here is our --debug output without adding scss to the watch (css only, like it was before and intended usually):

bundle exec guard --debug
01:12:59 - DEBUG - Command execution: compass compile --time --quiet
Compilation took 0.138s
01:13:00 - DEBUG - Command execution: hash stty
01:13:00 - DEBUG - Guard starts all plugins
01:13:00 - DEBUG - Hook :start_begin executed for Guard::Compass
01:13:00 - INFO - Guard::Compass is waiting to compile your stylesheets.
01:13:00 - DEBUG - Hook :start_end executed for Guard::Compass
01:13:00 - DEBUG - Hook :start_begin executed for Guard::LiveReload
01:13:00 - INFO - LiveReload is waiting for a browser to connect.
01:13:00 - DEBUG - Hook :start_end executed for Guard::LiveReload
01:13:00 - INFO - Guard is now watching at '/var/www/project/sites/all/themes/THEME'
01:13:00 - DEBUG - Start interactor
[1] guard(main)> 
01:13:06 - INFO - Browser connected.
01:13:07 - DEBUG - Stop interactor
01:13:07 - DEBUG - Trying to run Guard::Compass#run_on_modifications with ["sass/base/_typography.scss", "sass/base/_typography.scss"]
01:13:07 - DEBUG - Trying to run Guard::Compass#run_on_changes with ["sass/base/_typography.scss", "sass/base/_typography.scss"]
01:13:07 - DEBUG - Hook :run_on_change_begin executed for Guard::Compass
unchanged sass/THEME.hacks.scss
overwrite css/THEME.styles.css 
unchanged sass/THEME.normalize.scss
overwrite css/THEME.no-query.css 
01:13:07 - DEBUG - Hook :run_on_change_end executed for Guard::Compass
01:13:07 - DEBUG - Start interactor
01:13:08 - DEBUG - Stop interactor
01:13:08 - DEBUG - Trying to run Guard::LiveReload#run_on_additions with ["css/THEME.styles.css", "css/THEME.no-query.css"]
01:13:08 - DEBUG - Trying to run Guard::LiveReload#run_on_changes with ["css/THEME.styles.css", "css/THEME.no-query.css"]
01:13:08 - DEBUG - Trying to run Guard::LiveReload#run_on_change with ["css/THEME.styles.css", "css/THEME.no-query.css"]
01:13:08 - DEBUG - Start interactor
[1] guard(main)> 

No Browser refresh, but changes are made to the scss and css files. And here is the --debug output with adding scss to the watch:

bundle exec guard --debug
01:04:54 - DEBUG - Command execution: compass compile --time --quiet
Compilation took 0.128s
01:04:55 - DEBUG - Command execution: hash stty
01:04:55 - DEBUG - Guard starts all plugins
01:04:55 - DEBUG - Hook :start_begin executed for Guard::Compass
01:04:55 - INFO - Guard::Compass is waiting to compile your stylesheets.
01:04:55 - DEBUG - Hook :start_end executed for Guard::Compass
01:04:55 - DEBUG - Hook :start_begin executed for Guard::LiveReload
01:04:55 - INFO - LiveReload is waiting for a browser to connect.
01:04:55 - DEBUG - Hook :start_end executed for Guard::LiveReload
01:04:55 - INFO - Guard is now watching at '/var/www/project/sites/all/themes/THEME'
01:04:55 - DEBUG - Start interactor
[1] guard(main)> 
01:05:11 - DEBUG - Serving file /home/user/.rvm/gems/ruby-2.1.2@THEME/gems/guard-livereload-2.2.0/js/livereload.js
01:05:12 - INFO - Browser connected.
01:05:28 - DEBUG - Stop interactor
01:05:28 - DEBUG - Trying to run Guard::Compass#run_on_modifications with ["sass/base/_typography.scss", "sass/base/_typography.scss"]
01:05:28 - DEBUG - Trying to run Guard::Compass#run_on_changes with ["sass/base/_typography.scss", "sass/base/_typography.scss"]
01:05:28 - DEBUG - Hook :run_on_change_begin executed for Guard::Compass
unchanged sass/THEME.hacks.scss
overwrite css/THEME.styles.css 
unchanged sass/THEME.normalize.scss
overwrite css/THEME.no-query.css 
01:05:29 - DEBUG - Hook :run_on_change_end executed for Guard::Compass
01:05:29 - DEBUG - Hook :run_on_modifications_begin executed for Guard::LiveReload
01:05:29 - INFO - Reloading browser: sass/base/_typography.scss
01:05:29 - DEBUG - {:command=>"reload", :path=>"/var/www/project/sites/all/themes/THEME/sass/base/_typography.scss", :liveCSS=>true}
01:05:29 - DEBUG - Hook :run_on_modifications_end executed for Guard::LiveReload
01:05:29 - DEBUG - Start interactor
01:05:29 - DEBUG - Stop interactor
01:05:29 - DEBUG - Trying to run Guard::LiveReload#run_on_additions with ["css/THEME.styles.css", "css/THEME.no-query.css"]
01:05:29 - DEBUG - Trying to run Guard::LiveReload#run_on_changes with ["css/THEME.styles.css", "css/THEME.no-query.css"]
01:05:29 - DEBUG - Trying to run Guard::LiveReload#run_on_change with ["css/THEME.styles.css", "css/THEME.no-query.css"]
01:05:29 - DEBUG - Start interactor
[1] guard(main)> 
01:05:30 - DEBUG - Serving file /home/user/.rvm/gems/ruby-2.1.2@THEME/gems/guard-livereload-2.2.0/js/livereload.js

Browser refreshes and changes are made.

Here the Guardfile function:

notification :off
group :development do   
  # Only run Compass if we have a config.rb file in place.
  if File.exists?("config.rb")
    # Compile on start.
    puts `compass compile --time --quiet`    
    # https://github.com/guard/guard-compass
    guard :compass do
      watch(%r{.+\.s[ac]ss$})
    end
  end
  # https://github.com/guard/guard-livereload.
  # Ignore *.normalize.scss to prevent flashing content when re-rendering.
  # We added scss here in (css|js) -> (scss|css|js) as a work-around when browser won't refresh
  guard 'livereload', grace_period: 0.5, override_url: false do
    watch(%r{^((?!\.normalize\.).)*\.(css|js)$})
  end
end
e2 commented 9 years ago

Both Guard and Listen gems have been updated since.

I believe this is fixed. It's best to use Listen 3.x to make sure.

If there are still problems - please open a new issue, thanks!