Some checks failed
CI pipeline for ffd-indexer / checks (go test + golangci) (push) Failing after 26s
CI pipeline for ffd-indexer / build binaries + containers (push) Failing after 3s
CI pipeline for ffd-indexer / format (gofmt + nixfmt) (push) Failing after 3s
CI pipeline for ffd-indexer / integration (live scrape + Typesense) (push) Failing after 20s
234 lines
7.5 KiB
Nix
234 lines
7.5 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
cfg = config.services.federated-forge-discovery.indexer;
|
|
yaml = pkgs.lib.generators.toYAML { };
|
|
|
|
# NixOS package for the indexer, injected via the flake.
|
|
pkg = cfg.package;
|
|
|
|
# Location where the rendered config is written.
|
|
configDir = "/etc/ffd-indexer";
|
|
configFile = "${configDir}/config.yaml";
|
|
|
|
# The rendered YAML config, mirroring the config.go structure.
|
|
nixConfig = {
|
|
db_path = cfg.dbPath;
|
|
typesense = {
|
|
host = cfg.typesense.host;
|
|
api_key = cfg.typesense.apiKey;
|
|
}
|
|
// lib.optionalAttrs (cfg.typesense.apiKeyFile != null) {
|
|
# A secret file (sops/agenix) overrides the inline api_key in Go's
|
|
# config resolution, so it also wins here.
|
|
api_key_file = cfg.typesense.apiKeyFile;
|
|
};
|
|
# The metrics daemon is an optional component: only render the block
|
|
# when it is enabled, so an operator running just ffd-indexer can omit
|
|
# the metrics section entirely.
|
|
}
|
|
// lib.optionalAttrs cfg.metrics.enable {
|
|
metrics = {
|
|
listen = cfg.metrics.listen;
|
|
};
|
|
};
|
|
forges = cfg.forges;
|
|
};
|
|
|
|
configYAML = pkgs.writeText "config.yaml" (yaml nixConfig);
|
|
in
|
|
{
|
|
options.services.federated-forge-discovery.indexer = {
|
|
enable = lib.mkEnableOption "Enables the Federated Forge Discovery Indexer systemd service and timer.";
|
|
|
|
package = lib.mkOption {
|
|
description = "Indexer package to use.";
|
|
type = lib.types.package;
|
|
# Provided by the flake overlay (see flake.nix overlays.default), so
|
|
# `pkgs.ffd-indexer` resolves to this project's package.
|
|
default = pkgs.ffd-indexer;
|
|
};
|
|
|
|
schedule = lib.mkOption {
|
|
description = ''
|
|
How often the indexer runs. Either one of "daily", "hourly", "weekly",
|
|
"monthly", or any systemd OnCalendar expression.
|
|
'';
|
|
type = lib.types.str;
|
|
default = "daily";
|
|
example = "daily";
|
|
};
|
|
|
|
dbPath = lib.mkOption {
|
|
description = "Path to the SQLite database holding the index.";
|
|
type = lib.types.path;
|
|
default = "/var/lib/ffd-indexer/index.db";
|
|
example = "/var/lib/ffd-indexer/index.db";
|
|
};
|
|
|
|
typesense = {
|
|
host = lib.mkOption {
|
|
description = "Typesense server URL.";
|
|
type = lib.types.str;
|
|
default = "http://localhost:8108";
|
|
example = "http://localhost:8108";
|
|
};
|
|
apiKey = lib.mkOption {
|
|
description = ''
|
|
Typesense API key. May be inlined here, or supplied via a secret file with apiKeyFile (which takes precedence)
|
|
'';
|
|
type = lib.types.str;
|
|
default = "";
|
|
};
|
|
apiKeyFile = lib.mkOption {
|
|
description = ''
|
|
Path to a file containing the Typesense API key. Takes precedence over apiKey, so the key need not appear in
|
|
the rendered config or the Nix store.
|
|
'';
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
example = "/run/secrets/typesense-api-key";
|
|
};
|
|
};
|
|
|
|
metrics = {
|
|
enable = lib.mkEnableOption "the ffd-metrics Prometheus daemon (also controls whether the metrics block is rendered into the config)";
|
|
listen = lib.mkOption {
|
|
description = "Listen address the ffd-metrics daemon binds for /metrics.";
|
|
type = lib.types.str;
|
|
default = "127.0.0.1:9090";
|
|
example = "127.0.0.1:9090";
|
|
};
|
|
};
|
|
|
|
forges = lib.mkOption {
|
|
description = ''
|
|
Forge instances to scrape, as a list of "url" -> "type" entries,
|
|
e.g. [{ url = "git.example.org"; type = "forgejo"; }]. The url is a
|
|
bare hostname (FQDN); a scheme prefix (http:// or https://) is
|
|
allowed to force the connection protocol, but the stored identity and
|
|
metrics labels always use the bare hostname.
|
|
|
|
The recommended type is a pinned "forgejo" or "gitea", which selects
|
|
the scraper deterministically and skips probing. The default "auto"
|
|
re-detects the type on every run and is EXPERIMENTAL: detection is
|
|
probed via the version API, honors robots.txt (a forge that disallows
|
|
/api/ is reported "unknown" unless hard-coded), and may misclassify
|
|
until more detectors land. Prefer setting the type by hand.
|
|
'';
|
|
type = lib.types.listOf (
|
|
lib.types.submodule {
|
|
options = {
|
|
url = lib.mkOption {
|
|
description = "Forge hostname (FQDN) to scrape, e.g. \"git.example.org\".";
|
|
type = lib.types.str;
|
|
};
|
|
type = lib.mkOption {
|
|
description = ''
|
|
Forge type. Recommended: "forgejo" or "gitea" (pin the scraper,
|
|
skip detection). Default "auto" re-detects on every scan
|
|
(EXPERIMENTAL).
|
|
'';
|
|
type = lib.types.enum [
|
|
"auto"
|
|
"forgejo"
|
|
"gitea"
|
|
];
|
|
default = "auto";
|
|
};
|
|
};
|
|
}
|
|
);
|
|
default = [ ];
|
|
example = [
|
|
{
|
|
url = "git.example.org";
|
|
type = "forgejo";
|
|
}
|
|
{
|
|
url = "git.example.net";
|
|
type = "forgejo";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# Write the rendered config and make it readable by the service user.
|
|
environment.etc."${lib.removePrefix "/etc/" configFile}" = {
|
|
source = configYAML;
|
|
user = "ffd-indexer";
|
|
group = "ffd-indexer";
|
|
mode = "0400";
|
|
};
|
|
|
|
users.users.ffd-indexer = {
|
|
description = "Federated Forge Discovery Indexer";
|
|
isSystemUser = true;
|
|
group = "ffd-indexer";
|
|
home = "/var/lib/ffd-indexer";
|
|
createHome = true;
|
|
};
|
|
users.groups.ffd-indexer = { };
|
|
|
|
systemd.services.ffd-indexer = {
|
|
description = "Federated Forge Discovery Indexer";
|
|
wantedBy = [ "multi-user.target" ];
|
|
wants = [ "network-online.target" ];
|
|
after = [ "network-online.target" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
ExecStart = "${pkg}/bin/ffd-indexer -config ${configFile}";
|
|
User = "ffd-indexer";
|
|
Group = "ffd-indexer";
|
|
NoNewPrivileges = true;
|
|
# The DB and Typesense data live under /var/lib/ffd-indexer.
|
|
StateDirectory = "ffd-indexer";
|
|
Restart = "on-failure";
|
|
RestartSec = 30;
|
|
};
|
|
};
|
|
|
|
# The timer drives the schedule; the service is oneshot.
|
|
systemd.timers.ffd-indexer = {
|
|
description = "Runs the Federated Forge Discovery Indexer on schedule";
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig = {
|
|
OnCalendar = cfg.schedule;
|
|
Persistent = true;
|
|
Unit = "ffd-indexer.service";
|
|
};
|
|
};
|
|
|
|
# Long-running metrics daemon: reads the same DB the indexer writes to and
|
|
# serves Prometheus metrics at the configured address. It is a separate
|
|
# service so the indexer can stay a oneshot scheduled job, and it is
|
|
# optional (disable metrics.enable to run only the indexer).
|
|
systemd.services.ffd-metrics = lib.mkIf cfg.metrics.enable {
|
|
description = "Federated Forge Discovery Indexer metrics daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
wants = [ "network-online.target" ];
|
|
after = [
|
|
"network-online.target"
|
|
"ffd-indexer.service"
|
|
];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStart = "${pkg}/bin/ffd-metrics -config ${configFile}";
|
|
User = "ffd-indexer";
|
|
Group = "ffd-indexer";
|
|
NoNewPrivileges = true;
|
|
StateDirectory = "ffd-indexer";
|
|
Restart = "on-failure";
|
|
RestartSec = 5;
|
|
};
|
|
};
|
|
};
|
|
|
|
meta.maintainers = with lib.maintainers; [ ];
|
|
}
|