Tutorial: Minimal git hosting

Time 5 minutes
Aim Setup a publicly accessible read-only git repo with a CI

Prerequisites

Setup the Git Repo

  1. Create a directory for your projects somewhere publicly accessible within the repository

    # mkdir -p /var/git-public/
    # chown -R you:you /var/git-public
    $ mkdir /var/git-public/my-project
    $ cd /var/git-public/my-project && git init --bare
    	  
  2. Locally on your computer, add the new remote and push your project.

    $ git remote add my-server you@server:/var/git-public/my-project
    $ git push my-server main
    	  

Setup the CI

  1. Add a post-receive hook to add a CI. We can leverage landdown if we want to further secure it.

    $ cat > /var/git-public/my-project/hooks/post-receive <<EOF
    #!/bin/sh
    export NQDIR=/home/you/.nqdir # set to whatever you want. 
    echo "Starting job for my-project"
    nq echo "My job goes here"
    EOF
    $ chmod +x /var/git-public/my-project/hooks/post-receive
    	  

    You may want to set the variable NQDIR in your ssh profile to match the post-receive script. This will require PermitUserEnvironment yes in your sshd config.


    Now when pushing to your repository, it will run whatever is in the post-receive hook, and nq makes it run async.


    You can inspect the latest job with ssh my-server nqtail or optionally view prior jobs by supplying the jobid, or view all jobs by appending the -a parameter.

When implementing a real CI, I recommend using mktemp -d to create a temporary directory where you can then clone the repository with git clone file:///var/public-git/my-project .

Expose to the www

You have a few options

If you just need to expose the project without a public website, you can use git-daemon

Since I already had a static website up and running, I'm going with stagit.

I created two scripts, one to generate the index for stagit. Below is the full script I use as my post-receive hook for my fork of stagit.

Sample: Stagit pre-receive hook
#!/bin/sh
NQDIR=/home/marc/.nqdir
# By adding -s, we add labels that show up when we run
# nqtail -aq
nq sh -s $(date -Iseconds) stagit <<EOF
set -xe
echo ">>> Building stagit"
cd $(mktemp -d)
git clone file:///var/git-public/stagit
cd stagit
make
echo ">>> Installing"
make install
echo ">>> Regenerating stagit"
cd /var/www-data/git
stagit-index /var/git-public/* > index.html
for repo in /var/git-public/*; do
[ -d "$repo" ] || continue
name=$(basename "$repo")
mkdir -p "/var/www-data/git/$name"
(cd "/var/www-data/git/$name" && stagit -u "https://mccd.space/git/$name/" "$repo")
done
EOF
      

Then I can simply run this script in my post-receive hook with nq to make it refresh in the background.

To serve the static git repos, I use apache httpd and added a directive:

Alias /git ${GITDIR}/
<Directory ${GITDIR}>
Require all granted
DirectoryIndex index.html
</Directory>
      

You can see my repo up and running here.

Expose git repos to a limited set of people

If you would like limited access to the repository, such as only friends access, you can set up a unix user with git-shell set as its shell, and home directory to the directory of all git repositories. Then set the user's authorized_keys to the ssh keys of the people you want to have access, and use a pre-receive hook to manage access control along with unix user permissions.

You can add interactive shell commands as well to ~/git-shell-commands, which is handy if you want to allow them to, for example, list all available repositories.

Alternatively, you can also just use http with git's http backend. It is a CGI-bin script that works with Apache HTTPD, Lighttpd and more. This can be used to set up per-user access with each user having their own credentials, using the http server's password config (for example htpasswd).

SEE ALSO