Getting Started with Gitness

Published: Oct 13, 2023 by Isaac Johnson

I noted a couple weeks ago a TechCrunch Article about how Harness has released a free Git system called “Gitness”. The PR Newswire quoted the CEO Jyoti Bansal:

“Gitness marks a significant milestone for Harness and the software development community and represents our commitment to driving innovation and empowering developers worldwide. As the first significant release of an open source Git platform in nearly a decade, Gitness is equipped to provide all developers with the tools they need to streamline their workflows, collaborate effectively, and ensure code quality”

From what I can see, this is just a gifted free tool. So let’s check it out.

Setup

We start by going to gitness.com.

We can see the initial Getting Started page that shows how to run in docker

docker run -d \
  -p 3000:3000 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /tmp/gitness:/data \
  --name gitness \
  --restart always \
  harness/gitness

This will launch as a background daemon on port 3000 and restart with the host. You can remove the “restart” and “-d” options if you want to try it locally without making a background container.

I went ahead and launched as a daemon (can always stop in Docker/Podman desktop)

builder@DESKTOP-QADGF36:~$ docker run -d \
>   -p 3000:3000 \
>   -v /var/run/docker.sock:/var/run/docker.sock \
>   -v /tmp/gitness:/data \
>   --name gitness \
>   --restart always \
>   harness/gitness
Unable to find image 'harness/gitness:latest' locally
latest: Pulling from harness/gitness
96526aa774ef: Pull complete
d4d14a1f5c94: Pull complete
f64b697cf540: Pull complete
ce6acd792289: Pull complete
1c0d819d2d83: Pull complete
602c0335f152: Pull complete
Digest: sha256:81508315971e915cd09634c064c7ed48fc1f0b2c08affe6efb46a09e92762463
Status: Downloaded newer image for harness/gitness:latest
4b36604209fec50f88db768c1e29ff1c27a3a64c1ce073f6803ed63f0b2e82a7

We now head to http://localhost:3000/signin.

Depending on your screen width, you’ll see either

/content/images/2023/10/gitness-01.png

or

/content/images/2023/10/gitness-02.png

Ignore that Chrome decided to prefill gitea, We’ll click “Sign Up”.

/content/images/2023/10/gitness-03.png

This doesn’t require a confirmation out of the box, so we are launched right into Gitness

/content/images/2023/10/gitness-04.png

I double checked, and no confirmation email was sent (if it had, I would be curious what smtp service was built-in).

Projects

Let’s create a new project

/content/images/2023/10/gitness-05.png

I’ll give it a name and description

/content/images/2023/10/gitness-06.png

I now see it listed

/content/images/2023/10/gitness-07.png

From there I can create a new repository in the project

/content/images/2023/10/gitness-08.png

Let’s start with a Public repo and see we get

/content/images/2023/10/gitness-09.png

It initialized as I asked with a .gitignore, LICENSE and README.md on main (the other option for initial branch was master, or set your own).

/content/images/2023/10/gitness-10.png

In settings, we could switch it between Public and Private - so you are not locked in at create time.

/content/images/2023/10/gitness-11.png

I’ll click “Clone” to get an HTTP url I can use. To start, I will not generate credentials

/content/images/2023/10/gitness-12.png

Cloning worked just fine

builder@DESKTOP-QADGF36:~/Workspaces$ git clone http://localhost:3000/git/MyTestProject/MyFirstRepo.git
Cloning into 'MyFirstRepo'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), 6.42 KiB | 2.14 MiB/s, done.

builder@DESKTOP-QADGF36:~/Workspaces$ cd MyFirstRepo/
builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ ls
LICENSE  README.md

I’ll now make a change to an existing file

builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ vi README.md
builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git diff README.md
diff --git a/README.md b/README.md
index 97ec28d..59acfb6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,5 @@
 # MyFirstRepo
-My First Repo
\ No newline at end of file
+My First Repo
+
+# Author
+Isaac

Then add and commit

builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git add README.md
builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git commit -m "Update Readme.md"
[main fd3a15b] Update Readme.md
 1 file changed, 4 insertions(+), 1 deletion(-)

I could not push anonmyously

builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 297 bytes | 297.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
error: RPC failed; HTTP 401 curl 22 The requested URL returned error: 401
fatal: the remote end hung up unexpectedly
fatal: the remote end hung up unexpectedly
Everything up-to-date
builder@DESKTOP-QADGF36:~/Workspace

I’ll take a step back and get those GIT credentials

/content/images/2023/10/gitness-13.png

Trying to push again prompted me for creds which I used the credentials shown above

builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 297 bytes | 297.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
Username for 'http://localhost:3000': idjohnson
Password for 'http://idjohnson@localhost:3000':
remote:
remote: Create a new PR for branch 'main'
remote:   http://localhost:3000/MyTestProject/MyFirstRepo/pulls/compare/main...main
remote:
To http://localhost:3000/git/MyTestProject/MyFirstRepo.git
   b393190..fd3a15b  main -> main

We can see the update now in gitness

/content/images/2023/10/gitness-14.png

We can see that even with “public” repos, we must auth for the WebUI

Pipelines

Let’s create a pipeline

/content/images/2023/10/gitness-15.png

We can give it a name and path

/content/images/2023/10/gitness-16.png

This brings up a rather clean in-line editor. For such a beta product, I was slightly impressed

/content/images/2023/10/gitness-17.png

I’ll quick create a Webhook in Discord for Gitness

/content/images/2023/10/gitness-18.png

I’ll use a custom icon (because I like pretty icons) and copy the webhook URL

/content/images/2023/10/gitness-19.png

It should be in the format https://discord.com/api/webhooks/WEBHOOKID/WEBHOOKTOKEN

I’ll now use that in the “Add discord step”

/content/images/2023/10/gitness-20.png

By default, that is going to put a real token into a “public” repo, probably not ideal.

/content/images/2023/10/gitness-21.png

I’ll test with this first to see if it works, then we will address that secret info. I click “Save and Run” in the upper right which prompts for a branch

/content/images/2023/10/gitness-22.png

And it kicks in the pipeline

/content/images/2023/10/gitness-23.png

And I see an update in my #monitoring channel

/content/images/2023/10/gitness-24.png

Secrets

Let’s try and sort out that secret.

We’ll create a new Secret in the Secrets section

/content/images/2023/10/gitness-25.png

I’ll give it a name, description and paste the value. Since the value is hidden, i first pasted into the description to check my clipboard contents

/content/images/2023/10/gitness-26.png

which confirms creation when we save

/content/images/2023/10/gitness-27.png

I’ll try using the standard harness syntax for accessing secrets first

/content/images/2023/10/gitness-28.png

and commit right to main

/content/images/2023/10/gitness-29.png

Since there is no CI trigger (yet), I’ll need to hop over to pipelines to manually run

/content/images/2023/10/gitness-30.png

It didn’t work. I tried a few other syntaxes

                webhook_id: "1160907943923953715"
                webhook_token: {{.Values.discordtoken | quote}}

generated a pipeline internal error and

                webhook_id: "1160907943923953715"
                webhook_token: {secrets.getValue("discordtoken")}

didn’t do anything (tho it did run). I also tried with $ at the start.

In the end, I tried

webhook_token: {{.Values.discordtoken | quote}}
webhook_token: ${{.Values.discordtoken | quote}}
webhook_token: {secrets.getValue("discordtoken")}
webhook_token: ${secrets.getValue("discordtoken")}
webhook_token: ${discordtoken}
webhook_token: <+secrets.getValue("discordtoken")>

At this point I’m out of ideas (and google searching and github searching found no good examples).

I tried an option from Bing

          - type: plugin
            configuration:
              discordtoken:
                type: SECRET
                value: ${secrets.discordtoken}
            spec:
              name: discord
              inputs:
                message: PipelineDone2
                tts: true
                username: _isaacjohnson
                webhook_id: "1160907943923953715"
                webhook_token: "${discordtoken}"

Suffice to say, nothing I tried worked.

CI

So right now, the default trigger just runs on PRs

/content/images/2023/10/gitness-31.png

Here I’ll set it to run when a branch is updated

/content/images/2023/10/gitness-32.png

I think it might work, but there is nothing that let’s me scope to the main branch or shows that ‘branch updated’ setting on the right-hand side after saving

/content/images/2023/10/gitness-33.png

Webhooks

I’ll use webhook.site to do a quick webhook test

/content/images/2023/10/gitness-34.png

Now that the webhook is created

/content/images/2023/10/gitness-35.png

I’ll push a change to main

builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git pull
remote: Enumerating objects: 53, done.
remote: Counting objects: 100% (53/53), done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 52 (delta 24), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (52/52), 4.66 KiB | 433.00 KiB/s, done.
From http://localhost:3000/git/MyTestProject/MyFirstRepo
   fd3a15b..8480b91  main       -> origin/main
Updating fd3a15b..8480b91
Fast-forward
 .harness/MyFirstPipeline.yaml | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 .harness/MyFirstPipeline.yaml
builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ vi README.md
builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git diff README.md
diff --git a/README.md b/README.md
index 59acfb6..232ae25 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,4 @@
 My First Repo

 # Author
-Isaac
+Isaac Johnson
builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git add README.md
builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git commit -m "minor change"
[main 5c701c8] minor change
 1 file changed, 1 insertion(+), 1 deletion(-)
builder@DESKTOP-QADGF36:~/Workspaces/MyFirstRepo$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 303 bytes | 303.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote:
remote: Create a new PR for branch 'main'
remote:   http://localhost:3000/MyTestProject/MyFirstRepo/pulls/compare/main...main
remote:
To http://localhost:3000/git/MyTestProject/MyFirstRepo.git
   8480b91..5c701c8  main -> main

And see a branch_updated event was pushed

/content/images/2023/10/gitness-36.png

I can see that it created a pipeline run, which validated the main trigger, but that itself didn’t push any new webhooks.

/content/images/2023/10/gitness-37.png

Neither did a subsequent manual run.

/content/images/2023/10/gitness-38.png

Kubernetes

There is no chart I can find published for Gitness.

So let’s make one!

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/instance: gitness
    app.kubernetes.io/name: gitness
  name: gitness
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/instance: gitness
      app.kubernetes.io/name: gitness
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app.kubernetes.io/instance: gitness
        app.kubernetes.io/name: gitness
    spec:
      containers:
      - image: harness/gitness:latest
        imagePullPolicy: IfNotPresent
        name: gitness
        ports:
        - containerPort: 3000
          name: gitness
          protocol: TCP
        resources:
          requests:
            cpu: 250m
            memory: 256Mi
        securityContext:
          runAsNonRoot: true
          runAsUser: 1001
        volumeMounts:
        - mountPath: /data
          name: data
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        fsGroup: 1001
      serviceAccount: default
      serviceAccountName: default
      terminationGracePeriodSeconds: 30
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: gitness-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app.kubernetes.io/instance: gitness
    app.kubernetes.io/name: gitness
  name: gitness-data
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: managed-nfs-storage
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    app.kubernetes.io/instance: gitness
    app.kubernetes.io/name: gitness
  labels:
    app.kubernetes.io/instance: gitness
    app.kubernetes.io/name: gitness
  name: gitness
spec:
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: gitness
    port: 3000
    protocol: TCP
    targetPort: gitness
  selector:
    app.kubernetes.io/instance: gitness
    app.kubernetes.io/name: gitness
  sessionAffinity: None
  type: ClusterIP

We can now port-forward

$ kubectl port-forward svc/gitness 3000:3000
Forwarding from 127.0.0.1:3000 -> 3000
Forwarding from [::1]:3000 -> 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000

and create a new account

/content/images/2023/10/gitness-39.png

I’ll make a second project

/content/images/2023/10/gitness-40.png

And a repo inside of it

/content/images/2023/10/gitness-41.png

What we want to do next is double check the PVC really stores the data by deleting the running pod and logging in again

Upgrades

This is a free offering from Harness.io, but we can see they plan to add a “migrate to Harness Software Delivery platform” option in future versions:

/content/images/2023/10/gitness-42.png

Summary

If I can solve secrets in the pipelines, then I might have a very usable basic Gitea equivalent. Let’s be fair; there is no email or notifications setup outside of pipelines and webhooks, there are no frills; plugins, federated identity, etc. But it’s a very fast lightweight container-based tool.

When added with a protected drive (like using a NAS to host it) or using Helm and partnering with a PVC, this is actually a fairly usable product. We still need to explore a few more features such as PRs, but I’ll be keen to see where Harness takes this offering.

Git Gitness Docker Kubernetes

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes