about summary refs log tree commit diff stats
path: root/content/post/self-hosted-git.md
blob: 1cceff3b84adc2affa2a247e9cbed4ac9d7c7090 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
+++
description = "I describe my git server setup (using cgit and gitolite), and what it allows"
date = 2017-06-04T12:33:02+02:00
title = "A simple, powerful self-hosted git setup"
[taxonomies]
tags = ["development","git"]
+++

I had been using [gogs][] for about a year.  It worked reasonably
well, as it focuses on being a lightweight self-hosted GitHub
replacement.  However, that wasn't really what I wanted.  I just
wanted to host my own projects, I didn't need things like issues, pull
requests or wikis.

I recently switched to [gitolite][] and [cgit][], as they were even
lighter on resources, don't require another login and work without
an external database.  Gitolite is unusual in its configuration: it
creates a git repository with its configuration file.  I will describe
how I use them, rather than how to set them up, as they both have
enough documentation on that.

My gitolite configuration file looks like this:

```
repo gitolite-admin
    RW+     =   alan

repo dotfiles
    C   =   alan
    RW+ =   alan
    R   =   READERS
    option hook.post-update   =    github-mirror

repo [a-z].*
    C   =   alan
    RW+ =   CREATOR
    RW  =   WRITERS
    R   =   READERS
```

The first block just allows me to work with the configuration
repository, as the initial setup only enables one specific public SSH
key, whereas I have three keys that I configure gitolite with.

The second configures my dotfiles specifically.  Naturally, I should
be the only person with read/write access.  The `R = READERS` line
allows remote configuration of read permissions via `ssh $DOMAIN
perms` (explained further below).  The last line runs a mirror script
(just `git push --mirror…`) so that
my [dotfiles repository on GitHub][dotfiles-github] is updated when I
push to my private version.

## Wild (or magic) repositories

The third block is where things get interesting.  gitolite has a
feature called [wildrepos][], which allows configuring a set of
repositories at once, using a regular expression to match the
repository name.

The really nice thing here is that the repository need not exist
before applying the configuration.  Therefore, the line `C = alan`
means that I can create a remote repository automatically by cloning a
repository URL that doesn't already exist.
I can clone and create a new repo simultaneously like so:

```shell
cd ~/projects
git clone alanpearce.eu:some-new-repository
```

But with [ghq][], which I [blogged about before][using-ghq], I don't
have to concern myself with where to put the repository:

```shell
$ ghq get alanpearce.eu:some-new-repository
     clone ssh://alanpearce.eu/some-new-repository -> /Volumes/Code/projects/alanpearce.eu/some-new-repository
       git clone ssh://alanpearce.eu/some-new-repository /Volumes/Code/projects/alanpearce.eu/some-new-repository
Cloning into '/Volumes/Code/projects/alanpearce.eu/some-new-repository'...
Initialized empty Git repository in /var/lib/gitolite/repositories/some-new-repository.git/
warning: You appear to have cloned an empty repository.
```

The nice URLs come from this piece of my SSH configuration:

```
Host alanpearce.eu
  HostName git.alanpearce.eu
  User gitolite
```

## Configuring wild repositories

This repository would be private by default, but I can change that by an
SSH command.  Here's how I would do it:

```shell
ssh alanpearce.eu perms some-new-repository + READERS gitweb
ssh alanpearce.eu perms some-new-repository + READERS daemon
```

The first command makes it visible in cgit, whilst the second makes it
clonable via `git://` url.  I can make a repository
publically-clonable, but invisible on cgit by only allowing the `daemon`
user and not `gitweb`, if I wanted.

I can also add or change the description of a repository shown on cgit like
so:

```shell
ssh alanpearce.eu desc some-new-repository 'A new repository'
```

All the remote commands exposed by gitolite are described in the
`help` command e.g. `ssh alanpearce.eu help`

```
hello alan, this is gitolite@oak running gitolite3 (unknown) on git 2.12.2

list of remote commands available:

	D
	desc
	help
	info
	motd
	perms
	writable

```

## Conclusion

I much prefer creating repositories in this way.  It's much simpler
and allows me to get on with working on the repositories rather than
going through a multi-step process in a web browser.

With cgit and gitolite, I have a minimal setup, that does exactly what
I want, without consuming many system resources with daemons.

[gogs]:https://gogs.io/ "Go Git Service"
[gitolite]:http://gitolite.com/gitolite/
[cgit]:https://git.zx2c4.com/cgit/
[NixOS]:http://nixos.org
[dotfiles-github]:https://github.com/alanpearce/dotfiles
[wildrepos]:http://gitolite.com/gitolite/wild/
[ghq]:https://github.com/motemen/ghq
[using-ghq]:{{< relref "/post/repository-management-with-ghq.md" >}} "Repository management with ghq"