I don't really like how everything and everyone is on Github, so I want to do something about it. It srarts small with hosting your own git stuff elsewhere.
For a while, I had been using charmbracelet/soft-serve as my git host. But it's just a git host, you can host a repo, and do some ci with webhooks, but there' no review integration, and it's very much ssh first.
I thought I'd try out gerrit again, this one is review-first rather than host first. Now, I don't really review my own code, after all I just wrote it..., but I could maybe make it work.
Right, to spin up a gerrit server. My server runs kubernetes, so gerrit will run inside kubernetes too.
Gerrit have published prebuilt containers at
index.docker.io/gerritcodereview/gerrit
,
which is great, I don't want to figure out how to build java services.
Annoyingly, almost all gerrit data and config is mixed together in /var/gerrit
,
which is also its $HOME
directory.
You'll have to mount the individual cache
, data
, db
, etc
, git
, index
, logs
, plugins
directories as volumes.
I'm not quite sure if it's gerrit or its init process,
but it does write to the config files as well,
you'll need some way of managing that.
This is also the reason I don't use the embedded entrypoint script,
TBD if I should skip the gerrit run script too.
The first user to log in become admin, you can later promote other users if you want.
As a single user instance, and not having an openid instance idp around, I opted for using HTTP Basic Auth. Gerrit has 2 parts that need authentication, and they don't really speak to each other. The web ui does basic auth by offloading it to whatever reverse proxy you have, while the git http daemon requires credentials generated from the web ui... In effect, this means you should:
Side note,
running in K8s, I'm using the new Gateway API,
specifically Envoy Gateway.
I kept getting 500 internal server errors until I read the instructions more carefully,
and realized it only supports SHA
as the password hash method for .htpasswd
.
Everything seemed to work, until I clicked on a diff and it came back with a cryptic:
An error occurred You might have not enough privileges. Error 404: Not found: envoy-gateway Endpoint: /changes/*~*/revisions/*/files/*/reviewed
Poking around a bit, for a diff affecting a path in a directory like deploy/envoy-gateway/kubernetes.yaml
,
the gerrit web ui sent a request for https://gerrit.liao.dev/c/mono/+/21/2/deploy%2Fenvoy-gateway%2Fkubernetes.yaml
,
note the slashes encoded as %2F
.
My reverse proxy was canonicalizing requests to
https://gerrit.liao.dev/c/mono/+/21/2/deploy/envoy-gateway/kubernetes.yaml
,
which then gerrit failed to match.
Gerrit has this documented as AllowEncodedSlashes On
and nocanon
for apache httpd.
For envoy gateway, this meant modifying my ClientTrafficPolicy
with path.escapedSlashesAction: KeepUnchanged
1kind: ClientTrafficPolicy
2spec:
3 http3: {}
4 path:
5 escapedSlashesAction: KeepUnchanged
6 disableMergeSlashes: true
7 targetRefs:
8 - group: gateway.networking.k8s.io
9 kind: Gateway
10 name: http-gateway
11metadata:
12 name: http-gateway
13 namespace: envoy-gateway-system
14 labels:
15 app.kubernetes.io/part-of: envoy-gateway
16 app.kubernetes.io/managed-by: kpt
17 app.kubernetes.io/name: envoy-gateway
18 annotations:
19 config.kubernetes.io/origin: "\tmono/deploy/envoy-gateway/*.cue"
20apiVersion: gateway.envoyproxy.io/v1alpha1
gerrit config options are documented online
but lets go over some of the settings.
The config file format is the same as git config, #
are comments.
# This is a plugin for auto submit when all requriements are met
[automerge]
botEmail = gerrit@liao.dev
# basic auth handled by gateway
[auth]
type = HTTP
gitBasicAuthPolicy = HTTP
# gerrit wants emails for users,
# this maps the username to a fixed domain part
emailFormat = {0}@liao.dev
[cache]
directory = cache
[gerrit]
# directory within /var/gerrit to store bare git repos
basePath = git
defaultBranch = refs/heads/main
canonicalWebUrl = https://gerrit.liao.dev/
serverId = e536212b-9667-4845-8848-736bb2f4a5f0
[httpd]
listenUrl = proxy-http://*:8080
[index]
type = lucene
# allow managing plugins from web interface,
# seems it may sometime still be necessary to install them by hand though...
[plugins]
allowRemoteAdmin = true
[receive]
enableSignedPush = false
# gerrit really wants to send email,
# but I don't have a SMTP server to give it yet
[sendemail]
enable = false
[sshd]
listenAddress = *:29418
Gerrit runs a built in gitiles server for the web git view, I couldn't quite figure out how to make it run on its own hostname.
A snapshot of what I currently use to deploy gerrit
1kind: Namespace
2metadata:
3 name: gerrit
4 labels:
5 app.kubernetes.io/part-of: gerrit
6 app.kubernetes.io/managed-by: kpt
7 app.kubernetes.io/name: gerrit
8 annotations:
9 config.kubernetes.io/origin: "\tmono/deploy/gerrit/*.cue"
10apiVersion: v1
11---
12spec:
13 type: ClusterIP
14 ports:
15 - name: http
16 port: 80
17 protocol: TCP
18 appProtocol: http
19 targetPort: http
20 selector:
21 app.kubernetes.io/name: gerrit
22kind: Service
23apiVersion: v1
24metadata:
25 name: gerrit
26 namespace: gerrit
27 annotations:
28 config.kubernetes.io/origin: "\tmono/deploy/gerrit/*.cue"
29 labels:
30 app.kubernetes.io/part-of: gerrit
31 app.kubernetes.io/managed-by: kpt
32 app.kubernetes.io/name: gerrit
33---
34kind: Deployment
35metadata:
36 name: gerrit
37 namespace: gerrit
38 labels:
39 app.kubernetes.io/part-of: gerrit
40 app.kubernetes.io/managed-by: kpt
41 app.kubernetes.io/name: gerrit
42 annotations:
43 config.kubernetes.io/origin: "\tmono/deploy/gerrit/*.cue"
44spec:
45 selector:
46 matchLabels:
47 app.kubernetes.io/name: gerrit
48 template:
49 metadata:
50 labels:
51 app.kubernetes.io/name: gerrit
52 spec:
53 initContainers:
54 - image: index.docker.io/gerritcodereview/gerrit:3.11.0-rc3-ubuntu24
55 name: gerrit-init
56 command:
57 - sh
58 - -c
59 - |-
60 if [ ! -d /var/gerrit/git/All-Projects.git ]
61 then
62 echo "Initializing Gerrit site ..."
63 java $JAVA_OPTS -jar /var/gerrit/bin/gerrit.war init --batch --install-all-plugins -d /var/gerrit
64 java $JAVA_OPTS -jar /var/gerrit/bin/gerrit.war reindex -d /var/gerrit
65 git config -f /var/gerrit/etc/gerrit.config --add container.javaOptions "-Djava.security.egd=file:/dev/./urandom"
66 git config -f /var/gerrit/etc/gerrit.config --add container.javaOptions "--add-opens java.base/java.net=ALL-UNNAMED"
67 git config -f /var/gerrit/etc/gerrit.config --add container.javaOptions "--add-opens java.base/java.lang.invoke=ALL-UNNAMED"
68 fi
69 env:
70 - name: JAVA_OPTS
71 value: --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED
72 volumeMounts:
73 - mountPath: /var/gerrit/cache
74 subPath: cache
75 name: data
76 - mountPath: /var/gerrit/data
77 subPath: data
78 name: data
79 - mountPath: /var/gerrit/db
80 subPath: db
81 name: data
82 - mountPath: /var/gerrit/etc
83 subPath: etc
84 name: data
85 - mountPath: /var/gerrit/git
86 subPath: git
87 name: data
88 - mountPath: /var/gerrit/index
89 subPath: index
90 name: data
91 - mountPath: /var/gerrit/logs
92 subPath: logs
93 name: data
94 containers:
95 - image: index.docker.io/gerritcodereview/gerrit:3.11.0-rc3-ubuntu24
96 name: gerrit
97 command:
98 - /var/gerrit/bin/gerrit.sh
99 - run
100 env:
101 - name: JAVA_OPTS
102 value: --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED
103 ports:
104 - containerPort: 29418
105 name: git-ssh
106 - containerPort: 8080
107 name: http
108 volumeMounts:
109 - mountPath: /var/gerrit/cache
110 subPath: cache
111 name: data
112 - mountPath: /var/gerrit/data
113 subPath: data
114 name: data
115 - mountPath: /var/gerrit/db
116 subPath: db
117 name: data
118 - mountPath: /var/gerrit/etc
119 subPath: etc
120 name: data
121 - mountPath: /var/gerrit/git
122 subPath: git
123 name: data
124 - mountPath: /var/gerrit/index
125 subPath: index
126 name: data
127 - mountPath: /var/gerrit/logs
128 subPath: logs
129 name: data
130 - mountPath: /var/gerrit/plugins
131 subPath: plugins
132 name: data
133 volumes:
134 - hostPath:
135 path: /opt/volumes/gerrit
136 name: data
137 enableServiceLinks: false
138 strategy:
139 type: Recreate
140 revisionHistoryLimit: 1
141apiVersion: apps/v1
142---
143kind: SecurityPolicy
144spec:
145 targetRefs:
146 - group: gateway.networking.k8s.io
147 kind: HTTPRoute
148 name: gerrit
149 basicAuth:
150 users:
151 name: basic-auth
152metadata:
153 name: gerrit
154 namespace: gerrit
155 labels:
156 app.kubernetes.io/part-of: gerrit
157 app.kubernetes.io/managed-by: kpt
158 app.kubernetes.io/name: gerrit
159 annotations:
160 config.kubernetes.io/origin: "\tmono/deploy/gerrit/*.cue"
161apiVersion: gateway.envoyproxy.io/v1alpha1
162---
163kind: HTTPRoute
164spec:
165 hostnames:
166 - gerrit.liao.dev
167 parentRefs:
168 - name: http-gateway
169 namespace: envoy-gateway-system
170 rules:
171 - backendRefs:
172 - name: gerrit
173 port: 80
174metadata:
175 name: gerrit
176 namespace: gerrit
177 labels:
178 app.kubernetes.io/part-of: gerrit
179 app.kubernetes.io/managed-by: kpt
180 app.kubernetes.io/name: gerrit
181 annotations:
182 config.kubernetes.io/origin: "\tmono/deploy/gerrit/*.cue"
183apiVersion: gateway.networking.k8s.io/v1