{"id":13435245,"url":"https://github.com/rustyio/sync","last_synced_at":"2025-10-21T18:46:58.554Z","repository":{"id":948505,"uuid":"727813","full_name":"rustyio/sync","owner":"rustyio","description":"On-the-fly recompiling and reloading in Erlang. Code without friction.","archived":false,"fork":false,"pushed_at":"2023-11-10T05:07:23.000Z","size":1250,"stargazers_count":744,"open_issues_count":16,"forks_count":163,"subscribers_count":43,"default_branch":"master","last_synced_at":"2024-05-02T12:41:34.826Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rustyio.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2010-06-18T14:25:31.000Z","updated_at":"2024-03-27T04:22:04.000Z","dependencies_parsed_at":"2024-01-05T22:11:34.813Z","dependency_job_id":null,"html_url":"https://github.com/rustyio/sync","commit_stats":{"total_commits":168,"total_committers":45,"mean_commits":"3.7333333333333334","dds":0.8154761904761905,"last_synced_commit":"aa27b66ccfbfc798b57288af4cf7dc386e205d68"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustyio%2Fsync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustyio%2Fsync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustyio%2Fsync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustyio%2Fsync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rustyio","download_url":"https://codeload.github.com/rustyio/sync/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244144008,"owners_count":20405318,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T03:00:34.164Z","updated_at":"2025-10-21T18:46:58.548Z","avatar_url":"https://github.com/rustyio.png","language":"Erlang","readme":"# Stay in Sync\n\n## What is Sync?\n\nSync is a developer utility. It recompiles and reloads your Erlang code\non-the-fly. With Sync, you can code without friction.\n\n![Successful compilation image.](http://rusty.io.s3.amazonaws.com/sync/sync_01.png)\n\nWhat does \"code without friction\" mean? It means that with Sync running, you no\nlonger need to worry about running `make`, or `c:l(Module)` again. Write\ncode, save the file, and watch as Erlang automatically detects your changes,\nrecompiles the code, and reloads the module.\n\n## How can I use Sync?\n\n### Add as a rebar dependency\n\n#### Rebar 3\n\n```erlang\n{deps, [\n    sync\n]}.\n```\n\nTo auto-start with rebar3: `rebar3 shell --eval \"sync:go\".\"`\n\n#### Rebar 2.x\n\n```erlang\n{deps, [\n  {sync, \".*\",\n   {git, \"https://github.com/rustyio/sync.git\", {branch, \"master\"}}}\n]}.\n```\n\nTo auto-start with rebar2, edit your `etc/vm.args` and add:\n\n```bash\n-eval \"sync:go()\"\n```\n\n````\n\n### Manual system-wide install\n\n```bash\ncd $ERL_LIBS\ngit clone git@github.com:rustyio/sync.git\n(cd sync; make)\n````\n\nThe recommended approach for system-wide install is to put sync in your\n`$ERL_LIBS` directory.\n\nThen, go in the Erlang console of an application you are developing, run\n`sync:go().`. You can also start sync using `application:ensure_all_started(sync).`\n\nStarting up:\n\n```\n(rustyio@127.0.0.1)6\u003e sync:go().\n\nStarting Sync (Automatic Code Compiler / Reloader)\nScanning source files...\nok\n08:34:18.609 [info] Application sync started on node 'rustyio@127.0.0.1'\n```\n\nSuccessfully recompiling a module:\n\n```\n08:34:43.255 [info] /Code/Webmachine/src/webmachine_dispatcher.erl:0: Recompiled.\n08:34:43.265 [info] webmachine_dispatcher: Reloaded! (Beam changed.)\n```\n\nWarnings:\n\n```\n08:35:06.660 [info] /Code/Webmachine/src/webmachine_dispatcher.erl:33: Warning: function dispatch/3 is unused\n```\n\nErrors:\n\n```\n08:35:16.881 [info] /Code/Webmachine/src/webmachine_dispatcher.erl:196: Error: function reconstitute/1 undefined\n/Code/Webmachine/src/webmachine_dispatcher.erl:250: Error: syntax error before: reconstitute\n```\n\n## Stopping and Pausing\n\nYou can stop the `sync` application entirely (wiping its internal state) with\n`sync:stop()`. You can then restart the application with a new state using `sync:go()`\n\nIf, however, you would rather pause `sync` so that it will not update anything\nduring some period, you can pause the scanner with `sync:pause()`. You might\ndo this while upgrading and you wish not to have immediately loaded until\neverything is complete. Calling `sync:go()` once again will unpause the scanner.\n\nBear in mind that running `pause()` will not stop files that are currently\nbeing compiled.\n\n## Specifying folders to sync\n\nTo your erlang `config` add\n\n```erlang\n[\n    {sync, [\n        {src_dirs, {strategy(), [src_dir_descr()]}}\n    ]}\n].\n```\n\n```erlang\n-type strategy() :: add | replace.\n```\n\nIf `strategy()` is `replace`, sync will use ONLY specified dirs to sync. If `strategy()` is `add`, sync will add specific dirs to list of dirs to sync.\n\n```erlang\n-type src_dir_descr() :: { Dir :: file:filename(), [Options :: compile_option()]}.\n```\n\nYou probably want to specify `outdir` compile option.\n\nFor example\n\n```erlang\n[\n    {sync, [\n        {src_dirs, {replace, [{\"./priv/plugins\", [{outdir,\"./priv/plugins_bin\"}]}]}}\n    ]}\n].\n```\n\n## Console Logging\n\nBy default, sync will print sucess / warning / failure notifications to the\nerlang console. You can control this behaviour with the `log` configuration options.\n\n### Valid Values For `log`\n\n- `all`: Print all console notifications\n- `none`: Print no console notifications\n- `[success | warnings | errors]`: A list of any combination of the atoms\n  `success`, `warnings`, or `errors`. Example: `[warnings, errors]` will only\n  show warnings and errors, but suppress success messages.\n- `true`: An alias to `all`, kept for backwards compatibility\n- `false`: An alias to `none`, kept for backwards compatibility\n- `skip_success`: An alias to `[errors, warnings]`, kept for backwards compatibility.\n\nThe `log` value can be specified in any of the following ways:\n\n#### 1. Loaded from a .config file\n\n    {log, all},\n    {log, [success, warnings]},\n\n#### 2. As an environment variable called from the erlang command line\n\n    erl -sync log all\n    erl -sync log none\n\n#### 3. From within the Erlang shell\n\n    sync:log(all);\n    sync:log(none);\n    sync:log([errors, warnings]);\n\n## Desktop Notifications\n\nSync can pop success / warning / failure notifications onto your desktop to\nkeep you informed of compliation results. All major operating systems are\nsupported: Mac via [Growl](http://growl.info), Linux via Libnotify, Windows via\n[Notifu](http://www.paralint.com/projects/notifu/) and Emacs via lwarn /\nmessage command. Below are Growl screenshots.\n\nSuccess:\n\n![Successful compilation image.](http://rusty.io.s3.amazonaws.com/sync/sync_01.png)\n\nWarnings:\n\n![Compilation warnings image.](http://rusty.io.s3.amazonaws.com/sync/sync_02.png)\n\nErrors:\n\n![Compilation errors image.](http://rusty.io.s3.amazonaws.com/sync/sync_03.png)\n\n### Disabling Desktop Notifications\n\nDesktop notifications follow the same conventions as the console logging above,\nand can be selectively enabled or disabled with the `growl` configuration variable:\n\n### Valid Values For `growl`\n\n- `all`: Print all console notifications\n- `none`: Print no console notifications\n- `[success | warnings | errors]`: A list of any combination of the atoms\n  `success`, `warnings`, or `errors`. Example: `[warnings, errors]` will only\n  show warnings and errors, but suppress success messages.\n- `true`: An alias to `all`, kept for backwards compatibility\n- `false`: An alias to `none`, kept for backwards compatibility\n- `skip_success`: An alias to `[errors, warnings]`, kept for backwards compatibility.\n\nThe `growl` value can be specified in any of the following ways:\n\n#### 1. Loaded from a .config file\n\n    {growl, all},\n    {growl, [success, warnings]},\n\n#### 2. As an environment variable called from the erlang command line\n\n    erl -sync growl all\n    erl -sync growl none\n\n#### 3. From within the Erlang shell\n\n    sync:growl(all);\n    sync:growl(none);\n    sync:growl([errors, warnings]);\n\n### Troubleshooting Growl Notifications\n\nSync attempts to auto-detect the notification package to use via the\n`os:type()` command.\n\nIf this isn't working for you, or you would like to override the default, use\nthe `executable` configuration parameter:\n\n    {executable, TYPE}\n\nWhere `TYPE` is:\n\n- `'auto'` Autodetermine (default behaviour)\n- `'growlnotify'` for Mac / Growl.\n- `'notification_center'` for Mac OS X built-in Notification Center.\n- `'notify-send'` for Linux / libnotify.\n- `'notifu'` for Windows / Notifu.\n- `'emacsclient'` for Emacs notifications.\n\nLike all configuration parameters, this can also be specified when launching\nErlang with:\n\n    erl -sync executable TYPE\n\n## Remote Server \"Patching\"\n\nIf you are developing an application that runs on an Erlang cluster, you may\nneed to recompile a module on one node, and then broadcast the changed module\nto other nodes. Sync helps you do that with a feature called \"patching.\"\n\nTo use the patching feature:\n\n1. Connect to any machine in your cluster via distributed erlang. A simple\n   `net_adm:ping(Node)` should suffice.\n\n2. Run `sync:patch()`. This will start the Sync application if it's not already\n   started, and enable \"patch mode\".\n\n3. Start editing code.\n\nSync will detect changes to code, recompile your modules, and then sent the\nupdated modules to every Erlang node connected to your cluster. If the module\nalready exists on the node, then it will be overwritten on disk with the new\n.beam file and reloaded. If the module doesn't exist on the new node, then it\nis simply updated in memory.\n\n## How does Sync work?\n\nUpon startup, Sync gathers information about loaded modules, ebin directories,\nsource files, compilation options, etc.\n\nSync then periodically checks the last modified date of source files. If a file\nhas changed since the last scan, then Sync automatically recompiles the module\nusing the previous set of compilation options. If compilation was successful,\nit loads the updated module. Otherwise, it prints compilation errors to the\nconsole.\n\nSync also periodically checks the last modified date of any beam files, and\nautomatically reloads the file if it has changed.\n\nThe scanning process adds 1% to 2% CPU load on a running Erlang VM. Much care\nhas been taken to keep this low. Shouldn't have to say this, but this is for\ndevelopment mode only, don't run it in production.\n\n## Sync Post-hooks\n\nYou can register a post-hook to run after Sync reloads modules. This can allow\nyou to run tests on modules if you like, or anything else for that matter.\n\nYou can register a post-hook with:\n\n```erlang\nsync:onsync(fun(Mods) -\u003e\n    io:format(\"Reloaded Modules: ~p~n\",[Mods])\n   end).\n```\n\nThis will simply print the list of modules that were successfully recompiled.\n\nFor example, if you wanted to automatically run a unit test on each reloaded\nmodule that has a `test()` function exported, you could do the following:\n\n```erlang\nRunTests = fun(Mods) -\u003e\n [Mod:test() || Mod \u003c- Mods, erlang:function_exported(Mod, test, 0)]\nend,\nsync:onsync(RunTests).\n```\n\nA post-hook can also be specified as a `{Module,Function}` tuple, which assumes\n`Module:Function/1`\n\n_Note:_ Currently, only one post-hook can be registered at a time.\n\n### Unregistering a post-hook\n\nTo unregister a post-hook, call\n\n    sync:clear_onsync().\n\n### Registering Automatic Tests\n\nThere is a handy shortcut to enable automatic tests using the Sync post-hooks, you can simply call:\n\n```erlang\nsync:enable_autotest().\n```\n\n## Whitelisting/Excluding modules from the scanning process\n\nSometimes you may want to focus only on a few modules, or prevent some modules\nfrom being scanned by sync. To achive this modify `whitelisted_modules` or\n`excluded_modules` configuration parameter in the\n[node's config file](http://www.erlang.org/doc/man/config.html).\n\nBeyond specifying modules one by one, identified by atoms, you can also specify\nthem in bulk, identified by regular expressions, but with a slower sync.\n\nEven further, you may wish to prevent the scanner from searching for files in\nentire directory structures that could bog down the scanner. For this, you can\nmodify the `excluded_paths` configuration parameter.\n\n## Moving Application Location\n\nPreviously, if an entire application (like a reltool-generated release) was\nmoved from one location to another, sync would fail to recompile files that\nwere changed until all the beams were remade. While this is typically as\nsimple as typing `rebar compile`, it was still a hassle.\n\nThe solution to this was to enable the ability for sync to \"heal\" the paths\nwhen it turned out they had been moved.\n\nThe way this works is by checking if the `source` path inside the beam is a\nfile that exists, and by checking if that path is a descendant of the root of\nyour application. If sync has been set to fix the paths, and module's source\nis pointing at a path that isn't a descendant of the current working directory,\nit will attempt to find the correct file.\n\nYou can change how this will be handled with a `non_descendants` setting in the\nconfig:\n\n- `fix`: Fix any file that isn't a descendant\n\n- `allow`: Use the original path in the module, regardless of its location,\n  recompiling only if the original location changes.\n\n- `ignore`: If a file is not a descendant, sync will completely ignore it.\n\n## Sample Configuration File\n\nPlease note that sync loads with the following defaults:\n\n```erlang\n[\n {sync, [\n  {growl, all},\n  {log, all},\n  {non_descendants, fix},\n  {executable, auto},\n  {whitelisted_modules, []},\n  {excluded_modules, []}\n ]}\n].\n```\n\nYou can view a full sample configuration file\n([sync.sample.config](https://github.com/rustyio/sync/blob/master/sync.sample.config))\nthat you're free to include in your application. Remember to use the\n`-config` switch for the `erl` program:\n\n    erl -config sync.config\n","funding_links":[],"categories":["Erlang","杂项","Build Tools"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustyio%2Fsync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frustyio%2Fsync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustyio%2Fsync/lists"}