Created
April 6, 2020 15:09
-
-
Save infostreams/0bf20b217bb813eaa21e17ee22df9c1f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
/** | |
* This script monitors a folder for changes and forces a change in the inode or file-modification time | |
* if it detects a change or deletion. | |
* | |
* Basically it's a PHP re-implementation of https://github.com/sillypog/inotify-proxy | |
* | |
* I do this because docker-sync was not copying changes from my MacOS folder to volume that was used by | |
* my docker container. And docker-sync is the only way to get a FAST filesystem if you're using Docker | |
* on Mac. | |
* | |
* Unfortunately, Virtual Box does not trigger inotify events within the container when a file is modified | |
* on the OSX host. This issue is described here: https://www.virtualbox.org/ticket/10660 | |
* https://www.virtualbox.org/ticket/14234 and marked as WONT-FIX. | |
* | |
* Also see https://github.com/EugenMayer/docker-sync/issues/410 | |
* | |
* @author E. Akerboom | |
* @date 2020 | |
*/ | |
class Watcher | |
{ | |
protected $folder; | |
protected $timestamps; | |
protected $exclude = []; | |
public function __construct($folder, $exclude = ["node_modules", "vendor", "storage", "resources", "public"]) | |
{ | |
$this->folder = $folder; | |
$this->exclude = $exclude; | |
echo "\nFiles in " . realpath($folder) . ":\n\n"; | |
foreach ($this->files() as $f) { | |
echo "- " . $f->getRealPath() . "\n"; | |
$this->timestamps[$f->getRealPath()] = $f->getMTime(); | |
} | |
$this->watch(); | |
} | |
protected function watch() { | |
do { | |
sleep(1); | |
$this->process(); | |
} while (true); | |
} | |
protected function process() { | |
$seen = []; | |
foreach ($this->files() as $f) { | |
$path = $f->getRealPath(); | |
$seen[$path] = 1; | |
try { | |
$modified = $f->getMTime(); | |
} catch (RuntimeException $e) { | |
$this->deleted($path); | |
continue; | |
} | |
if (!array_key_exists($path, $this->timestamps)) { | |
$this->changed($path); | |
} else { | |
if ($this->timestamps[$path] !== $modified) { | |
$this->changed($path); | |
} | |
} | |
} | |
$deleted = array_diff_key($this->timestamps, $seen); | |
foreach ($deleted as $path=>$file) { | |
$this->deleted($path); | |
} | |
} | |
protected function changed($path) { | |
// make sure to trigger filesystem event | |
echo "\nChanged: $path"; | |
if (abs(filemtime($path) - $this->timestamps[$path]) > 1) { | |
touch($path); | |
} | |
$this->timestamps[$path] = filemtime($path); | |
} | |
protected function deleted($path) { | |
// make sure to trigger filesystem event | |
echo "\nDeleted: $path"; | |
// the original deletion of the file didn't trigger an inotify event | |
// therefore, we re-create the file that was deleted ... | |
touch($path); | |
// ... and give unison chance to catch up ... | |
sleep(5); | |
// ... after which we delete it with a 'native' delete event | |
// that IS picked up by unison | |
unlink($path); | |
// and we forget about the file completely | |
unset($this->timestamps[$path]); | |
} | |
/** | |
* @return SplFileInfo[] A list of files | |
*/ | |
protected function files() | |
{ | |
$exclude = $this->exclude; | |
$directory = new \RecursiveDirectoryIterator($this->folder, \FilesystemIterator::FOLLOW_SYMLINKS); | |
$filter = new \RecursiveCallbackFilterIterator($directory, function ($current, $key, $iterator) use ($exclude) { | |
// Skip hidden files and directories. | |
if ($current->getFilename()[0] === '.') { | |
return FALSE; | |
} | |
if ($current->isDir()) { | |
return !in_array($current->getFilename(), $exclude); | |
} | |
// Only recurse into intended subdirectories. | |
return !in_array($current->getRealPath(), $exclude); | |
}); | |
$iterator = new \RecursiveIteratorIterator($filter); | |
foreach ($iterator as $info) { | |
yield $info; | |
} | |
} | |
} | |
if ($argc !== 2) { | |
echo "SYNTAX: ./watcher.php <folder>\n"; | |
die; | |
} | |
$folder = $argv[1]; | |
if (!file_exists($folder)) { | |
echo "Folder {$folder} does not exist\n"; | |
die; | |
} | |
new Watcher($argv[1]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you want to make the
native_osx
synching strategy work on docker-machine, you can do that by adding thenative_osx_image
option to theoptions
section of yourdocker-sync.yaml
:Whatever is listed in sync_excludes is configuration for unison. The script that watches for file changes does not actually use the configuration here. Instead, the script excludes the following folders by default: ["node_modules", "vendor", "storage", "resources", "public"] - and there's no way to change that. If you find a way to change it and make it listen to whatever is in
sync_excludes
, fix it in the PHP script above and I will update the Docker image on Docker hub.