Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/sourcehut: init #113244

Merged
merged 16 commits into from
Jun 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@
./services/misc/siproxd.nix
./services/misc/snapper.nix
./services/misc/sonarr.nix
./services/misc/sourcehut
./services/misc/spice-vdagentd.nix
./services/misc/ssm-agent.nix
./services/misc/sssd.nix
Expand Down
220 changes: 220 additions & 0 deletions nixos/modules/services/misc/sourcehut/builds.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
{ config, lib, pkgs, ... }:

with lib;
let
cfg = config.services.sourcehut;
scfg = cfg.builds;
rcfg = config.services.redis;
iniKey = "builds.sr.ht";

drv = pkgs.sourcehut.buildsrht;
in
{
options.services.sourcehut.builds = {
user = mkOption {
type = types.str;
default = "buildsrht";
description = ''
User for builds.sr.ht.
'';
};

port = mkOption {
type = types.port;
default = 5002;
description = ''
Port on which the "builds" module should listen.
'';
};

database = mkOption {
type = types.str;
default = "builds.sr.ht";
description = ''
PostgreSQL database name for builds.sr.ht.
'';
};

statePath = mkOption {
type = types.path;
default = "${cfg.statePath}/buildsrht";
description = ''
State path for builds.sr.ht.
'';
};

enableWorker = mkOption {
type = types.bool;
default = false;
description = ''
Run workers for builds.sr.ht.
Perform manually on machine: `cd ${scfg.statePath}/images; docker build -t qemu -f qemu/Dockerfile .`
'';
};

images = mkOption {
type = types.attrsOf (types.attrsOf (types.attrsOf types.package));
default = { };
example = lib.literalExample ''(let
# Pinning unstable to allow usage with flakes and limit rebuilds.
pkgs_unstable = builtins.fetchGit {
url = "https://github.com/NixOS/nixpkgs";
rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
ref = "nixos-unstable";
};
image_from_nixpkgs = pkgs_unstable: (import ("${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
pkgs = (import pkgs_unstable {});
});
in
{
nixos.unstable.x86_64 = image_from_nixpkgs pkgs_unstable;
}
)'';
description = ''
Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
'';
};

};

config = with scfg; let
image_dirs = lib.lists.flatten (
lib.attrsets.mapAttrsToList
(distro: revs:
lib.attrsets.mapAttrsToList
(rev: archs:
lib.attrsets.mapAttrsToList
(arch: image:
pkgs.runCommandNoCC "buildsrht-images" { } ''
mkdir -p $out/${distro}/${rev}/${arch}
ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
'')
archs)
revs)
scfg.images);
image_dir_pre = pkgs.symlinkJoin {
name = "builds.sr.ht-worker-images-pre";
paths = image_dirs ++ [
"${pkgs.sourcehut.buildsrht}/lib/images"
];
};
image_dir = pkgs.runCommandNoCC "builds.sr.ht-worker-images" { } ''
mkdir -p $out/images
cp -Lr ${image_dir_pre}/* $out/images
'';
in
lib.mkIf (cfg.enable && elem "builds" cfg.services) {
users = {
users = {
"${user}" = {
isSystemUser = true;
group = user;
extraGroups = lib.optionals cfg.builds.enableWorker [ "docker" ];
description = "builds.sr.ht user";
};
};

groups = {
"${user}" = { };
};
};

services.postgresql = {
authentication = ''
local ${database} ${user} trust
'';
ensureDatabases = [ database ];
ensureUsers = [
{
name = user;
ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
}
];
};

systemd = {
tmpfiles.rules = [
"d ${statePath} 0755 ${user} ${user} -"
] ++ (lib.optionals cfg.builds.enableWorker
[ "d ${statePath}/logs 0775 ${user} ${user} - -" ]
);

services = {
buildsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey
{
after = [ "postgresql.service" "network.target" ];
requires = [ "postgresql.service" ];
wantedBy = [ "multi-user.target" ];

description = "builds.sr.ht website service";

serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";

# Hack to bypass this hack: https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/item/srht-update-profiles#L6
} // { preStart = " "; };

buildsrht-worker = {
enable = scfg.enableWorker;
after = [ "postgresql.service" "network.target" ];
requires = [ "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
partOf = [ "buildsrht.service" ];
description = "builds.sr.ht worker service";
path = [ pkgs.openssh pkgs.docker ];
serviceConfig = {
Type = "simple";
User = user;
Group = "nginx";
Restart = "always";
};
serviceConfig.ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
};
};
};

services.sourcehut.settings = {
# URL builds.sr.ht is being served at (protocol://domain)
"builds.sr.ht".origin = mkDefault "http://builds.${cfg.originBase}";
# Address and port to bind the debug server to
"builds.sr.ht".debug-host = mkDefault "0.0.0.0";
"builds.sr.ht".debug-port = mkDefault port;
# Configures the SQLAlchemy connection string for the database.
"builds.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
# Set to "yes" to automatically run migrations on package upgrade.
"builds.sr.ht".migrate-on-upgrade = mkDefault "yes";
# builds.sr.ht's OAuth client ID and secret for meta.sr.ht
# Register your client at meta.example.org/oauth
"builds.sr.ht".oauth-client-id = mkDefault null;
"builds.sr.ht".oauth-client-secret = mkDefault null;
# The redis connection used for the celery worker
"builds.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/3";
# The shell used for ssh
"builds.sr.ht".shell = mkDefault "runner-shell";
# Register the builds.sr.ht dispatcher
"git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys"} = mkDefault "${user}:${user}";

# Location for build logs, images, and control command
} // lib.attrsets.optionalAttrs scfg.enableWorker {
# Default worker stores logs that are accessible via this address:port
"builds.sr.ht::worker".name = mkDefault "127.0.0.1:5020";
"builds.sr.ht::worker".buildlogs = mkDefault "${scfg.statePath}/logs";
"builds.sr.ht::worker".images = mkDefault "${image_dir}/images";
"builds.sr.ht::worker".controlcmd = mkDefault "${image_dir}/images/control";
"builds.sr.ht::worker".timeout = mkDefault "3m";
};

services.nginx.virtualHosts."logs.${cfg.originBase}" =
if scfg.enableWorker then {
listen = with builtins; let address = split ":" cfg.settings."builds.sr.ht::worker".name;
in [{ addr = elemAt address 0; port = lib.toInt (elemAt address 2); }];
locations."/logs".root = "${scfg.statePath}";
} else { };

services.nginx.virtualHosts."builds.${cfg.originBase}" = {
forceSSL = true;
locations."/".proxyPass = "http://${cfg.address}:${toString port}";
locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
locations."/static".root = "${pkgs.sourcehut.buildsrht}/${pkgs.sourcehut.python.sitePackages}/buildsrht";
};
};
}
Loading