knurling / ServiceStation

Service Station
37 stars 1 forks source link

Sync settings between systems #34

Open gordon8214 opened 4 years ago

gordon8214 commented 4 years ago

I have a laptop and a desktop, and I have dozens of rules set up on the desktop. I really don't want to have to go and manually recreate them all on the laptop. It would be fantastic to be able to sync settings and scripts with iCloud.

gordon8214 commented 4 years ago

I should note that for anyone who wants to sync their settings manually, you just need to copy rules.json from ~/Library/Group Containers/4G65N8LGGS.ServiceStationAppGroup/ to the same location on your new system, as well as any scripts from ~/Library/Application Scripts/com.knurling.ServiceStation.Attendant/.

pkamb commented 4 years ago

Syncing across devices is on the list – thanks for making a topic.

Will likely use Apple's CloudKit:

https://developer.apple.com/icloud/cloudkit/

https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitQuickStart/Introduction/Introduction.html

For the time being, yes, you can back up and manually sync the Rules file located at:

~/Library/Group Containers/4G65N8LGGS.ServiceStationAppGroup/rules.json
luckman212 commented 4 years ago

Hmm, what I like to do is store all configs in a central location managed by Resilio Sync (could easily be Dropbox, Syncthing, a Git repo etc) and then create symlinks to. E.g.

CENTRAL_STORE=/path/to/repo/settings/servicestation
LOCAL_CFG="$HOME/Library/Group Containers/4G65N8LGGS.ServiceStationAppGroup"
mkdir -p $CENTRAL_STORE
mv "$LOCAL_CFG/rules.json" $CENTRAL_STORE
ln -s $CENTRAL_STORE/rules.json $LOCAL_CFG

But, when I tried this method with ServiceStation, it deleted my symlink instead and replaced it with a "real" file.

pkamb commented 4 years ago

it deleted my symlink instead and replaced it with a "real" file.

Service Station uses Swift Codable to encode the Rules and Data(contentsOf: url)/rulesData.write(to: url, options: [.atomic]) to read/write to the file system. I'll look in to if there are any issues with this approach and symlinks.

gordon8214 commented 4 years ago

it deleted my symlink instead and replaced it with a "real" file.

Service Station uses Swift Codable to encode the Rules and Data(contentsOf: url)/rulesData.write(to: url, options: [.atomic]) to read/write to the file system. I'll look in to if there are any issues with this approach and symlinks.

I believe removing .atomic would fix this. The atomic write is writing a copy of the file to a new location then copying it in place of the symlink.

gordon8214 commented 4 years ago

I'm not a filesystem expert, but I believe that APFS's copy-on-write behavior largely makes the atomic option unnecessary. The main benefit of using this option would be to avoid an issue in which a write error when saving the file to disk would end up corrupting the original file. APFS performs all writes this way at the filesystem level though (e.g., writes the data to a new location on disk, then updates the filesystem to point to the newly-written data after the write is successful).

luckman212 commented 4 years ago

I agree the atomic write seems like the culprit. A better method might be to attempt to detect if rules.json or other configs are symlinks and if so, resolve the original location and write to that (atomically if desired)

gingerbeardman commented 4 years ago

Out of interest, what happens if you use a hard link (ln) rather than symlink (ln -s)?

pkamb commented 4 years ago

I tried removing .atomic and running the script above to install a ln -s alias file.

Upon running Service Station, that results in the error:

Error Domain=NSCocoaErrorDomain Code=257 "The file “rules.json” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/Users/user/Library/Group Containers/4G65N8LGGS.ServiceStationAppGroup/rules.json, NSUnderlyingError=0x600000c99260 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}

I believe the issue is that the app has file access to the alias by way of being in its Group Container, but it does NOT have Sandbox-escaped access to the source of the alias.

Described here: https://stackoverflow.com/questions/12850786/mac-os-app-how-to-resolve-alias-and-read-original-file-within-sandbox


Using a ln hard link does seem to mirror the changes in both locations and avoid Sandbox problems. That is probably the better way to go unless the symlink is needed for some reason?

.atomic must be removed in this case as well, which will require an app update.

luckman212 commented 4 years ago

A hardlink might solve the problem locally on 1 machine, but then the problem becomes this:

The link gets destroyed when the prefs are changed on the other machine: Dropbox, Resilio Sync etc will unlink the original and link the updated copy. This works fine when it's just a file referenced by a symlink, since the location is the same. But, hardlinks are linked by inode, so this breaks the linkage, thus prefs are no longer updated.

gingerbeardman commented 4 years ago

Oh, damn, is this stalemate then?

luckman212 commented 4 years ago

Seems so for now. I think it might be "easier" to just import CloudKit and use that for sync vs any more mucking around with the fragility of sandbox/hardlinks.