<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>performances Archives | Clever Cloud</title>
	<atom:link href="https://www.clever.cloud/blog/tag/performances/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.clever.cloud/blog/tag/performances/</link>
	<description>From Code to Product</description>
	<lastBuildDate>Tue, 09 Jun 2015 14:01:00 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://cdn.clever-cloud.com/uploads/2023/03/cropped-cropped-favicon-32x32.png</url>
	<title>performances Archives | Clever Cloud</title>
	<link>https://www.clever.cloud/blog/tag/performances/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How We Cut Latency Down by 30k% on Our Git Server</title>
		<link>https://www.clever.cloud/blog/engineering/2015/06/09/git-server-30k-improvement/</link>
		
		<dc:creator><![CDATA[Marc-Antoine Perennou]]></dc:creator>
		<pubDate>Tue, 09 Jun 2015 14:01:00 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[performances]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2015/06/09/git-server-30k-improvement/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="ars perfs 1" decoding="async" fetchpriority="high" srcset="https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1.jpg 1400w, https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1-300x116.jpg 300w, https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1-1024x395.jpg 1024w, https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1-768x296.jpg 768w, https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1-1368x528.jpg 1368w" sizes="(max-width: 1400px) 100vw, 1400px" /></p><p>At Clever Cloud, git is core in the deployment process. With the years, our needs evolved, especially in terms of performance. This is how we dealt with it.</p>
<span id="more-2800"></span>

<h3 id="a-bit-of-background-first">A bit of background first</h3>
<p>When we started Clever Cloud, we chose to use <a href="http://gitolite.com/gitolite/index.html">gitolite</a> to manage our git repositories. It appeared to be a complete and functional solution. We used it internally first, for managing our own internal repositories even when the product wasn&#39;t released yet.</p>
<p>After several months of testing, we were convinced that it was a really good solution, and chose to go with it.</p>
<h4 id="managing-gitolite-configuration">Managing gitolite configuration</h4>
<p>gitolite is primary designed to be configured manually by a sysadmin. You have to describe every user, group of users and repositories in configuration files. Gitolite will then use these files to generate its internal stuff for dealing with access rights. Gitolite ensures repositories are created and hooks propagated.</p>
<p>We were in the need of automatic reconfiguration, for quite obvious reasons. We therefore developed a tool called <code>etilotig</code> which aim was to keep gitolite&#39;s configuration up to date. On startup, this tool would load its initial configuration from the API and then listen to <a href="https://www.amqp.org/">AMQP</a> events to update its configuration cache and write the new gitolite configuration accordingly.</p>
<p>It worked actually quite well for longer than we expected, and we used it in production until May 5th 2015.</p>
<h3 id="the-drawbacks-of-gitolite">The drawbacks of gitolite</h3>
<p>gitolite was great but had a few major drawbacks we weren&#39;t happy with.</p>
<ul>
<li>as previously said, it wasn&#39;t meant to be dynamically configurable, which required some non-trivial hacks</li>
<li>it required duplicating the configuration in both the API and gitolite itself</li>
<li>all the repositories were created in a same directory</li>
<li>at each repository creation, it did a full pass on all the repositories to check whether the hooks were up to date
or not</li>
<li>rewriting only part of its configuration wasn&#39;t trivial at all so we ended up rewriting most of it for each change</li>
</ul>
<p>Those problems were annoying but not critical at the beginning, but some of them became quite interesting to us.</p>
<p>The fact that all repositories are created in a same directory is a huge performance issue when the number of repositories become really high.</p>
<p>We actually dropped the part that checks for the hooks at each repository creation from the gitolite code a while back as it took half the time of the gitolite internal configuration regeneration on update.</p>
<h3 id="the-new-etilotig">The new etilotig</h3>
<p>While gitolite used to be efficient, lately it had a lot of performance problems, and depending on the timing of the events, it could take up to five (!) minutes to create a new repository. This went far beyond our acceptable limits, so we had to find another solution.</p>
<p>The idea was simple: drop gitolite totally and improve our configuration management tool etilotig to do what was needed by itself.</p>
<p>The checklist we needed to accomplish was quite tiny:</p>
<ul>
<li>ssh keys management</li>
<li>authorization management</li>
<li>repositories creation</li>
<li>hooks installation</li>
</ul>
<h4 id="ssh-keys-management">Ssh keys management</h4>
<p>When its goal was only to manage the gitolite configuration, etilotig only forwarded them to gitolite, which in turn handled them. Now, we have to manage the <code>authorized_keys</code> file by ourselves.</p>
<p>We basically write a new one each time an ssh key is added or removed and then replace the old one with the new one.</p>
<p>Each line is printed as such:</p>
<pre><code class="language-text">command=&quot;AUTH_SCRIPT USER_ID&quot;,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty PUBLIC_KEY
</code></pre>
<p>With <code>AUTH_SCRIPT</code> pointing to the authorization script that I&#39;ll speak of later, <code>USER_ID</code> being the user id of the key owner and <code>PUBLIC_KEY</code> being their public key.</p>
<p>The authorization script will thus be called with the user id as first parameter.</p>
<p>That solves the first point of our TODO list.</p>
<h4 id="repositories-management">Repositories management</h4>
<p>At startup, etilotig writes some bash configuration files defining basic things such as the directory in which repositories have to be created.</p>
<p>Then we have a tiny bash script in charge of the repository creation. We now create them in a deeper directory hierarchy to get as few repositories as possible per directory. Say we had <code>/data/app_18c6021b-0860-4f97-a08d-0663f45cf3f0.git</code> before, we now have <code>/data/app_18/c6/02/app_18c6021b-0860-4f97-a08d-0663f45cf3f0.git</code> which makes things waaaaay faster.</p>
<pre><code class="language-bash">#!/bin/bash

create_repo() {
local repo_dir=&quot;${1}&quot;

mkdir -p &quot;${repo_dir}&quot;
pushd &quot;${repo_dir}&quot; &amp;&gt;/dev/null
git init --bare
popd &amp;&gt;/dev/null
}

main() {
    local repo=&quot;${1}&quot;
    local repo_dir
    
repo_dir=&quot;${REPOS_DIR}/${repo:0:6}/${repo:6:2}/${repo:8:2}/${repo}&quot;

[[ -d &quot;${repo_dir}&quot;/hooks ]] || create_repo &quot;${repo_dir}&quot;
}

. &quot;${HOME}&quot;/.etilotig/.etilotigrc

main &quot;${@}&quot;
</code></pre>
<p>Then we have another one for hooks installation.</p>
<pre><code class="language-bash">#!/bin/bash

shopt -s nullglob

main() {
    local repo=&quot;${1}&quot;
    local repo_dir
    
repo_dir=&quot;${REPOS_DIR}/${repo:0:6}/${repo:6:2}/${repo:8:2}/${repo}&quot;

for hook in ${HOME}/.etilotig/hooks/*; do
ln -sf &quot;${hook}&quot; &quot;${repo_dir}&quot;/hooks/
done
}

. &quot;${HOME}&quot;/.etilotig/.etilotigrc

main &quot;${@}&quot;
</code></pre>
<p>With those two simple scripts, we only have to call them for each repository at startup to ensure everything is OK, and at each repository creation, which solves two of the four points from our TODO list, only one left.</p>
<h4 id="authorization">Authorization</h4>
<p>Now, the goal was <em>not</em> to duplicate the configuration anymore, but rather use the configuration from the API, thus externalising the whole thing. Dropping all the configuration management from etilotig reduced its size by more than 50%.</p>
<p>The way etilotig manages authorization is quite simple: when it generates its internal configuration, it actually generates a perl script which is called on each ssh connection attempt. The script then authorises or not the transaction.</p>
<p>We made a similar script which prints the users some information about which repositories they have access to if they just run <code>ssh git@push.par.clever-cloud.com</code> or such, checks if they are authorized when they try to git push/pull or rejects any other request.</p>
<p>It looks like this (with some extra stuff added):</p>
<pre><code class="language-bash">#!/bin/bash

sanity_check() {
    if [[ -z &quot;${SSH_CONNECTION}&quot; ]]; then
        echo &quot;Who the hell are you?&quot; &gt;&amp;2
        exit 1
    fi

if [[ -z &quot;${SSH_ORIGINAL_COMMAND}&quot; ]]; then
        export SSH_ORIGINAL_COMMAND=&quot;info&quot;
fi
}

ask_for_info() {
    local userid=&quot;${1}&quot;

# make the request to the API to retrieve user info message
echo &quot;some info&quot;
}

ask_for_authorization() {
    local userid=&quot;${1}&quot;
    local appid=&quot;${2}&quot;

# make the request and return the HTTP status code here. 200 means authorized.
echo 200
}

authorize() {
    local userid=&quot;${1}&quot;
    local verb=&quot;${2}&quot;
    local appid=&quot;${3}&quot;
    local ret=1
    case &quot;${verb}&quot; in
        &quot;git-receive-pack&quot;|&quot;git-upload-pack&quot;)
            local code
            code=$(ask_for_authorization &quot;${userid}&quot; &quot;${appid}&quot;)
            [[ &quot;${code}&quot; == &quot;200&quot; ]] &amp;&amp; ret=0
            ;;
    esac
    return &quot;${ret}&quot;
}

final_abort() {
    echo &quot;What are you trying to achieve here?&quot; &gt;&amp;2
    exit 2
}
main() {
    sanity_check
    local userid=&quot;${1}&quot;
    local verb
    local repo
    local repo_dir
    verb=$(echo &quot;${SSH_ORIGINAL_COMMAND}&quot; | cut -d &#39; &#39; -f 1)
    repo=$(echo &quot;${SSH_ORIGINAL_COMMAND}&quot; | cut -d &#39; &#39; -f 2 | tr -d &quot;&#39;\&quot;&quot;)
    [[ &quot;${repo}&quot; == /* ]] &amp;&amp; repo=${repo:1}
    repo_dir=&quot;${REPOS_DIR}/${repo:0:6}/${repo:6:2}/${repo:8:2}/${repo}&quot;
    if [[ &quot;${verb}&quot; == &quot;info&quot; ]]; then
        ask_for_info &quot;${userid}&quot;
    elif authorize &quot;${userid}&quot; &quot;${verb}&quot; &quot;${repo}&quot;; then
        export CC_USER=&quot;${userid}&quot;
        export CC_NOTIFY_SCRIPT=&quot;${HOME}/.etilotig/send-push-event&quot;
        exec &quot;${verb}&quot; &quot;${repo_dir}&quot;
    else
        final_abort
    fi
}
. &quot;${HOME}&quot;/.etilotig/.etilotigrc
main &quot;${@}&quot;
</code></pre>
<p>With this in place, nearly everything was ready. Two tiny hooks on top of that to only allow users to push on the master branch, and to trigger a deployment on git push:</p>
<p><code>hooks/update</code></p>
<pre><code class="language-bash">#!/bin/bash

main() {
    local rev=&quot;${1}&quot;
    if [[ &quot;${rev}&quot; == refs/tags/* ]]; then
        exit 0
    fi
    if [[ &quot;${rev}&quot; != &quot;refs/heads/master&quot; ]]; then
        echo &quot;You tried to push to a custom branch.&quot;
        echo &quot;Only master is allowed.&quot;
        exit 1
    fi
}

main &quot;${@}&quot;
</code></pre>
<p><code>hooks/post-update</code></p>
<pre><code class="language-bash">#!/bin/bash

sanity_check() {
    local rev=&quot;${1}&quot;
    if [[ &quot;${rev}&quot; == refs/tags/* ]]; then
        exit 0
    fi
}

main () {
    local rev=&quot;${1}&quot;
    sanity_check &quot;${rev}&quot;
    local repo=$(basename $(pwd))
    local appId=${repo/.git/}
    local commitId=$(git rev-parse &quot;${rev}&quot;)
    &quot;${CC_NOTIFY_SCRIPT}&quot; &quot;${appId}&quot; &quot;${commitId}&quot; &quot;${CC_USER}&quot;
    echo &quot;[SUCCESS] The application has successfully been queued for redeploy.&quot;
}

main &quot;${@}&quot;
</code></pre>
<h3 id="conclusion">Conclusion</h3>
<p>That&#39;s it, we have our new git server manager up and running, which works pretty well.</p>
<p>The performance gain? We went from between 3 and more than 5 minutes to less than 1 second per action, while dropping the whole gitolite codebase and reducing the size of etilotig by 50%, with an average performance gain of 30k%.</p>
<p>gitolite has been very useful both in its utilisation and its codebase to better comprehend the whole authentication mechanism, so a great thanks to this awesome tool.</p>
<p>Now gitolite is dead, long live etilotig!</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="ars perfs 1" decoding="async" srcset="https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1.jpg 1400w, https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1-300x116.jpg 300w, https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1-1024x395.jpg 1024w, https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1-768x296.jpg 768w, https://cdn.clever-cloud.com/uploads/2021/08/ars-perfs-1-1368x528.jpg 1368w" sizes="(max-width: 1400px) 100vw, 1400px" /></p><p>At Clever Cloud, git is core in the deployment process. With the years, our needs evolved, especially in terms of performance. This is how we dealt with it.</p>
<span id="more-2800"></span>

<h3 id="a-bit-of-background-first">A bit of background first</h3>
<p>When we started Clever Cloud, we chose to use <a href="http://gitolite.com/gitolite/index.html">gitolite</a> to manage our git repositories. It appeared to be a complete and functional solution. We used it internally first, for managing our own internal repositories even when the product wasn&#39;t released yet.</p>
<p>After several months of testing, we were convinced that it was a really good solution, and chose to go with it.</p>
<h4 id="managing-gitolite-configuration">Managing gitolite configuration</h4>
<p>gitolite is primary designed to be configured manually by a sysadmin. You have to describe every user, group of users and repositories in configuration files. Gitolite will then use these files to generate its internal stuff for dealing with access rights. Gitolite ensures repositories are created and hooks propagated.</p>
<p>We were in the need of automatic reconfiguration, for quite obvious reasons. We therefore developed a tool called <code>etilotig</code> which aim was to keep gitolite&#39;s configuration up to date. On startup, this tool would load its initial configuration from the API and then listen to <a href="https://www.amqp.org/">AMQP</a> events to update its configuration cache and write the new gitolite configuration accordingly.</p>
<p>It worked actually quite well for longer than we expected, and we used it in production until May 5th 2015.</p>
<h3 id="the-drawbacks-of-gitolite">The drawbacks of gitolite</h3>
<p>gitolite was great but had a few major drawbacks we weren&#39;t happy with.</p>
<ul>
<li>as previously said, it wasn&#39;t meant to be dynamically configurable, which required some non-trivial hacks</li>
<li>it required duplicating the configuration in both the API and gitolite itself</li>
<li>all the repositories were created in a same directory</li>
<li>at each repository creation, it did a full pass on all the repositories to check whether the hooks were up to date
or not</li>
<li>rewriting only part of its configuration wasn&#39;t trivial at all so we ended up rewriting most of it for each change</li>
</ul>
<p>Those problems were annoying but not critical at the beginning, but some of them became quite interesting to us.</p>
<p>The fact that all repositories are created in a same directory is a huge performance issue when the number of repositories become really high.</p>
<p>We actually dropped the part that checks for the hooks at each repository creation from the gitolite code a while back as it took half the time of the gitolite internal configuration regeneration on update.</p>
<h3 id="the-new-etilotig">The new etilotig</h3>
<p>While gitolite used to be efficient, lately it had a lot of performance problems, and depending on the timing of the events, it could take up to five (!) minutes to create a new repository. This went far beyond our acceptable limits, so we had to find another solution.</p>
<p>The idea was simple: drop gitolite totally and improve our configuration management tool etilotig to do what was needed by itself.</p>
<p>The checklist we needed to accomplish was quite tiny:</p>
<ul>
<li>ssh keys management</li>
<li>authorization management</li>
<li>repositories creation</li>
<li>hooks installation</li>
</ul>
<h4 id="ssh-keys-management">Ssh keys management</h4>
<p>When its goal was only to manage the gitolite configuration, etilotig only forwarded them to gitolite, which in turn handled them. Now, we have to manage the <code>authorized_keys</code> file by ourselves.</p>
<p>We basically write a new one each time an ssh key is added or removed and then replace the old one with the new one.</p>
<p>Each line is printed as such:</p>
<pre><code class="language-text">command=&quot;AUTH_SCRIPT USER_ID&quot;,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty PUBLIC_KEY
</code></pre>
<p>With <code>AUTH_SCRIPT</code> pointing to the authorization script that I&#39;ll speak of later, <code>USER_ID</code> being the user id of the key owner and <code>PUBLIC_KEY</code> being their public key.</p>
<p>The authorization script will thus be called with the user id as first parameter.</p>
<p>That solves the first point of our TODO list.</p>
<h4 id="repositories-management">Repositories management</h4>
<p>At startup, etilotig writes some bash configuration files defining basic things such as the directory in which repositories have to be created.</p>
<p>Then we have a tiny bash script in charge of the repository creation. We now create them in a deeper directory hierarchy to get as few repositories as possible per directory. Say we had <code>/data/app_18c6021b-0860-4f97-a08d-0663f45cf3f0.git</code> before, we now have <code>/data/app_18/c6/02/app_18c6021b-0860-4f97-a08d-0663f45cf3f0.git</code> which makes things waaaaay faster.</p>
<pre><code class="language-bash">#!/bin/bash

create_repo() {
local repo_dir=&quot;${1}&quot;

mkdir -p &quot;${repo_dir}&quot;
pushd &quot;${repo_dir}&quot; &amp;&gt;/dev/null
git init --bare
popd &amp;&gt;/dev/null
}

main() {
    local repo=&quot;${1}&quot;
    local repo_dir
    
repo_dir=&quot;${REPOS_DIR}/${repo:0:6}/${repo:6:2}/${repo:8:2}/${repo}&quot;

[[ -d &quot;${repo_dir}&quot;/hooks ]] || create_repo &quot;${repo_dir}&quot;
}

. &quot;${HOME}&quot;/.etilotig/.etilotigrc

main &quot;${@}&quot;
</code></pre>
<p>Then we have another one for hooks installation.</p>
<pre><code class="language-bash">#!/bin/bash

shopt -s nullglob

main() {
    local repo=&quot;${1}&quot;
    local repo_dir
    
repo_dir=&quot;${REPOS_DIR}/${repo:0:6}/${repo:6:2}/${repo:8:2}/${repo}&quot;

for hook in ${HOME}/.etilotig/hooks/*; do
ln -sf &quot;${hook}&quot; &quot;${repo_dir}&quot;/hooks/
done
}

. &quot;${HOME}&quot;/.etilotig/.etilotigrc

main &quot;${@}&quot;
</code></pre>
<p>With those two simple scripts, we only have to call them for each repository at startup to ensure everything is OK, and at each repository creation, which solves two of the four points from our TODO list, only one left.</p>
<h4 id="authorization">Authorization</h4>
<p>Now, the goal was <em>not</em> to duplicate the configuration anymore, but rather use the configuration from the API, thus externalising the whole thing. Dropping all the configuration management from etilotig reduced its size by more than 50%.</p>
<p>The way etilotig manages authorization is quite simple: when it generates its internal configuration, it actually generates a perl script which is called on each ssh connection attempt. The script then authorises or not the transaction.</p>
<p>We made a similar script which prints the users some information about which repositories they have access to if they just run <code>ssh git@push.par.clever-cloud.com</code> or such, checks if they are authorized when they try to git push/pull or rejects any other request.</p>
<p>It looks like this (with some extra stuff added):</p>
<pre><code class="language-bash">#!/bin/bash

sanity_check() {
    if [[ -z &quot;${SSH_CONNECTION}&quot; ]]; then
        echo &quot;Who the hell are you?&quot; &gt;&amp;2
        exit 1
    fi

if [[ -z &quot;${SSH_ORIGINAL_COMMAND}&quot; ]]; then
        export SSH_ORIGINAL_COMMAND=&quot;info&quot;
fi
}

ask_for_info() {
    local userid=&quot;${1}&quot;

# make the request to the API to retrieve user info message
echo &quot;some info&quot;
}

ask_for_authorization() {
    local userid=&quot;${1}&quot;
    local appid=&quot;${2}&quot;

# make the request and return the HTTP status code here. 200 means authorized.
echo 200
}

authorize() {
    local userid=&quot;${1}&quot;
    local verb=&quot;${2}&quot;
    local appid=&quot;${3}&quot;
    local ret=1
    case &quot;${verb}&quot; in
        &quot;git-receive-pack&quot;|&quot;git-upload-pack&quot;)
            local code
            code=$(ask_for_authorization &quot;${userid}&quot; &quot;${appid}&quot;)
            [[ &quot;${code}&quot; == &quot;200&quot; ]] &amp;&amp; ret=0
            ;;
    esac
    return &quot;${ret}&quot;
}

final_abort() {
    echo &quot;What are you trying to achieve here?&quot; &gt;&amp;2
    exit 2
}
main() {
    sanity_check
    local userid=&quot;${1}&quot;
    local verb
    local repo
    local repo_dir
    verb=$(echo &quot;${SSH_ORIGINAL_COMMAND}&quot; | cut -d &#39; &#39; -f 1)
    repo=$(echo &quot;${SSH_ORIGINAL_COMMAND}&quot; | cut -d &#39; &#39; -f 2 | tr -d &quot;&#39;\&quot;&quot;)
    [[ &quot;${repo}&quot; == /* ]] &amp;&amp; repo=${repo:1}
    repo_dir=&quot;${REPOS_DIR}/${repo:0:6}/${repo:6:2}/${repo:8:2}/${repo}&quot;
    if [[ &quot;${verb}&quot; == &quot;info&quot; ]]; then
        ask_for_info &quot;${userid}&quot;
    elif authorize &quot;${userid}&quot; &quot;${verb}&quot; &quot;${repo}&quot;; then
        export CC_USER=&quot;${userid}&quot;
        export CC_NOTIFY_SCRIPT=&quot;${HOME}/.etilotig/send-push-event&quot;
        exec &quot;${verb}&quot; &quot;${repo_dir}&quot;
    else
        final_abort
    fi
}
. &quot;${HOME}&quot;/.etilotig/.etilotigrc
main &quot;${@}&quot;
</code></pre>
<p>With this in place, nearly everything was ready. Two tiny hooks on top of that to only allow users to push on the master branch, and to trigger a deployment on git push:</p>
<p><code>hooks/update</code></p>
<pre><code class="language-bash">#!/bin/bash

main() {
    local rev=&quot;${1}&quot;
    if [[ &quot;${rev}&quot; == refs/tags/* ]]; then
        exit 0
    fi
    if [[ &quot;${rev}&quot; != &quot;refs/heads/master&quot; ]]; then
        echo &quot;You tried to push to a custom branch.&quot;
        echo &quot;Only master is allowed.&quot;
        exit 1
    fi
}

main &quot;${@}&quot;
</code></pre>
<p><code>hooks/post-update</code></p>
<pre><code class="language-bash">#!/bin/bash

sanity_check() {
    local rev=&quot;${1}&quot;
    if [[ &quot;${rev}&quot; == refs/tags/* ]]; then
        exit 0
    fi
}

main () {
    local rev=&quot;${1}&quot;
    sanity_check &quot;${rev}&quot;
    local repo=$(basename $(pwd))
    local appId=${repo/.git/}
    local commitId=$(git rev-parse &quot;${rev}&quot;)
    &quot;${CC_NOTIFY_SCRIPT}&quot; &quot;${appId}&quot; &quot;${commitId}&quot; &quot;${CC_USER}&quot;
    echo &quot;[SUCCESS] The application has successfully been queued for redeploy.&quot;
}

main &quot;${@}&quot;
</code></pre>
<h3 id="conclusion">Conclusion</h3>
<p>That&#39;s it, we have our new git server manager up and running, which works pretty well.</p>
<p>The performance gain? We went from between 3 and more than 5 minutes to less than 1 second per action, while dropping the whole gitolite codebase and reducing the size of etilotig by 50%, with an average performance gain of 30k%.</p>
<p>gitolite has been very useful both in its utilisation and its codebase to better comprehend the whole authentication mechanism, so a great thanks to this awesome tool.</p>
<p>Now gitolite is dead, long live etilotig!</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
