{
description = "a git server that your cow will love";
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
outputs = { self, nixpkgs }:
let
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
in
{
packages = forAllSystems (pkgs:
let version = self.rev or "dev";
in {
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mugit;
mugit = pkgs.buildGoModule {
pname = "mugit";
version = version;
src = ./.;
vendorHash = "sha256-xF8IRS0Ne1zp4u6uolKFpKEZObSM6VhV95JUj2krXPY=";
ldflags = [ "-s" "-w" "-X main.version=${version}" ];
meta = with pkgs.lib; {
homepage = "https://git.olexsmir.xyz/mugit";
license = licenses.mit;
};
};
});
nixosModules.default = { config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.mugit;
format = pkgs.formats.yaml { };
configFile = format.generate "config.yaml" cfg.config;
in
{
options.services.mugit = {
enable = mkEnableOption "mugit service";
package = mkOption {
type = types.package;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mugit;
defaultText = literalExpression "self.packages.\${pkgs.stdenv.hostPlatform.system}.mugit";
description = "The mugit package to use.";
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = "Whether to open the firewall for mugit. Can only be used with `config`, not `configFile`.";
};
exposeCli = mkOption {
type = types.bool;
default = false;
description = "Whether to expose a mugit CLI wrapper to all system users, runs as the mugit user/group.";
};
configFile = mkOption {
type = types.nullOr types.path;
default = null;
description = "Path to an existing mugit configuration file. Mutually exclusive with `config`.";
};
user = mkOption {
type = types.str;
default = "mugit";
description = "User account under which mugit runs.";
};
group = mkOption {
type = types.str;
default = "mugit";
description = "Group under which mugit runs.";
};
config = mkOption {
default = {};
description = ''
The primary mugit configuration.
See [docs](https://github.com/olexsmir/mugit) for possible values.
'';
example = literalExpression ''
{
meta.host = "git.example.org";
repo.dir = "/var/lib/mugit";
ssh = {
enable = true;
host_key = "/var/lib/mugit/key";
};
}
'';
type = types.submodule {
options.meta = {
title = mkOption {
type = types.str;
default = "mugit";
description = "Website title";
};
description = mkOption {
type = types.str;
default = "";
description = "Website description";
};
host = mkOption {
type = types.str;
default = "";
description = "Website CNAME (required)";
};
};
options.server = {
host = mkOption {
type = types.str;
default = "";
description = "Host address";
};
port = mkOption {
type = types.port;
default = 8080;
description = "Website port";
};
};
options.repo = {
dir = mkOption {
type = types.str;
default = "";
description = "Directory which mugit will scan for repositories (required)";
};
masters = mkOption {
type = types.listOf types.str;
default = ["master" "main"];
description = "Master branch to look for";
};
readmes = mkOption {
type = types.listOf types.str;
default = ["README.md" "readme.md" "README.html" "readme.html" "README.txt" "readme.txt" "readme"];
description = "Readme files to look for";
};
};
options.ssh = {
enable = mkOption {
type = types.bool;
default = false;
description = "Wharever to run ssh server";
};
port = mkOption {
type = types.port;
default = 2222;
description = "Website port";
};
host_key = mkOption {
type = types.str;
default = "";
description = "Path to ssh private key (required if ssh enabled)";
};
keys = mkOption {
type = types.listOf types.str;
default = [];
description = "List of public ssh keys which are allows to do git pushes, and access private repositories";
};
};
options.mirror = {
enable = mkOption {
type = types.bool;
default = false;
description = "Wharever to run mirroring worker";
};
interval = mkOption {
type = types.str;
default = "8h";
description = "Interval in which mirroring will happen";
};
github_token = mkOption {
type = types.str;
default = "";
description = "Github token for pulling from github repos";
};
};
options.cache = {
home_page = mkOption {
type = types.str;
default = "5m";
description = "For how long index page is cached";
};
readme = mkOption {
type = types.str;
default = "1m";
description = "For how long repos readme is cached";
};
};
};
};
};
config = mkIf cfg.enable {
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts =
let
serverPort = cfg.config.server.port or 8080;
sshPort = cfg.config.ssh.port or 2222;
sshEnabled = cfg.config.ssh.enable or false;
in
[ serverPort ] ++ lib.optional sshEnabled sshPort;
};
users.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
home = cfg.config.repo.dir;
createHome = true;
description = "mugit service user";
};
users.groups.${cfg.group} = { };
environment.systemPackages = lib.mkIf cfg.exposeCli [
(pkgs.runCommandLocal "mugit-completions" {} ''
mkdir -p $out/share/bash-completion/completions
mkdir -p $out/share/zsh/site-functions
mkdir -p $out/share/fish/vendor_completions.d
${cfg.package}/bin/mugit completion bash > $out/share/bash-completion/completions/mugit
${cfg.package}/bin/mugit completion zsh > $out/share/zsh/site-functions/_mugit
${cfg.package}/bin/mugit completion fish > $out/share/fish/vendor_completions.d/mugit.fish
'')
];
security.wrappers = lib.mkIf cfg.exposeCli {
mugit = {
source =
let
resolvedConfig = if cfg.configFile != null then cfg.configFile else configFile;
mugitWrapped = pkgs.writeScriptBin "mugit" ''
#!${pkgs.bash}/bin/bash
exec ${cfg.package}/bin/mugit --config ${resolvedConfig} "$@"
'';
in
"${mugitWrapped}/bin/mugit";
owner = cfg.user;
group = cfg.group;
setuid = true;
setgid = true;
permissions = "u+rx,g+rx,o+rx";
};
};
systemd.services.mugit = {
description = "mugit service";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
path = [ pkgs.git ];
serviceConfig =
let
serverPort = cfg.config.server.port or 8080;
sshPort = cfg.config.ssh.port or 2222;
sshEnabled = cfg.config.ssh.enable or false;
needsPrivPort = serverPort < 1024 || (sshEnabled && sshPort < 1024);
in
{
Type = "simple";
User = cfg.user;
Group = cfg.group;
WorkingDirectory = cfg.config.repo.dir;
StateDirectory = "mugit";
ExecStart = "${cfg.package}/bin/mugit serve --config ${configFile}";
Restart = "on-failure";
RestartSec = "5s";
NoNewPrivileges = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [ cfg.config.repo.dir ];
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
}
// lib.optionalAttrs needsPrivPort {
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
};
};
};
};
};
}
|