git: support alternate signing methods (#5516)

The Git module now supports SSH and X.509 signing in addition to
OpenPGP/GnuPG, via setting the `programs.git.signing.format` option.
It defaults to `openpgp` for now as a backwards compatibility measure,
but I feel like we shouldn't enforce GPG as the default on everyone,
especially for people who use SSH signing like me.

Accordingly, `programs.git.signing.gpgPath` has been renamed to
`programs.git.signing.signer`, as now the signer binary is not
restricted to GnuPG. Users should only get a warning and everything
should continue to work.

Fixes #4221, supersedes #4235

Co-authored-by: Mario Rodas <marsam@users.noreply.github.com>
Co-authored-by: Sumner Evans <me@sumnerevans.com>
Co-authored-by: Leah Amelia Chen <hi@pluie.me>
This commit is contained in:
Sizhe Zhao
2025-02-15 02:47:27 +08:00
committed by GitHub
parent 5031c6d297
commit 7da01bc47a
19 changed files with 201 additions and 45 deletions

View File

@@ -14,34 +14,6 @@ let
supersectionType = attrsOf (either multipleType sectionType);
in attrsOf supersectionType;
signModule = types.submodule {
options = {
key = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The default GPG signing key fingerprint.
Set to `null` to let GnuPG decide what signing key
to use depending on commits author.
'';
};
signByDefault = mkOption {
type = types.bool;
default = false;
description = "Whether commits and tags should be signed by default.";
};
gpgPath = mkOption {
type = types.str;
default = "${pkgs.gnupg}/bin/gpg2";
defaultText = "\${pkgs.gnupg}/bin/gpg2";
description = "Path to GnuPG binary to use.";
};
};
};
includeModule = types.submodule ({ config, ... }: {
options = {
condition = mkOption {
@@ -133,10 +105,40 @@ in {
description = "Git aliases to define.";
};
signing = mkOption {
type = types.nullOr signModule;
default = null;
description = "Options related to signing commits using GnuPG.";
signing = {
key = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The default signing key fingerprint.
Set to `null` to let the signer decide what signing key
to use depending on commits author.
'';
};
format = mkOption {
type = types.enum [ "openpgp" "ssh" "x509" ];
defaultText = literalExpression ''
"openpgp" for state version < 24.11,
undefined for state version 24.11
'';
description = ''
The signing method to use when signing commits and tags.
Valid values are `openpgp` (OpenPGP/GnuPG), `ssh` (SSH), and `x509` (X.509 certificates).
'';
};
signByDefault = mkOption {
type = types.bool;
default = false;
description = "Whether commits and tags should be signed by default.";
};
signer = mkOption {
type = types.str;
description = "Path to signer binary to use.";
};
};
extraConfig = mkOption {
@@ -409,9 +411,19 @@ in {
};
};
imports = [
(mkRenamedOptionModule [ "programs" "git" "signing" "gpgPath" ] [
"programs"
"git"
"signing"
"signer"
])
];
config = mkIf cfg.enable (mkMerge [
{
home.packages = [ cfg.package ];
assertions = [{
assertion = let
enabled = [
@@ -475,12 +487,28 @@ in {
(filterAttrs hasSmtp config.accounts.email.accounts);
}
(mkIf (cfg.signing != null) {
programs.git.iniContent = {
user.signingKey = mkIf (cfg.signing.key != null) cfg.signing.key;
commit.gpgSign = mkDefault cfg.signing.signByDefault;
tag.gpgSign = mkDefault cfg.signing.signByDefault;
gpg.program = cfg.signing.gpgPath;
(mkIf (cfg.signing != { }) {
programs.git = {
signing = {
format = mkIf (versionOlder config.home.stateVersion "24.11")
(mkOptionDefault "openpgp");
signer = mkIf (cfg.signing.format != null) (mkOptionDefault {
openpgp = getExe config.programs.gpg.package;
ssh = getExe' pkgs.openssh "ssh-keygen";
x509 = getExe' config.programs.gpg.package "gpgsm";
}.${cfg.signing.format});
};
iniContent = let inherit (cfg.signing) format;
in {
user.signingKey = mkIf (cfg.signing.key != null) cfg.signing.key;
commit.gpgSign = mkDefault cfg.signing.signByDefault;
tag.gpgSign = mkDefault cfg.signing.signByDefault;
gpg = {
inherit format;
${format}.program = cfg.signing.signer;
};
};
};
})