<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset='utf-8'>
<title>Gitolite</title>
</head>
<body>
<a href="index.html">Tools Index</a>
<h1>Gitolite</h1>
<h2 id="install">1. Install Gitolite</h2>
<p>Install <a href="../ports/gitolite/">Gitolite port</a> first;</p>
<pre>
$ prt-get depinst gitolite
</pre>
<h2 id="config">2. Configure Gitolite</h2>
<p>Create user and home directory;</p>
<pre>
# useradd -r -s /bin/bash -U -m -d /srv/gitolite gitolite
# chown gitolite:gitolite /srv/gitolite
</pre>
<p>Password is necessary so the user is not
locked and can login via ssh. This password
will not be used.</p>
<pre>
# passwd gitolite
</pre>
<p>Copy public key that will be used for
authenticating gitolite administrator, read
<a href="openssh.html#ssh">ssh</a> how to
create one, key don't need to have password.
If the server is on remote a remote
machine you can use scp to copy the public key;</p>
<pre>
$ scp /home/bob/.ssh/gitolite.pub admin@core.privat-network.net:/home/admin/
bob@core.privat-network.net's password:
gitolite.pub 100% 390 0.4KB/s 00:00
</pre>
<p>Then on remote machine;</p>
<pre>
# cp /home/admin/gitolite.pub /srv/gitolite.pub
</pre>
<pre>
# su - gitolite
$ gitolite setup -pk gitolite.pub
Initialized empty Git repository in /srv/gitolite/repositories/gitolite-admin.git/
Initialized empty Git repository in /srv/gitolite/repositories/testing.git/
WARNING: /srv/gitolite/.ssh missing; creating a new one
(this is normal on a brand new install)
WARNING: /srv/gitolite/.ssh/authorized_keys missing; creating a new one
(this is normal on a brand new install)
$
</pre>
<pre>
$ rm gitolite.pub
$ exit
</pre>
<pre>
$ ssh -v -i ~/.ssh/gitolite gitolite@localhost -p 2222
</pre>
<h2 id="admin">3. Gitolite Administration</h2>
<p>Read how to setup <a href="openssh.html#sshid">ssh identities</a>,
<a href="http://gitolite.com/gitolite/gitolite.html#basic-admin">gitolite documentation</a>.
Start by cloning gitolite-adimin;</p>
<pre>
$ git clone git-admin:gitolite-admin
</pre>
<h3>3.1. Recover Admin Account</h3>
<p>First copy the key to remote server, in this example key is on
same server;</p>
<pre>
# install -o gitolite -g gitolite /home/bob/.ssh/gitolite.pub /srv/gitolite/gitolite.pub
</pre>
<p>Update gitolite key with new key;</p>
<pre>
# su - gitolite
$ gitolite setup -pk gitolite.pub
</pre>
<pre>
$ rm gitolite.pub
$ exit
</pre>
<h3 id="adminusers">3.2. Users</h3>
<p>Gitolite helps add and remove users from authorized_keys
by allowing add or remove keys from keydir directory in
clone.</p>
<h4>3.2.1. Add User</h4>
<pre>
$ mv bob.pub keydir/
$ git add keydir
$ git commit -m "Added bob public key"
$ git push
</pre>
<h4>3.2.2. Remove User</h4>
<pre>
$ git rm keydir/bob.pub
$ git commit -m "Removed bob public key"
$ git push
</pre>
<h3 id="adminrep">3.3. Repositories</h3>
<h4>3.3.1. Add Repository</h4>
<p>Add repository atom and user bob to devteam group,
edit conf/gitolite.conf;</p>
<pre>
@guests = gitweb
@interns = bob alice
@dev = fred mary joe
@teamleads = mary
@staff = @interns @dev @teamleads
repo @secret
- = @guests
option deny-rules = 1
repo @floss
RW+ = @dev @staff
R = @all
repo @project
RW+ = @teamleads
- master = @dev
- refs/tags/v[0-9] = @dev
RW+ develop/ = @dev
RW+ feature/ = @dev
RW+ hot-fix/ = @dev
RW = @dev
R = @interns
repo gitolite-admin
RW+ = gitolite
repo doc ports pmwiki assistant
config gitweb.owner = "Team"
config gitweb.category = "Projects"
repo doc
config gitweb.description = "Documentation"
option hook.post-receive = deploy-web-doc
repo ports
config gitweb.description = "System Ports"
repo pmwiki
config gitweb.description = "Wiki"
option hook.post-receive = deploy-web-doc
repo assistant
config gitweb.description = "Open Assistant"
@secret = gitolite-admin
@project = doc ports pmwiki assistant
</pre>
<p>Commit and push;</p>
<pre>
$ git add -u
$ git push
</pre>
<h4>3.3.2 Rename Repository</h4>
<p>Rename rep void to sysdoc, on remote host;</p>
<pre>
# cd /srv/gitolite/repositories/
# mv void.git doc.git
</pre>
<p>On workstation edit conf/gitolite.conf;</p>
<pre>
repo doc
RW+ = bob
</pre>
<p>Commit and push;</p>
<pre>
$ git add -u
$ git push
</pre>
<h4>3.3.3 Delete Repository</h4>
<pre>
# cd /srv/gitolite/repositories/
# rm -rf doc.git
</pre>
<p>On workstation edit conf/gitolite.conf and remove c9-doc.</p>
<h2 id="hooks">4. Gitolite Hooks</h2>
<p>This document creates three scripts, one is run when gitolite receives
push to a project with hook active, second script is run under root
user to allow operations where gitolite user have no rights, third one
is project specific.</p>
<p>This example try to have a separate creation of a package and its deployment,
in case deploy script is not on the same machine other method can be used to send
the package.</p>
<p>A normal package will have a files.tar with all or new files to extract,
if necessary a deleted file with the list of files to be removed and a
project file with data about the package like new hash commit, or witch
script to call to deploy.</p>
<p>Package is created under gitolite
<a href="conf/srv/gitolite/hook.sh">/srv/gitolite/deploy/hook.sh</a>
script and
<a href="conf/srv/gitolite/deploy.sh">/srv/gitolite/deploy/deploy.sh</a>,
deploy in this example is called called by cron.</p>
<h3 id="gtl-activate">4.1. Activate Hooks</h3>
<p>Example from
<a href="http://gitolite.com/gitolite/cookbook.html#v3.6-variation-repo-specific-hooks">Cookbook</a>
how to apply hooks only to certain repos. Uncomment or add
this line on <a href="conf/srv/gitolite/.gitolite.rc">/srv/gitolite/.gitolite.rc</a>,
within the %RC block;</p>
<pre>
LOCAL_CODE => "$rc{GL_ADMIN_BASE}/local",
</pre>
<p>Uncomment the 'repo-specific-hooks' line in the rc file
or add it to the ENABLE list if it doesn't exist.</p>
<pre>
# allow repo-specific hooks to be added
'repo-specific-hooks',
</pre>
<p>Create directory on gitolite-admin clone;</p>
<pre>
$ cd ~/gitolite-admin
$ mkdir -p local/hooks/repo-specific
</pre>
<p>Now add your hooks to that directory, but instead of using the
git "standard" names (pre-receive, post-receive, post-update),
you use descriptive names (e.g. "deploy", "RSS-post", etc).</p>
<p>On remote run;</p>
<pre>
# su - gitolite
$ mkdir .gitolite/local/hooks/repo-specific
$ gitolite setup
</pre>
<h3 id="gtl-deploy">4.2. Deploy and Hook script</h3>
<p>Create deploy directory on remote, /srv/gitolite/deploy
was chosen to have less impact on the package system;</p>
<pre>
$ sudo -u gitolite mkdir /srv/gitolite/deploy
</pre>
<p>Script
<a href="conf/srv/gitolite/hook.sh">/srv/gitolite/deploy/hook.sh</a>
receives call create_package "project-name" "git-dir"
"valid oldrev/invalid" "newrev" "script/to/call.sh" from gitolite hook
and creates a package.</p>
<pre>
$ sudo -u gitolite cp conf/srv/gitolite/hook.sh /srv/gitolite/deploy/
</pre>
<p>Script
<a href="conf/srv/gitolite/deploy.sh">/srv/gitolite/deploy/deploy.sh</a>
loops for each package, extracts, order commit hashes to create final
snapshot of files and call script to handle deploy.</p>
<pre>
$ sudo -u gitolite cp conf/srv/gitolite/deploy.sh /srv/gitolite/deploy/
</pre>
<h3 id="gtl-setup">4.3. Setup project hook</h3>
<p>Project hooks create a package by calling hook.sh script and
deploy a package being called by deploy.sh. Deploy script is a
simple example that handle multiple web projects.</p>
<h4>4.3.1. Hook Script</h4>
<p>Create
<a href="conf/srv/gitolite/deploy-web-doc">gitolite-admin/local/hooks/repo-specific/deploy-web-doc</a>;</p>
<pre>
#!/bin/bash
######################################################################
#
# Put this file in your gitolite-admin;
# ~/gitolite-admin/local/hooks/repo-specific/deploy-web-doc
#
# set host to empty to create package for each push
# or set remote host to create package based on last deployed push
# host="https://doc.localhost"
host=""
# set name of witch branch should be deployed
branch_to_deploy="deploy_branch"
######################################################################
url="$host/.last_deploy"
source /srv/gitolite/deploy/hook.sh
read oldrev newrev refname
push_branch=$(git rev-parse --symbolic --abbrev-ref $refname)
#SCRIPT_VARS=$(set)
#echo "project: $PROJECT"
#echo "local dir: $PWD" > /srv/gitolite/deploy/${GL_REPO}
#echo "${SCRIPT_VARS}" >> /srv/gitolite/deploy/${GL_REPO}
if [[ $push_branch = $branch_to_deploy ]]; then
# if host empty we make local tracking
if [[ $host = "" ]]; then
if [[ $(is_initial ${GL_REPO}) = "true" ]]; then
oldrev="initial"
fi
else
if [[ ! $(valid_url $url) = "true" ]]; then
echo "Deploy: set $url on remote to start creating packages"
exit 1
fi
oldrev=$(get_remote_rev $url)
fi
create_package ${GL_REPO} ${PWD} ${oldrev} ${newrev} "deploy-web.sh"
fi
</pre>
<p>Create
<a href="conf/srv/gitolite/deploy-web.sh">/srv/gitolite/deploy/scripts/deploy-web.sh</a>;</p>
<pre>
#!/bin/bash
pkg_path=$1
www_root="/srv/www"
www_user="nginx"
www_group="www"
pkg_file="${pkg_path}/project"
pkg_rm="${pkg_path}/deleted"
pkg_files="${pkg_path}/files"
if [ ! -f ${pkg_file} ]; then
echo "Deploy web: invalid pkg_file ${pkg_file}"
exit 1
fi
pkg_name=$(head -1 ${pkg_file})
pkg_new=$(head -3 ${pkg_file} | tail -1)
pkg_new7=$(echo ${pkg_new} | cut -c1-7)
pkg_www="${www_root}/${pkg_name}"
pkg_back="${pkg_www}/backup_deploy"
pkg_last="${pkg_www}/.last_deploy"
if [ ! -d ${pkg_www} ]; then
echo "Deploy web: invalid pkg_www ${pkg_www}"
exit 1
fi
# first backup all data
if [[ ! $(ls ${pkg_www} | grep -v "backup_deploy") = "" ]]; then
if [ ! -d ${pkg_back} ]; then
sudo -u ${www_user} mkdir -p ${pkg_back}
fi
backup_file="${pkg_back}/${pkg_name}-$(date '+%Y-%j-%H-%M-%S').tar.gz"
echo "Deploy web: making backup ${backup_file}"
sudo -u ${www_user} tar --exclude ${pkg_back} --xattrs -zcpf ${backup_file} ${pkg_www}
fi
# remove files and directories that have been deleted
if [ -f ${pkg_rm} ]; then
echo "Deploy web: files to delete:"
# first we delete files
while read deleted_file; do
deleted_file="${pkg_www}/${deleted_file}"
if [ -f ${deleted_file} ]; then
echo "file rm ${deleted_file}"
rm ${deleted_file}
fi
done <${pkg_rm}
# delete directories
while read deleted_file; do
deleted_file="${pkg_www}/${deleted_file}"
if [ -d ${deleted_file} ]; then
echo "file rm ${deleted_file}"
rm ${deleted_file}
fi
done <${pkg_rm}
fi
# copy new files to destination
if [ -d ${pkg_files} ]; then
echo "Deploy web: cp from ${pkg_files} to ${pkg_www}"
sudo -u ${www_user} cp -r ${pkg_files}/* ${pkg_www}
fi
echo ${pkg_new} > ${pkg_last}
echo "Deploy: scripts/deployweb.sh ${pkg_name} ${pkg_new7} deployed."
#remove temporary package
rm -r ${pkg_path}
</pre>
<p>Activate this hook, the idea is to start with this one as a template working
and then implement the final one. Edit gitolite admin configuration file and
activate:</p>
<pre>
repo doc
config gitweb.description = "Documentation"
option hook.post-receive = deploy-web-doc
</pre>
<p>Add, commit, and push the admin repo;</p>
<pre>
$ git add local/hooks/repo-specific/hook-deployweb
$ git add -u && git commit -m "added deploy c9 hook"
</pre>
<p>Now we can test if our script is functioning by creating a branch on c9-doc
making a random change and push;<p>
<pre>
$ cd c9-doc
$ git checkout -b deploy_branch
</pre>
<h4>4.3.2. Deploy Script</h4>
<p>Create <a href="conf/srv/gitolite/deploy.sh">/srv/gitolite/deploy/scripts/deploy.sh</a>;</p>
<pre>
#!/bin/bash
# origin package directory
packages_dir="/srv/gitolite/deploy/packages"
# temporary work directory
deploy_dir="/srv/gitolite/deploy/deploy_dir"
# scripts to deploy packages
deploy_scripts="/srv/gitolite/deploy/scripts"
function get_script(){
# receives package path return script to call
local pkg_path=$1
echo $(head -2 ${pkg_path}/project | tail -1)
}
function get_new(){
# receives package path return commit hash (new)
local pkg_path=$1
echo $(head -3 ${pkg_path}/project | tail -1)
}
function get_dep(){
# receives package path return previews commit hash (old)
local pkg_path=$1
new=$(head -3 ${pkg_path}/project | tail -1)
old=$(head -4 ${pkg_path}/project | tail -1)
if [[ ! ${new} = ${old} ]]; then
echo ${old} | cut -c1-7
fi
}
function project_extract(){
# project directory containing extracted packages
local prj_dir=$1
# final extracted package
local prj_pkg="${prj_dir}/package"
# temporary vars for swapping/iterating pkg_news
local pkg_new=""
local pkg_old=""
local pkg_dir=""
local pkg_temp=""
local pkg_next=1
local pkg_del=""
local x=0
local y=0
# array with all the news hashes
local pkg_news=($(ls ${prj_dir}))
# total new packages
local total=${#pkg_news[@]}
echo "Deploy: $(basename ${prj_dir}) extracting packages ${pkg_news[*]}"
# find first package
for pkg_new in ${pkg_news[@]}
do
# get package dependency
pkg_dir="${prj_dir}/${pkg_new}"
pkg_old=$(get_dep ${pkg_dir})
if [[ ! " ${pkg_news[@]} " =~ " ${pkg_old} " ]]; then
# pkg_news don't contain package
# we found initial package
pkg_temp=${pkg_news[0]}
pkg_news[0]=${pkg_new}
pkg_news[${x}]=${pkg_temp}
break
fi
x=$((${x}+1))
done
# Order packages by dependency start with first package
for (( y=0; y<${total}; y++ ))
do
pkg_next=$(($y+1))
if [[ ${pkg_next} = ${total} ]]; then
## we are in the last one
break
fi
pkg_new=${pkg_news[$y]}
for (( x=pkg_next; x<${total}; x++ ))
do
pkg_dir="${prj_dir}/${pkg_news[${x}]}"
pkg_old=$(get_dep ${pkg_dir})
# is dependent on current
if [[ ${pkg_old} = ${pkg_new} ]]; then
pkg_temp=${pkg_news[${pkg_next}]}
pkg_news[${pkg_next}]=${pkg_news[${x}]}
pkg_news[${x}]=${pkg_temp}
# we can break and pass next one
break
fi
done
done
# create project final package directory
mkdir -p ${prj_pkg}/files
# copy project information of last commit
cp ${prj_dir}/${pkg_news[$((${total}-1))]}/project ${prj_pkg}
# now that packages are ordered we can start creating files
for pkg_new in ${pkg_news[@]}
do
pkg_dir=${prj_dir}/${pkg_new}
tar xf ${pkg_dir}/files.tar.xz \
--directory ${prj_pkg}/files
# if deleted files exists
if [ -f "${pkg_dir}/deleted" ]; then
# first collect all files/directories don't exist
while read pkg_del; do
# if file don't exist add entry to project deleted file
pkg_temp="${prj_pkg}/files/${pkg_del}"
if [ ! -f ${pkg_temp} ]; then
if [ ! -d ${pkg_temp} ]; then
# is not a file or directory from previous packages
echo ${pkg_del} >> ${prj_pkg}/deleted
fi
fi
done <${prj_dir}/${pkg_new}/deleted
# delete directories and files
while read pkg_del; do
pkg_temp="${prj_pkg}/files/${pkg_del}"
if [ -d ${pkg_temp} ]; then
rm -r ${pkg_temp}
elif [ -f ${pkg_temp} ]; then
rm ${pkg_temp}
fi
done <${prj_dir}/${pkg_new}/deleted
fi
#remove temporary directory
rm -r ${prj_dir}/${pkg_new}
done
# call project deploy script
call_script=${deploy_scripts}/$(get_script $prj_pkg)
echo "Deploy: calling deploy script: ${call_script}"
/bin/bash ${call_script} ${prj_pkg}
}
if [[ ! $(ls ${deploy_dir}) = "" ]]; then
rm -r ${deploy_dir}/*
fi
# first extract all packages from origin directory
for pkg_path in `find ${packages_dir} -type f -name "*.tar.gz"`
do
if [ -f ${pkg_path} ]; then
pkg_name=$(basename ${pkg_path})
pkg_proj=$(echo ${pkg_name} | cut -d "_" -f 1)
pkg_new7=$(echo ${pkg_name} | tail -c -15 | cut -c -7)
pkg_temp=${deploy_dir}/${pkg_proj}/${pkg_new7}
mkdir -p ${pkg_temp}
tar xf ${pkg_path} --directory ${pkg_temp}
rm ${pkg_path}
fi
done
# loop for all projects and deploy them
for prj_dir in `find ${deploy_dir} -maxdepth 1 -mindepth 1 -type d`
do
# order index of hashes based on old commit
echo "prj_dir $prj_dir"
project_extract ${prj_dir}
done
</pre>
<p>Add cron job to call deploy script every minute;</p>
<pre>
# crontab -e
</pre>
<pre>
#
# /etc/crontab: crond(8) configuration
#
# this way it will log
# * * * * * /srv/gitolite/deploy/deploy.sh
# without log
* * * * * /srv/gitolite/deploy/deploy.sh > /dev/null 2>&1
@hourly ID=sys.hourly /usr/sbin/runjobs /etc/cron/hourly
@daily ID=sys.daily /usr/sbin/runjobs /etc/cron/daily
@weekly ID=sys.weekly /usr/sbin/runjobs /etc/cron/weekly
@monthly ID=sys.monthly /usr/sbin/runjobs /etc/cron/monthly
# End of file
</pre>
<h4>4.3.3. Debuging hooks</h4>
<p>Comment gitolite admin repo script "if" and uncomment debug sections, this allow to
source the file with environment of hook.</p>
<p>Later you can delete this branch locally and remote and start fresh. To test
if hook is called each time you push run;</p>
<pre>
$ echo $(( ( RANDOM % 10 ) +1 )) >> index.html && git add -u && git commit -m "test deploy" && git push git
</pre>
<p>See if a file was created in /srv/gitolite/deploy with name of project and
with environmental variables of gitolite script.</p>
<p>From now on you can test changes directly on
/srv/gitolite/.gitolite/local/hooks/repo-specific/hook-deployweb
and repeat above command to see the results or create a separate script with
all variables generated by above script set so you don't have to push at all.</p>
<h2 id="gitweb">5. Gitweb</h2>
<pre>
$ prt-get depinst p5-cgi p5-cgi-session libcgi \
fcgiwrap spawn-fcgi highlight
</pre>
<h3>5.1. Configure gitweb</h3>
<p>Edit /etc/gitweb.conf</p>
<pre>
our $git_temp = "/srv/www/gitweb_tmp";
our $projects_list = "/srv/gitolite/projects.list";
# The directories where your projects are. Must not end with a slash.
our $projectroot = "/srv/gitolite/repositories";
# Base URLs for links displayed in the web interface.
our @git_base_url_list = qw(git://core.privat-network.com http://git@core.private-network.com);
our $projects_list_group_categories = 1;
</pre>
<h3>5.2. Configure nginx</h3>
<p>Edit /etc/rc.d/fcgiwrap to run as www user member of www group;</p>
<pre>
USER=www
GROUP=www
</pre>
<p>Permissions for /srv/gitolite/repositories directories should be
0750/drwxr-x---.</p>
<pre>
$ chown gitolite:www /srv/gitolite
$ chown -R gitolite:www /srv/gitolite/projects.list
$ chown -R gitolite:www /srv/gitolite/repositories
</pre>
<p>A link from gitweb don't work under grsecurity since user www can't
follow simlink to root. Make a copy, later deploy via gitolite hook.</p>
<p>Link;</p>
<pre>
# ln -s /usr/share/gitweb /srv/www
</pre>
<p>Copy;</p>
<pre>
# cp -r /usr/share/gitweb /srv/www
</pre>
<p>Add this to default or main
<a href="nginx.html#virtual-host">nginx virtual host</a>;</p>
<pre>
location /gitweb/gitweb.cgi {
alias /srv/www/gitweb;
include fastcgi_params;
gzip off;
fastcgi_param SCRIPT_FILENAME /srv/www/gitweb/gitweb.cgi;
fastcgi_param GITWEB_CONFIG /etc/gitweb.conf;
fastcgi_pass unix:/var/run/fcgiwrap.sock;
}
location /gitweb {
alias /srv/www/gitweb;
index gitweb.cgi;
}
</pre>
<h3>5.3. Configure gitolite</h3>
<p>Edit
<a href="conf/srv/gitolite/.gitolite.rc">/srv/gitolite/.gitolite.rc</a>,
change UMASK and GIT_CONFIG_KEYS to;</p>
<pre>
# default umask gives you perms of '0700'; see the rc file docs for
# how/why you might change this
UMASK => 027,
# look for "git-config" in the documentation
GIT_CONFIG_KEYS => '.*',
</pre>
<p>Fix permissions;</p>
<pre>
$ sudo chown -R gitolite:gitolite /srv/gitolite
$ sudo chmod g+rx /srv/gitolite/projects.list
$ sudo chmod -R 755 /srv/gitolite/repositories/
</pre>
<p>Edit gitolite-admin/conf/gitolite.conf;</p>
<pre>
repo doc
config gitweb.owner = team
config gitweb.description = Documentation
config gitweb.category = Projects
repo ports
config gitweb.owner = team
config gitweb.description = System ports
config gitweb.category = Projects
</pre>
<a href="index.html">Tools Index</a>
<p>This is part of the c9-doc Manual.
Copyright (C) 2016
c9 team.
See the file <a href="../fdl-1.3-standalone.html">Gnu Free Documentation License</a>
for copying conditions.</p>
</body>
</html>