diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Reorganize the tree into vendor/ and custom/
Diffstat (limited to 'custom')
-rw-r--r--custom/cgitrc566
-rw-r--r--custom/extensions/about-render.lua592
-rw-r--r--custom/extensions/auth-file.lua556
-rw-r--r--custom/extensions/auth-inline.lua528
-rw-r--r--custom/extensions/email-gravatar.lua115
-rw-r--r--custom/extensions/email-libravatar.lua114
-rw-r--r--custom/extensions/link-commits.lua153
-rw-r--r--custom/extensions/syntax-highlight.lua278
-rwxr-xr-xcustom/hooks/post-receive.agefile19
-rwxr-xr-xcustom/hooks/post-update.update-server-info18
-rw-r--r--custom/servers/apache.conf152
-rw-r--r--custom/servers/lighttpd.conf132
-rw-r--r--custom/servers/nginx.conf193
13 files changed, 3416 insertions, 0 deletions
diff --git a/custom/cgitrc b/custom/cgitrc
new file mode 100644
index 0000000..85929d4
--- /dev/null
+++ b/custom/cgitrc
This diff is too large to be rendered inline. View it on its own page.
diff --git a/custom/extensions/about-render.lua b/custom/extensions/about-render.lua
new file mode 100644
index 0000000..073b531
--- /dev/null
+++ b/custom/extensions/about-render.lua
This diff is too large to be rendered inline. View it on its own page.
diff --git a/custom/extensions/auth-file.lua b/custom/extensions/auth-file.lua
new file mode 100644
index 0000000..5415ca7
--- /dev/null
+++ b/custom/extensions/auth-file.lua
This diff is too large to be rendered inline. View it on its own page.
diff --git a/custom/extensions/auth-inline.lua b/custom/extensions/auth-inline.lua
new file mode 100644
index 0000000..168a444
--- /dev/null
+++ b/custom/extensions/auth-inline.lua
This diff is too large to be rendered inline. View it on its own page.
diff --git a/custom/extensions/email-gravatar.lua b/custom/extensions/email-gravatar.lua
new file mode 100644
index 0000000..47c359e
--- /dev/null
+++ b/custom/extensions/email-gravatar.lua
@@ -0,0 +1,115 @@
+-- cgit email-filter that shows a Gravatar icon next to author names. Use it
+-- with the email-filter or repo.email-filter setting and the lua: prefix.
+--
+-- email-filter=lua:/path/to/email-gravatar.lua
+--
+-- SUPPORTED LUA
+--
+-- Lua 5.1, 5.2, 5.3, 5.4 and LuaJIT. Lua 5.5 is not supported, because luaossl
+-- has no 5.5 build.
+--
+-- DEPENDENCY
+--
+-- luaossl OpenSSL binding, provides openssl.digest
+-- <https://github.com/wahern/luaossl>
+--
+-- # Debian and Ubuntu
+-- sudo apt install luarocks libssl-dev
+-- sudo luarocks --lua-version 5.1 install luaossl
+--
+-- # Fedora
+-- sudo dnf install luarocks openssl-devel
+-- sudo luarocks --lua-version 5.1 install luaossl
+--
+-- # macOS with Homebrew
+-- brew install luarocks openssl
+-- luarocks install luaossl OPENSSL_DIR="$(brew --prefix openssl)"
+--
+-- PRIVACY
+--
+-- Every page view sends the visitor's IP address and a hash of each
+-- committer's email to a third-party service. Leave this filter off if that is
+-- not acceptable for your instance.
+--
+-- Addresses are hashed with MD5, which Gravatar still accepts. Gravatar also
+-- supports SHA-256 now, change the digest in hash_hex if you prefer it.
+
+local digest = require("openssl.digest")
+
+--
+-- ===== CONFIGURATION =====
+--
+
+-- Pixel size of the avatar.
+local avatar_size = 13
+
+-- Fallback style for an address with no avatar. See the Gravatar docs for the
+-- choices, for example retro, identicon, monsterid or mp.
+local default_image = "retro"
+
+-- Avatar endpoint. Kept https so the image is not blocked as mixed content on
+-- an https page.
+local base_url = "https://www.gravatar.com/avatar/"
+
+-- Text for the image alt attribute.
+local alt_text = "Gravatar"
+
+--
+-- =========================
+--
+
+-- State shared across the open, write and close calls of one invocation.
+local buffer = ""
+local avatar = nil
+
+local function hash_hex(input)
+ local b = digest.new("md5"):final(input)
+ local x = ""
+ for i = 1, #b do
+ x = x .. string.format("%.2x", string.byte(b, i))
+ end
+ return x
+end
+
+-- Take the address, strip the angle brackets if present, then trim and
+-- lowercase as the avatar services expect. Returns nil for a missing or empty
+-- address.
+local function normalize_email(email)
+ if email == nil then
+ return nil
+ end
+ local inner = email:match("<(.*)>")
+ if inner ~= nil then
+ email = inner
+ end
+ email = (email:gsub("^%s*(.-)%s*$", "%1")):lower()
+ if email == "" then
+ return nil
+ end
+ return email
+end
+
+function filter_open(email, page)
+ buffer = ""
+ local addr = normalize_email(email)
+ if addr == nil then
+ avatar = nil
+ else
+ avatar = hash_hex(addr)
+ end
+end
+
+function filter_close()
+ if avatar == nil then
+ -- No usable address, render the name without an icon.
+ html(buffer)
+ else
+ html("<img src='" .. base_url .. avatar .. "?s=" .. avatar_size .. "&amp;d=" .. default_image ..
+ "' width='" .. avatar_size .. "' height='" .. avatar_size .. "' alt='" .. alt_text .. "' /> " .. buffer)
+ end
+ return 0
+end
+
+function filter_write(str)
+ buffer = buffer .. str
+end
diff --git a/custom/extensions/email-libravatar.lua b/custom/extensions/email-libravatar.lua
new file mode 100644
index 0000000..812bef5
--- /dev/null
+++ b/custom/extensions/email-libravatar.lua
@@ -0,0 +1,114 @@
+-- cgit email-filter that shows a Libravatar icon next to author names. Use it
+-- with the email-filter or repo.email-filter setting and the lua: prefix.
+--
+-- email-filter=lua:/path/to/email-libravatar.lua
+--
+-- SUPPORTED LUA
+--
+-- Lua 5.1, 5.2, 5.3, 5.4 and LuaJIT. Lua 5.5 is not supported, because luaossl
+-- has no 5.5 build.
+--
+-- DEPENDENCY
+--
+-- luaossl OpenSSL binding, provides openssl.digest
+-- <https://github.com/wahern/luaossl>
+--
+-- # Debian and Ubuntu
+-- sudo apt install luarocks libssl-dev
+-- sudo luarocks --lua-version 5.1 install luaossl
+--
+-- # Fedora
+-- sudo dnf install luarocks openssl-devel
+-- sudo luarocks --lua-version 5.1 install luaossl
+--
+-- # macOS with Homebrew
+-- brew install luarocks openssl
+-- luarocks install luaossl OPENSSL_DIR="$(brew --prefix openssl)"
+--
+-- PRIVACY
+--
+-- Every page view sends the visitor's IP address and a hash of each
+-- committer's email to a third-party service. Leave this filter off if that is
+-- not acceptable for your instance.
+--
+-- The secure CDN is always used, so the icon loads over https and is never
+-- blocked as mixed content. Addresses are hashed with MD5.
+
+local digest = require("openssl.digest")
+
+--
+-- ===== CONFIGURATION =====
+--
+
+-- Pixel size of the avatar.
+local avatar_size = 13
+
+-- Fallback style for an address with no avatar. See the Libravatar docs for
+-- the choices, for example retro, identicon, monsterid or mm.
+local default_image = "retro"
+
+-- Avatar endpoint. The secure CDN is used so the image loads over https.
+local base_url = "https://seccdn.libravatar.org/avatar/"
+
+-- Text for the image alt attribute.
+local alt_text = "Libravatar"
+
+--
+-- =========================
+--
+
+-- State shared across the open, write and close calls of one invocation.
+local buffer = ""
+local avatar = nil
+
+local function hash_hex(input)
+ local b = digest.new("md5"):final(input)
+ local x = ""
+ for i = 1, #b do
+ x = x .. string.format("%.2x", string.byte(b, i))
+ end
+ return x
+end
+
+-- Take the address, strip the angle brackets if present, then trim and
+-- lowercase as the avatar services expect. Returns nil for a missing or empty
+-- address.
+local function normalize_email(email)
+ if email == nil then
+ return nil
+ end
+ local inner = email:match("<(.*)>")
+ if inner ~= nil then
+ email = inner
+ end
+ email = (email:gsub("^%s*(.-)%s*$", "%1")):lower()
+ if email == "" then
+ return nil
+ end
+ return email
+end
+
+function filter_open(email, page)
+ buffer = ""
+ local addr = normalize_email(email)
+ if addr == nil then
+ avatar = nil
+ else
+ avatar = hash_hex(addr)
+ end
+end
+
+function filter_close()
+ if avatar == nil then
+ -- No usable address, render the name without an icon.
+ html(buffer)
+ else
+ html("<img src='" .. base_url .. avatar .. "?s=" .. avatar_size .. "&amp;d=" .. default_image ..
+ "' width='" .. avatar_size .. "' height='" .. avatar_size .. "' alt='" .. alt_text .. "' /> " .. buffer)
+ end
+ return 0
+end
+
+function filter_write(str)
+ buffer = buffer .. str
+end
diff --git a/custom/extensions/link-commits.lua b/custom/extensions/link-commits.lua
new file mode 100644
index 0000000..e7b17cc
--- /dev/null
+++ b/custom/extensions/link-commits.lua
@@ -0,0 +1,153 @@
+-- cgit commit-filter that turns git object names and configurable text
+-- references in commit messages into links. Use it with the commit-filter or
+-- repo.commit-filter setting and the lua: prefix.
+--
+-- commit-filter=lua:/path/to/link-commits.lua
+--
+-- cgit hands the filter the message already HTML-escaped, so this only wraps
+-- matches in anchors. No external dependencies. Runs on Lua 5.1 through 5.4
+-- and LuaJIT.
+--
+-- Two kinds of thing are linked, object names (runs of hex that look like git
+-- hashes) and any number of text-reference rules you define, each a pattern
+-- and a URL. Both are configured in the block below. All matches are resolved
+-- in a single left-to-right pass, so nothing is ever linked twice.
+
+--
+-- ===== CONFIGURATION =====
+--
+
+-- Object names (git hashes). Handled specially, because the length rule cannot
+-- be written as a plain Lua pattern.
+--
+-- Recognition is by shape, since a commit-filter cannot ask the repository
+-- whether a hash is real. Any hex run within the length bounds is linked,
+-- whatever mix of digits and letters it has, so abbreviated and all-digit
+-- hashes are both caught. The cost is that a long hex-looking number can now
+-- and then link to an object that does not exist, which cgit renders as a
+-- harmless "bad object name" page. Shape matching is inherently approximate,
+-- the length bounds are the only filter.
+local objects = {
+ -- Set false to stop linking bare hashes.
+ enabled = true,
+ -- A hex run within these lengths is linked. Git abbreviations run about 7
+ -- to 12 characters, full names are 40 (sha1) or 64 (sha256).
+ min_length = 7,
+ max_length = 64,
+ -- Link target, %s is replaced with the matched hash. "./?id=%s" is relative
+ -- to the current page and works for the common virtual-root layout.
+ url = "./?id=%s",
+}
+
+-- Text-reference rules. Each rule is a Lua pattern with ONE capture and a URL
+-- where %s is replaced by that capture, percent-encoded. The whole match is
+-- shown, the capture is what goes in the URL. Rules are tried in order and the
+-- leftmost match on the line wins, so put more specific patterns first. Leave
+-- the list empty to link only object names.
+--
+-- Lua patterns are not regular expressions. There is no alternation and no
+-- {n,m} repetition. %d is a digit, %a a letter, %w a letter or digit, %x a hex
+-- digit, and a literal magic character is escaped with %, so a literal '-' is
+-- '%-'. Reference: https://www.lua.org/manual/5.1/manual.html#5.4.1
+local rules = {
+ { pattern = "#(%d+)", url = "https://bugs.example.com/?bug=%s" },
+ -- { pattern = "CVE%-(%d%d%d%d%-%d+)", url = "https://www.cve.org/CVERecord?id=CVE-%s" },
+ -- { pattern = "!(%d+)", url = "https://gitlab.example.com/group/repo/-/merge_requests/%s" },
+ -- { pattern = "RFC%s?(%d+)", url = "https://www.rfc-editor.org/rfc/rfc%s" },
+}
+
+--
+-- =========================
+--
+
+local chunks = {}
+
+-- Percent-encode everything but the URL-unreserved characters, so a captured
+-- value cannot break out of the href attribute or the URL.
+local function url_encode(s)
+ return (string.gsub(s, "[^%w._~-]", function(c)
+ return string.format("%%%02X", string.byte(c))
+ end))
+end
+
+-- Build one anchor. url_template has %s where the encoded capture goes, display
+-- is the text shown. A function replacement is used so a '%' in the encoded
+-- value is not treated as a gsub reference.
+local function make_link(url_template, capture, display)
+ local encoded = url_encode(capture)
+ local href = string.gsub(url_template, "%%s", function() return encoded end)
+ return '<a href="' .. href .. '">' .. display .. '</a>'
+end
+
+-- Collect every candidate match as {s, e, pri, link}. A lower pri wins a tie on
+-- the same start position.
+local function collect(text)
+ local cands = {}
+ for pri, rule in ipairs(rules) do
+ -- A malformed pattern is an operator error, skip that rule rather than
+ -- failing the whole page.
+ pcall(function()
+ local init = 1
+ while init <= #text do
+ local s, e, cap = string.find(text, rule.pattern, init)
+ if not s then break end
+ if cap == nil then
+ cap = string.sub(text, s, e)
+ end
+ cands[#cands + 1] = {
+ s = s, e = e, pri = pri,
+ link = make_link(rule.url, cap, string.sub(text, s, e)),
+ }
+ init = (e >= s) and e + 1 or s + 1
+ end
+ end)
+ end
+ if objects.enabled then
+ local objpri = #rules + 1
+ local init = 1
+ while init <= #text do
+ local s, e, run = string.find(text, "%f[%w](%x+)%f[%W]", init)
+ if not s then break end
+ if #run >= objects.min_length and #run <= objects.max_length then
+ cands[#cands + 1] = {
+ s = s, e = e, pri = objpri,
+ link = make_link(objects.url, run, run),
+ }
+ end
+ init = e + 1
+ end
+ end
+ return cands
+end
+
+function filter_open(...)
+ chunks = {}
+end
+
+function filter_write(str)
+ chunks[#chunks + 1] = str
+end
+
+function filter_close()
+ local text = table.concat(chunks)
+ local cands = collect(text)
+ table.sort(cands, function(a, b)
+ if a.s ~= b.s then
+ return a.s < b.s
+ end
+ return a.pri < b.pri
+ end)
+ local out = {}
+ local i = 1
+ for _, c in ipairs(cands) do
+ -- Skip a candidate that overlaps one already emitted.
+ if c.s >= i then
+ out[#out + 1] = string.sub(text, i, c.s - 1)
+ out[#out + 1] = c.link
+ i = c.e + 1
+ end
+ end
+ out[#out + 1] = string.sub(text, i)
+ html(table.concat(out))
+ return 0
+end
diff --git a/custom/extensions/syntax-highlight.lua b/custom/extensions/syntax-highlight.lua
new file mode 100644
index 0000000..8310504
--- /dev/null
+++ b/custom/extensions/syntax-highlight.lua
@@ -0,0 +1,278 @@
+-- Server-side syntax highlighting for the tree and blob views, used with the
+-- source-filter setting in cgitrc and the lua: prefix so it runs in cgit's
+-- embedded interpreter with no per-request process.
+--
+-- source-filter=lua:/usr/lib/cgit/extensions/syntax-highlight.lua
+--
+-- Highlighting is deliberately not built into cgit itself. Without this filter
+-- cgit serves plain escaped text, and any other program can take its place.
+--
+-- SUPPORTED LUA
+--
+-- Lua 5.1 through 5.4 and LuaJIT. The Scintillua version matters too. Recent
+-- Scintillua (6.x) needs Lua 5.3 or newer to load its lexers, older Scintillua
+-- releases still load under 5.1 and 5.2. Pick a Scintillua release that matches
+-- the Lua cgit is built against.
+--
+-- REQUIREMENTS
+--
+-- Two pieces, and BOTH must be installed. When either is missing the filter
+-- serves plain escaped text by design, so uncolored code means a missing
+-- dependency, not an error.
+--
+-- 1. lpeg, the parsing module, for the Lua cgit is linked against. Scintillua
+-- does NOT bundle it, it must come from the system, and forgetting it is the
+-- usual reason nothing happens.
+--
+-- # Debian and Ubuntu
+-- sudo apt install lua-lpeg
+-- # Fedora
+-- sudo dnf install lua-lpeg
+-- # Alpine
+-- sudo apk add lua5.1-lpeg
+-- # or with LuaRocks, matched to your Lua version
+-- sudo luarocks --lua-version 5.1 install lpeg
+--
+-- 2. Scintillua, the lexer collection from the Textadept editor. Roughly 120
+-- languages as plain .lua files, nothing to compile. Download a release and
+-- unpack it anywhere.
+--
+-- https://orbitalquark.github.io/scintillua/
+--
+-- The lexers are found by probing, in order
+--
+-- $CGIT_SCINTILLUA_PATH (used alone when set, no fallback)
+-- <dir of $CGIT_CONFIG>/scintillua/lexers
+-- the scintillua_dirs list in the CONFIGURATION block below
+--
+-- so either set the variable in the web server environment, or place (or
+-- symlink) the scintillua directory next to your cgitrc.
+--
+-- SECURITY
+--
+-- Every probed directory is placed on package.path and its Lua is executed in
+-- cgit's process. Make sure none of them is writable by other users, or someone
+-- who can write there gains code execution as the web server. On macOS in
+-- particular, /opt/homebrew/share is group-writable by default.
+--
+-- LIMITATIONS
+--
+-- cgit sends the filter output through a C string sink that stops at the first
+-- NUL byte, so a blob containing a NUL is truncated there. This affects binary
+-- files that slip past cgit's text detection, not ordinary source.
+--
+-- OUTPUT
+--
+-- Tokens are wrapped in <span> elements carrying the hl- classes that
+-- assets/cgit.css styles. Every input byte up to the first NUL is preserved, so
+-- the line number gutter stays aligned.
+
+--
+-- ===== CONFIGURATION =====
+--
+
+-- Files larger than this many bytes are served escaped but unhighlighted, so a
+-- huge blob does not cost a lexing pass. Kept well below cgit's max-blob-size.
+local max_bytes = 512 * 1024
+
+-- Environment variable that, when set, points straight at the Scintillua
+-- lexers directory and is used alone.
+local scintillua_env = "CGIT_SCINTILLUA_PATH"
+
+-- Directories probed for the lexers when that variable is not set. The
+-- directory of $CGIT_CONFIG, when set, is tried ahead of these. Keep every one
+-- of these unwritable by others, see the SECURITY note above.
+local scintillua_dirs = {
+ "/usr/local/share/scintillua/lexers",
+ "/usr/share/scintillua/lexers",
+ "/opt/homebrew/share/scintillua/lexers",
+}
+
+-- Scintillua tag name (its first dotted component) to a cgit css class. Only
+-- the six classes below exist in assets/cgit.css. Add a class there and a row
+-- here to style more token kinds. Tokens with no row render as plain text,
+-- which is what most themes want for operators and identifiers.
+local css = {
+ comment = "hl-comment",
+ string = "hl-string",
+ regex = "hl-string",
+ number = "hl-number",
+ constant = "hl-number",
+ keyword = "hl-keyword",
+ preprocessor = "hl-keyword",
+ tag = "hl-keyword",
+ label = "hl-keyword",
+ annotation = "hl-keyword",
+ type = "hl-type",
+ class = "hl-type",
+ attribute = "hl-type",
+ ["function"] = "hl-func",
+}
+
+-- Extension to lexer-name fixes for the fallback path, used only when this
+-- Scintillua has no detect(). Most extensions already equal their lexer name,
+-- these are the frequent exceptions. A wrong guess just falls back to plain
+-- text, so there is no harm in listing best-effort entries.
+local ext_lexer = {
+ py = "python", js = "javascript", ts = "typescript",
+ rb = "ruby", pl = "perl", pm = "perl", sh = "bash",
+ md = "markdown", htm = "html", yml = "yaml",
+ rs = "rust", c = "ansi_c", h = "ansi_c",
+}
+
+--
+-- =========================
+--
+
+local lexer_mod = nil
+local filename = ""
+local chunks = {}
+
+local escape_map = { ["&"] = "&amp;", ["<"] = "&lt;", [">"] = "&gt;" }
+
+-- Escape the three HTML metacharacters in a single pass.
+local function escape(s)
+ return (string.gsub(s, "[&<>]", escape_map))
+end
+
+local function scintillua_path()
+ local env = os.getenv(scintillua_env)
+ if env then
+ return env
+ end
+ local candidates = {}
+ local config = os.getenv("CGIT_CONFIG")
+ if config then
+ local dir = string.match(config, "^(.*)/[^/]+$")
+ if dir then
+ candidates[#candidates + 1] = dir .. "/scintillua/lexers"
+ end
+ end
+ for _, d in ipairs(scintillua_dirs) do
+ candidates[#candidates + 1] = d
+ end
+ for _, dir in ipairs(candidates) do
+ local f = io.open(dir .. "/lexer.lua", "r")
+ if f then
+ f:close()
+ return dir
+ end
+ end
+ return nil
+end
+
+local function load_scintillua()
+ local dir = scintillua_path()
+ if not dir then
+ return nil
+ end
+ if not string.find(package.path, dir, 1, true) then
+ package.path = dir .. "/?.lua;" .. package.path
+ end
+ local ok, mod = pcall(require, "lexer")
+ -- A real Scintillua exposes load(). Anything else on the path that happens
+ -- to be called lexer is not usable.
+ if ok and type(mod) == "table" and type(mod.load) == "function" then
+ return mod
+ end
+ return nil
+end
+
+local function load_lexer_name(name)
+ if name == nil then
+ return nil
+ end
+ local ok, lex = pcall(lexer_mod.load, name)
+ if ok and lex then
+ return lex
+ end
+ return nil
+end
+
+-- Resolve a lexer for the file, preferring Scintillua's own filename detection
+-- when this version provides it, then an extension map, then the raw extension.
+local function lexer_for(name)
+ if type(lexer_mod.detect) == "function" then
+ local ok, lang = pcall(lexer_mod.detect, name)
+ if ok and lang then
+ local lex = load_lexer_name(lang)
+ if lex then
+ return lex
+ end
+ end
+ end
+ local ext = string.match(name, "%.([^.]+)$")
+ if not ext then
+ return nil
+ end
+ ext = string.lower(ext)
+ return load_lexer_name(ext_lexer[ext]) or load_lexer_name(ext)
+end
+
+local function highlight(text)
+ local lex = lexer_for(filename)
+ if not lex then
+ return nil
+ end
+ local ok, tokens = pcall(lex.lex, lex, text)
+ if not ok or type(tokens) ~= "table" then
+ return nil
+ end
+ local out = {}
+ local pos = 1
+ for i = 1, #tokens, 2 do
+ local tag = tokens[i]
+ local fin = tokens[i + 1]
+ local part = escape(string.sub(text, pos, fin - 1))
+ local class = css[string.match(tag, "^[%w_]+")]
+ if class and part ~= "" then
+ part = "<span class='" .. class .. "'>" .. part .. "</span>"
+ end
+ out[#out + 1] = part
+ pos = fin
+ end
+ -- Anything the lexer left unconsumed is kept, escaped.
+ if pos <= #text then
+ out[#out + 1] = escape(string.sub(text, pos))
+ end
+ return table.concat(out)
+end
+
+function filter_open(name)
+ filename = name or ""
+ chunks = {}
+end
+
+function filter_write(str)
+ chunks[#chunks + 1] = str
+end
+
+function filter_close()
+ local text = table.concat(chunks)
+ chunks = {}
+ if #text <= max_bytes then
+ if lexer_mod == nil then
+ lexer_mod = load_scintillua() or false
+ end
+ if lexer_mod then
+ local ok, marked = pcall(highlight, text)
+ if ok and marked then
+ html(marked)
+ return 0
+ end
+ end
+ end
+ -- Fallback, escaped plain text emitted in slices so a large blob does not
+ -- cost a full-size second copy all at once.
+ local n = #text
+ if n == 0 then
+ html("")
+ return 0
+ end
+ local pos = 1
+ while pos <= n do
+ html(escape(string.sub(text, pos, pos + 65535)))
+ pos = pos + 65536
+ end
+ return 0
+end
diff --git a/custom/hooks/post-receive.agefile b/custom/hooks/post-receive.agefile
new file mode 100755
index 0000000..2f72ae9
--- /dev/null
+++ b/custom/hooks/post-receive.agefile
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# An example hook to update the "agefile" for CGit's idle time calculation.
+#
+# This hook assumes that you are using the default agefile location of
+# "info/web/last-modified". If you change the value in your cgitrc then you
+# must also change it here.
+#
+# To install the hook, copy (or link) it to the file "hooks/post-receive" in
+# each of your repositories.
+#
+
+agefile="$(git rev-parse --git-dir)"/info/web/last-modified
+
+mkdir -p "$(dirname "$agefile")" &&
+git for-each-ref \
+ --sort=-authordate --count=1 \
+ --format='%(authordate:iso8601)' \
+ >"$agefile"
diff --git a/custom/hooks/post-update.update-server-info b/custom/hooks/post-update.update-server-info
new file mode 100755
index 0000000..d499ce5
--- /dev/null
+++ b/custom/hooks/post-update.update-server-info
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# Example hook that keeps cgit's dumb HTTP clone data current.
+#
+# cgit can serve "git clone" over HTTP by itself using the dumb protocol,
+# which is on by default through enable-http-clone. The dumb protocol
+# reads a few static files that git refreshes only when you run
+# "git update-server-info", so without this hook a fresh push can leave a
+# clone unable to see the new refs and objects.
+#
+# You do not need this when you clone through git-http-backend (the smart
+# protocol) or over ssh or git://. It matters only when cgit itself is the
+# clone endpoint.
+#
+# To install it, copy or link this file to "hooks/post-update" in each
+# repository and make sure it is executable.
+#
+exec git update-server-info
diff --git a/custom/servers/apache.conf b/custom/servers/apache.conf
new file mode 100644
index 0000000..d042dd9
--- /dev/null
+++ b/custom/servers/apache.conf
@@ -0,0 +1,152 @@
+# Apache httpd 2.4 configuration for cgit.
+#
+# Apache runs the cgit.cgi binary directly through mod_cgid, so no FastCGI
+# bridge is needed. Drop this file in your vhost directory, for example
+# /etc/apache2/sites-available/cgit.conf on Debian and Ubuntu or
+# /etc/httpd/conf.d/cgit.conf on RHEL and Fedora, then enable it and reload.
+#
+# Paths assumed below, edit them to match your install.
+# cgit CGI binary /usr/lib/cgit/cgit.cgi
+# static assets /usr/share/cgit (cgit.css cgit.js cgit.png favicon.ico robots.txt)
+# cgit config /etc/cgitrc
+# public URL https://git.example.org/ (cgit at the domain root)
+#
+# cgit is one CGI executable. It learns the repository and the page from
+# PATH_INFO and reads page options such as h= and id= from QUERY_STRING.
+# ScriptAlias runs the binary and forwards the trailing path as PATH_INFO, so
+# no extra path tuning is needed. cgit builds its own link base from
+# SCRIPT_NAME. The five static assets are served straight off disk. mod_alias
+# resolves Alias and ScriptAlias in order and the first match wins, so the
+# static Alias lines come before the catch-all ScriptAlias to stop the two
+# routes from shadowing each other.
+
+
+# --- Required modules -------------------------------------------------------
+# mod_cgid suits the threaded MPMs that ship by default. Use mod_cgi instead
+# only on the old prefork MPM. mod_alias provides Alias and ScriptAlias and
+# mod_env provides SetEnv. On Debian and Ubuntu run a2enmod cgid alias env
+# rather than editing these lines. The guards make double-loading harmless.
+<IfModule !mod_cgid.c>
+ LoadModule cgid_module modules/mod_cgid.so
+</IfModule>
+<IfModule !mod_alias.c>
+ LoadModule alias_module modules/mod_alias.so
+</IfModule>
+<IfModule !mod_env.c>
+ LoadModule env_module modules/mod_env.so
+</IfModule>
+<IfModule !mod_headers.c>
+ LoadModule headers_module modules/mod_headers.so
+</IfModule>
+
+
+# --- Plain HTTP virtual host ------------------------------------------------
+# This vhost only bounces plain HTTP up to HTTPS. It serves no cgit itself,
+# every cgit directive lives in the HTTPS vhost below. To run without TLS for
+# now, convert the HTTPS vhost to port 80 and delete this whole block rather
+# than editing it, since deleting only the Redirect line would leave a vhost
+# that serves nothing.
+<VirtualHost *:80>
+ ServerName git.example.org
+
+ ErrorLog /var/log/apache2/cgit_error.log
+ CustomLog /var/log/apache2/cgit_access.log combined
+
+ Redirect permanent / https://git.example.org/
+</VirtualHost>
+
+
+# --- HTTPS virtual host, this one serves cgit -------------------------------
+# To run without TLS for now, change this opening line to <VirtualHost *:80>,
+# delete the three SSL lines, and delete the port 80 vhost above so there is
+# only one vhost. Everything else stays the same.
+<VirtualHost *:443>
+ ServerName git.example.org
+
+ ErrorLog /var/log/apache2/cgit_ssl_error.log
+ CustomLog /var/log/apache2/cgit_ssl_access.log combined
+
+ # TLS needs mod_ssl (a2enmod ssl). Point these at your certificate.
+ SSLEngine on
+ SSLCertificateFile /etc/ssl/certs/git.example.org.crt
+ SSLCertificateKeyFile /etc/ssl/private/git.example.org.key
+
+ # Which config cgit reads. It falls back to the compiled-in /etc/cgitrc,
+ # the same path used here, but setting it makes the location explicit and
+ # lets you point at a per-vhost file later.
+ SetEnv CGIT_CONFIG /etc/cgitrc
+
+ # --- Security headers (needs mod_headers, a2enmod headers) --------------
+ # Set here, not in cgit, so they also cover the static assets Apache
+ # serves. script-src stays self because cgit loads only its own cgit.js,
+ # and style-src allows inline for the diffstat bars. If you enable the
+ # gravatar or libravatar avatar filter, add its host to img-src, for
+ # example https://www.gravatar.com.
+ Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'"
+ Header always set X-Content-Type-Options "nosniff"
+ Header always set Referrer-Policy "no-referrer"
+ # Enable only once you serve HTTPS exclusively, since it is hard to undo.
+ #Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
+
+ # --- Static assets, served directly by Apache ---------------------------
+ # These five files are the only things served off disk. Each Alias maps
+ # one URL to one file. Because they come before the ScriptAlias below, a
+ # request for /cgit.css is answered from disk and never reaches cgit.
+ # cgit.css and cgit.js are the paths cgit's HTML points at by default, so
+ # if you relocate the assets update both these Alias targets and the css,
+ # js, logo and favicon settings in cgitrc to agree.
+ Alias /cgit.css /usr/share/cgit/cgit.css
+ Alias /cgit.js /usr/share/cgit/cgit.js
+ Alias /cgit.png /usr/share/cgit/cgit.png
+ Alias /favicon.ico /usr/share/cgit/favicon.ico
+ Alias /robots.txt /usr/share/cgit/robots.txt
+
+ # Apache 2.4 denies filesystem access by default, so open the asset
+ # directory for reading.
+ <Directory "/usr/share/cgit">
+ Options None
+ AllowOverride None
+ Require all granted
+
+ # Optional. These assets rarely change, so let browsers cache them.
+ # Needs mod_expires (a2enmod expires). Safe to delete this block.
+ <IfModule mod_expires.c>
+ ExpiresActive On
+ ExpiresDefault "access plus 30 days"
+ </IfModule>
+ </Directory>
+
+ # --- cgit, the catch-all ------------------------------------------------
+ # ScriptAlias maps a URL prefix to a path, marks it executable, and
+ # forwards the rest of the URL as PATH_INFO. Mapping / makes cgit the
+ # handler for every URL the static Aliases above did not already claim.
+ #
+ # The trailing slash on cgit.cgi/ is load bearing. It tells Apache that
+ # cgit.cgi is the program and the rest of the URL is PATH_INFO. So a
+ # request for /torvalds/linux/tree/kernel?h=next runs the binary with
+ # PATH_INFO set to /torvalds/linux/tree/kernel and QUERY_STRING set to
+ # h=next. cgit derives its link base from SCRIPT_NAME, which at the domain
+ # root is / and needs no tuning. For a sub-path install see the note below.
+ ScriptAlias / /usr/lib/cgit/cgit.cgi/
+
+ <Directory "/usr/lib/cgit">
+ # Allow CGI execution here. ScriptAlias implies it, stating it makes
+ # the intent clear.
+ Options +ExecCGI
+ # Run cgit.cgi as a CGI even if it is ever reached through a plain
+ # Alias rather than ScriptAlias.
+ SetHandler cgi-script
+ AllowOverride None
+ Require all granted
+ </Directory>
+</VirtualHost>
+
+
+# --- Sub-path install, only if cgit is not at the domain root ---------------
+# To serve cgit at https://git.example.org/cgit/ instead of the root, change
+# the ScriptAlias to
+# ScriptAlias /cgit/ /usr/lib/cgit/cgit.cgi/
+# and move the static assets under the same prefix, for example
+# Alias /cgit/cgit.css /usr/share/cgit/cgit.css
+# SCRIPT_NAME then becomes /cgit and cgit auto-detects it. If links come out
+# wrong, pin the base in cgitrc with virtual-root=/cgit.
diff --git a/custom/servers/lighttpd.conf b/custom/servers/lighttpd.conf
new file mode 100644
index 0000000..3111ed0
--- /dev/null
+++ b/custom/servers/lighttpd.conf
@@ -0,0 +1,132 @@
+# lighttpd configuration for cgit.
+#
+# lighttpd runs the cgit.cgi binary directly through mod_cgi, so no FastCGI
+# bridge is needed. This is cgit's classic reference deployment, mod_cgi with
+# mod_alias and mod_setenv.
+#
+# Paths assumed below, edit them to match your install.
+# cgit CGI binary /usr/lib/cgit/cgit.cgi
+# static assets /usr/share/cgit (cgit.css cgit.js cgit.png favicon.ico robots.txt)
+# cgit config /etc/cgitrc
+# public URL https://git.example.org/ (cgit at the domain root)
+#
+# cgit is one CGI executable. It learns the repository and the page from
+# PATH_INFO and reads page options such as h= and id= from QUERY_STRING. cgit
+# builds its own link base from SCRIPT_NAME. The five static assets are served
+# straight off disk and must never be routed through cgit.
+
+
+# --- Modules ----------------------------------------------------------------
+# Append the three modules cgit needs so the distro's base config is kept.
+# mod_alias maps URL paths onto files, mod_setenv injects CGIT_CONFIG, and
+# mod_cgi runs cgit.cgi.
+server.modules += ( "mod_alias", "mod_setenv", "mod_cgi" )
+
+
+# --- Server basics ----------------------------------------------------------
+server.port = 80
+server.username = "http" # Debian and Ubuntu use www-data
+server.groupname = "http"
+server.document-root = "/usr/share/cgit" # a valid docroot must exist, the
+ # alias rules below do the routing
+server.errorlog = "/var/log/lighttpd/error.log"
+# Access logging needs mod_accesslog. Load it and uncomment to enable.
+#server.modules += ( "mod_accesslog" )
+#accesslog.filename = "/var/log/lighttpd/access.log"
+
+
+# --- MIME types for the static assets ---------------------------------------
+# mod_alias serves the assets off disk, so lighttpd must know their content
+# types. Without this the stylesheet is sent as application/octet-stream and
+# the browser ignores it.
+mimetype.assign = (
+ ".css" => "text/css",
+ ".js" => "text/javascript",
+ ".png" => "image/png",
+ ".ico" => "image/vnd.microsoft.icon",
+ ".txt" => "text/plain",
+)
+
+
+# --- Virtual host, git.example.org ------------------------------------------
+# A top-level conditional, so it matches on both the port 80 socket and the
+# optional TLS socket at the end of this file.
+$HTTP["host"] == "git.example.org" {
+
+ # Which config cgit reads. It falls back to the compiled-in /etc/cgitrc,
+ # the same path used here, but setting it makes the location explicit.
+ setenv.add-environment = ( "CGIT_CONFIG" => "/etc/cgitrc" )
+
+ # --- Security headers ---------------------------------------------------
+ # Set here, not in cgit, so they also cover the static assets lighttpd
+ # serves. script-src stays self because cgit loads only its own cgit.js,
+ # and style-src allows inline for the diffstat bars. If you enable the
+ # gravatar or libravatar avatar filter, add its host to img-src.
+ setenv.add-response-header = (
+ "Content-Security-Policy" => "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'",
+ "X-Content-Type-Options" => "nosniff",
+ "Referrer-Policy" => "no-referrer"
+ )
+ # Enable only once you serve HTTPS exclusively, since it is hard to undo.
+ #setenv.add-response-header += ( "Strict-Transport-Security" => "max-age=63072000; includeSubDomains" )
+
+ # Register the cgit binary as a CGI program. The key cgit.cgi matches the
+ # binary's name and the empty value means the file is itself the program,
+ # with no interpreter in front of it. This is what makes lighttpd split
+ # the trailing path off as PATH_INFO, so never drop it.
+ cgi.assign = ( "cgit.cgi" => "" )
+
+ # Routing. lighttpd's alias.url is first-match in declaration order, not
+ # longest prefix, so the five static entries must come before the / entry.
+ # If / came first it would swallow every request and recent lighttpd
+ # refuses to start. The static entries are served off disk and the / entry
+ # hands everything else to cgit.
+ #
+ # The trailing slash on cgit.cgi/ is load bearing. lighttpd builds the
+ # physical path by stripping the matched key off the front of the URL and
+ # appending the rest to the value. For the key / the remainder carries no
+ # leading slash, so without the trailing slash a request for
+ # /linux/tree/kernel/sched.c glues onto the binary name as
+ # /usr/lib/cgit/cgit.cgilinux/tree/... and 404s. The trailing slash
+ # restores the separator, giving cgit SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi
+ # and PATH_INFO /linux/tree/kernel/sched.c.
+ alias.url = (
+ "/cgit.css" => "/usr/share/cgit/cgit.css",
+ "/cgit.js" => "/usr/share/cgit/cgit.js",
+ "/cgit.png" => "/usr/share/cgit/cgit.png",
+ "/favicon.ico" => "/usr/share/cgit/favicon.ico",
+ "/robots.txt" => "/usr/share/cgit/robots.txt",
+ "/" => "/usr/lib/cgit/cgit.cgi/",
+ )
+
+ # Served at / the SCRIPT_NAME is empty and cgit derives its link base
+ # correctly. For a sub-path install use a key without a trailing slash
+ # mapped to the binary without a trailing slash, for example
+ # "/git" => "/usr/lib/cgit/cgit.cgi"
+ # so /git/linux/tree resolves to /usr/lib/cgit/cgit.cgi/linux/tree, giving
+ # SCRIPT_NAME /git and PATH_INFO /linux/tree. cgit auto-detects the prefix.
+ # If links come out wrong, pin it in cgitrc with virtual-root=/git.
+}
+
+
+# --- Optional HTTPS on 443 --------------------------------------------------
+# Uncomment this whole block to enable TLS. The host block above is socket
+# independent, so it serves cgit over this socket too once the crypto is set.
+#server.modules += ( "mod_openssl" )
+#
+#$SERVER["socket"] == ":443" {
+# ssl.engine = "enable"
+# ssl.pemfile = "/etc/lighttpd/certs/git.example.org.crt"
+# ssl.privkey = "/etc/lighttpd/certs/git.example.org.key"
+# ssl.ca-file = "/etc/lighttpd/certs/git.example.org.chain.pem"
+# ssl.openssl.ssl-conf-cmd = ( "MinProtocol" => "TLSv1.2" )
+#}
+#
+# Redirect plain HTTP to HTTPS, scoped to the port 80 socket. Needs
+# mod_redirect.
+#server.modules += ( "mod_redirect" )
+#$SERVER["socket"] == ":80" {
+# $HTTP["host"] == "git.example.org" {
+# url.redirect = ( "^/(.*)" => "https://git.example.org/$1" )
+# }
+#}
diff --git a/custom/servers/nginx.conf b/custom/servers/nginx.conf
new file mode 100644
index 0000000..76ac260
--- /dev/null
+++ b/custom/servers/nginx.conf
@@ -0,0 +1,193 @@
+# nginx configuration for cgit.
+#
+# nginx cannot run CGI programs itself, so a small bridge called fcgiwrap
+# runs the cgit.cgi binary and speaks FastCGI to nginx. Starting fcgiwrap is
+# covered in the notes at the end of this file.
+#
+# Paths assumed below, edit them to match your install.
+# cgit CGI binary /usr/lib/cgit/cgit.cgi
+# static assets /usr/share/cgit (cgit.css cgit.js cgit.png favicon.ico robots.txt)
+# cgit config /etc/cgitrc
+# public URL https://git.example.org/ (cgit at the domain root)
+#
+# cgit is one CGI executable. It learns the repository and the page from
+# PATH_INFO and reads page options such as h= and id= from QUERY_STRING, so
+# nginx must pass PATH_INFO through to the binary. cgit builds its own link
+# base from SCRIPT_NAME. The five static assets are served straight off disk
+# and must never be routed through cgit. Passing PATH_INFO through is the
+# single most important part of the config below.
+
+
+# --- Optional HTTP to HTTPS redirect ----------------------------------------
+# Delete this whole server block if you serve plain HTTP only.
+server {
+ listen 80;
+ listen [::]:80;
+ server_name git.example.org;
+
+ # ACME http-01 challenge files, if you use certbot in webroot mode.
+ location ^~ /.well-known/acme-challenge/ {
+ root /var/www/html;
+ }
+
+ # Everything else moves to HTTPS.
+ location / {
+ return 301 https://$host$request_uri;
+ }
+}
+
+
+# --- Main site --------------------------------------------------------------
+# Written for TLS on 443. For a quick plain-HTTP test, change the two listen
+# lines to port 80, delete the redirect block above, and delete the four ssl
+# lines below. Everything else stays the same.
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ http2 on;
+ server_name git.example.org;
+
+ ssl_certificate /etc/letsencrypt/live/git.example.org/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/git.example.org/privkey.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers HIGH:!aNULL:!MD5;
+
+ # --- Security headers ---------------------------------------------------
+ # These sit here, not in cgit, because they must also cover the static
+ # assets nginx serves directly. cgit loads only its own /cgit.js and uses
+ # inline style on the diffstat bars, so script-src stays self while
+ # style-src allows inline. always applies them to error responses too. If
+ # you enable the gravatar or libravatar avatar filter, add its host to
+ # img-src, for example https://www.gravatar.com or https://seccdn.libravatar.org.
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Referrer-Policy "no-referrer" always;
+ # Enable only once you serve HTTPS exclusively, since it is hard to undo.
+ #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
+
+ # The document root is the directory that holds the static assets. cgit
+ # emits absolute links to /cgit.css and /cgit.png by default, so those
+ # files must resolve at the root of the URL space. Pointing root at the
+ # asset directory makes /cgit.css map to /usr/share/cgit/cgit.css.
+ root /usr/share/cgit;
+
+ # Upload cap for large form posts. Snapshots are generated rather than
+ # uploaded, so this does not limit them.
+ client_max_body_size 64m;
+
+ access_log /var/log/nginx/cgit.access.log;
+ error_log /var/log/nginx/cgit.error.log;
+
+ # --- Static assets, served directly -------------------------------------
+ # Match the assets by their exact root-level names, never by bare
+ # extension. cgit routes on PATH_INFO and a repository can hold files
+ # ending in .css or .png, so /myrepo/tree/style.css and /myrepo/plain/
+ # logo.png are real cgit URLs. A broad extension match would capture
+ # those, look for them on disk, and return 404 before cgit could render
+ # them. Anchoring the regex at the start of the path matches /cgit.css but
+ # not /myrepo/tree/cgit.css, so it can never shadow a repository file. An
+ # nginx regex location is matched before the prefix location below, so
+ # these assets win for their exact URLs and cgit wins for the rest.
+ location ~ ^/(cgit\.css|cgit\.js|cgit\.png|favicon\.ico|robots\.txt)$ {
+ expires 30d;
+ access_log off;
+ try_files $uri =404;
+ }
+
+ # --- cgit, the catch-all ------------------------------------------------
+ # Everything that is not a static asset above is a cgit URL, the repo
+ # index, a repository, a page within a repository, a snapshot, a feed.
+ location / {
+ # nginx's standard FastCGI parameters, some of which are overridden
+ # below. A later fastcgi_param wins, so include order does not matter.
+ include fastcgi_params;
+
+ # The program fcgiwrap runs. It must be the cgit binary itself, not
+ # $document_root$fastcgi_script_name, which would try to run a repo
+ # path and is the usual cause of a failed request.
+ fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
+
+ # cgit builds its link base, the virtual root, from SCRIPT_NAME. The
+ # stock parameters set SCRIPT_NAME to the whole request path, which
+ # would make cgit prepend that path to every link. Served at the
+ # domain root the script has no prefix, so force SCRIPT_NAME empty and
+ # cgit uses / as its base. A sub-path install sets it instead, see the
+ # end of this file.
+ fastcgi_param SCRIPT_NAME "";
+
+ # How cgit learns the repository and page. At the domain root the
+ # whole request path is the PATH_INFO.
+ fastcgi_param PATH_INFO $uri;
+
+ # Page options such as h=branch, id=sha and the snapshot format.
+ fastcgi_param QUERY_STRING $query_string;
+
+ # Where the static assets live, kept consistent with root above.
+ fastcgi_param DOCUMENT_ROOT $document_root;
+
+ # The browser's Host header, so cgit builds clone URLs against the
+ # name the visitor used rather than server_name.
+ fastcgi_param HTTP_HOST $http_host;
+
+ # Which config cgit reads. It checks CGIT_CONFIG and falls back to the
+ # compiled-in /etc/cgitrc. Setting it makes the location explicit and
+ # lets you move cgitrc without recompiling.
+ fastcgi_param CGIT_CONFIG /etc/cgitrc;
+
+ # The real scheme, so cgit builds correct https clone URLs.
+ fastcgi_param HTTPS $https if_not_empty;
+
+ # Hand off to the fcgiwrap socket. See the notes for how to create it.
+ # A TCP fcgiwrap would use for example 127.0.0.1:9000 here.
+ fastcgi_pass unix:/run/fcgiwrap.socket;
+
+ # Large outputs such as snapshot tarballs and blame on big files can
+ # take a while, so give cgit room and stream rather than buffer.
+ fastcgi_read_timeout 300s;
+ fastcgi_buffering off;
+ }
+}
+
+
+# --- Notes, starting fcgiwrap -----------------------------------------------
+# cgit is a CGI binary and fcgiwrap is the CGI to FastCGI bridge nginx talks
+# to. On Debian and Ubuntu the packaged systemd socket provides
+# /run/fcgiwrap.socket, so enabling it is enough.
+# apt install fcgiwrap
+# systemctl enable --now fcgiwrap.socket
+# The socket must be readable by nginx's user. The packaged unit runs fcgiwrap
+# as www-data, which nginx also uses on those systems. Without systemd you can
+# run
+# spawn-fcgi -s /run/fcgiwrap.socket -M 660 -- /usr/sbin/fcgiwrap
+# or run fcgiwrap over TCP and point fastcgi_pass at 127.0.0.1:9000.
+
+
+# --- Alternative, serving cgit under a sub-path -----------------------------
+# To serve cgit at https://git.example.org/cgit/ instead of the root, split
+# the URL so SCRIPT_NAME is the prefix and PATH_INFO is the rest.
+#
+# location /cgit/ {
+# include fastcgi_params;
+# fastcgi_split_path_info ^(/cgit)(/.*)$;
+# fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
+# fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+# fastcgi_param PATH_INFO $fastcgi_path_info;
+# fastcgi_param QUERY_STRING $query_string;
+# fastcgi_param HTTP_HOST $http_host;
+# fastcgi_param CGIT_CONFIG /etc/cgitrc;
+# fastcgi_param HTTPS $https if_not_empty;
+# fastcgi_pass unix:/run/fcgiwrap.socket;
+# }
+#
+# Serve the assets from the sub-path too, again anchored to the exact names.
+#
+# location ~ ^/cgit/(cgit\.css|cgit\.js|cgit\.png|favicon\.ico|robots\.txt)$ {
+# alias /usr/share/cgit/$1;
+# expires 30d;
+# access_log off;
+# }
+#
+# cgit's default css=/cgit.css and logo=/cgit.png point at the domain root, so
+# under a sub-path also set css=/cgit/cgit.css and logo=/cgit/cgit.png in
+# cgitrc. cgit derives the /cgit prefix from SCRIPT_NAME. If links come out
+# wrong, pin it in cgitrc with virtual-root=/cgit/.