blog.notmyidea.org/working-directly-on-your-server-how-to-backup-and-sync-your-dev-environment-with-unison.html

186 lines
No EOL
12 KiB
HTML

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" media="screen" charset="utf-8">
<link href="./feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Alexis' log ATOM Feed" />
<title>Alexis Métaireau</title>
</head>
<body>
<div id="top">
<p class="author"><a href=".">Alexis Métaireau</a>'s thougths</p>
<ul class="links"></ul>
</div>
<div class="content clear">
<h1>Working directly on your server? How to backup and sync your dev environment with unison</h1>
<p class="date">Published on Wed 16 March 2011</p>
<p>I have a server running freebsd since some time now, and was wondering about
the possibility to directly have a development environment ready to use when
I get a internet connexion, even if I'm not on my computer.</p>
<p>Since I use vim to code, and spend most of my time in a console while
developing, it's possible to work via ssh, from everywhere.</p>
<p>The only problem is the synchronisation of the source code, config files etc.
from my machine to the server.</p>
<p>Unison provides an interesting way to synchronise two folders, even over
a network. So let's do it !</p>
<div class="section" id="creating-the-jail">
<h2>Creating the jail</h2>
<p>In case you don't use FreeBSD, you can skip this section.</p>
<div class="highlight"><pre><span class="c"># I have a flavour jail named default</span>
<span class="nv">$ </span>ezjail-admin -f default workspace.notmyidea.org 172.19.1.6
<span class="nv">$ </span>ezjail-admin start workspace.notmyidea.org
</pre></div>
<p>In my case, because the &quot;default&quot; flavour contains already a lot of interesting
things, my jail come already setup with ssh, bash and vim for instance, but
maybe you'll need it in your case.</p>
<p>I want to be redirected to the ssh of the jail when I connect to the host with
the 20006 port. Add lines in <cite>/etc/pf.conf</cite>:</p>
<pre class="literal-block">
workspace_jail=&quot;172.19.1.6&quot;
rdr on $ext_if proto tcp from any to $ext_ip port 20006 -&gt; $workspace_jail port 22
</pre>
<p>Reload packet filter rules</p>
<div class="highlight"><pre><span class="nv">$ </span>/etc/rc.d/pf reload
</pre></div>
</div>
<div class="section" id="working-with-unison">
<h2>Working with unison</h2>
<p>Now that we've set up the jail. Set up unison on the server and on your client.
Unison is available on the freebsd ports so just install it</p>
<div class="highlight"><pre><span class="nv">$ </span>ssh notmyidea.org -p 20006
<span class="nv">$ </span>make -C /usr/ports/net/unison-nox11 config-recursive
<span class="nv">$ </span>make -C /usr/ports/net/unison-nox11 package-recursive
</pre></div>
<p>Install as well unison on your local machine. Double check to install the same
version on the client and on the server. Ubuntu contains the 2.27.57 as well as
the 2.32.52.</p>
<p>Check that unison is installed and reachable via ssh from your machine</p>
<div class="highlight"><pre><span class="nv">$ </span>ssh notmyidea.org -p 20006 unison -version
unison version 2.27.157
<span class="nv">$ </span>unison -version
unison version 2.27.57
</pre></div>
</div>
<div class="section" id="let-sync-our-folders">
<h2>Let sync our folders</h2>
<p>The first thing I want to sync is my vim configuration. Well, it's already <a class="reference external" href="http://github.com/ametaireau/dotfiles/">in
a git repository</a> but let's try to use
unison for it right now.</p>
<p>I have two machines then: <cite>workspace</cite>, the jail, and <cite>ecureuil</cite> my laptop.</p>
<div class="highlight"><pre>unison .vim ssh://notmyidea.org:20006/.vim
unison .vimrc ssh://notmyidea.org:20006/.vimrc
</pre></div>
<p>It is also possible to put all the informations in a config file, and then to
only run <cite>unison</cite>. (fire up <cite>vim ~/.unison/default.prf</cite>.</p>
<p>Here is my config:</p>
<pre class="literal-block">
root = /home/alexis
root = ssh://notmyidea.org:20006
path = .vimrc
path = dotfiles
path = dev
follow = Name *
</pre>
<p>My vimrc is in fact a symbolic link on my laptop, but I don't want to specify
each of the links to unison. That's why the <cite>follow = Name *</cite> is for.</p>
<p>The folders you want to synchronize are maybe a bit large. If so, considering
others options such as rsync for the first import may be a good idea (I enjoyed
my university huge upload bandwith to upload 2GB in 20mn ;)</p>
</div>
<div class="section" id="run-the-script-frequently">
<h2>Run the script frequently</h2>
<p>Once that done, you just need to run the unison command line some times when
you want to sync your two machines. I've wrote a tiny script to get some
feedback from the sync:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="n">DEFAULT_LOGFILE</span> <span class="o">=</span> <span class="s">&quot;~/unison.log&quot;</span>
<span class="n">PROGRAM_NAME</span> <span class="o">=</span> <span class="s">&quot;Unison syncer&quot;</span>
<span class="k">def</span> <span class="nf">sync</span><span class="p">(</span><span class="n">logfile</span><span class="o">=</span><span class="n">DEFAULT_LOGFILE</span><span class="p">,</span> <span class="n">program_name</span><span class="o">=</span><span class="n">PROGRAM_NAME</span><span class="p">):</span>
<span class="c"># init</span>
<span class="n">display_message</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">error</span> <span class="o">=</span> <span class="bp">False</span>
<span class="n">before</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="c"># call unison to make the sync</span>
<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s">&#39;unison -batch &gt; {0}&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">logfile</span><span class="p">))</span>
<span class="c"># get the duration of the operation</span>
<span class="n">td</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">before</span>
<span class="n">delta</span> <span class="o">=</span> <span class="p">(</span><span class="n">td</span><span class="o">.</span><span class="n">microseconds</span> <span class="o">+</span> <span class="p">(</span><span class="n">td</span><span class="o">.</span><span class="n">seconds</span> <span class="o">+</span> <span class="n">td</span><span class="o">.</span><span class="n">days</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">3600</span><span class="p">)</span> <span class="o">*</span> <span class="mi">10</span><span class="o">**</span><span class="mi">6</span><span class="p">)</span> <span class="o">/</span> <span class="mi">10</span><span class="o">**</span><span class="mi">6</span>
<span class="c"># check what was the last entry in the log</span>
<span class="n">log</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="n">logfile</span><span class="p">))</span>
<span class="n">lines</span> <span class="o">=</span> <span class="n">log</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span>
<span class="k">if</span> <span class="s">&#39;No updates to propagate&#39;</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
<span class="n">display_message</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">output</span> <span class="o">=</span> <span class="p">[</span><span class="n">l</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">lines</span> <span class="k">if</span> <span class="s">&quot;Synchronization&quot;</span> <span class="ow">in</span> <span class="n">l</span><span class="p">]</span>
<span class="n">message</span> <span class="o">=</span> <span class="n">output</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">message</span> <span class="o">+=</span> <span class="s">&quot; It took {0}s.&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
<span class="k">if</span> <span class="n">display_message</span><span class="p">:</span>
<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s">&#39;notify-send -i {2} &quot;{0}&quot; &quot;{1}&quot;&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">program_name</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span>
<span class="s">&#39;error&#39;</span> <span class="k">if</span> <span class="n">error</span> <span class="k">else</span> <span class="s">&#39;info&#39;</span><span class="p">))</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span>
<span class="n">sync</span><span class="p">()</span>
</pre></div>
<p>This is probably perfectible, but that does the job.</p>
<p>Last step is to tell you machine to run that frequently. That's what <cite>crontab</cite>
is made for, so let's <cite>crontab -e</cite>:</p>
<pre class="literal-block">
$ * */3 * * * . ~/.Xdbus; /usr/bin/python /home/alexis/dev/python/unison-syncer/sync.py
</pre>
<p>The <cite>~/.Xdbus</cite> allows cron to communicate with your X11 session. Here is its
content.</p>
<div class="highlight"><pre><span class="c">#!/bin/bash</span>
<span class="c"># Get the pid of nautilus</span>
<span class="nv">nautilus_pid</span><span class="o">=</span><span class="k">$(</span>pgrep -u <span class="nv">$LOGNAME</span> -n nautilus<span class="k">)</span>
<span class="c"># If nautilus isn&#39;t running, just exit silently</span>
<span class="k">if</span> <span class="o">[</span> -z <span class="s2">&quot;$nautilus_pid&quot;</span> <span class="o">]</span>; <span class="k">then</span>
<span class="nb">exit </span>0
<span class="k">fi</span>
<span class="c"># Grab the DBUS_SESSION_BUS_ADDRESS variable from nautilus&#39;s environment</span>
<span class="nb">eval</span> <span class="k">$(</span>tr <span class="s1">&#39;\0&#39;</span> <span class="s1">&#39;\n&#39;</span> &lt; /proc/<span class="nv">$nautilus_pid</span>/environ | grep <span class="s1">&#39;^DBUS_SESSION_BUS_ADDRESS=&#39;</span><span class="k">)</span>
<span class="c"># Check that we actually found it</span>
<span class="k">if</span> <span class="o">[</span> -z <span class="s2">&quot;$DBUS_SESSION_BUS_ADDRESS&quot;</span> <span class="o">]</span>; <span class="k">then</span>
<span class="nb">echo</span> <span class="s2">&quot;Failed to find bus address&quot;</span> &gt;&amp;2
<span class="nb">exit </span>1
<span class="k">fi</span>
<span class="c"># export it so that child processes will inherit it</span>
<span class="nb">export </span>DBUS_SESSION_BUS_ADDRESS
</pre></div>
<p>And it comes from <a class="reference external" href="http://ubuntuforums.org/showthread.php?p=10148738#post10148738">here</a>.</p>
<p>A sync takes about 20s + the upload time on my machine, which stay acceptable for
all of my developments.</p>
</div>
<div class="comments">
<h2>Comments</h2>
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_identifier = "working-directly-on-your-server-how-to-backup-and-sync-your-dev-environment-with-unison.html";
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://blog-notmyidea.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
</div>
</div>
</body>
</html>