2.home-manager/modules/programs/radicle.nix
Matthias Beyer b3258dece8 programs.radicle: Document how "rad auth" must be used with this
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2026-02-13 14:34:42 -06:00

275 lines
8.2 KiB
Nix

{
config,
options,
lib,
pkgs,
...
}:
let
inherit (lib)
attrValues
filter
filterAttrs
getExe
hasSuffix
head
id
length
listToAttrs
mkDefault
mkEnableOption
mkIf
mkOption
mkPackageOption
replaceStrings
;
inherit (lib.types)
listOf
nullOr
str
submodule
;
cfg = config.programs.radicle;
opt = options.programs.radicle;
configFile = rec {
format = pkgs.formats.json { };
name = "config.json";
path = pkgs.runCommand name { nativeBuildInputs = [ cfg.cli.package ]; } ''
mkdir keys
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/this/is/not/a/real/key/only/a/placeholder" \
> keys/radicle.pub
cp ${format.generate name cfg.settings} ${name}
RAD_HOME=$PWD rad config
cp ${name} $out
'';
};
publicExplorerSuffix = "$rid$path";
in
{
meta.maintainers = with lib.maintainers; [
lorenzleutgeb
matthiasbeyer
];
options = {
programs.radicle = {
enable = mkEnableOption "Radicle";
cli.package = mkPackageOption pkgs "radicle-node" { };
uri = {
rad = {
browser = {
enable = mkOption {
description = "Whether to enable `rad:`-URI handling by web browser";
default =
(hasSuffix publicExplorerSuffix cfg.settings.publicExplorer) && pkgs.stdenv.hostPlatform.isLinux;
defaultText = "`true` if a suitable public explorer is detected.";
example = false;
};
preferredNode = mkOption {
type = str;
description = "The hostname of an instance of `radicle-node`, reachable via HTTPS.";
default = "iris.radicle.xyz";
example = "radicle-node.example.com";
};
};
vscode = {
enable = mkEnableOption "`rad:`-URI handling by VSCode";
extension = mkOption {
type = str;
description = "The unique identifier of the VSCode extension that should handle `rad:`-URIs.";
default = "radicle-ide-plugins-team.radicle";
};
};
};
web-rad =
let
detected =
let
detectionList = attrValues (
filterAttrs (n: _: config.programs.${n}.enable) {
librewolf = "librewolf.desktop";
firefox = "firefox.desktop";
chromium = "chromium-browser.desktop";
}
);
in
lib.optionals (detectionList == [ ]) (head detectionList);
in
{
enable = mkEnableOption "`web+rad:`-URI handling by web browser";
browser = mkOption {
description = ''
Name of the XDG Desktop Entry for your browser.
LibreWolf, Firefox and Chromium configured via home-manager will
be detected automatically. The value of this option should likely
be the same as the output of
`xdg-mime query default x-scheme-handler/https`.
'';
type = nullOr str;
default = detected;
defaultText = "Automatically detected browser.";
example = "brave.desktop";
};
};
};
settings = mkOption {
default = { };
description = "Radicle configuration, written to `~/.radicle/config.json.";
type = submodule {
freeformType = configFile.format.type;
options = {
publicExplorer = mkOption {
type = str;
description = "HTTPS URL pattern used to generate links to view content on Radicle via the browser.";
default = "https://app.radicle.xyz/nodes/$host/$rid$path";
example = "https://radicle.example.com/nodes/seed.example.com/$rid$path";
};
node = {
alias = mkOption {
type = str;
description = ''
Human readable alias for your node.
When running 'rad auth' for the first time, you need to use the exact same alias,
otherwise 'rad auth' will fail because it cannot write the configuration
file in your home directory (which is generated by nix).
'';
default = config.home.username;
defaultText = lib.literalExpression "config.home.username";
};
listen = mkOption {
type = listOf str;
description = "Addresses to bind to and listen for inbound connections.";
default = [ ];
example = [ "127.0.0.1:58776" ];
};
};
};
};
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion =
!pkgs.stdenv.hostPlatform.isLinux
-> (
(filter id [
cfg.uri.rad.browser.enable
cfg.uri.rad.vscode.enable
]) == [ ]
);
message = "`rad:`-URI handlers are only supported on Linux.";
}
{
assertion = cfg.uri.web-rad.enable -> cfg.uri.web-rad.browser != null;
message = "Could not detect preferred browser. Please set `${toString opt.uri.web-rad.browser}`.";
}
{
assertion =
1 >= length (
filter id [
cfg.uri.rad.browser.enable
cfg.uri.rad.vscode.enable
]
);
message = "At most one `rad:`-URI handler may be enabled.";
}
{
assertion =
cfg.uri.rad.browser.enable -> hasSuffix publicExplorerSuffix cfg.settings.publicExplorer;
message = "${opt.uri.rad.browser.enable} is only compatible with a public explorer URL ending in '${publicExplorerSuffix}' but '${cfg.settings.publicExplorer}' does not end with '${publicExplorerSuffix}'.";
}
];
home = {
packages = [ cfg.cli.package ];
file.".radicle/${configFile.name}".source = configFile.path;
};
xdg = {
mimeApps.defaultApplications = {
"x-scheme-handler/rad" =
let
isEnabled = cfg.uri.rad.browser.enable || cfg.uri.rad.vscode.enable;
handlerTarget =
if cfg.uri.rad.browser.enable then
"rad-to-browser.desktop"
else if cfg.uri.rad.vscode.enable then
"rad-to-vscode.desktop"
else
throw "unreachable";
handler = mkDefault handlerTarget;
in
mkIf isEnabled handler;
"x-scheme-handler/web+rad" = mkIf cfg.uri.web-rad.enable (mkDefault cfg.uri.web-rad.browser);
};
desktopEntries =
let
mkHandler =
{
name,
shortName,
prefix,
}:
{
name = "Open Radicle URIs with ${name}";
genericName = "Code Forge";
categories = [
"Development"
"RevisionControl"
];
exec = getExe (
pkgs.writeShellApplication {
name = "rad-to-${shortName}";
meta.mainProgram = "rad-to-${shortName}";
text = ''xdg-open "${prefix}$1"'';
}
);
mimeType = [ "x-scheme-handler/rad" ];
noDisplay = true;
};
toHandler = v: {
name = "rad-to-${v.shortName}";
value = mkIf cfg.uri.rad.${v.shortName}.enable (mkHandler v);
};
in
listToAttrs (
map toHandler [
{
name = "Web Browser";
shortName = "browser";
prefix =
replaceStrings
[ "$host" publicExplorerSuffix ]
[
cfg.uri.rad.browser.preferredNode
""
]
cfg.settings.publicExplorer;
}
{
name = "VSCode";
shortName = "vscode";
prefix = "vscode://${cfg.uri.rad.vscode.extension}/";
}
]
);
};
};
}