diff options
| author | Bryce Kwon <bryce@brycekwon.com> | |
|---|---|---|
| committer | Bryce Kwon <bryce@brycekwon.com> | |
| commit | ||
| parent | ||
| tree | ||
| download | ||
Reorganize the tree into vendor/ and custom/
Diffstat (limited to 'custom')
| -rw-r--r-- | custom/cgitrc | 566 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/extensions/about-render.lua | 592 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/extensions/auth-file.lua | 556 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/extensions/auth-inline.lua | 528 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/extensions/email-gravatar.lua | 115 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/extensions/email-libravatar.lua | 114 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/extensions/link-commits.lua | 153 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/extensions/syntax-highlight.lua | 278 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rwxr-xr-x | custom/hooks/post-receive.agefile | 19 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rwxr-xr-x | custom/hooks/post-update.update-server-info | 18 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/servers/apache.conf | 152 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/servers/lighttpd.conf | 132 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/servers/nginx.conf | 193 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 .. "&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 .. "&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 = { ["&"] = "&", ["<"] = "<", [">"] = ">" } + +-- 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/. |
