Last active
January 21, 2021 21:32
-
-
Save hmenke/de2db3e303192a29e4bb6837e0bf48ff to your computer and use it in GitHub Desktop.
cgit for nginx on NixOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ config, lib, pkgs, ... }: | |
with lib; | |
let | |
globalConfig = config; | |
settingsFormat = { | |
type = with lib.types; let | |
value = oneOf [ int str ] // { | |
description = "INI-like atom (int or string)"; | |
}; | |
values = coercedTo value lib.singleton (listOf value) // { | |
description = value.description + " or a list of them for duplicate keys"; | |
}; | |
in | |
attrsOf (values); | |
generate = name: values: | |
pkgs.writeText name (lib.generators.toKeyValue { listsAsDuplicateKeys = true; } values); | |
}; | |
in | |
{ | |
options.services.nginx.virtualHosts = mkOption { | |
type = types.attrsOf (types.submodule ({ config, ... }: | |
let | |
cfg = config.cgit; | |
# These are the global options for this submodule, but for nicer UX they | |
# are inlined into the freeform settings. Hence they MUST NOT INTERSECT | |
# with any settings from cgitrc! | |
options = { | |
enable = mkEnableOption "cgit"; | |
location = mkOption { | |
default = "/"; | |
type = types.str; | |
description = '' | |
Location to serve cgit on. | |
''; | |
}; | |
}; | |
# Remove the global options for serialization into cgitrc | |
settings = removeAttrs cfg (attrNames options); | |
in | |
{ | |
options.cgit = mkOption { | |
type = types.submodule { | |
freeformType = settingsFormat.type; | |
inherit options; | |
config = { | |
css = mkDefault "/cgit.css"; | |
logo = mkDefault "/cgit.png"; | |
favicon = mkDefault "/favicon.ico"; | |
}; | |
}; | |
default = { }; | |
example = literalExample '' | |
{ | |
enable = true; | |
virtual-root = "/"; | |
source-filter = "''${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py"; | |
about-filter = "''${pkgs.cgit}/lib/cgit/filters/about-formatting.sh"; | |
cache-size = 1000; | |
scan-path = "/srv/git"; | |
include = [ | |
(builtins.toFile "cgitrc-extra-1" ''' | |
# Anything that has to be in a particular order | |
''') | |
(builtins.toFile "cgitrc-extra-2" ''' | |
# Anything that has to be in a particular order | |
''') | |
]; | |
} | |
''; | |
description = '' | |
Verbatim contents of the cgit runtime configuration file. Documentation | |
(with cgitrc example file) is available in "man cgitrc". Or online: | |
http://git.zx2c4.com/cgit/tree/cgitrc.5.txt | |
''; | |
}; | |
config = let | |
location = removeSuffix "/" cfg.location; | |
in mkIf cfg.enable { | |
locations."${location}/" = { | |
root = "${pkgs.cgit}/cgit/"; | |
tryFiles = "$uri @cgit"; | |
}; | |
locations."~ ^${location}/(cgit.(css|png)|favicon.ico|robots.txt)$" = { | |
alias = "${pkgs.cgit}/cgit/$1"; | |
}; | |
locations."@cgit" = { | |
extraConfig = '' | |
include ${pkgs.nginx}/conf/fastcgi_params; | |
fastcgi_param CGIT_CONFIG ${settingsFormat.generate "cgitrc" settings}; | |
fastcgi_param SCRIPT_FILENAME ${pkgs.cgit}/cgit/cgit.cgi; | |
fastcgi_param QUERY_STRING $args; | |
fastcgi_param HTTP_HOST $server_name; | |
fastcgi_pass unix:${globalConfig.services.fcgiwrap.socketAddress}; | |
'' + ( | |
if cfg.location == "/" | |
then | |
'' | |
fastcgi_param PATH_INFO $uri; | |
'' | |
else | |
'' | |
fastcgi_split_path_info ^(${location}/)(/?.+)$; | |
fastcgi_param PATH_INFO $fastcgi_path_info; | |
'' | |
); | |
}; | |
}; | |
})); | |
}; | |
config = | |
let | |
vhosts = config.services.nginx.virtualHosts; | |
in | |
mkIf (any (name: vhosts.${name}.cgit.enable) (attrNames vhosts)) { | |
# make the cgitrc manpage available | |
environment.systemPackages = [ pkgs.cgit ]; | |
services.fcgiwrap.enable = true; | |
}; | |
meta = { | |
maintainers = with lib.maintainers; [ afix-space hmenke ]; | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# nix-build test.nix -A driver | |
# ./result/bin/nixos-test-driver | |
# >>> test_script() | |
{ | |
"http-clone" = import <nixpkgs/nixos/tests/make-test-python.nix> ({ lib, ... }: { | |
name = "nginx-cgit-http-clone"; | |
meta = { | |
maintainers = with lib.maintainers; [ afix-space hmenke ]; | |
}; | |
machine = { pkgs, ... }: { | |
imports = [ | |
./cgit.nix | |
]; | |
environment.systemPackages = [ pkgs.git ]; | |
services.nginx.enable = true; | |
services.nginx.virtualHosts."localhost" = { | |
cgit = { | |
enable = true; | |
virtual-root = "/"; | |
include = [ | |
(builtins.toFile "cgitrc-extra-1" '' | |
repo.url=test-repo.git | |
repo.path=/srv/git/test-repo.git | |
repo.desc=the master foo repository | |
[email protected] | |
'') | |
(builtins.toFile "cgitrc-extra-2" '' | |
# Allow http transport git clone | |
enable-http-clone=1 | |
'') | |
]; | |
}; | |
}; | |
}; | |
testScript = '' | |
# Set up a test repository with only a single file that contains the string | |
# "Hello World!". | |
machine.succeed( | |
""" | |
git config --global user.name "NixOS Test" | |
git config --global user.email "NixOS Test" | |
git init | |
echo -n "Hello world!" > test.txt | |
git add test.txt | |
git commit -am "Test commit" | |
git init --bare /srv/git/test-repo.git | |
git push /srv/git/test-repo.git master | |
""" | |
) | |
machine.wait_for_unit("nginx.service") | |
# Clone the repo on the client through cgit's http clone interface and | |
# verify that the test file string is correct. | |
text = machine.succeed( | |
""" | |
git clone http://127.0.0.1/test-repo.git /tmp/test-repo | |
cat /tmp/test-repo/test.txt | |
""" | |
) | |
assert text == "Hello world!", "Defective clone from cgit" | |
''; | |
}); | |
"location" = import <nixpkgs/nixos/tests/make-test-python.nix> ({ lib, ... }: { | |
name = "nginx-cgit-location"; | |
meta = { | |
maintainers = with lib.maintainers; [ afix-space hmenke ]; | |
}; | |
machine = { pkgs, ... }: { | |
imports = [ | |
./cgit.nix | |
]; | |
environment.systemPackages = [ pkgs.git ]; | |
services.nginx.enable = true; | |
services.nginx.virtualHosts."localhost" = { | |
cgit = { | |
enable = true; | |
location = "/somewhere/else/"; | |
}; | |
}; | |
}; | |
testScript = '' | |
machine.wait_for_unit("nginx.service") | |
machine.succeed("curl -fo /dev/null http://127.0.0.1/somewhere/else/cgit.png") | |
machine.succeed("curl -fo /dev/null http://127.0.0.1/somewhere/else/cgit.css") | |
machine.succeed("curl -fo /dev/null http://127.0.0.1/somewhere/else/favicon.ico") | |
machine.succeed("curl -fo /dev/null http://127.0.0.1/somewhere/else/robots.txt") | |
''; | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
there is a need to add mkIf line 62, otherwise cgit is always configured :
config = mkIf cfg.enable {
otherwise I am not able to run test with option -p nixos-shell.override.
error: cannot coerce a set to a string, at /nix/store/1lmicz4kfxk407gx0rjq79biwx7j9bp1-nixos-20.09.2624.1eff582e7ea/nixos/pkgs/build-support/trivial-builders.nix:7:7
but I've not yet learned much about this...
Thanks for this gist, it seems to me that this is a good example of best practice.