| --- |
| apiVersion: apps/v1 |
| kind: Deployment |
| metadata: |
| name: git |
| namespace: mulk |
| labels: |
| name: git |
| k8s-app: git |
| spec: |
| replicas: 1 |
| selector: |
| matchLabels: |
| k8s-app: git |
| name: git |
| template: |
| metadata: |
| labels: |
| name: git |
| k8s-app: git |
| spec: |
| containers: |
| - name: cgit |
| # Serves cgit at / (lighttpd rewrites /<repo>/... to cgit.cgi?url=...). |
| # The image's own sshd listens on :22 and is unused; gitolite is on :2222. |
| image: docker.io/woahbase/alpine-cgit:1.3_20260805 |
| imagePullPolicy: IfNotPresent |
| env: |
| # Formatters for the about pages and syntax highlighting. |
| - name: S6_NEEDED_PACKAGES |
| value: "groff python3 py3-docutils py3-markdown py3-pygments" |
| # /home/git is gitolite's volume (owned by gitolite's uid 106), so |
| # cgit must not chown it: CGIT_SKIP_PERMFIX skips cgit's permfix, |
| # and an empty S6_USER skips the base image's usermod/chown of the |
| # user's home (scripts fall back to user "git"). |
| - name: CGIT_SKIP_PERMFIX |
| value: "true" |
| - name: S6_USER |
| value: "" |
| # The image's sshd setup runs `chown -R git ~/.ssh`, and |
| # /home/git/.ssh is gitolite's .ssh (same volume); give cgit's |
| # user a home inside the container instead. |
| - name: S6_USERHOME |
| value: /var/lib/cgit |
| # The setup then generates a key there as PUID; must match the |
| # image's git user (1001) that owns that directory. |
| - name: PUID |
| value: "1001" |
| - name: CGIT_REPODIR |
| value: /home/git/repositories |
| resources: |
| limits: |
| cpu: 2000m |
| memory: 100Mi |
| requests: |
| cpu: 10m |
| memory: 50Mi |
| ports: |
| - containerPort: 80 |
| name: cgit-http |
| protocol: TCP |
| volumeMounts: |
| - name: data |
| mountPath: /home/git |
| - name: config |
| mountPath: /etc/cgitrc |
| subPath: cgitrc |
| readOnly: true |
| - name: gitolite |
| image: docker.io/elsdoerfer/gitolite:v3.6.15-ubuntu-26.04-20260824.3 |
| imagePullPolicy: IfNotPresent |
| env: [] |
| #tty: true |
| #stdin: true |
| #command: |
| # - /bin/cat |
| resources: |
| limits: |
| cpu: 2000m |
| memory: 100Mi |
| requests: |
| cpu: 10m |
| memory: 50Mi |
| ports: |
| - containerPort: 2222 |
| name: gitolite-ssh |
| protocol: TCP |
| volumeMounts: |
| - name: data |
| mountPath: /home/git/repositories |
| subPath: repositories |
| #- name: data |
| # mountPath: /home/git/projects.list |
| # subPath: projects.list |
| - name: data |
| mountPath: /home/git/.gitolite |
| subPath: .gitolite |
| - name: data |
| mountPath: /home/git/.gitolite.rc |
| subPath: .gitolite.rc |
| - name: data |
| mountPath: /home/git/.ssh |
| subPath: .ssh |
| - name: ssh |
| mountPath: /etc/ssh |
| volumes: |
| - name: data |
| persistentVolumeClaim: |
| claimName: git-data |
| - name: ssh |
| persistentVolumeClaim: |
| claimName: git-ssh |
| - name: config |
| configMap: |
| name: git-config |
| --- |
| apiVersion: v1 |
| kind: Service |
| metadata: |
| name: git-cgit |
| namespace: mulk |
| labels: |
| k8s-app: git |
| name: git-cgit |
| spec: |
| selector: |
| k8s-app: git |
| type: ClusterIP |
| ports: |
| - name: http |
| port: 80 |
| targetPort: cgit-http |
| protocol: TCP |
| --- |
| apiVersion: v1 |
| kind: Service |
| metadata: |
| name: git-gitolite |
| namespace: mulk |
| labels: |
| k8s-app: git |
| name: git-gitolite |
| spec: |
| selector: |
| k8s-app: git |
| type: NodePort |
| ports: |
| - name: ssh |
| port: 22 |
| targetPort: gitolite-ssh |
| protocol: TCP |
| --- |
| apiVersion: v1 |
| kind: PersistentVolumeClaim |
| metadata: |
| name: git-data |
| namespace: mulk |
| labels: |
| name: git-data |
| k8s-app: git |
| spec: |
| accessModes: |
| - ReadWriteOnce |
| resources: |
| requests: |
| storage: 200Gi |
| storageClassName: local-path |
| --- |
| apiVersion: v1 |
| kind: PersistentVolumeClaim |
| metadata: |
| name: git-ssh |
| namespace: mulk |
| labels: |
| name: git-ssh |
| k8s-app: git |
| spec: |
| accessModes: |
| - ReadWriteOnce |
| resources: |
| requests: |
| storage: 50Mi |
| storageClassName: local-path |
| --- |
| # TLS certificate for the cgit IngressRoute below. It used to be created |
| # by cert-manager's ingress-shim from an nginx Ingress, which has been |
| # removed; the HTTP-01 solver still goes through Traefik's nginx-class |
| # compat provider (see the letsencrypt-prod ClusterIssuer). |
| apiVersion: cert-manager.io/v1 |
| kind: Certificate |
| metadata: |
| name: git-cgit-cgi-tls |
| namespace: mulk |
| spec: |
| secretName: git-cgit-cgi-tls |
| dnsNames: |
| - git.benkard.de |
| issuerRef: |
| kind: ClusterIssuer |
| name: letsencrypt-prod |
| --- |
| # Traefik route for cgit. Traefik's ingress-nginx provider does not |
| # support nginx snippet annotations, and it has no global per-host rate |
| # limit, so cgit gets a native IngressRoute instead. |
| # |
| # A crawler botnet with thousands of rotating IPs and browser user agents |
| # hammers cgit; cap it at 5 r/s in total (not per client). A single router |
| # keeps the limit in one bucket (each router gets its own limiter). |
| apiVersion: traefik.io/v1alpha1 |
| kind: Middleware |
| metadata: |
| name: git-cgit-ratelimit |
| namespace: mulk |
| spec: |
| rateLimit: |
| average: 5 |
| period: 1s |
| burst: 20 |
| sourceCriterion: |
| requestHost: true |
| --- |
| # Same as the rewrite-target of the Ingress above: strip a ".git" suffix |
| # from the repository name and route everything to cgit.cgi. |
| apiVersion: traefik.io/v1alpha1 |
| kind: Middleware |
| metadata: |
| name: git-cgit-rewrite |
| namespace: mulk |
| spec: |
| replacePathRegex: |
| regex: "^/(?:(.*)[.]git(/.*)?|(.*))$" |
| replacement: "/${1}${2}${3}" |
| --- |
| apiVersion: traefik.io/v1alpha1 |
| kind: Middleware |
| metadata: |
| name: git-cgit-https-redirect |
| namespace: mulk |
| spec: |
| redirectScheme: |
| scheme: https |
| permanent: true |
| --- |
| apiVersion: traefik.io/v1alpha1 |
| kind: IngressRoute |
| metadata: |
| name: git-cgit-cgi |
| namespace: mulk |
| spec: |
| entryPoints: |
| - websecure |
| routes: |
| - kind: Rule |
| match: Host(`git.benkard.de`) |
| middlewares: |
| - name: git-cgit-ratelimit |
| - name: git-cgit-rewrite |
| services: |
| - name: git-cgit |
| port: 80 |
| tls: |
| secretName: git-cgit-cgi-tls |
| --- |
| apiVersion: traefik.io/v1alpha1 |
| kind: IngressRoute |
| metadata: |
| name: git-cgit-cgi-http |
| namespace: mulk |
| spec: |
| entryPoints: |
| - web |
| routes: |
| - kind: Rule |
| match: Host(`git.benkard.de`) |
| middlewares: |
| - name: git-cgit-https-redirect |
| services: |
| - name: git-cgit |
| port: 80 |
| --- |
| apiVersion: networking.k8s.io/v1 |
| kind: Ingress |
| metadata: |
| name: git-cgit-assets |
| namespace: mulk |
| annotations: |
| cert-manager.io/cluster-issuer: letsencrypt-prod |
| kubernetes.io/ingress.class: nginx |
| nginx.ingress.kubernetes.io/rewrite-target: /cgit/$1 |
| spec: |
| rules: |
| - host: static.git.benkard.de |
| http: |
| paths: |
| - backend: |
| service: |
| name: git-cgit |
| port: |
| number: 80 |
| path: /(.*) |
| pathType: ImplementationSpecific |
| tls: |
| - hosts: |
| - static.git.benkard.de |
| secretName: git-cgit-assets-tls |
| --- |
| apiVersion: v1 |
| kind: ConfigMap |
| metadata: |
| name: git-config |
| namespace: mulk |
| data: |
| cgitrc: |- |
| css=https://static.git.benkard.de/cgit.css |
| logo=https://static.git.benkard.de/cgit.png |
| |
| remove-suffix=1 |
| enable-commit-graph=1 |
| enable-index-links=1 |
| enable-log-filecount=1 |
| enable-log-linecount=1 |
| enable-git-config=1 |
| case-sensitive-sort=0 |
| |
| virtual-root=https://git.benkard.de/ |
| |
| #cache-size=10000 |
| #cache-root=/var/cache/cgit |
| |
| # needs Pygments |
| #source-filter=/usr/lib/cgit/filters/syntax-highlighting.py |
| |
| about-filter=/usr/lib/cgit/filters/about-formatting.sh |
| |
| clone-url=https://git.benkard.de/$CGIT_REPO_URL |
| |
| readme=:README.md |
| readme=:README.markdown |
| readme=:README.html |
| readme=:README.txt |
| readme=:README |
| |
| project-list=/home/git/projects.list |
| scan-path=/home/git/repositories/ |
| |
| # |
| |
| --- |
| |
| apiVersion: batch/v1 |
| kind: CronJob |
| |
| metadata: |
| name: git-github-replication |
| namespace: mulk |
| labels: &labels |
| k8s-app: git |
| |
| spec: |
| schedule: "*/120 * * * *" |
| concurrencyPolicy: Forbid |
| |
| jobTemplate: |
| |
| spec: |
| ttlSecondsAfterFinished: 3600 |
| |
| template: |
| metadata: |
| name: git-github-replication |
| labels: *labels |
| |
| spec: |
| restartPolicy: OnFailure |
| |
| volumes: |
| - name: github-secrets |
| secret: |
| secretName: github-secrets |
| defaultMode: 0444 |
| - name: data |
| persistentVolumeClaim: |
| claimName: git-data |
| - name: etc |
| emptyDir: {} |
| |
| initContainers: |
| - name: init |
| image: docker.io/library/buildpack-deps:scm |
| securityContext: |
| runAsUser: 100 |
| runAsGroup: 101 |
| command: |
| - bash |
| - -c |
| - | |
| echo git:x:106:107:Git:/home/git:/bin/sh >/etc/passwd |
| volumeMounts: |
| - name: etc |
| mountPath: /etc |
| |
| containers: |
| - name: main |
| image: docker.io/library/buildpack-deps:scm |
| securityContext: |
| runAsUser: 106 |
| runAsGroup: 107 |
| command: |
| - bash |
| - -c |
| - | |
| for x in \ |
| kartipalist |
| do |
| if ! test -e "/git/repositories/kellertomaten/$x.git"; then |
| printf "SKIP %s / %s\n" kellertomaten "$x" |
| continue |
| fi |
| |
| cd "/git/repositories/kellertomaten/$x.git" |
| |
| for branch in master main |
| do |
| printf "COPY %s / %s / %s\n" kellertomaten "$x" "$branch" |
| git fetch "ssh://git@gitlab.benkard.de:32222/kellertomaten/${x}.git" "$branch:$branch" && \ |
| ( git push "ssh://mulk@gerrit.benkard.de:29418/${x}.git" "$branch"; \ |
| git push "git@github.com:kellertomaten/${x}.git" "$branch" ) |
| done |
| done |
| |
| for x in \ |
| .github \ |
| aendggner \ |
| aquifer \ |
| benki \ |
| cellspp \ |
| cl-json-template \ |
| cl-mulkutils \ |
| cl-protocols \ |
| cljssss-g \ |
| demos \ |
| dose \ |
| fibers \ |
| hellonet \ |
| hello-world-ng \ |
| instadump \ |
| jgvariant \ |
| json-template-r6rs \ |
| json-template-racket \ |
| json-template-typed-racket \ |
| kubeia \ |
| logikorr \ |
| mulkbav \ |
| mulkcms \ |
| mulkcms2 \ |
| mulkjournal \ |
| mulklib \ |
| mulkontainers \ |
| mulkrypt-for-racket \ |
| mulkyid \ |
| objective-cl \ |
| quarkus-googlecloud-jsonlogging \ |
| renovate-config \ |
| rust-samples \ |
| rust-tutorial \ |
| sb-eval2 \ |
| toilet |
| do |
| if ! test -e "/git/repositories/mulk/$x.git"; then |
| printf "SKIP %s / %s\n" mulk "$x" |
| continue |
| fi |
| |
| cd "/git/repositories/mulk/$x.git" |
| |
| for branch in master main |
| do |
| printf "COPY %s / %s / %s\n" mulk "$x" "$branch" |
| git fetch "https://gitlab.benkard.de/mulk/${x}.git" "$branch:$branch" && \ |
| ( git push "ssh://mulk@gerrit.benkard.de:29418/${x}.git" "$branch"; \ |
| git push "git@github.com:benkard/${x}.git" "$branch" ) |
| done |
| done |
| |
| true |
| env: |
| - name: HOME |
| value: /home/git |
| resources: |
| limits: |
| memory: 100Mi |
| requests: |
| cpu: 20m |
| memory: 100Mi |
| volumeMounts: |
| - name: github-secrets |
| mountPath: /home/git/.ssh |
| readOnly: true |
| - name: data |
| mountPath: /git |
| - name: etc |
| subPath: passwd |
| mountPath: /etc/passwd |