summaryrefslogtreecommitdiffstats
path: root/modules/tv/git
diff options
context:
space:
mode:
Diffstat (limited to 'modules/tv/git')
-rw-r--r--modules/tv/git/cgit.nix93
-rw-r--r--modules/tv/git/config.nix272
-rw-r--r--modules/tv/git/default.nix27
-rw-r--r--modules/tv/git/options.nix93
-rw-r--r--modules/tv/git/public.nix82
5 files changed, 0 insertions, 567 deletions
diff --git a/modules/tv/git/cgit.nix b/modules/tv/git/cgit.nix
deleted file mode 100644
index 747a93135..000000000
--- a/modules/tv/git/cgit.nix
+++ /dev/null
@@ -1,93 +0,0 @@
-{ cfg, config, lib, pkgs, ... }:
-
-let
- inherit (builtins) attrValues filter getAttr;
- inherit (lib) concatMapStringsSep mkIf optionalString;
-
- location = lib.nameValuePair; # TODO this is also in modules/wu/default.nix
-
- isPublicRepo = getAttr "public"; # TODO this is also in ./default.nix
-in
-
-{
- users.extraUsers = lib.singleton {
- name = "fcgiwrap";
- uid = 2851179180; # genid fcgiwrap
- group = "fcgiwrap";
- home = toString (pkgs.runCommand "empty" {} "mkdir -p $out");
- };
-
- users.extraGroups = lib.singleton {
- name = "fcgiwrap";
- gid = 2851179180; # genid fcgiwrap
- };
-
- services.fcgiwrap = {
- enable = true;
- user = "fcgiwrap";
- group = "fcgiwrap";
- # socketAddress = "/run/fcgiwrap.sock" (default)
- # socketType = "unix" (default)
- };
-
- environment.etc."cgitrc".text = ''
- css=/cgit-static/cgit.css
- logo=/cgit-static/cgit.png
-
- # if you do not want that webcrawler (like google) index your site
- robots=noindex, nofollow
-
- virtual-root=/cgit
-
- # TODO make this nicer (and/or somewhere else)
- cache-root=/tmp/cgit
-
- cache-size=1000
- enable-commit-graph=1
- enable-index-links=1
- enable-index-owner=0
- enable-log-filecount=1
- enable-log-linecount=1
- enable-remote-branches=1
-
- root-title=public repositories at ${config.networking.hostName}
- root-desc=keep calm and engage
-
- snapshots=0
- max-stats=year
-
- ${concatMapStringsSep "\n" (repo: ''
- repo.url=${repo.name}
- repo.path=${cfg.dataDir}/${repo.name}
- ${optionalString (repo.section != null) "repo.section=${repo.section}"}
- ${optionalString (repo.desc != null) "repo.desc=${repo.desc}"}
- '') (filter isPublicRepo (attrValues cfg.repos))}
- '';
-
- system.activationScripts.cgit = ''
- mkdir -m 0700 -p /tmp/cgit
- chown fcgiwrap: /tmp/cgit
- '';
-
- tv.nginx = {
- enable = true;
- retiolum-locations = [
- (location "/cgit/" ''
- include ${pkgs.nginx}/conf/fastcgi_params;
- fastcgi_param SCRIPT_FILENAME ${pkgs.cgit}/cgit/cgit.cgi;
- fastcgi_split_path_info ^(/cgit/?)(.+)$;
- fastcgi_param PATH_INFO $fastcgi_path_info;
- fastcgi_param QUERY_STRING $args;
- fastcgi_param HTTP_HOST $server_name;
- fastcgi_pass unix:${config.services.fcgiwrap.socketAddress};
- '')
- (location "= /cgit" ''
- return 301 /cgit/;
- '')
- (location "/cgit-static/" ''
- root ${pkgs.cgit}/cgit;
- rewrite ^/cgit-static(/.*)$ $1 break;
- '')
- ];
- };
-}
diff --git a/modules/tv/git/config.nix b/modules/tv/git/config.nix
deleted file mode 100644
index 4f44c3839..000000000
--- a/modules/tv/git/config.nix
+++ /dev/null
@@ -1,272 +0,0 @@
-arg@{ cfg, lib, pkgs, ... }:
-
-let
- inherit (builtins) head tail typeOf;
- inherit (lib)
- attrValues concatStringsSep concatMapStringsSep escapeShellArg filter
- getAttr hasAttr hasPrefix lessThan makeSearchPath mapAttrsToList
- optional optionalString removePrefix singleton sort unique;
- inherit (pkgs) linkFarm writeScript;
-
- ensureList = x:
- if typeOf x == "list" then x else [x];
-
- getName = x: x.name;
-
- isPublicRepo = getAttr "public"; # TODO this is also in ./cgit.nix
-
- makeAuthorizedKey = git-ssh-command: user@{ name, pubkey }:
- # TODO assert name
- # TODO assert pubkey
- let
- options = concatStringsSep "," [
- ''command="exec ${git-ssh-command} ${name}"''
- "no-agent-forwarding"
- "no-port-forwarding"
- "no-pty"
- "no-X11-forwarding"
- ];
- in
- "${options} ${pubkey}";
-
- # [case-pattern] -> shell-script
- # Create a shell script that succeeds (exit 0) when all its arguments
- # match the case patterns (in the given order).
- makeAuthorizeScript =
- let
- # TODO escape
- to-pattern = x: concatStringsSep "|" (ensureList x);
- go = i: ps:
- if ps == []
- then "exit 0"
- else ''
- case ''$${toString i} in ${to-pattern (head ps)})
- ${go (i + 1) (tail ps)}
- esac'';
- in
- patterns: ''
- #! /bin/sh
- set -euf
- ${concatStringsSep "\n" (map (go 1) patterns)}
- exit -1
- '';
-
- reponames = rules: sort lessThan (unique (map (x: x.repo.name) rules));
-
- # TODO makeGitHooks that uses runCommand instead of scriptFarm?
- scriptFarm =
- farm-name: scripts:
- let
- makeScript = script-name: script-string: {
- name = script-name;
- path = writeScript "${farm-name}_${script-name}" script-string;
- };
- in
- linkFarm farm-name (mapAttrsToList makeScript scripts);
-
-
- git-ssh-command = writeScript "git-ssh-command" ''
- #! /bin/sh
- set -euf
-
- PATH=${makeSearchPath "bin" (with pkgs; [
- coreutils
- git
- gnugrep
- gnused
- systemd
- ])}
-
- abort() {
- echo "error: $1" >&2
- systemd-cat -p err -t git echo "error: $1"
- exit -1
- }
-
- GIT_SSH_USER=$1
-
- systemd-cat -p info -t git echo \
- "authorizing $GIT_SSH_USER $SSH_CONNECTION $SSH_ORIGINAL_COMMAND"
-
- # References: The Base Definitions volume of
- # POSIX.1‐2013, Section 3.278, Portable Filename Character Set
- portable_filename_bre="^[A-Za-z0-9._-]\\+$"
-
- command=$(echo "$SSH_ORIGINAL_COMMAND" \
- | sed -n 's/^\([^ ]*\) '"'"'\(.*\)'"'"'/\1/p' \
- | grep "$portable_filename_bre" \
- || abort 'cannot read command')
-
- GIT_SSH_REPO=$(echo "$SSH_ORIGINAL_COMMAND" \
- | sed -n 's/^\([^ ]*\) '"'"'\(.*\)'"'"'/\2/p' \
- | grep "$portable_filename_bre" \
- || abort 'cannot read reponame')
-
- ${cfg.etcDir}/authorize-command \
- "$GIT_SSH_USER" "$GIT_SSH_REPO" "$command" \
- || abort 'access denied'
-
- repodir=${escapeShellArg cfg.dataDir}/$GIT_SSH_REPO
-
- systemd-cat -p info -t git \
- echo "authorized exec $command $repodir"
-
- export GIT_SSH_USER
- export GIT_SSH_REPO
- exec "$command" "$repodir"
- '';
-
- init-script = writeScript "git-init" ''
- #! /bin/sh
- set -euf
-
- PATH=${makeSearchPath "bin" (with pkgs; [
- coreutils
- findutils
- gawk
- git
- gnugrep
- gnused
- ])}
-
- dataDir=${escapeShellArg cfg.dataDir}
- mkdir -p "$dataDir"
-
- # Notice how the presence of hooks symlinks determine whether
- # we manage a repositry or not.
-
- # Make sure that no existing repository has hooks. We can delete
- # symlinks because we assume we created them.
- find "$dataDir" -mindepth 2 -maxdepth 2 -name hooks -type l -delete
- bad_hooks=$(find "$dataDir" -mindepth 2 -maxdepth 2 -name hooks)
- if echo "$bad_hooks" | grep -q .; then
- printf 'error: unknown hooks:\n%s\n' \
- "$(echo "$bad_hooks" | sed 's/^/ /')" \
- >&2
- exit -1
- fi
-
- # Initialize repositories.
- ${concatMapStringsSep "\n" (repo:
- let
- hooks = scriptFarm "git-hooks" (makeHooks repo);
- in
- ''
- reponame=${escapeShellArg repo.name}
- repodir=$dataDir/$reponame
- mode=${toString (if isPublicRepo repo then 0711 else 0700)}
- if ! test -d "$repodir"; then
- mkdir -m "$mode" "$repodir"
- git init --bare --template=/var/empty "$repodir"
- chown -R git:nogroup "$repodir"
- fi
- ln -s ${hooks} "$repodir/hooks"
- ''
- ) (attrValues cfg.repos)}
-
- # Warn about repositories that exist but aren't mentioned in the
- # current configuration (and thus didn't receive a hooks symlink).
- unknown_repos=$(find "$dataDir" -mindepth 1 -maxdepth 1 \
- -type d \! -exec test -e '{}/hooks' \; -print)
- if echo "$unknown_repos" | grep -q .; then
- printf 'warning: stale repositories:\n%s\n' \
- "$(echo "$unknown_repos" | sed 's/^/ /')" \
- >&2
- fi
- '';
-
- makeHooks = repo: removeAttrs repo.hooks [ "pre-receive" ] // {
- pre-receive = ''
- #! /bin/sh
- set -euf
-
- PATH=${makeSearchPath "bin" (with pkgs; [
- coreutils # env
- git
- systemd
- ])}
-
- accept() {
- #systemd-cat -p info -t git echo "authorized $1"
- accept_string="''${accept_string+$accept_string
- }authorized $1"
- }
- reject() {
- #systemd-cat -p err -t git echo "denied $1"
- #echo 'access denied' >&2
- #exit_code=-1
- reject_string="''${reject_string+$reject_string
- }access denied: $1"
- }
-
- empty=0000000000000000000000000000000000000000
-
- accept_string=
- reject_string=
- while read oldrev newrev ref; do
-
- if [ $oldrev = $empty ]; then
- receive_mode=create
- elif [ $newrev = $empty ]; then
- receive_mode=delete
- elif [ "$(git merge-base $oldrev $newrev)" = $oldrev ]; then
- receive_mode=fast-forward
- else
- receive_mode=non-fast-forward
- fi
-
- if ${cfg.etcDir}/authorize-push \
- "$GIT_SSH_USER" "$GIT_SSH_REPO" "$ref" "$receive_mode"; then
- accept "$receive_mode $ref"
- else
- reject "$receive_mode $ref"
- fi
- done
-
- if [ -n "$reject_string" ]; then
- systemd-cat -p err -t git echo "$reject_string"
- exit -1
- fi
-
- systemd-cat -p info -t git echo "$accept_string"
-
- ${optionalString (hasAttr "post-receive" repo.hooks) ''
- # custom post-receive hook
- ${repo.hooks.post-receive}''}
- '';
- };
-
- etc-base =
- assert (hasPrefix "/etc/" cfg.etcDir);
- removePrefix "/etc/" cfg.etcDir;
-in
-
-{
- system.activationScripts.git-init = "${init-script}";
-
- # TODO maybe put all scripts here and then use PATH?
- environment.etc."${etc-base}".source =
- scriptFarm "git-ssh-authorizers" {
- authorize-command = makeAuthorizeScript (map ({ repo, user, perm }: [
- (map getName (ensureList user))
- (map getName (ensureList repo))
- (map getName perm.allow-commands)
- ]) cfg.rules);
-
- authorize-push = makeAuthorizeScript (map ({ repo, user, perm }: [
- (map getName (ensureList user))
- (map getName (ensureList repo))
- (ensureList perm.allow-receive-ref)
- (map getName perm.allow-receive-modes)
- ]) (filter (x: hasAttr "allow-receive-ref" x.perm) cfg.rules));
- };
-
- users.extraUsers = singleton {
- description = "Git repository hosting user";
- name = "git";
- shell = "/bin/sh";
- openssh.authorizedKeys.keys =
- mapAttrsToList (_: makeAuthorizedKey git-ssh-command) cfg.users;
- uid = 112606723; # genid git
- };
-}
diff --git a/modules/tv/git/default.nix b/modules/tv/git/default.nix
deleted file mode 100644
index 17bc3738b..000000000
--- a/modules/tv/git/default.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-arg@{ config, pkgs, lib, ... }:
-
-let
- inherit (lib) mkIf mkMerge;
-
- cfg = config.tv.git;
- arg' = arg // { inherit cfg; };
-in
-
-# TODO unify logging of shell scripts to user and journal
-# TODO move all scripts to ${etcDir}, so ControlMaster connections
-# immediately pick up new authenticators
-# TODO when authorized_keys changes, then restart ssh
-# (or kill already connected users somehow)
-
-{
- imports = [
- ../../tv/nginx
- ];
-
- options.tv.git = import ./options.nix arg';
-
- config = mkIf cfg.enable (mkMerge [
- (import ./config.nix arg')
- (mkIf cfg.cgit (import ./cgit.nix arg'))
- ]);
-}
diff --git a/modules/tv/git/options.nix b/modules/tv/git/options.nix
deleted file mode 100644
index c251d7d4c..000000000
--- a/modules/tv/git/options.nix
+++ /dev/null
@@ -1,93 +0,0 @@
-{ lib, ... }:
-
-let
- inherit (lib) literalExample mkOption types;
-in
-
-{
- enable = mkOption {
- type = types.bool;
- default = false;
- description = "Enable Git repository hosting.";
- };
- cgit = mkOption {
- type = types.bool;
- default = true;
- description = "Enable cgit."; # TODO better desc; talk about nginx
- };
- dataDir = mkOption {
- type = types.str;
- default = "/var/lib/git";
- description = "Directory used to store repositories.";
- };
- etcDir = mkOption {
- type = types.str;
- default = "/etc/git";
- };
- rules = mkOption {
- type = types.unspecified;
- };
- repos = mkOption {
- type = types.attrsOf (types.submodule ({
- options = {
- desc = mkOption {
- type = types.nullOr types.str;
- default = null;
- description = ''
- Repository description.
- '';
- };
- section = mkOption {
- type = types.nullOr types.str;
- default = null;
- description = ''
- Repository section.
- '';
- };
- name = mkOption {
- type = types.str;
- description = ''
- Repository name.
- '';
- };
- hooks = mkOption {
- type = types.attrsOf types.str;
- description = ''
- Repository-specific hooks.
- '';
- };
- public = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Allow everybody to read the repository via HTTP if cgit enabled.
- '';
- # TODO allow every configured user to fetch the repository via SSH.
- };
- };
- }));
-
- default = {};
-
- example = literalExample ''
- {
- testing = {
- name = "testing";
- hooks.post-update = '''
- #! /bin/sh
- set -euf
- echo post-update hook: $* >&2
- ''';
- };
- testing2 = { name = "testing2"; };
- }
- '';
-
- description = ''
- Repositories.
- '';
- };
- users = mkOption {
- type = types.unspecified;
- };
-}
diff --git a/modules/tv/git/public.nix b/modules/tv/git/public.nix
deleted file mode 100644
index de6ed7fdf..000000000
--- a/modules/tv/git/public.nix
+++ /dev/null
@@ -1,82 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
- inherit (builtins) map readFile;
- inherit (lib) concatMap listToAttrs;
- # TODO lib should already include our stuff
- inherit (import ../../../lib { inherit lib pkgs; }) addNames git;
-
- public-git-repos = [
- (public "cgserver")
- (public "crude-mail-setup")
- (public "dot-xmonad")
- (public "hack")
- (public "load-env")
- (public "make-snapshot")
- (public "mime")
- (public "much")
- (public "nixos-infest")
- (public "nixpkgs")
- (public "painload")
- (public "regfish")
- (public' {
- name = "shitment";
- desc = "turn all the computers into one computer!";
- })
- (public "wai-middleware-time")
- (public "web-routes-wai-custom")
- ];
-
- users = addNames {
- tv = { pubkey = readFile <pubkeys/tv_wu.ssh.pub>; };
- lass = { pubkey = readFile <pubkeys/lass.ssh.pub>; };
- uriel = { pubkey = readFile <pubkeys/uriel.ssh.pub>; };
- makefu = { pubkey = readFile <pubkeys/makefu.ssh.pub>; };
- };
-
- repos = listToAttrs (map ({ repo, ... }: { name = repo.name; value = repo; }) public-git-repos);
-
- rules = concatMap ({ rules, ... }: rules) public-git-repos;
-
- public' = { name, desc }:
- let
- x = public name;
- in
- x // { repo = x.repo // { inherit desc; }; };
-
- public = repo-name:
- rec {
- repo = {
- name = repo-name;
- hooks = {
- post-receive = git.irc-announce {
- nick = config.networking.hostName; # TODO make this the default
- channel = "#retiolum";
- server = "ire.retiolum";
- };
- };
- public = true;
- };
- rules = with git; with users; [
- { user = tv;
- repo = [ repo ];
- perm = push "refs/*" [ non-fast-forward create delete merge ];
- }
- { user = [ lass makefu uriel ];
- repo = [ repo ];
- perm = fetch;
- }
- ];
- };
-
-in
-
-{
- imports = [
- ./.
- ];
- tv.git = {
- enable = true;
- inherit repos rules users;
- };
-}