mirror of
https://github.com/nix-community/home-manager.git
synced 2025-11-08 19:46:05 +01:00
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:
parent
5031c6d297
commit
7da01bc47a
19 changed files with 201 additions and 45 deletions
|
|
@ -1653,6 +1653,23 @@ in {
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
time = "2024-05-21T20:22:57+00:00";
|
||||||
|
condition = config.programs.git.signing != { };
|
||||||
|
message = ''
|
||||||
|
The Git module now supports signing via SSH and X.509 keys, in addition to OpenPGP/GnuPG,
|
||||||
|
via the `programs.git.signing.format` option.
|
||||||
|
|
||||||
|
The format defaults to `openpgp` for now, due to backwards compatibility reasons — this is
|
||||||
|
not guaranteed to last! GPG users should manually set `programs.git.signing.format` to
|
||||||
|
`openpgp` as soon as possible.
|
||||||
|
|
||||||
|
Accordingly, `programs.git.signing.gpgPath` has been renamed to the more generic option
|
||||||
|
`programs.git.signing.signer` as not everyone uses GPG.
|
||||||
|
Please migrate to the new option to suppress the generated warning.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
time = "2024-05-25T14:36:03+00:00";
|
time = "2024-05-25T14:36:03+00:00";
|
||||||
message = ''
|
message = ''
|
||||||
|
|
|
||||||
|
|
@ -14,34 +14,6 @@ let
|
||||||
supersectionType = attrsOf (either multipleType sectionType);
|
supersectionType = attrsOf (either multipleType sectionType);
|
||||||
in attrsOf supersectionType;
|
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 commit’s 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, ... }: {
|
includeModule = types.submodule ({ config, ... }: {
|
||||||
options = {
|
options = {
|
||||||
condition = mkOption {
|
condition = mkOption {
|
||||||
|
|
@ -133,10 +105,40 @@ in {
|
||||||
description = "Git aliases to define.";
|
description = "Git aliases to define.";
|
||||||
};
|
};
|
||||||
|
|
||||||
signing = mkOption {
|
signing = {
|
||||||
type = types.nullOr signModule;
|
key = mkOption {
|
||||||
default = null;
|
type = types.nullOr types.str;
|
||||||
description = "Options related to signing commits using GnuPG.";
|
default = null;
|
||||||
|
description = ''
|
||||||
|
The default signing key fingerprint.
|
||||||
|
|
||||||
|
Set to `null` to let the signer decide what signing key
|
||||||
|
to use depending on commit’s 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 {
|
extraConfig = mkOption {
|
||||||
|
|
@ -409,9 +411,19 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
imports = [
|
||||||
|
(mkRenamedOptionModule [ "programs" "git" "signing" "gpgPath" ] [
|
||||||
|
"programs"
|
||||||
|
"git"
|
||||||
|
"signing"
|
||||||
|
"signer"
|
||||||
|
])
|
||||||
|
];
|
||||||
|
|
||||||
config = mkIf cfg.enable (mkMerge [
|
config = mkIf cfg.enable (mkMerge [
|
||||||
{
|
{
|
||||||
home.packages = [ cfg.package ];
|
home.packages = [ cfg.package ];
|
||||||
|
|
||||||
assertions = [{
|
assertions = [{
|
||||||
assertion = let
|
assertion = let
|
||||||
enabled = [
|
enabled = [
|
||||||
|
|
@ -475,12 +487,28 @@ in {
|
||||||
(filterAttrs hasSmtp config.accounts.email.accounts);
|
(filterAttrs hasSmtp config.accounts.email.accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
(mkIf (cfg.signing != null) {
|
(mkIf (cfg.signing != { }) {
|
||||||
programs.git.iniContent = {
|
programs.git = {
|
||||||
user.signingKey = mkIf (cfg.signing.key != null) cfg.signing.key;
|
signing = {
|
||||||
commit.gpgSign = mkDefault cfg.signing.signByDefault;
|
format = mkIf (versionOlder config.home.stateVersion "24.11")
|
||||||
tag.gpgSign = mkDefault cfg.signing.signByDefault;
|
(mkOptionDefault "openpgp");
|
||||||
gpg.program = cfg.signing.gpgPath;
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,17 @@
|
||||||
|
[commit]
|
||||||
|
gpgSign = false
|
||||||
|
|
||||||
[credential "https://github.com"]
|
[credential "https://github.com"]
|
||||||
helper = "@gh@/bin/gh auth git-credential"
|
helper = "@gh@/bin/gh auth git-credential"
|
||||||
|
|
||||||
[credential "https://github.example.com"]
|
[credential "https://github.example.com"]
|
||||||
helper = "@gh@/bin/gh auth git-credential"
|
helper = "@gh@/bin/gh auth git-credential"
|
||||||
|
|
||||||
|
[gpg]
|
||||||
|
format = "openpgp"
|
||||||
|
|
||||||
|
[gpg "openpgp"]
|
||||||
|
program = "path-to-gpg"
|
||||||
|
|
||||||
|
[tag]
|
||||||
|
gpgSign = false
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.git.enable = true;
|
programs.git = {
|
||||||
|
enable = true;
|
||||||
|
signing.signer = "path-to-gpg";
|
||||||
|
};
|
||||||
|
|
||||||
nmt.script = ''
|
nmt.script = ''
|
||||||
assertFileExists home-files/.config/git/config
|
assertFileExists home-files/.config/git/config
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
git-with-most-options = ./git.nix;
|
git-with-most-options = ./git.nix;
|
||||||
git-with-msmtp = ./git-with-msmtp.nix;
|
git-with-msmtp = ./git-with-msmtp.nix;
|
||||||
git-with-str-extra-config = ./git-with-str-extra-config.nix;
|
git-with-str-extra-config = ./git-with-str-extra-config.nix;
|
||||||
|
git-with-signing-key-id-legacy = ./git-with-signing-key-id-legacy.nix;
|
||||||
git-with-signing-key-id = ./git-with-signing-key-id.nix;
|
git-with-signing-key-id = ./git-with-signing-key-id.nix;
|
||||||
git-without-signing-key-id = ./git-without-signing-key-id.nix;
|
git-without-signing-key-id = ./git-without-signing-key-id.nix;
|
||||||
git-with-hooks = ./git-with-hooks.nix;
|
git-with-hooks = ./git-with-hooks.nix;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,9 @@
|
||||||
smudge = "git-lfs smudge -- %f"
|
smudge = "git-lfs smudge -- %f"
|
||||||
|
|
||||||
[gpg]
|
[gpg]
|
||||||
|
format = "openpgp"
|
||||||
|
|
||||||
|
[gpg "openpgp"]
|
||||||
program = "path-to-gpg"
|
program = "path-to-gpg"
|
||||||
|
|
||||||
[interactive]
|
[interactive]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,12 @@
|
||||||
|
[commit]
|
||||||
|
gpgSign = false
|
||||||
|
|
||||||
|
[gpg]
|
||||||
|
format = "openpgp"
|
||||||
|
|
||||||
|
[gpg "openpgp"]
|
||||||
|
program = "path-to-gpg"
|
||||||
|
|
||||||
[sendemail "hm-account"]
|
[sendemail "hm-account"]
|
||||||
from = "H. M. Test Jr. <hm@example.org>"
|
from = "H. M. Test Jr. <hm@example.org>"
|
||||||
smtpEncryption = "tls"
|
smtpEncryption = "tls"
|
||||||
|
|
@ -12,6 +21,9 @@
|
||||||
smtpSslCertPath = "/etc/ssl/certs/ca-certificates.crt"
|
smtpSslCertPath = "/etc/ssl/certs/ca-certificates.crt"
|
||||||
smtpUser = "home.manager"
|
smtpUser = "home.manager"
|
||||||
|
|
||||||
|
[tag]
|
||||||
|
gpgSign = false
|
||||||
|
|
||||||
[user]
|
[user]
|
||||||
email = "hm@example.com"
|
email = "hm@example.com"
|
||||||
name = "H. M. Test"
|
name = "H. M. Test"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
programs.git = {
|
programs.git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
signing.signer = "path-to-gpg";
|
||||||
userEmail = "hm@example.com";
|
userEmail = "hm@example.com";
|
||||||
userName = "H. M. Test";
|
userName = "H. M. Test";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,12 @@
|
||||||
|
[commit]
|
||||||
|
gpgSign = false
|
||||||
|
|
||||||
|
[gpg]
|
||||||
|
format = "openpgp"
|
||||||
|
|
||||||
|
[gpg "openpgp"]
|
||||||
|
program = "path-to-gpg"
|
||||||
|
|
||||||
[sendemail "hm-account"]
|
[sendemail "hm-account"]
|
||||||
from = "H. M. Test Jr. <hm@example.org>"
|
from = "H. M. Test Jr. <hm@example.org>"
|
||||||
smtpEncryption = "tls"
|
smtpEncryption = "tls"
|
||||||
|
|
@ -10,6 +19,9 @@
|
||||||
from = "H. M. Test <hm@example.com>"
|
from = "H. M. Test <hm@example.com>"
|
||||||
smtpServer = "@msmtp@/bin/msmtp"
|
smtpServer = "@msmtp@/bin/msmtp"
|
||||||
|
|
||||||
|
[tag]
|
||||||
|
gpgSign = false
|
||||||
|
|
||||||
[user]
|
[user]
|
||||||
email = "hm@example.com"
|
email = "hm@example.com"
|
||||||
name = "H. M. Test"
|
name = "H. M. Test"
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
programs.git = {
|
programs.git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
signing.signer = "path-to-gpg";
|
||||||
userEmail = "hm@example.com";
|
userEmail = "hm@example.com";
|
||||||
userName = "H. M. Test";
|
userName = "H. M. Test";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
gpgSign = true
|
gpgSign = true
|
||||||
|
|
||||||
[gpg]
|
[gpg]
|
||||||
program = "path-to-gpg"
|
format = "ssh"
|
||||||
|
|
||||||
|
[gpg "ssh"]
|
||||||
|
program = "path-to-ssh"
|
||||||
|
|
||||||
[tag]
|
[tag]
|
||||||
gpgSign = true
|
gpgSign = true
|
||||||
|
|
@ -10,4 +13,4 @@
|
||||||
[user]
|
[user]
|
||||||
email = "user@example.org"
|
email = "user@example.org"
|
||||||
name = "John Doe"
|
name = "John Doe"
|
||||||
signingKey = "00112233445566778899AABBCCDDEEFF"
|
signingKey = "ssh-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
[commit]
|
||||||
|
gpgSign = true
|
||||||
|
|
||||||
|
[gpg]
|
||||||
|
format = "openpgp"
|
||||||
|
|
||||||
|
[gpg "openpgp"]
|
||||||
|
program = "path-to-gpg"
|
||||||
|
|
||||||
|
[tag]
|
||||||
|
gpgSign = true
|
||||||
|
|
||||||
|
[user]
|
||||||
|
email = "user@example.org"
|
||||||
|
name = "John Doe"
|
||||||
|
signingKey = "00112233445566778899AABBCCDDEEFF"
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
{ lib, options, ... }: {
|
||||||
|
config = {
|
||||||
|
programs.git = {
|
||||||
|
enable = true;
|
||||||
|
userName = "John Doe";
|
||||||
|
userEmail = "user@example.org";
|
||||||
|
|
||||||
|
signing = {
|
||||||
|
gpgPath = "path-to-gpg";
|
||||||
|
key = "00112233445566778899AABBCCDDEEFF";
|
||||||
|
signByDefault = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
test.asserts.warnings.expected = [
|
||||||
|
"The option `programs.git.signing.gpgPath' defined in ${
|
||||||
|
lib.showFiles options.programs.git.signing.gpgPath.files
|
||||||
|
} has been renamed to `programs.git.signing.signer'."
|
||||||
|
];
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/git/config
|
||||||
|
assertFileContent home-files/.config/git/config ${
|
||||||
|
./git-with-signing-key-id-legacy-expected.conf
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,10 @@
|
||||||
userEmail = "user@example.org";
|
userEmail = "user@example.org";
|
||||||
|
|
||||||
signing = {
|
signing = {
|
||||||
gpgPath = "path-to-gpg";
|
signer = "path-to-ssh";
|
||||||
key = "00112233445566778899AABBCCDDEEFF";
|
format = "ssh";
|
||||||
|
key =
|
||||||
|
"ssh-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
signByDefault = true;
|
signByDefault = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,17 @@
|
||||||
This can be anything.
|
This can be anything.
|
||||||
|
|
||||||
|
[commit]
|
||||||
|
gpgSign = false
|
||||||
|
|
||||||
|
[gpg]
|
||||||
|
format = "openpgp"
|
||||||
|
|
||||||
|
[gpg "openpgp"]
|
||||||
|
program = "path-to-gpg"
|
||||||
|
|
||||||
|
[tag]
|
||||||
|
gpgSign = false
|
||||||
|
|
||||||
[user]
|
[user]
|
||||||
email = "user@example.org"
|
email = "user@example.org"
|
||||||
name = "John Doe"
|
name = "John Doe"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
programs.git = {
|
programs.git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
signing.signer = "path-to-gpg";
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
This can be anything.
|
This can be anything.
|
||||||
'';
|
'';
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
gpgSign = true
|
gpgSign = true
|
||||||
|
|
||||||
[gpg]
|
[gpg]
|
||||||
|
format = "openpgp"
|
||||||
|
|
||||||
|
[gpg "openpgp"]
|
||||||
program = "path-to-gpg"
|
program = "path-to-gpg"
|
||||||
|
|
||||||
[tag]
|
[tag]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
userEmail = "user@example.org";
|
userEmail = "user@example.org";
|
||||||
|
|
||||||
signing = {
|
signing = {
|
||||||
gpgPath = "path-to-gpg";
|
signer = "path-to-gpg";
|
||||||
key = null;
|
key = null;
|
||||||
signByDefault = true;
|
signByDefault = true;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ in {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
signing = {
|
signing = {
|
||||||
gpgPath = "path-to-gpg";
|
signer = "path-to-gpg";
|
||||||
|
format = "openpgp";
|
||||||
key = "00112233445566778899AABBCCDDEEFF";
|
key = "00112233445566778899AABBCCDDEEFF";
|
||||||
signByDefault = true;
|
signByDefault = true;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue