Unison File Synchronizer

From MagnetoWiki
Revision as of 13:16, 12 January 2016 by Chuck (talk | contribs) (Added notes on setting up Unison on DreamHost's shared hosting.)
Jump to navigation Jump to search

Unison project website.

Unison Manual.

Unison Mac OS X FAQs.

Set Up

Downloaded GUI Universal Binary 2.32.12 "(2009.05.06, stable, should work on Tiger and Leopard)."

1. Installed on both Principia (King office) and houptlab.org (magnet.neuro.fsu.edu) by screen sharing...

2. Logged into Magnet as User, and launched Unison on Magnet.

3. On Magnet, under System Preferences, set Unison to run automatically on start-up on houpt log-in. (Will it be running even if I don't log in to magnet as user?)

4. On Principia, In the finder, made a new folder in home directory "/Users/houpt/uniDisk"

5. Ran Unison on Principia; created a new profile:

Profile name: "uniDisk"
First Root:File: "/Users/houpt/uniDisk" 
Second Root:Remote
Second Root:User:"houpt"
Second Root:Host:"houptlab.org"
Second Root:File:"uniDisk"

Save the profile.

(Apparently don't need to setup a profile on the server.)

(Profiles are stored as .prf files in "/Library/Application Support/Unison" folder; apparently only way to delete a profile is to trash the .prf file manually.)

6. To sync, run Unison, choose uniDisk profile, hit "Open". First time of launch will be prompted for password.

7. Hit "Go" for default synchronization.

And it just worked! (apparently don't need to create a profile on the remote host. Folder will be created on the server side on the first sync.)

If you close the profile window, you can't reopen the window except by quiting and relaunching Unison? Hit "restart" button to get back to list of profiles. Apparently no way to edit the profiles after creation.

Set Up for Shared Hosting on DreamHost (Ubuntu)

DreamHost's shared hosts don't have Unison, but it is straight-forward to build it.

First step is to build OCaml and install it into a scratch directory (~/ocaml):

mkdir ~/ocaml
curl -OL http://caml.inria.fr/pub/distrib/ocaml-4.02/ocaml-4.02.3.tar.gz
tar xzf ocaml-4.02.3.tar.gz
cd ocaml-4.02.3
./configure --prefix ~/ocaml
make world.opt
make install

Next, add OCaml to the path, and build Unison:

PATH=~/ocaml/bin:$PATH
curl -OL http://www.seas.upenn.edu/~bcpierce/unison/download/releases/stable/unison-2.48.3.tar.gz
tar tzf unison-2.48.3.tar.gz
cd unison-2.48.3
make

Command Line

To synchronize the folder "/Users/houpt/uniDisk" from the command line (without invoking GUI app):

1. Make sure you are starting from directory "/Users/houpt" (maybe should use absolute paths in command line?))

2. type following command:

unison uniDisk ssh://houptlab.org/uniDisk -ui text -batch -times

3. prompted for password

4. If there are any conflicts etc., will be prompted for directions. These can be suppressed with various command line options, for example:

-auto 

if "auto" is set the user interface will not ask to confirm non-conflicts, but only ask questions about conflicts

-batch 

if "batch" is set the user interface will ask no questions at all; non-conlficts will be propogated, conflicts will be skipped

-silent  

if "silent", the textual interface will porint nothing at ell, except errors (automatically sets -batch TRUE)

-times

"times" synchronizes the modification times?

-terse

if "terse" is set, status messages are not reported, so only conflicts are reported.

-ignore 'Name .DS_Store"

Unison will ignore all files with the name .DS_Store (On Mac OS X, .DS_Store files encode only trivial appearance stuff like icon position, but .DS_Store files are changed everytime a folder is opened so frequently generate irrelevant conflicts.)

AppleScript

To avoid opening the terminal window, create the following AppleScript and save as an application.

      try
           -- add "-terse" if you want less verbose results
           set resultsText to do shell script "unison /Users/houpt/uniDisk ssh://houptlab.org/uniDisk -ui text -batch -times  -ignore 'Name .DS_Store'"
           
       on error errStr number errorNumber
           
           set unisonResultCodeString to (errStr & return & return & "Result: number " & errorNumber & return & return)
           
           if (errorNumber is equal to 0) then
           
               set resultsText to (unisonResultCodeString & "Successful synchronization; everything is up-to-date now")
           
           else if (errorNumber is equal to 1) then
           
               set resultsText to (unisonResultCodeString & "Some files were skipped, but all file transfers were successful.")
           
           else if (errorNumber is equal to 2) then
           
               set resultsText to (unisonResultCodeString & "Non-fatal failures occurred during file transfer.")
           
           else if (errorNumber is equal to 3) then
           
               set resultsText to (unisonResultCodeString & "A fatal error occurred, or the execution was interrupted.")
           
           else
           
               set resultsText to unisonResultCodeString
           
           end if
           
           
       end try


       display alert "Unison completed sync" giving up after 2

       -- display the results text in a new TextEdit window
       tell application "TextEdit"
           activate
           set NewDoc to make new document
           if (0 is length of resultsText) then
               set text of NewDoc to (date string of (current date)) & " " & (time string of (current date)) & return & return & "Unison reports no conflicts."
               else
               set text of NewDoc to (date string of (current date)) & " " &(time string of (current date)) & return & return & resultsText
           end if
       end tell

tell me to quit

Results of the synch will be stored in the "unison.log" file, and the applescript will display results in a TextEdit window.

Unison With Scrivener

Scrivener stores its projects as bundles (i.e. folders). Because Unison does not sync modification times of folders, changes to Scrivener projects do not get properly propagated. To get around this, I use this more complicated AppleScript to zip up any Scrivener projects -- the zipped file gets propagated properly, with the Scrivener project modification times properly preserved and transmitted. Needless to say, this is a hack; but I really want to sync my Scrivener projects on multiple machines!

--
--  AppDelegate.applescript
--  SyncUniDisk
--
--  Created by Tom Houpt on 12/12/28.
--  Copyright (c) 2012 Tom Houpt. All rights reserved.
--

 script AppDelegate
       property parent : class "NSObject"
	
	on applicationWillFinishLaunching_(aNotification)
		-- Insert code here to initialize your application before any files are opened
       
       -- to handle incompatibilities of Unison file-synching with Scrivener's use of bundles for files
       -- T.A. Houpt, 2014-1-22
       --
       -- find all Scrivener "*.scriv" files (actually bundles) and zip them up
       -- then rename original file as "*.scriv YYYYMMDD hhmmss"
       -- then call unison in batch mode
       -- post an alert that we finished syncing
       -- display the unison results in a TextEdit window
       -- (we could unzip any *.scriv.zip files, but we do the unzipping manually for the moment)
 
 try
 
       set allScrivenerFileNames to do shell script "find '/Users/houpt/uniDisk' -name '*.scriv' -print"
       set AppleScript's text item delimiters to return
       set scrivenerFileRecords to text items of allScrivenerFileNames
       
       -- zip all the .scriv files
       -- for each .scriv file we found:
       --   1) delete previous zip file if it exists
       --   2) zip the .scriv file
       --   3) if we created the zip file, rename the "*.scriv" file as "*.scriv YYYYMMDD hhmmss"
       
       repeat with aRecord in scrivenerFileRecords
           if length of aRecord is greater than 0 then
               
               set scrivenerFile to aRecord
               set filePath to quoted form of scrivenerFile
               set zipFile to quoted form of (scrivenerFile & ".zip")
               
               -- if there is a previous zip file, then delete it
               set existsResults to do shell script "[ -e " & zipFile & " ] && echo 'Found' || echo 'Not found'"
               
               if (existsResults is "Found") then
                   do shell script "rm -f " & filePath
               end if
               
               do shell script "zip -r  " & zipFile & " " & filePath
               
               -- if the file was zipped, then rename the original file with date & time
               set existsResults to do shell script "[ -e " & zipFile & " ] && echo 'Found' || echo 'Not found'"
               
               if (existsResults is "Found") then
                   set {year:y, month:m, day:d, hours:h, minutes:mi, seconds:s} to (current date)
                   set dateNumber to y * 10000 + m * 100 + d
                   set timeNumber to h * 10000 + mi * 100 + s
                   
                   set newFilePath to quoted form of (scrivenerFile & " " & dateNumber & " " & timeNumber)
                   
                   do shell script "mv " & filePath & " " & newFilePath
               end if
           end if
       end repeat
       
       on error errStr number errorNumber
 
           set zipErrorString to ("Zip Error: " & errStr & " number " & errorNumber)
           
           display alert zipErrorString giving up after 10
 
       end try
 
 
       try
           -- -terse
           set resultsText to do shell script "unison /Users/houpt/uniDisk ssh://houptlab.org/uniDisk -ui text -batch -times  -ignore 'Name .DS_Store'"
           
       on error errStr number errorNumber
           
           set unisonResultCodeString to (errStr & return & return & "Result: number " & errorNumber & return & return)
           
           if (errorNumber is equal to 0) then
           
               set resultsText to (unisonResultCodeString & "Successful synchronization; everything is up-to-date now")
           
           else if (errorNumber is equal to 1) then
           
               set resultsText to (unisonResultCodeString & "Some files were skipped, but all file transfers were successful.")
           
           else if (errorNumber is equal to 2) then
           
               set resultsText to (unisonResultCodeString & "Non-fatal failures occurred during file transfer.")
           
           else if (errorNumber is equal to 3) then
           
               set resultsText to (unisonResultCodeString & "A fatal error occurred, or the execution was interrupted.")
           
           else
           
               set resultsText to unisonResultCodeString
           
           end if
           
           
       end try
  
  
       display alert "Unison completed sync" giving up after 2
       
       -- display the results text in a new TextEdit window
       tell application "TextEdit"
           activate
           set NewDoc to make new document
           if (0 is length of resultsText) then
               set text of NewDoc to (date string of (current date)) & " " & (time string of (current date)) & return & return & "Unison reports no conflicts."
               else
               set text of NewDoc to (date string of (current date)) & " " &(time string of (current date)) & return & return & resultsText
           end if
       end tell
       
       
       tell me to quit 
  
   end applicationWillFinishLaunching_
 	
   on applicationShouldTerminate_(sender)
         -- Insert code here to do any housekeeping before your application quits 
        return current application's NSTerminateNow
   end applicationShouldTerminate_
  
end script

Conflicts

Conflicts arise when the server and the local directory have conflicting dates (i.e. the versions on office computer and laptop were independently edited, so are now out of sync.) If there are conflicts to be resolved, then the applescript will post a dialog window.

To resolve the conflicts, run the following command (make sure you are in Users/houpt directory)

unison uniDisk ssh://houptlab.org/uniDisk -ui text -times -ignore 'Name .DS_Store'

For each conflict, < moves from server to local, > moves from local to server, / skips the file, f means follow Unison recommendation.