Originally published on 08 November 2019
Last updated on 29 October 2021
I’ve been thinking that I should start putting my blog posts on some type of version control system both to allow me to track changes and different versions as well as to provide another location to store them. (Right now, everything lives on either my local laptop or the virtual private server hosting this site.) I’ve used GitHub before and really like its interface and the fact that you find a fairly large community there. But I’ve also wanted to try to self host something if I could. I looked at local hosting options from both GitLab and Gitea but could never manage to make all the pieces work, especially on FreeBSD. I finally settled on an application called cgit which is the creation of Jason Donenfeld who is also the developer of the command-line password tool pass and the relatively new VPN tool Wireguard. cgit is a really fast and lightweight front-end for git repositories that is written in C. It also comes as a package for FreeBSD. It doesn’t have many of the features of GitHub or GitLab such as an issue tracker, pull requests or wikis, but it serves as a nice way for me to browse any repositories and clone them back to another local machine.1
Let’s get started on installing and configuring cgit on FreeBSD.
I installed cgit on a separate VPS than the one serving this site. This way, if anything ever happened to the VPS hosting my site, my git repositories would not be impacted (at least in theory). Once I spun up the VPS, I went through the following posts from my site to prepare the VPS to host my repositories:
I also followed my post on how to Create Azure DNS Zone for my VPS but when creating a Record Set, I used the name “git” to reflect a new subdomain. This way, my git repositories would be accessible via git.example.com. I also used the IP address of the VPS hosting my repositories.
git
User AccountAll of the repos on the new VPS will have to owned by a user. I decided to create a separate user account for this. While it’s probably best practice not to provide a shell for this user, I went ahead and provided one as it makes it easier to configure the git repo directories and to troubleshoot any issues.
$ sudo adduser Username: git Full name: git user account Uid (Leave empty for default): Login group [git]: Login group is git. Invite git into other groups? []: Login class [default]: Shell (sh csh tcsh git-shell nologin) [sh]: Home directory [/home/git]: Home directory permissions (Leave empty for default): Use password-based authentication? [yes]: no Lock out the account after creation? [no]: Username : git Password :Full Name : git user account Uid : 1003 Class : Groups : git Home : /home/git Home Mode : Shell : /bin/sh Locked : no OK? (yes/no): yes adduser: INFO: Successfully added (git) to the user database. Add another user? (yes/no): no Goodbye!
git
User AccountSince we provided the git user with a shell, it only makes sense that we can log into that user via SSH.
$ sudo su git # change to git user $ mkdir /home/git/.ssh # create the SSH directory $ vi /home/git/.ssh/authorized_keys create the authorized_keys file
You can also use something like ssh-copy-id(1). Once you’ve pasted in or copied over the public SSH keys into the authorized_keys
file, you can test it out by trying to log in:
$ ssh -p 22 git@ip.address
git
RepositoriesNow we can create the directories where our git repositories will reside. Make sure you are logged in as the user git
.
$ mkdir -p /home/git/repos/myFirstRepo $ cd /home/git/repos/myFirstRepo/ $ git init --bare Initialized empty Git repository in /usr/home/git/repos/myFirstRepo/
If you created the directories housing the git repos using another user, make sure that your permission and ownership attributes are set correctly using chmod(1) and chown(8).
Edit the description
file as this is what will be displayed by cgit on the initial “Index” page.
$ vi description
Log out and then see if you can push changes from local repository up to the hosted git repo on the FreeBSD virtual machine.
$ exit
git
RepositoryThis is an example of how you can create a git
repository on your local machine and then push changes up to your remote git
host that you just created. From the local client:
$ cd /path/to/local/directory $ git init Initialized empty Git repository in /path/to/local/directory
Add a README
file which can be used in the “About” page of your repo on cgit letting people know what the repository is about.
If you need to, edit the .gitignore file. For example:
# ignore all files
*.*
# except the ones with the following extensions
!*.c
!*.cpp
Continue on with the traditional git
commands to add the files to the repository and then push the commits to the remote host:
$ git add -A $ git status On branch master Initial commit Changes to be committed: (use "git rm --cached..." to unstage) new file: .gitignore new file: README new file: color.png new file: dub.json new file: dub.selections.json new file: matplotlib new file: simple.png new file: source/app.d new file: source/app.d.1 $ git commit -am "add new repo" [master (root-commit) 0a5871c] add new repo 9 files changed, 69 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 color.png create mode 100644 dub.json create mode 100644 dub.selections.json create mode 100755 matplotlib create mode 100644 simple.png create mode 100644 source/app.d create mode 100644 source/app.d.1 $ git status On branch master nothing to commit, working directory clean $ git remote add origin ssh://git@ip.address:/usr/home/git/repos/myFirstRepo $ git remote -v origin ssh://git@ip.address:/usr/home/git/repos/myFirstRepo (fetch) origin ssh://git@ip.address:/usr/home/git/repos/myFirstRepo (push) $ git push -u origin master Counting objects: 12, done. Delta compression using up to 4 threads. Compressing objects: 100% (11/11), done. Writing objects: 100% (12/12), 633.93 KiB | 0 bytes/s, done. Total 12 (delta 0), reused 0 (delta 0) To ssh://git@ip.address:/usr/home/git/repos/myFirstRepo * [new branch] master -> master Branch master set up to track remote branch master from origin.
Just to be sure, you can add additional files and then push them up to the remote repository:
$ touch post7.md $ git status On branch master Your branch is up-to-date with 'origin/master'. Untracked files: (use "git add..." to include in what will be committed) post7.md nothing added to commit but untracked files present (use "git add" to track) $ git add -A $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD ..." to unstage) new file: post7.md $ git commit -am "add post7.md" [master 8a5e32d] add post7.md 1 file changed, 1 insertion(+) create mode 100644 blog/post7.md $ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working directory clean $ git push Counting objects: 4, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 383 bytes | 0 bytes/s, done. Total 4 (delta 1), reused 0 (delta 0) To ssh://git@ip.address:/usr/home/git/repos/myFirstRepo 8331a60..8a5e32d master -> master $ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean
Now it’s time to install nginx, fcgiwrap and cgit. Log back into the FreeBSD VM as the user with sudo
privileges. The remaining steps reference Pascal Schmid’s blog post.
$ sudo pkg install nginx fcgiwrap cgit
Enable nginx and fcgiwrap by adding the following lines in /etc/rc.conf
:
nginx_enable="YES"
fcgiwrap_enable="YES"
fcgiwrap_user="www"
fcgiwrap_socket_owner="www"
fcgiwrap_socket_group="www"
Create an “About” page for the overall site via the file /usr/home/git/repos/about
:
<br>This is a compilation of the git repositories that I own.
<br>
<br>This file is supposed to tell you more about them.
<br>
<br>But I have nothing more to write.
<br>
<br>Sorry.
Add your favicon.png
file to /usr/local/www/cgit
.
There are two main configuration files that need to be defined: /usr/local/etc/cgitrc
and /usr/local/etc/nginx/nginx.conf
.
Here is my /usr/local/etc/cgitrc
. See the cgit man page for more details on how to configure the various options in this file.
css=/cgit.css
logo=/cgit.png
# Add a cgit favicon
favicon=/favicon.png
robots=noindex, nofollow
virtual-root=/
root-title=My cgit Repository
root-desc=wealth of useless information
clone-url=git://git.example.com/$CGIT_REPO_URL
enable-index-links=1
enable-log-filecount=1
enable-log-linecount=1
enable-commit-graph=1
enable-remote-branches=1
snapshots=tar.gz tar.bz
max-stats=quarter
#root-readme=/usr/local/www/cgit/about.htm
root-readme=/usr/home/git/repos/about
# Show owner on index page
enable-index-owner=0
##
## Search for these files in the root of the default branch of repositories
## for coming up with the about page:
##
readme=:README.md
readme=:readme.md
readme=:README.mkd
readme=:readme.mkd
readme=:README.rst
readme=:readme.rst
readme=:README.html
readme=:readme.html
readme=:README.htm
readme=:readme.htm
readme=:README.txt
readme=:readme.txt
readme=:README
readme=:readme
readme=:INSTALL.md
readme=:install.md
readme=:INSTALL.mkd
readme=:install.mkd
readme=:INSTALL.rst
readme=:install.rst
readme=:INSTALL.html
readme=:install.html
readme=:INSTALL.htm
readme=:install.htm
readme=:INSTALL.txt
readme=:install.txt
readme=:INSTALL
readme=:install
scan-path=/usr/home/git/repos
Here is my /usr/local/etc/nginx/nginx.conf
. Don’t forget to update the server_name
.
user www;
worker_processes 4;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
error_log /var/log/nginx.error.log error;
sendfile on;
keepalive_timeout 70;
server {
listen 80;
#server_name localhost;
server_name git.example.com;
index cgit.cgi;
access_log /var/log/git.access.log;
root /usr/local/www/cgit;
try_files $uri @cgit;
# Require auth for requests sent to cgit that originated in location /
location @cgit {
# $document_root is now set properly, and you don't need to override it
index cgit.cgi;
fastcgi_param SCRIPT_FILENAME $document_root/cgit.cgi;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param PATH_INFO $uri;
fastcgi_param QUERY_STRING $args;
fastcgi_param HTTP_HOST $server_name;
fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock;
include fastcgi_params;
gzip off;
#rewrite ^ https://$server_name$request_uri permanent;
rewrite ^/([^/]+/.*)?$ /cgit.cgi?url=$1 break;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/local/www/nginx-dist;
}
}
}
Once you’ve updated the cgit and nginx configuration files, it’s time to restart the services:
$ sudo service fcgiwrap start $ sudo service nginx start
The last step is to provision an SSL certificate to enable HTTPS to the remote git host.
I would be remiss if I did not also mention Drew DeVault’s sourcehut which, at the time of this writing, is a relatively new set of open source software development tools which also includes the ability to host git repositories. It’s currently in the alpha stage but is worth keeping an eye on.↩