Sync only parts of a file

Hi there, not sure whether this is the correct category, but I think it fits.

I am trying to sync a settings (xml) file between two devices. But the files contain paths that are specific to the devices - so they need to stay different but will also not change. The part that does change and I want to sync is clearly separated from the “fixed” part.

Now the question is, can I handle this with syncthing? Or maybe with syncthing in addition with another program / little script? Does syncthing support something like git’s pre/post commit hooks?

My idea is to not sync the original file, but to “watch” it for changes and copy the 2nd part to another file, which will be synced via syncthing. Similarly, I then need to watch this “intermediate” file for changes, and if its changes are newer than the xml file, I will update the xml with its contents.

Any ideas on how to handle this?

Okay I found:

… But I actually sync with 3 devices, my (linux based) NAS just works as backup with versioning, and because the windows machines usually are not running at the same time…

So I could run a post script on this machine… but if I change the file to fit “the other machine” it will try to “sync back” to the first machine…

The solution might be using 2 folders synced on my NAS, and the post hook scripts work to update the file in the other folder? I need to

  • find out where to install/run the post hook script(s) - syncthing container or host system?
  • decide whether it’s better to run 1 script that listens to (events from) both folders and compares the file dates, or run 1 script for each folder…
  • make sure updating the with the script doesn’t trigger the daemon again (infite loop)
  • script content… I can probably handle it with by removing everything between ` and …

I think there might be a simpler solution (that can still use Syncthing for the syncing part).

How/what modifies the XMl config file?

Could you post samples snippets of the XML that are unique to each Windows machine?


<config-version>2</config-version>
<version>3.0.11</version>
<build>5710ea736478fd7193e68b70defc0ee404ba4576</build>
<libs-build>34</libs-build>
<client>
        <concurrent-updates>2</concurrent-updates>
    [...]
<backup-dir>C:\Users\mahor/.minion</backup-dir>
</client>
<user>
<ga-user-id>5607007b-79b1-4f99-a829-109fb6b7182f</ga-user-id>
</user>
[some same settings]
<drive-config drive="V:\" scannable="false"/>
<drive-config drive="S:\" scannable="false"/>
<drive-config drive="O:\" scannable="false"/>
<drive-config drive="L:\" scannable="false"/>
<drive-config drive="M:\" scannable="false"/>
<drive-config drive="K:\" scannable="false"/>
<drive-config drive="C:\" scannable="true"/>
</drive-configs>
<games>
<game addon-path="QzpcVXNlcnNcbWFob3JcRG9jdW1lbnRzXEVsZGVyIFNjcm9sbHMgT25saW5lXGxpdmVcQWRkT25z" auto-update="false" display-name="Elder Scrolls Online" game-id="ESO" unique-game-id="ESO-1">

A little java application called Minion (that handles addon updates for a game) is the one writing / adjusting the xml.

There are some parts other settings that might stay the same. And I’m not sure about the user-ID, it is probably generated on the go, maybe used for telemetry (did I ever accept that? :-/) - but should work when synced. Build Data will be similar most of the time. Game path will be different but static…

Thanks for the sample XML and additional info.

So, my approach would be a wrapper script around Minion. Instead of launching Minion directly, the wrapper would consist of 3 stages:

  1. Use string substitution to modify a copy of minion.xml (e.g. C:\Users\mahor\Syncthing\minion-template.xml), saving the results to C:\Users\mahor\.minion\minion.xml.
  2. Launch Minion.
  3. After Minion shuts down, copy C:\Users\mahor\.minion\minion.xml to C:\Users\mahor\Syncthing\minion-template.xml so it gets picked up by Syncthing for syncing to/from the NAS.

Depending on how many lines in minion.xml need to be modified, the wrapper script could be as simple as this:

sed "s/<game addon.+>/%ADDON%/" minion-template.xml > minion.xml
java -jar Minion-jfx.jar
copy /y minion.xml minion-template.xml

I used sed in the example above, but PowerShell could also work even though it’s heavyweight compared to the simplicity of Sed.

The variable %ADDON% holds the replacement string.

A more sophisticated approach would be a program/script that ingests minion.xml and outputs a modified version suitable for the target machine (there are plenty of libraries to simplify processing XML files).

The wrapper script avoids the issue of infinite-loops that you already noted earlier because it doesn’t try to use Syncthing to trigger any actions via pre-/post- sync hooks.

(I actually have a somewhat similar situation at work involving deploying a web application. Several staging and production servers share a common configuration file with just a couple lines of differences that are unique to each server. Syncthing handles distributing a template configuration file from a NAS, while an identical script on each server makes the necessary localized changes.)

:smile: … yes, unfortunately the norm nowadays. At least for me, it’s a fair trade as long as the process and use is upfront and transparent.

Yes, that definitely sounds like a good idea! I think I have used sed once (on linux) but wiki already says in the first lines it is available on most OS…

When I read through this the first time, I thought I’d only need to write the wrapper on one device, but I think running a wrapper on both has the benefit of avoiding syncthing and minion trying to access the file at the same time.

One thing I’m not certain about is how to handle the version. I don’t want that to be synced, but also not “fixed”… I guess I could create create the “minion.xml” from a “synced” file and a “local only” file that I exclude from syncing. This would mean instead of copying the file over I split and concatenate them, but I think all script languages can do that on their own…

I’ll try to put write that script tomorrow (if it rains).

I might even become really extravagant and try to create a .exe from the script, so I can also just rename the original Minion.exe and all existing links keep working… although it will be overwritten on update :smiley:

I’d try using some config management system like ansible, chef, puppet. They are kind of made for deploying templated config files.

That sounds a bit overkill for my use case, although it would better fit @gadget’s case.

Do those systems support pulling changes from every client though?

Some do, but most are designed for pushing out to clients from a management mode.

You’re right in that it might be overkill for your particular use-case with just two nodes and a single config file to manage unless you already have a system in place you’re familiar with.