[PATCH] cmd/cgitd: HTTP server for cgit CGI
Shulhan
ms at kilabit.info
Fri Apr 17 19:43:38 UTC 2026
The cmd/cgitd provides a quick alternatives to setup and run cgit
without installing another second application (usually a web server with
their own configuration).
To build and run cgitd you needs Go compiler.
Its only needs five environment variables, two requireds, and three
optionals.
Required variables are,
- CGITD_CGIT_BIN: path to cgit.cgi
- CGITD_CGIT_STATIC_DIR: path to directory that contains cgit.css and
cgit.js
The other three optional variables are,
- CGITD_LISTEN: address and port to serve HTTP connections, default to
"127.0.0.1:19418" if it not set.
- CGITD_REPO_PREFIXES: comma separated prefix that used in repo.url.
- CGITD_STATIC_DIR: path to directory that contains favicon and logo.
- CGIT_CONFIG: path to cgitrc
Once sets, you can build and run cgitd using the following commands,
$ go build ./cmd/cgitd
$ ./cgitd
Signed-off-by: Shulhan <ms at kilabit.info>
---
This patch is leaning more toward sharing rather than expecting it to be
merged to upstream.
When the first time I try to setup cgit, I am stuck on "should I install
this another package (Apache/lighttpd/...)? Is there any other way I can
see how it works before deploying it on server?"
The original idea is to be able running cgit on new cloned/modified cgit
source code,
$ CGIT_CONFIG=${PWD}/cgitrc.test go run ./cmd/cgitd
and then update cgitrc.test to see how it works using my local
repositories.
Then, I think it can be helpful to others, not only for quick testing
during cgit development itself, but can be used on live deployment too.
PS: Any Arch Linux users who wants to try or like this patch can install
the prebuild package from https://build.kilabit.info/aur.
You can see whats included in the package here:
https://git.kilabit.info/aur-cgit-git and
https://git.kilabit.info/cgit
.gitignore | 1 +
Makefile | 13 +++-
README | 33 ++++++++
cmd/cgitd/main.go | 192 ++++++++++++++++++++++++++++++++++++++++++++++
go.mod | 11 +++
go.sum | 4 +
6 files changed, 252 insertions(+), 2 deletions(-)
create mode 100644 cmd/cgitd/main.go
create mode 100644 go.mod
create mode 100644 go.sum
diff --git a/.gitignore b/.gitignore
index 661df34..0d05b2f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Files I don't care to see in git-status/commit
/cgit
+/cgitd
cgit.conf
CGIT-CFLAGS
VERSION
diff --git a/Makefile b/Makefile
index 16bfc55..888a5c4 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,7 @@ CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
CGIT_CONFIG = /etc/cgitrc
CACHE_ROOT = /var/cache/cgit
prefix = /usr/local
+bindir = $(prefix)/bin
libdir = $(prefix)/lib
filterdir = $(libdir)/cgit/filters
docdir = $(prefix)/share/doc/cgit
@@ -23,6 +24,7 @@ MAN_TXT = $(MAN5_TXT)
DOC_MAN5 = $(patsubst %.txt,%,$(MAN5_TXT))
DOC_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
DOC_PDF = $(patsubst %.txt,%.pdf,$(MAN_TXT))
+GO := $(shell which go 2>/dev/null)
ASCIIDOC = asciidoc
ASCIIDOC_EXTRA =
@@ -68,13 +70,17 @@ ifndef V
export V
endif
+
.SUFFIXES:
-all:: cgit
+all:: cgit cgitd
cgit:
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk ../cgit $(EXTRA_GIT_TARGETS) NO_CURL=1
+cgitd: cmd/cgitd/main.go
+ if [ -n "$(GO)" ]; then $(GO) build ./cmd/cgitd; fi
+
sparse:
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk NO_CURL=1 cgit-sparse
@@ -85,6 +91,8 @@ test:
install: all
$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH)
$(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
+ $(INSTALL) -m 0755 -d $(DESTDIR)$(bindir)
+ if [ -n "$(GO)" ]; then $(INSTALL) -m 0755 cgitd $(DESTDIR)$(bindir)/cgitd; fi
$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH)
$(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
$(INSTALL) -m 0644 cgit.js $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js
@@ -115,6 +123,7 @@ endef
uninstall:
rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
+ rm -f $(DESTDIR)$(bindir)/cgitd
rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js
rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
@@ -157,7 +166,7 @@ $(DOC_PDF): %.pdf : %.txt
a2x -f pdf cgitrc.5.txt
clean: clean-doc
- $(RM) cgit VERSION CGIT-CFLAGS *.o tags
+ $(RM) cgit cgitd VERSION CGIT-CFLAGS *.o tags
$(RM) -r .deps
cleanall: clean
diff --git a/README b/README
index 7a6b4a4..24cf52e 100644
--- a/README
+++ b/README
@@ -51,6 +51,7 @@ Dependencies
* libzip
* libcrypto (OpenSSL)
* libssl (OpenSSL)
+* optional: Go compiler, to build cgitd
* optional: luajit or lua, most reliably used when pkg-config is available
Apache configuration
@@ -67,6 +68,38 @@ like this:
</Directory>
+Running with cgitd
+-------------------
+
+The cmd/cgitd provides a quick alternatives to setup and run cgit
+without installing another second application (usually a web server with
+their own configuration).
+
+To build and run cgitd you needs the Go compiler.
+
+Cgitd needs five environment variables, two are requireds, and three are
+optionals.
+Required variables are,
+
+- CGITD_CGIT_BIN: path to cgit.cgi
+- CGITD_CGIT_STATIC_DIR: path to directory that contains cgit.css and
+ cgit.js
+
+The three optional variables are,
+
+- CGITD_LISTEN: address and port to serve HTTP connections, default to
+ "127.0.0.1:19418".
+- CGITD_REPO_PREFIXES: comma separated prefix that used in repo.url.
+- CGITD_STATIC_DIR: path to directory that contains favicon and logo.
+ If set, the content of this directory will be served under "/static/"
+ path, so make sure to add that prefix to favicon= and logo= in cgitrc.
+- CGIT_CONFIG: path to cgitrc
+
+Once sets, you can build and run cgitd using the following commands,
+
+ $ go build ./cmd/cgitd
+ $ ./cgitd
+
Runtime configuration
---------------------
diff --git a/cmd/cgitd/main.go b/cmd/cgitd/main.go
new file mode 100644
index 0000000..56b1b28
--- /dev/null
+++ b/cmd/cgitd/main.go
@@ -0,0 +1,192 @@
+// Copyright (C) 2026 cgit Development Team <cgit at lists.zx2c4.com>
+// Licensed under GNU General Public License v2
+// (see COPYING for full license text)
+
+// Program cgitd is the HTTP server for cgit CGI.
+// This program can be built into single binary, provides as an alternatives
+// to common setup that require second application (web server with their own
+// configuration).
+//
+// cgitd application can be customized through environment variables or by
+// modifying this code directly before build.
+//
+// # Environment variables
+//
+// The following environment variables affect on cgitd only.
+//
+// CGITD_CGIT_BIN - The full path to cgit.cgi.
+// It should be in the same directory that contains the "filters" scripts.
+// This environment is required, default to "/usr/lib/cgit/cgit.cgi".
+//
+// CGITD_CGIT_STATIC_DIR - The full path to directory that contains cgit
+// resources (cgit.css, cgit.js, cgit.png).
+// This environment is required, default to "/usr/share/webapps/cgit/".
+//
+// CGITD_LISTEN - IP address and port to accept HTTP connections.
+// This environment is required, default to "127.0.0.1:19418".
+//
+// CGITD_REPO_PREFIXES - Comma separated repo URL prefixes that will be
+// handled by cgit.
+// This environment is optional, no default value.
+//
+// CGITD_STATIC_DIR - The full path to serve static files, like favicon and
+// logo.
+// If set, the content of this directory will be served under "/static/" path,
+// so make sure to add that prefix to favicon= and logo= in cgitrc.
+// This environment is optional, no default value.
+//
+// # Environment passed to cgit
+//
+// The following environment variables is passed to cgit process.
+//
+// CGIT_CONFIG - The full path to cgitrc file.
+// This environment is optional.
+package main
+
+import (
+ "log"
+ "net"
+ "net/http"
+ "net/http/cgi"
+ "os"
+ "strings"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/systemd"
+)
+
+// List of known environment variables handled by cgitd.
+const (
+ envCgitBin = `CGITD_CGIT_BIN`
+ envCgitStaticDir = `CGITD_CGIT_STATIC_DIR`
+ envListen = `CGITD_LISTEN`
+ envRepoPrefixes = `CGITD_REPO_PREFIXES`
+ envStaticDir = `CGITD_STATIC_DIR`
+)
+
+const (
+ defCgitBin = `/usr/lib/cgit/cgit.cgi`
+ defCgitStaticDir = `/usr/share/webapps/cgit/`
+ defListen = `127.0.0.1:19418`
+ defStaticPrefix = `/static`
+)
+
+// List of known environment variables passed to cgit process.
+var listCgitEnv = []string{
+ `CGIT_CONFIG`,
+}
+
+type config struct {
+ cgitBin string // Path to cgit.cgi.
+ cgitStaticDir string // Path to cgit static resources.
+ listen string // Address for accepting connection.
+ staticDir string // Path to non-cgit resources like favicon and logo.
+
+ cgitEnvs []string // List of environment variables for cgit.
+ repoPrefixes []string // List of repo URL prefixes.
+}
+
+func initConfig() (cfg config) {
+ cfg.cgitBin = os.Getenv(envCgitBin)
+ if cfg.cgitBin == "" {
+ cfg.cgitBin = defCgitBin
+ }
+
+ cfg.cgitStaticDir = os.Getenv(envCgitStaticDir)
+ if cfg.cgitStaticDir == "" {
+ cfg.cgitStaticDir = defCgitStaticDir
+ }
+
+ cfg.listen = os.Getenv(envListen)
+ if cfg.listen == "" {
+ cfg.listen = defListen
+ }
+
+ repoPrefixes := os.Getenv(envRepoPrefixes)
+ for _, item := range strings.Split(repoPrefixes, ",") {
+ item = strings.TrimSpace(item)
+ if item != "" {
+ cfg.repoPrefixes = append(cfg.repoPrefixes, item)
+ }
+ }
+
+ cfg.staticDir = os.Getenv(envStaticDir)
+
+ for _, key := range listCgitEnv {
+ val := os.Getenv(key)
+ if val != "" {
+ cfg.cgitEnvs = append(cfg.cgitEnvs, key+`=`+val)
+ }
+ }
+
+ return cfg
+}
+
+func main() {
+ logp := `cgitd`
+ cfg := initConfig()
+
+ log.Printf(`%s: %s=%s`, logp, envCgitBin, cfg.cgitBin)
+ log.Printf(`%s: %s=%s`, logp, envCgitStaticDir, cfg.cgitStaticDir)
+ log.Printf(`%s: %s=%s`, logp, envListen, cfg.listen)
+ log.Printf(`%s: %s=%s`, logp, envRepoPrefixes, cfg.repoPrefixes)
+ log.Printf(`%s: %s=%s`, logp, envStaticDir, cfg.staticDir)
+ log.Printf(`%s: cgitEnvs=%s`, logp, cfg.cgitEnvs)
+
+ rootfs, err := os.OpenRoot(cfg.cgitStaticDir)
+ if err != nil {
+ log.Fatalf(`%s: %s`, logp, err)
+ }
+ cgitStaticFS := http.FileServerFS(rootfs.FS())
+
+ mux := http.NewServeMux()
+ mux.Handle(`GET /cgit.css`, cgitStaticFS)
+ mux.Handle(`GET /cgit.js`, cgitStaticFS)
+ mux.Handle(`GET /cgit.png`, cgitStaticFS)
+ mux.Handle(`GET /robots.txt`, cgitStaticFS)
+
+ if cfg.staticDir != "" {
+ rootfs, err = os.OpenRoot(cfg.staticDir)
+ if err != nil {
+ log.Fatalf(`%s: %s`, logp, err)
+ }
+ staticFS := http.FileServerFS(rootfs.FS())
+ mux.Handle(`GET `+defStaticPrefix+`/`,
+ http.StripPrefix(defStaticPrefix, staticFS))
+ }
+
+ cgitHandler := &cgi.Handler{
+ Path: cfg.cgitBin,
+ Env: cfg.cgitEnvs,
+ }
+ for _, prefix := range cfg.repoPrefixes {
+ mux.Handle(`GET `+prefix, cgitHandler)
+ }
+ mux.Handle(`GET /`, cgitHandler)
+
+ httpd := http.Server{
+ Addr: cfg.listen,
+ Handler: mux,
+ }
+
+ // Allow activation through systemd socket.
+ var listener net.Listener
+ listeners, err := systemd.Listeners(true)
+ if err != nil {
+ log.Fatalf(`%s: %s`, logp, err)
+ }
+ for _, l := range listeners {
+ if l.Addr().String() == cfg.listen {
+ listener = l
+ }
+ }
+ if listener == nil {
+ log.Printf(`%s: listening on address %s`, logp, cfg.listen)
+ err = httpd.ListenAndServe()
+ } else {
+ log.Printf(`%s: activated through systemd socket`, logp)
+ err = httpd.Serve(listener)
+ }
+ if err != nil {
+ log.Fatalf(`%s: %s`, logp, err)
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..6807df0
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,11 @@
+// Copyright (C) 2026 cgit Development Team <cgit at lists.zx2c4.com>
+// Licensed under GNU General Public License v2
+// (see COPYING for full license text)
+
+module git.zx2c4.com/cgit
+
+go 1.26.0
+
+require git.sr.ht/~shulhan/pakakeh.go v0.62.0
+
+require golang.org/x/sys v0.42.0 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..c6b687e
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,4 @@
+git.sr.ht/~shulhan/pakakeh.go v0.62.0 h1:rAX0NpfxGbRfe6YhG7I9mmETmcafAreXUZqwPWNDvw4=
+git.sr.ht/~shulhan/pakakeh.go v0.62.0/go.mod h1:M8FG29UN+TMqRsOvKFTjsBuZDiGeBzYlAFxHjBM03Rs=
+golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
+golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
base-commit: 829eb0711305e8946fa2f4a1c57c43354f35e208
--
2.54.0.rc2.6.g1194a4dfc5.dirty
More information about the CGit
mailing list