Setting up cgit on Apache

When it comes to Git hosting solutions, nothing beats the control and flexibility that comes with hosting Git on your own server and domain.

This guide will document how to create a personal self-hosted repository portfolio by installing and configuring cgit on top of an existing Apache installation. Many projects use cgit for their code management, including the Linux kernel and Gentoo, and Apache is the most popular web server in the world, making this a highly accessible self-hosted Git solution.

Table of contents#

Preamble to the guide#

Prior technical knowledge will be very helpful for following this guide’s procedures, especially knowledge of Git and Apache. In spite of this, the guide attempts to explain complex technical subjects in plain language to make it as accessible as possible for the general public.

The guide will provide brief summaries of the many technical concepts related to its setup procedures, but will not distract from the focus of the guide with lengthy explanations of these subjects. In those cases, the guide will link to external resources that provide a deeper background on those concepts.

Requirements#

This guide assumes the existence of a working Apache installation with support for both CGI and the mod_rewrite module. It also requires SSH access to the server running Apache. Permissions for modifying Apache’s configuration and the web server’s file system will also be essential. Lastly, since cgit is only distributed as source code, the server must have a C build environment and the make utility or one of its derivatives (gmake, etc.).

Setting up these requirements is beyond the scope of the guide, but if you already have a web server, it’s likely that you already meet all of these requirements. Consult your provider’s documentation to see if your environment is prepared to implement the procedures documented in this guide.

This guide was written against Apache 2.4 and cgit v1.2.1. Other version combinations may work as well.

Logging into the web server#

Log into the Web server over SSH:

local ~ $ ssh remoteuser@example.com
remote ~ $ hostname
example.com
remote ~ $ 

Apache configuration#

While not strictly necessary, configuring Apache before installing cgit is the best course of action. Not only does the Apache configuration declare the location of the cgit application on the server, but it also serves to implement a layer of security around the program before allowing any public access, which is always a good idea.

Configuration keywords in Apache are called directives. Each configuration setting in this guide will receive a brief explanation, but you are strongly encouraged to cross-check the settings listed here against the full list of Apache directives instead of blindly trusting the examples as they are listed. This will help bolster your comprehension of the source material and enable you to innovate upon the examples in this guide for your own specific needs.

Declare a public directory for cgit#

cgit should receive a dedicated directory on your site. This will distinguish it from the rest of the site’s contents and lead to easier configuration and management through Apache’s directory-specific configuration options.

This guide will assume your website is https://example.com, that the site’s root directory is /srv/web/mysite, and install cgit in a subdirectory of this path called code. Therefore, cgit’s full installation path in this guide will be /srv/web/mysite/code, accessible over the Web at https://example.com/code. Be sure to replace these example names as they appear throughout the guide with whatever names your setup will use.

After deciding upon a directory, go ahead and create the directory on the server:

remote ~ $ cd /srv/web/mysite
remote /srv/web/mysite $ ls -la
drwxr-xr-x  13 remoteuser   remoteuser  32 May 17 07:24 .
drwxr-xr-x   8 remoteuser   remoteuser  12 Aug 16 13:19 ..
-rw-r--r--   1 remoteuser   remoteuser  45 Jan 14 08:41 index.html
remote /srv/web/mysite $ mkdir code
remote /srv/web/mysite $ ls -la
drwxr-xr-x  13 remoteuser   remoteuser  32 May 17 07:24 .
drwxr-xr-x   8 remoteuser   remoteuser  12 Apr 16 13:19 ..
-rw-r--r--   1 remoteuser   remoteuser  45 Jan 14 08:41 index.html
drwxr-xr-x   2 remoteuser   remoteuser  20 Jun 11 20:42 code
remote /srv/web/mysite $

Notice the use of the -la flags with the ls command. The -l flag provides a long listing that includes additional information about each file, such as the file’s ownership and permissions settings. The -a flag ensures that normally-hidden files starting with the . character get included in the listing. The two listed files, . and .., are not really files but default locations available for every directory. They represent the current directory (.) and the parent directory (..).

It is essential to make sure the file ownership and permissions settings are accurate before proceeding. If these settings are too loose, they will allow unwanted access to the website, and if they are too strict, the site will become inaccessible.

Take a look at the long listing for the new directory along with a legend explaining the settings related to file ownership and permissions:

 permissions   user         group
drwxr-xr-x   2 remoteuser   remoteuser  20 Jun 11 20:42 code

The user and group settings indicate who owns the file. A user represents an individual account on the system, and a group can contain any number of users sharing common permissions on files owned by that group.

Many systems, including this one, will create a one-user group for each user, with the group’s name being the same as that one user. Your system may not do this, in which case you user may be part of a group called users which appears in this column instead.

You can use the whoami command to verify your user name and the groups command to show which groups your user belongs to:

remote /srv/web/mysite $ whoami
remoteuser
remote /srv/web/mysite $ groups
remoteuser ssh www
remote /srv/web/mysite $ 

With these ownership settings explained, take a look at the block of ten characters at the start of the line.

The first character indicates the type of file, which in this case is d for directory. A regular file like index.html will list a - here instead. There are many other types of special files not featured in this guide, such as sockets and links. Each of these special file types will show a different character here.

The next nine characters define the file’s permissions. It contains three sections of three characters each. In order, these sections define permissions for the file’s user, the file’s group, and everyone else on the system (called “others”). Each section defines the read, write, and execute permissions for that file, using the appropriate character if the permission is enabled and - if the permission is disabled.

This particular listing enables full read, write, and execute permissions for our user. The group and other settings allow for read and execute permissions, but importantly not write permissions. These are good general-purpose permissions for new directories, making it likely that your server will create your new directory with these permissions as well.

Apache generally runs as its own dedicated user which should not be able to modify (or write to) the website’s files, but which must be able to retrieve the contents of the website’s files (read permission) and run any programs which generate the website’s contents (execute permissions). The current permissions allow for this level of access, so this step is complete.

Declare a restricted directory for cgit#

In addition to cgit’s public directory, it is also a good idea to declare a restricted directory for cgit. This directory should provide full access for your login user plus read and execute permissions for Apache, but deny all other forms of access.

Many hosting providers offer such a directory for Web applications to store such privileged data. This guide will use /var/webdata/ to represent this directory:

remote /srv/web/mysite $ ls -la /var/webdata/
drwxrwx---  13 remoteuser   apache  32 Mar  9 22:22 .
drwxr-xr-x   8 root         root    12 Feb 26 09:04 ..
remote /srv/web/mysite $ 

When providing ls a directory instead of a regular file, ls will list the contents of that directory instead of the directory itself. This can be an issue when trying to determine the owner and permissions settings of the directory itself, but using -a will list those properties under the directory’s self-reference, ..

This directory offers full permissions for both your SSH login user as well as users in the apache group. Just as your user has a special group for itself, this server’s Apache user also has its own group. Others are not allowed any permissions to the contents of this directory.

While Apache has write permissions to this directory, it is possible to take away these permissions by creating a subdirectory with a narrower set of permissions:

remote /srv/web/mysite $ mkdir /var/webdata/cgit
remote /srv/web/mysite $ ls -la /var/webdata/
drwxrwx---  13 remoteuser   apache      32 Mar  9 22:22 .
drwxr-xr-x   8 root         root        12 Feb 26 09:04 ..
drwxr-xr-x   2 remoteuser   remoteuser  20 Jun 11 20:49 cgit
remote /srv/web/mysite $ 

Notice how the new cgit directory has different user, group, and permissions settings than the /var/webdata. These settings may look more permissive because they allow read and execute access for the file’s group and all other users.

However, these permissions ultimately take away write permissions for everyone except the owning user (notice the - in the permission bits for group and others). This achieves the desired effect of preserving Apache’s read and execute permissions while removing its write permissions.

If your host allows you the ability to alter permissions, you can make these permissions more specific for some additional security:

remote /srv/web/mysite $ sudo chown remoteuser /var/webdata/cgit
Password:
remote /srv/web/mysite $ sudo chgrp apache /var/webdata/cgit
Password:
remote /srv/web/mysite $ sudo chmod g=rx,o-rwx /var/webdata/cgit
Password:
remote /srv/web/mysite $ ls -la /var/webdata/
drwxrwx---  13 remoteuser   apache  32 Mar  9 22:22 .
drwxr-xr-x   8 root         root    12 Feb 26 09:04 ..
drwxr-x---   2 remoteuser   apache  20 Jun 11 20:49 cgit
remote /srv/web/mysite $ 

chown changes which user owns the provided file or directory. In this example, it effectively does nothing since remoteuser already owns the cgit directory, but it is listed here nonetheless because it is an essential command to know for changing file ownership.

chgrp changes the group for the provided file or directory. In this case, it changes the group of the cgit directory to apache.

chmod changes file permissions. This example uses = to set group permissions to read and execute (taking away write permissions if they are present) and uses - to take all permissions from others.

The best way to acquire more information about these commands is by consulting your server’s man pages:

remote /srv/web/mysite $ man chown
remote /srv/web/mysite $ man chgrp
remote /srv/web/mysite $ man chmod

Should your host not provide a restricted directory for Web applications, it may be necessary to use the public directory declared in the previous section for these resources. Of course, this has the notable disadvantage of exposing these resources to the public. Consult with your hosting provider or their documentation to determine if they offer a restricted directory for Web application data before resorting to this workaround.

This restricted directory will be used later in the guide when configuring cgit and its build process. Until then, simply make note of its location on your server.

Choose a configuration file#

Apache has two main types of configuration files. httpd.conf contains Apache’s global configuration, and each directory on a website can contain a file called .htaccess that contains configuration settings for that directory and all of its subdirectories.

Depending on your setup, you may not have access to one of these configuration methods, but you will need to be able to use at least one of them.

Decide which one to use and open it using your favorite text editor:

remote /srv/web/mysite $ vim /etc/apache2/httpd.conf
remote /srv/web/mysite $ vim /srv/web/mysite/code/.htaccess

httpd.conf may be in a different location than /etc/apache2/httpd.conf on your server. Check your server configuration along with your hosting provider’s documentation to determine the location of this file in your environment.

Base Apache configuration for cgit#

Add the following base configuration in your text editor:

<Directory "/srv/web/mysite/code">
    Options +ExecCGI
    AddHandler cgi-script .cgi
    DirectoryIndex cgit.cgi
</Directory>

The <Directory "/srv/web/mysite/code"> tag causes all configuration settings until the closing </Directory> tag to apply only to the /srv/web/mysite/code directory, which is desirable for creating an Apache configuration exclusive to cgit. If you are using .htaccess to supply your configuration settings, do not make a new <Directory> section and simply ignore these tags when listed in these examples. Apache already knows to restrict the configuration based on the location of the .htaccess file, making it unnecessary and potentially causing configuration issues.

The Options +ExecCGI setting enables server-side code execution, which is absolutely required in order to run cgit or any other CGI programs.

The AddHandler cgi-script .cgi setting tells Apache that files ending in .cgi are programs, not normal web pages or files. Instead of displaying them to the user like web pages or offering them for download as files, Apache will now attempt to run them as programs and display the results of that program to your visitors. The cgit program is called cgit.cgi, so it will be covered by this configuration setting because of its matching .cgi extension.

Likewise, the DirectoryIndex cgit.cgi setting tells Apache to use cgit.cgi as the default file name for the directory /srv/web/mysite/code. Since a URL like https://example.com/code/ does not contain a filename, Apache will look for a file with a default name in the requested directory. This default name is typically index.html, which will not exist in the final setup, so we must use this setting to change that default name to cgit.cgi. Without this setting, Apache will exhibit various undesirable behaviors like showing the contents of the directory or returning an error.

If your configuration looks accurate compared to the example above, save your changes to retain your current progress before moving on to the next section.

Enable the rewrite engine#

The rest of the configuration will make use of Apache’s mod_rewrite module, also called the rewrite engine. This is a powerful Apache feature that makes use of regular expressions to analyze URLs for achieving a wide range of helpful effects. The rewrite engine works by applying any number of rewrite rules, each of which can have a number of conditions that must be true in order for that rule to take effect.

Apache uses Perl Compatible Regular Expressions, or PCRE for short. Regular expressions provide the bulk of Apache’s rewrite engine’s flexibility. This guide will describe the regular expression patterns used in this guide, but will not provide a comprehensive discussion of all available PCRE patterns. That stated, the official PCRE pattern guide, especially the section on characters and metacharacters, is an excellent resource for learning how to build your own regular expression patterns and confirming the accuracy of the examples in this guide.

Rewrite rules are undeniably useful when properly implemented, but a poorly written rewrite rule can create an infinite loop and cause your site to become inaccessible until the rewrite rule is either fixed or removed. Pay close attention to syntax and triple-check your work before saving your changes, and be sure to consult Apache’s official mod_rewrite documentation.

This guide will use the rewrite engine for two major purposes:

Although this setup does not allow pushes over HTTP(S), you will be able to push to your Git repositories over SSH as documented later in the guide.

With this overview of the rewrite engine provided, go ahead and activate the rewrite engine in your configuration file:

<Directory "/srv/web/mysite/code">
    Options +ExecCGI
    AddHandler cgi-script .cgi
    DirectoryIndex cgit.cgi

    RewriteEngine on
</Directory>

The RewriteEngine on setting enables the rewrite engine. It is a simple setting without any further options beyond on and off.

Standardize URLs#

Apache expects the cgit.cgi program name to appear in URLs that request cgit and the repositories it is hosting.

This stated, it is not only possible to remove the program name from these URLs, but also desirable:

For these reasons, let’s add a first rewrite rule that will remove cgit.cgi from URLs referencing cgit:

<Directory "/srv/web/mysite/code">
    Options +ExecCGI
    AddHandler cgi-script .cgi
    DirectoryIndex cgit.cgi

    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule (.*) /code/cgit.cgi/$1 [END,QSA]
</Directory>

This is a single rewrite rule containing two conditions, both of which need to be true in order to process the rewrite rule.

The RewriteCond %{REQUEST_FILENAME} !-f setting takes the URL the visitor requested (%{REQUEST_FILENAME}) and check to see whether it does not (!) match an actual file (-f) on the server. Requests to cgit itself will never be actual files on the server, so this condition allow requests for such resources as cgit’s CSS stylesheet and logo to occur without unnecessarily modifying the URL and causing an HTTP 404 error.

The RewriteCond %{REQUEST_FILENAME} !-d setting is very similar to the previous setting, except that it matches against a directory (-d) instead of a file (-f). If there are any subdirectores in the directory where cgit exists, this rule will ensure that requests for those directories will successfully resolve without activating the rule and subsequently causing an error.

The next line, RewriteRule (.*) /code/cgit.cgi/$1 [END,QSA], provides the actual rewrite rule. The first part of the setting uses a regular expression to match any character (.) zero or more times (*), and to capture the results of that match (()) for use in the second part, which performs the actual URL modification. That second part /code/cgit.cgi/$1 simply appends that captured result ($1) to the string /code/cgit.cgi/, completing the silent and seamless modification of the URL on the backend.

The third part of this rule deserves special attention. A rewrite rule can apply any number of processing options called flags, supplied as the last part of the rule in square brackets ([]). This particular rule applies the END flag to stop processing any further rewrite rules for the provided request, and the QSA makes sure that the final URL includes the query string from the original request.

CGI programs make extensive use of query strings to affect their behavior, so it is essential to include the QSA flag to preserve cgit’s functionality. Query strings will receive further discussion in the next section, where they play a key role in implementing the next rewrite rule.

Forbid pushing over HTTP(S)#

cgit supports collaborative development by accepting Git pushes, but it does not implement any authentication by default, making this an unsafe option. While cgit does provides a simple authentication script, the least complicated solution to this is to simply deny all pushes over HTTP(S).

The rewrite engine offers a way to implement this behavior:

<Directory "/srv/web/mysite/code">
    Options +ExecCGI
    AddHandler cgi-script .cgi
    DirectoryIndex cgit.cgi

    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule (.*) /code/cgit.cgi/$1 [END,QSA]
    RewriteCond %{QUERY_STRING} service=git-receive-pack
    RewriteRule .* - [END,F]
</Directory>

As noted in the previous section, CGI programs depend on query strings to affect their behavior. Query strings appear at the end of a URL after a ? character and generally define a list of variables and their values separated with &.

For instance, the URL https://example.com/search?q=classical+music&lang=en contains the query string q=classical+music&lang=en, which sets the variable q to classical+music and the variable lang to en. It’s important to note that the ? is not actually part of the query string itself, serving merely as a separator between the query string and the rest of the URL.

The Git client uses query strings to define its interactions with a Git server, which can be used to detect Git pushes. The RewriteCond %{QUERY_STRING} service=git-receive-pack setting performs precisely this action, inspecting the query string for the string service=git-receive-pack. This string will only ever appear when someone is attempting to push changes to our Web server over Git.

When this occurs, the RewriteRule .* - [END,F] setting denies access by returning an HTTP 403 Forbidden error. The first part of this rule uses nearly the same pattern as the previous rule, just without attempting to capture the full URL (.*) using (). This capture is unnecessary, because the second part of this rule tells Apache to leave the URL unmodified (-).

Instead, this rule uses its flags to affect Apache’s behavior. The END flag introduced in the previous section is used here as well, but this rule also sets the F flag to return a HTTP 403 Forbidden error. It is this flag which achieves the desired effect of denying pushes. Later in the guide, you will configure your repositories to allow pushing using your existing SSH key.

Final configuration#

Your final configuration should look like this:

<Directory "/srv/web/mysite/code">
    Options +ExecCGI
    AddHandler cgi-script .cgi
    DirectoryIndex cgit.cgi

    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule (.*) /code/cgit.cgi/$1 [END,QSA]
    RewriteCond %{QUERY_STRING} service=git-receive-pack
    RewriteRule .* - [END,F]
</Directory>

Remember that if you are using .htaccess for your configuration file, do not include either the <Directory "/srv/web/mysite/code"> tag or its closing </Directory> tag.

If everything looks accurate, save the file and close your editor to return to the command line. It is time to build and install cgit.

Building cgit#

cgit is distributed as source code, so you must compile it into a form that your Web server understands as machine code.

Cloning the cgit source#

The first step to building cgit is to clone its source repository. While this can occur in any directory where you have full permissions, it’s best to find a personal directory which only your user can access.

Hosting providers generally offer their users some sort of dedicated personal space restricted from all other users. Setting a home directory for each user is probably the easiest means of implementing such a space, especially since it also gives users a place to define personal login settings.

The home directory could be located anywhere on the system, but its location will always be accessible through the shorthand ~ or the $HOME variable:

remote /srv/web/mysite $ echo ~
/home/remoteuser
remote /srv/web/mysite $ echo $HOME
/home/remoteuser
remote /srv/web/mysite $ 

echo can show the contents of variables, which always begin with $, as well as the ~ shorthand.

In the event that these echo commands return blank lines, your login user does not have a home directory defined. Consult your hosting provider to determine whether they offer such personal storage space and how to access it to ensure that nobody will access your cloned cgit repository.

Should you have a home directory defined, take a look at its permissions:

remote /srv/web/mysite $ ls -la $HOME
drwx------  10 remoteuser   remoteuser     21 Jun 20 02:33 .
drwxr-xr-x   8 remoteuser   remoteuser      8 Aug 16 13:42 ..
-rw-rw-r--   1 remoteuser   remoteuser    113 Sep 12 20:10 .bash_profile

As the ownership and permissions settings for . indicate, remoteuser has full access to this directory (rwx), but permissions for anyone else are completely denied (------). Therefore, this directory is accessible only for your login user, making it an ideal place for cloning cgit.

Switch to the directory and clone cgit:

remote /srv/web/mysite $ cd /home/remoteuser
remote /home/remoteuser $ git clone https://git.zx2c4.com/cgit
Cloning into 'cgit'...
remote: Enumerating objects: 6921, done.
remote: Counting objects: 100% (6921/6921), done.
remote: Compressing objects: 100% (2624/2624), done.
remote: Total 6921 (delta 4952), reused 5983 (delta 4281)
Receiving objects: 100% (6921/6921), 8.70 MiB | 3.41 MiB/s, done.
Resolving deltas: 100% (4952/4952), done.
remote /home/remoteuser $ ls
cgit
remote /home/remoteuser $ 

cgit relies on the source code for Git itself. Conveniently, it is a submodule of the cgit repository, so to satisfy this dependency, simply initialize and update the submodule:

remote /home/remoteuser $ cd cgit
remote /home/remoteuser/cgit $ git submodule init
Submodule 'git' (https://git.kernel.org/pub/scm/git/git.git) registered for path 'git'
remote /home/remoteuser/cgit $ git submodule update
Cloning into '/home/remoteuser/cgit/git'...
Submodule path 'git': checked out '2e1470b37befa08393093cd4d7e36429e1eeeef7'

Configure the build process#

Like many projects, cgit ships with a Makefile to automate its various build procedures. It is a standard text file that you can read and edit with a text editor:

remote /home/remoteuser/cgit $ vim Makefile

You should see the Makefile define a series of variables at the top, which it uses for the various build procedures it defines later in the file. Take a moment to appreciate the structure of the Makefile, but you do not need a deep comprehension of Makefile syntax to build and install cgit, so go ahead and close your editor once you are done looking at the Makefile.

For more information on make and its syntax, your best resource is to consult your server’s man pages depending on the name of your make program:

remote /home/remoteuser/cgit $ man make
remote /home/remoteuser/cgit $ man gmake

While a full comprehension of the Makefile is not necessary, you do need to redefine some of its default values to match your specific environment before building cgit. Instead of editing the Makefile itself, cgit uses a file called cgit.conf to record your environment-specific build settings.

This file does not exist by default. Go ahead and open this file with your text editor:

remote /home/remoteuser/cgit $ vim cgit.conf

The first configuration settings to define are cgit’s public paths:

CGIT_SCRIPT_PATH = /srv/web/mysite/code
CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)

CGIT_SCRIPT_PATH is the path where cgit will ultimately get installed. Be sure to replace /srv/web/mysite/code with whatever directory path you are using for your own cgit installation.

CGIT_DATA_PATH is the pathname where cgit stores its favicon, CSS file, and logo image. These resources should be visible for public web users, which makes it convenient to use the same directory as CGIT_SCRIPT_PATH. Be sure to use the proper Makefile syntax $(VARIABLE) for capturing the value of a variable for reassignment to another variable.

The next settings will make use of the restricted directory declared earlier in the guide:

CGIT_CONFIG = /var/webdata/cgit/cgitrc
CACHE_ROOT = /var/webdata/cgit/cache
prefix = /var/webdata/cgit/local

CGIT_CONFIG is the path for cgit’s configuration file, separate from this build configuration. Take note of the path you provide here, since we will set up this configuration file in the next section.

CACHE_ROOT provides the location for cgit’s cache, which is disabled by default but valuable to keep in a defined subdirectory if you want to use it.

prefix defines the base directory where cgit will save various local files, including helper scripts for syntax highlighting. This value is /usr/local by default, but you may not have access to this directory on your server, making the restricted directory or a subdirectory of the restricted directory a good place to put these files.

Configure any other variables whose Makefile defaults you want to override in your cgit.conf file, save the file, then exit your editor and run the make/gmake/etc. command to build cgit:

remote /home/remoteuser/cgit $ make
    SUBDIR git
CGIT_VERSION = v1.2.1-16-g7d87
    * new CGit build flags
    CC ../cgit.o
    CC ../cache.o
    <snip>
    CC xdiff/xpatience.o
    CC xdiff/xhistogram.o
    AR xdiff/lib.a
    * linking without autodetected Lua support
    LINK ../cgit
remote /home/remoteuser/cgit $ 

It’s important that the build process completes without error. If you encounter any errors, check your cgit.conf file for syntax errors or see if the build succeeds using a different make utility.

Once you compile cgit successfully, you can now install it using the make install or gmake install command:

remote /home/remoteuser/cgit $ make install
    SUBDIR git
    install -m 0755 -d /srv/www/mysite/code
    install -m 0755 cgit /srv/www/mysite/code/cgit.cgi
    install -m 0755 -d /srv/www/mysite/code
    install -m 0644 cgit.css /srv/www/mysite/code/cgit.css
    install -m 0644 cgit.png /srv/www/mysite/code/cgit.png
    install -m 0644 favicon.ico /srv/www/mysite/code/favicon.ico
    install -m 0644 robots.txt /srv/www/mysite/code/robots.txt
    install -m 0755 -d /var/webdata/cgit/local/lib/cgit/filters
    cp -r filters/* /var/webdata/cgit/local/lib/cgit/filters
remote /home/remoteuser/cgit $

As the command output suggests, “installation” simply means copying the compiled program into your designated destination directory.

At this point, cgit is live! However, your installation now needs configuration so that it knows where your repositories are, and you also need at least one repository for cgit to display.

Configure cgit#

Edit a new file at the pathname you used for CGIT_CONFIG:

remote /home/remoteuser/cgit $ grep CGIT_CONFIG /home/remoteuser/cgit.conf
CGIT_CONFIG = /var/webdata/cgit/cgitrc
remote /home/remoteuser/cgit $ vim /var/webdata/cgit/cgitrc

Supply a basic configuration for cgit in your cgitrc file:

# Use /code/ as the basis for generating URLs
virtual-root=/code/

# Uncomment this line to prevent web spiders from accessing repos
#robots=noindex, nofollow

# Define locations for the CSS and logo files
css=/code/cgit.css
logo=/code/cgit.png
favicon=/code/favicon.ico

# Describe the repository
root-title=example.com git
root-desc=a cgit instance running on example.com

# Clicking the link will take the user back to https://example.com/
logo-link=/

# Read each repo's Git configuration files
enable-git-config=1

# Run code source files through the syntax highlighter
# If the .sh file does not work and you have python, use the .py file instead
source-filter=/var/webdata/cgit/local/lib/cgit/filters/syntax-highlighting.sh

# Remove .git suffix from repo names
remove-suffix=1

# Define the default clone url
clone-url=https://example.com/code/$CGIT_REPO_URL

# Define the path where the repos live
scan-path=/var/webdata/cgit/repos

The accompanying comments explain the essence of each configuration option, but a few critical ones will receive additional coverage here.

virtual-root completes the standardization of cgit’s URL structure by providing the prefix for the URLs cgit generates. Without this option, cgit will generate links containing the undesirable cgit.cgi element. Be sure to match this directory to whatever folder contains cgit.

logo-link=/ will take visitors who click the upper-left cgit logo on each page back to the main page of your website. This is probably the best solution for most use cases, but you can point this link to wherever you want.

source-filter offers an opportunity to apply syntax highlighting to your project’s code files. If the .sh file does not work and you have Python installed on your web server, you can change this extension to .py. If neither script works on your host, you must unfortunately remove the entire line or else you code will not display at all.

clone-url ensures the presence of cgit displaying a clone link for each project. The URL format used here ensures the exclusion of any cgit.cgi element in the URL.

scan-path is the directory where cgit will search for the Git repositories that it will display. A subdirectory inside your restricted directory is the best choice for this path.

cgit has many more configuration values than those listed here. Consult the file cgitrc.5.txt wherever you cloned cgit as a reference for expanding this configuration to your needs:

remote /home/remoteuser/cgit $ vim cgitrc.5.txt

Add any extra configuration options you desire and make sure your syntax is accurate, then save your cgit configuration file and close the editor.

With cgit’s configuration established, it is time to add some repositories.

Creating and populating the repository store#

Create the directory whose path matches the value of scan-path from the cgit configuration file:

remote /home/remoteuser/cgit $ grep scan-path /var/webdata/cgit/cgitrc
scan-path=/var/webdata/cgit/repos
remote /home/remoteuser/cgit $ cd /var/webdata/cgit/
remote /var/webdata/cgit $ mkdir repos
remote /var/webdata/cgit $ cd repos
remote /var/webdata/cgit/repos $

The easiest way to set up new repositories is to make a bare repository on the remote server, after which you can create a remote on your local Git project pointing to that new bare repository.

cgit makes use of the gitweb.owner and gitweb.url configuration settings as well as the contents of the .git/description file. You can provide a default value for these variables by creating a new template from the default template located at /usr/share/git-core/templates:

remote /var/webdata/cgit/repos $ cp -r /usr/share/git-core/templates ~/git-template
remote /var/webdata/cgit/repos $ cd ~/git-template
remote /home/remoteuser/git-template $ git config --local gitweb.owner 'Test User'
remote /home/remoteuser/git-template $ git config --local gitweb.url 'https://example.com/'
remote /home/remoteuser/git-template $ echo "default description" > description

To use this template as the default, set the init.templateDir in your global Git configuration:

remote /home/remoteuser/git-template $ git config --global init.templateDir /home/remoteuser/git-template

You are now using your custom template as the default template for bare repositories.

To test the cgit setup, let’s start a new Git project. First, initialize a bare repository in the remote repository store:

remote /home/remoteuser/git-template $ cd /var/webdata/cgit/repos
remote /var/webdata/cgit/repos $ git init --bare testrepo.git
Initialized empty Git repository in /var/webdata/cgit/repos/testrepo.git/
remote /var/webdata/cgit/repos $ ls
testrepo.git

Provide a relevant description for the project by editing the description file inside the bare repository directory:

remote /var/webdata/cgit/repos $ echo "Example repository" > testrepo.git/description

Now log out from your server (or open a new terminal session on your local machine), initialize a new Git repository, and declare this new bare repository as an upstream remote accessible over SSH:

remote /var/webdata/cgit/repos $ exit
Connection to remote closed.
local$ git init ~/git/testrepo/
Initialized empty Git repository in /home/user/git/testrepo/.git/
local$ cd ~/git/testrepo/
local ~/git/testrepo/ $ git remote add public ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo.git/
local ~/git/testrepo/ $ git checkout -b release
Switched to a new branch 'release'
local ~/git/testrepo/ $

Let’s add a small script to this repository:

local ~/git/testrepo/ $ cat << __END_SCRIPT > example.sh
#!/bin/bash
#loop over a range of integers and note which are even
begin=1
end=20
for ((count=start;count<=end;count++)); do
    echo "You are on count \$count"
    if [[ \$((count % 2)) -eq 0 ]]; then
        echo "\$count is an even number"
    fi
done
__END_SCRIPT
local ~/git/testrepo/ $ chmod +x example.sh
local ~/git/testrepo/ $ git add example.sh
local ~/git/testrepo/ $ git commit -m'Include example script'
[release (root-commit) 7b6378f] Include example script
 1 file changed, 10 insertions(+)
  create mode 100755 example.sh
local ~/git/testrepo/ $ git push public release
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 2 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 386 bytes | 386.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo.git/
 * [new branch]      release -> release

Congratulations! You just pushed your first cgit-viewable repository!

At this point, you should have a functional instance of cgit running on your server ready to populate with more repositories.

Testing the setup#

Now that cgit has a repository in its store, let’s make sure it’s working correctly.

First, you should be able to pull up cgit in your web browser:

Pulling up cgit in a web browser

If this step does not work, make sure you are using the right directory and check your base Apache configuration to see if any piece of the setup is missing or inaccurate.

Access the repository#

Next, make sure that the repository links are working by clicking the repository:

Accessing the repository and indicating clean URLs

As the mouse cursor indicates in the graphic, cgit should be generating working links that do not contain cgit.cgi. If they do, go over your Apache rewrite rules and your cgitrc’s virtual-root setting to make sure these settings are properly configured.

Your server should have no problem with the lack of a trailing slash when clicking the clone URL, but if you require or prefer a trailing slash, simply add a trailing slash to the clone-url in your cgitrc file.

Test file display and syntax highlighting#

Navigate to the tree to view your script file and ensure it renders with syntax highlighting:

Ensuring the file appears with syntax highlighting

If you encounter problems with its display, you may need to use the other language version of the syntax highlighter filter. If neither one works, you may unfortunately need to disable syntax highlighting or else your code will not display at all. Review the cgitrc configuration section for more information.

Clone a copy of the repository#

See if you can clone the repository over HTTP(S) into a temporary directory:

local ~/git/testrepo $ cd /tmp
local /tmp $ git clone https://example.com/code/testrepo/
Cloning into 'testrepo'...
local /tmp $ cd testrepo/
local /tmp/testrepo $

Ensure pushing over HTTP(S) fails#

Your new cloned remote should have its upstream remote automatically set to the clone URL:

local /tmp/testrepo $ git remote -v
origin  https://example.com/code/testrepo/ (fetch)
origin  https://example.com/code/testrepo/ (push)

Public access should be read-only, so ensure this feature works to prevent unwanted pushes to your repositories:

local /tmp/testrepo $ git remote -v
fatal: unable to access 'https://example.com/code/testrepo/': The requested URL returned error: 403

If you do not receive a 403 error, Git is trying to find a way to authenticate your access to this repository. Unless you are attempting to set up an authentication mechanism, review how to prevent HTTP(S) pushing to ensure your server properly returns a 403 error.

You can now delete the temporary clone and revert back to your permanent repository:

local /tmp/testrepo $ cd ~/git/testrepo
local ~/git/testrepo $ rm -rf /tmp/testrepo

Last but not least, test the backlinks to verify the site’s navigability:

Clicking backlinks to ensure navigability

If the logo does not point to the right URL, check the value of link-logo in your cgitrc file.

Updating cgit#

Now that your cgit instance is fully deployed, it is important to make sure you keep it up to date with the latest features and fixes.

Updating cgit is as simple as pulling the latest cgit repository and its submodules, then building the new code from a clean environment:

remote /srv/www/mysite $ cd ~/cgit
remote /home/remoteuser/cgit $ git pull
remote /home/remoteuser/cgit $ git submodule update
remote /home/remoteuser/cgit $ make clean
rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
rm -f cgit VERSION CGIT-CFLAGS *.o tags
rm -f -r .deps
remote /home/remoteuser/cgit $ make
    SUBDIR git
CGIT_VERSION = v1.2.1-16-g54c4
    * new CGit build flags
    CC ../cgit.o
    CC ../cache.o
    <snip>
    CC xdiff/xpatience.o
    CC xdiff/xhistogram.o
    AR xdiff/lib.a
    * linking without autodetected Lua support
    LINK ../cgit
remote /home/remoteuser/cgit $ 

Once the build is complete, simply install the updated cgit:

remote /home/remoteuser/cgit $ make install
    SUBDIR git
    install -m 0755 -d /srv/www/mysite/code
    install -m 0755 cgit /srv/www/mysite/code/cgit.cgi
    install -m 0755 -d /srv/www/mysite/code
    install -m 0644 cgit.css /srv/www/mysite/code/cgit.css
    install -m 0644 cgit.png /srv/www/mysite/code/cgit.png
    install -m 0644 favicon.ico /srv/www/mysite/code/favicon.ico
    install -m 0644 robots.txt /srv/www/mysite/code/robots.txt
    install -m 0755 -d /var/webdata/cgit/local/lib/cgit/filters
    cp -r filters/* /var/webdata/cgit/local/lib/cgit/filters
remote /home/remoteuser/cgit $

Configuring separate push and fetch remote URLs#

Since this setup forbids pushing over HTTP(S), you may find it problematic to push modified repositories that were originally cloned from the Web.

Fortunately, Git supports configuring separate push and remote URLs for a single remote. The Git documentation stresses that these URLs should refer to the same location to maintain logical consistency as a single source, which fortunately matches the use case for the deployment described in this guide.

Normally, setting a remote’s URL without any options changes both the fetch and the push URLs:

local ~/git/testrepo $ git remote set-url public https://example.com/code/testrepo/
local ~/git/testrepo $ git remote -v
public  https://example.com/code/testrepo/ (fetch)
public  https://example.com/code/testrepo/ (push)
local ~/git/testrepo $

To change just the push URL for a remote, use the command git remote set-url --push:

local ~/git/testrepo $ git remote set-url --push public ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo
local ~/git/testrepo $ git remote -v
public  https://example.com/code/testrepo/ (fetch)
public  ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo (push)

Similarly, it is possible to change just the fetch URL without changing the push URL using the git remote set-url --no-push command:

local ~/git/testrepo $ git remote set-url --no-push public https://example.com/code/testrepo/
local ~/git/testrepo $ git remote -v
public  https://example.com/code/testrepo/ (fetch)
public  ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo (push)

However you configure your remotes, make sure both pushing and fetching work:

local ~/git/testrepo $ git push public release
Everything up-to-date
local ~/git/testrepo $ git pull public release
Already up to date.
local ~/git/testrepo $ 

Add a custom logo image#

cgit lets you display a small logo image in the upper-left corner of its interface. Determine the path of the logo in your cgitrc configuration:

remote /srv/www/mysite $ grep 'logo=' /var/webdata/cgit/cgitrc
logo=/code/cgit.png
remote /srv/www/mysite $ 

Note that the listed path is relative to your website’s root directory. Using the guide’s examples would make this path /srv/www/mysite/code/cgit.png, but be sure to use whatever settings you defined in your setup to locate the logo file on your own server.

The default image is a 96x64 PNG image. There is some flexibility in the size, but do try to find an image similar in size to this image. Afterwards, simply use scp or another upload utility to upload the file to your server:

local ~ $ scp mylogo.png remote:/srv/www/mysite/code/cgit.png
mylogo.png                                              100% 1284    16.1KB/s 00:00
local ~ $

cgit may overwrite the logo after upgrade. If this occurs, simply repeat these steps after upgrading cgit to restore your custom logo.

Conclusion#

You should now have a working self-hosted repository store! Go ahead and start populating it with your personal projects!

There are many more setup options that this guide does not cover. I invite you to look through the extensive documentation for both cgit and Apache to experiment these options and tailor this guide’s setup procedure to your specific requirements.

As you might expect, I am using the information from this guide for my own cgit deployment. Feel free to explore my repository store and clone my projects!


First published June 11, 2019
Last updated June 21, 2019

Filed in Computing
Only article in category