]> source.dussan.org Git - jgit.git/commitdiff
TagCommand: support signing annotated tags 39/173439/3
authorThomas Wolf <thomas.wolf@paranor.ch>
Sat, 5 Dec 2020 21:01:25 +0000 (22:01 +0100)
committerThomas Wolf <thomas.wolf@paranor.ch>
Mon, 7 Dec 2020 08:33:57 +0000 (09:33 +0100)
Add the two config constants from C git that can switch on signing
of annotated tags. Add them to the GpgConfig, and implement actually
signing a tag in TagCommand.

The interactions between command line options for "git tag" and config
options is a bit murky in C git. There are two config settings for it:

* tag.gpgSign is the main option, if set to true, it kicks in if
  neither -s nor -u are given on the command line.
* tag.forceSignAnnotated signs only tags created via "git tag -m",
  but only if command-line option "-a" is not present. It applies
  even if tag.gpgSign is set explicitly to false.

Giving -s or -u on the command line also forces an annotated tag
since lightweight tags cannot be signed.

Bug: 386908
Change-Id: Ic8a1a44b5f12f47d5cdf3aae2456c1f6ca9ef057
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java

index 6112a272e463368591b4dbc7e43d482fd7068e4d..bf2455283e70e8c691d45962a4cce6aaeb105720 100644 (file)
@@ -143,6 +143,7 @@ metaVar_s3Region=REGION
 metaVar_s3StorageClass=STORAGE-CLASS
 metaVar_seconds=SECONDS
 metaVar_service=SERVICE
+metaVar_tagLocalUser=<GPG key ID>
 metaVar_treeish=tree-ish
 metaVar_uriish=uri-ish
 metaVar_url=URL
@@ -421,8 +422,12 @@ usage_sshDriver=Selects the built-in ssh library to use, JSch or Apache MINA ssh
 usage_symbolicVersionForTheProject=Symbolic version for the project
 usage_tags=fetch all tags
 usage_notags=do not fetch tags
+usage_tagAnnotated=create an annotated tag, unsigned unless -s or -u are given, or config tag.gpgSign is true
 usage_tagDelete=delete tag
-usage_tagMessage=tag message
+usage_tagLocalUser=create a signed annotated tag using the specified GPG key ID
+usage_tagMessage=create an annotated tag with the given message, unsigned unless -s or -u are given, or config tag.gpgSign is true, or tar.forceSignAnnotated is true and -a is not given
+usage_tagSign=create a signed annotated tag
+usage_tagNoSign=suppress signing the tag
 usage_untrackedFilesMode=show untracked files
 usage_updateRef=reference to update
 usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository
index b408b78f3cd354d12f924337e685e12065291b31..4cc62b339c65ef1497f8aae027c33471741c0dce 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg.lists@dewire.com>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -40,8 +40,24 @@ class Tag extends TextBuiltin {
        @Option(name = "-d", usage = "usage_tagDelete")
        private boolean delete;
 
+       @Option(name = "--annotate", aliases = {
+                       "-a" }, usage = "usage_tagAnnotated")
+       private boolean annotated;
+
        @Option(name = "-m", metaVar = "metaVar_message", usage = "usage_tagMessage")
-       private String message = ""; //$NON-NLS-1$
+       private String message;
+
+       @Option(name = "--sign", aliases = { "-s" }, forbids = {
+                       "--no-sign" }, usage = "usage_tagSign")
+       private boolean sign;
+
+       @Option(name = "--no-sign", usage = "usage_tagNoSign", forbids = {
+                       "--sign" })
+       private boolean noSign;
+
+       @Option(name = "--local-user", aliases = {
+                       "-u" }, metaVar = "metaVar_tagLocalUser", usage = "usage_tagLocalUser")
+       private String gpgKeyId;
 
        @Argument(index = 0, metaVar = "metaVar_name")
        private String tagName;
@@ -70,6 +86,18 @@ class Tag extends TextBuiltin {
                                                        command.setObjectId(walk.parseAny(object));
                                                }
                                        }
+                                       if (noSign) {
+                                               command.setSigned(false);
+                                       } else if (sign) {
+                                               command.setSigned(true);
+                                       }
+                                       if (annotated) {
+                                               command.setAnnotated(true);
+                                       } else if (message == null && !sign && gpgKeyId == null) {
+                                               // None of -a, -m, -s, -u given
+                                               command.setAnnotated(false);
+                                       }
+                                       command.setSigningKey(gpgKeyId);
                                        try {
                                                command.call();
                                        } catch (RefAlreadyExistsException e) {
index b1c54b9effb5a0ccfb14ae53c06b4b09493017fa..9630474b87ce72fbc25aee34d724bb853b004f80 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2020 Chris Aniszczyk <caniszczyk@gmail.com> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -11,6 +11,8 @@ package org.eclipse.jgit.api;
 
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
@@ -28,6 +30,59 @@ import org.junit.Test;
 
 public class TagCommandTest extends RepositoryTestCase {
 
+       @Test
+       public void testTagKind() {
+               try (Git git = new Git(db)) {
+                       assertTrue(git.tag().isAnnotated());
+                       assertTrue(git.tag().setSigned(true).isAnnotated());
+                       assertTrue(git.tag().setSigned(false).isAnnotated());
+                       assertTrue(git.tag().setSigningKey(null).isAnnotated());
+                       assertTrue(git.tag().setSigningKey("something").isAnnotated());
+                       assertTrue(git.tag().setSigned(false).setSigningKey(null)
+                                       .isAnnotated());
+                       assertTrue(git.tag().setSigned(false).setSigningKey("something")
+                                       .isAnnotated());
+                       assertTrue(git.tag().setSigned(true).setSigningKey(null)
+                                       .isAnnotated());
+                       assertTrue(git.tag().setSigned(true).setSigningKey("something")
+                                       .isAnnotated());
+                       assertTrue(git.tag().setAnnotated(true).isAnnotated());
+                       assertTrue(
+                                       git.tag().setAnnotated(true).setSigned(true).isAnnotated());
+                       assertTrue(git.tag().setAnnotated(true).setSigned(false)
+                                       .isAnnotated());
+                       assertTrue(git.tag().setAnnotated(true).setSigningKey(null)
+                                       .isAnnotated());
+                       assertTrue(git.tag().setAnnotated(true).setSigningKey("something")
+                                       .isAnnotated());
+                       assertTrue(git.tag().setAnnotated(true).setSigned(false)
+                                       .setSigningKey(null).isAnnotated());
+                       assertTrue(git.tag().setAnnotated(true).setSigned(false)
+                                       .setSigningKey("something").isAnnotated());
+                       assertTrue(git.tag().setAnnotated(true).setSigned(true)
+                                       .setSigningKey(null).isAnnotated());
+                       assertTrue(git.tag().setAnnotated(true).setSigned(true)
+                                       .setSigningKey("something").isAnnotated());
+                       assertFalse(git.tag().setAnnotated(false).isAnnotated());
+                       assertTrue(git.tag().setAnnotated(false).setSigned(true)
+                                       .isAnnotated());
+                       assertFalse(git.tag().setAnnotated(false).setSigned(false)
+                                       .isAnnotated());
+                       assertFalse(git.tag().setAnnotated(false).setSigningKey(null)
+                                       .isAnnotated());
+                       assertTrue(git.tag().setAnnotated(false).setSigningKey("something")
+                                       .isAnnotated());
+                       assertFalse(git.tag().setAnnotated(false).setSigned(false)
+                                       .setSigningKey(null).isAnnotated());
+                       assertTrue(git.tag().setAnnotated(false).setSigned(false)
+                                       .setSigningKey("something").isAnnotated());
+                       assertTrue(git.tag().setAnnotated(false).setSigned(true)
+                                       .setSigningKey(null).isAnnotated());
+                       assertTrue(git.tag().setAnnotated(false).setSigned(true)
+                                       .setSigningKey("something").isAnnotated());
+               }
+       }
+
        @Test
        public void testTaggingOnHead() throws GitAPIException, IOException {
                try (Git git = new Git(db);
@@ -93,19 +148,6 @@ public class TagCommandTest extends RepositoryTestCase {
                }
        }
 
-       @Test
-       public void testFailureOnSignedTags() throws GitAPIException {
-               try (Git git = new Git(db)) {
-                       git.commit().setMessage("initial commit").call();
-                       try {
-                               git.tag().setSigned(true).setName("tag").call();
-                               fail("We should have failed with an UnsupportedOperationException due to signed tag");
-                       } catch (UnsupportedOperationException e) {
-                               // should hit here
-                       }
-               }
-       }
-
        private List<Ref> getTags() throws Exception {
                return db.getRefDatabase().getRefsByPrefix(R_TAGS);
        }
index 6d15464d5a5d1a64d439ef88661efc97758ab117..2b5f929dd528e913a6bf68c5abcea19b223567f0 100644 (file)
@@ -618,7 +618,6 @@ shortReadOfBlock=Short read of block.
 shortReadOfOptionalDIRCExtensionExpectedAnotherBytes=Short read of optional DIRC extension {0}; expected another {1} bytes within the section.
 shortSkipOfBlock=Short skip of block.
 signedTagMessageNoLf=A non-empty message of a signed tag must end in LF.
-signingNotSupportedOnTag=Signing isn't supported on tag operations yet.
 signingServiceUnavailable=Signing service is not available
 similarityScoreMustBeWithinBounds=Similarity score must be between 0 and 100.
 skipMustBeNonNegative=skip must be >= 0
index 9a328a6eaa1692e118f98b46a30b8edd529d9d9d..c8d4e413fb4a3c052717cf596b68e60d08fc7b4f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2020 Chris Aniszczyk <caniszczyk@gmail.com> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -18,8 +18,13 @@ import org.eclipse.jgit.api.errors.InvalidTagNameException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.api.errors.NoHeadException;
 import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
+import org.eclipse.jgit.api.errors.ServiceUnavailableException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgObjectSigner;
+import org.eclipse.jgit.lib.GpgSigner;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.PersonIdent;
@@ -29,8 +34,10 @@ import org.eclipse.jgit.lib.RefUpdate.Result;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryState;
 import org.eclipse.jgit.lib.TagBuilder;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.CredentialsProvider;
 
 /**
  * Create/update an annotated tag object or a simple unannotated tag
@@ -56,6 +63,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
  *      >Git documentation about Tag</a>
  */
 public class TagCommand extends GitCommand<Ref> {
+
        private RevObject id;
 
        private String name;
@@ -64,11 +72,17 @@ public class TagCommand extends GitCommand<Ref> {
 
        private PersonIdent tagger;
 
-       private boolean signed;
+       private Boolean signed;
 
        private boolean forceUpdate;
 
-       private boolean annotated = true;
+       private Boolean annotated;
+
+       private String signingKey;
+
+       private GpgObjectSigner gpgSigner;
+
+       private CredentialsProvider credentialsProvider;
 
        /**
         * <p>Constructor for TagCommand.</p>
@@ -77,6 +91,7 @@ public class TagCommand extends GitCommand<Ref> {
         */
        protected TagCommand(Repository repo) {
                super(repo);
+               this.credentialsProvider = CredentialsProvider.getDefault();
        }
 
        /**
@@ -108,10 +123,7 @@ public class TagCommand extends GitCommand<Ref> {
                                id = revWalk.parseCommit(objectId);
                        }
 
-                       if (!annotated) {
-                               if (message != null || tagger != null)
-                                       throw new JGitInternalException(
-                                                       JGitText.get().messageAndTaggerNotAllowedInUnannotatedTags);
+                       if (!isAnnotated()) {
                                return updateTagRef(id, revWalk, name,
                                                "SimpleTag[" + name + " : " + id //$NON-NLS-1$ //$NON-NLS-2$
                                                                + "]"); //$NON-NLS-1$
@@ -124,6 +136,11 @@ public class TagCommand extends GitCommand<Ref> {
                        newTag.setTagger(tagger);
                        newTag.setObjectId(id);
 
+                       if (gpgSigner != null) {
+                               gpgSigner.signObject(newTag, signingKey, tagger,
+                                               credentialsProvider);
+                       }
+
                        // write the tag object
                        try (ObjectInserter inserter = repo.newObjectInserter()) {
                                ObjectId tagId = inserter.insert(newTag);
@@ -177,20 +194,60 @@ public class TagCommand extends GitCommand<Ref> {
         *
         * @throws InvalidTagNameException
         *             if the tag name is null or invalid
-        * @throws UnsupportedOperationException
-        *             if the tag is signed (not supported yet)
+        * @throws ServiceUnavailableException
+        *             if the tag should be signed but no signer can be found
+        * @throws UnsupportedSigningFormatException
+        *             if the tag should be signed but {@code gpg.format} is not
+        *             {@link GpgFormat#OPENPGP}
         */
        private void processOptions(RepositoryState state)
-                       throws InvalidTagNameException {
-               if (tagger == null && annotated)
-                       tagger = new PersonIdent(repo);
-               if (name == null || !Repository.isValidRefName(Constants.R_TAGS + name))
+                       throws InvalidTagNameException, ServiceUnavailableException,
+                       UnsupportedSigningFormatException {
+               if (name == null
+                               || !Repository.isValidRefName(Constants.R_TAGS + name)) {
                        throw new InvalidTagNameException(
                                        MessageFormat.format(JGitText.get().tagNameInvalid,
                                                        name == null ? "<null>" : name)); //$NON-NLS-1$
-               if (signed)
-                       throw new UnsupportedOperationException(
-                                       JGitText.get().signingNotSupportedOnTag);
+               }
+               if (!isAnnotated()) {
+                       if ((message != null && !message.isEmpty()) || tagger != null) {
+                               throw new JGitInternalException(JGitText
+                                               .get().messageAndTaggerNotAllowedInUnannotatedTags);
+                       }
+               } else {
+                       if (tagger == null) {
+                               tagger = new PersonIdent(repo);
+                       }
+                       // Figure out whether to sign.
+                       if (!(Boolean.FALSE.equals(signed) && signingKey == null)) {
+                               GpgConfig gpgConfig = new GpgConfig(repo.getConfig());
+                               boolean doSign = isSigned() || gpgConfig.isSignAllTags();
+                               if (!Boolean.TRUE.equals(annotated) && !doSign) {
+                                       doSign = gpgConfig.isSignAnnotated();
+                               }
+                               if (doSign) {
+                                       if (signingKey == null) {
+                                               signingKey = gpgConfig.getSigningKey();
+                                       }
+                                       if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
+                                               throw new UnsupportedSigningFormatException(
+                                                               JGitText.get().onlyOpenPgpSupportedForSigning);
+                                       }
+                                       GpgSigner signer = GpgSigner.getDefault();
+                                       if (!(signer instanceof GpgObjectSigner)) {
+                                               throw new ServiceUnavailableException(
+                                                               JGitText.get().signingServiceUnavailable);
+                                       }
+                                       gpgSigner = (GpgObjectSigner) signer;
+                                       // The message of a signed tag must end in a newline because
+                                       // the signature will be appended.
+                                       if (message != null && !message.isEmpty()
+                                                       && !message.endsWith("\n")) { //$NON-NLS-1$
+                                               message += '\n';
+                                       }
+                               }
+                       }
+               }
        }
 
        /**
@@ -238,24 +295,31 @@ public class TagCommand extends GitCommand<Ref> {
        }
 
        /**
-        * Whether this tag is signed
+        * Whether {@link #setSigned(boolean) setSigned(true)} has been called or
+        * whether a {@link #setSigningKey(String) signing key ID} has been set;
+        * i.e., whether -s or -u was specified explicitly.
         *
         * @return whether the tag is signed
         */
        public boolean isSigned() {
-               return signed;
+               return Boolean.TRUE.equals(signed) || signingKey != null;
        }
 
        /**
         * If set to true the Tag command creates a signed tag object. This
-        * corresponds to the parameter -s on the command line.
+        * corresponds to the parameter -s (--sign or --no-sign) on the command
+        * line.
+        * <p>
+        * If {@code true}, the tag will be a signed annotated tag.
+        * </p>
         *
         * @param signed
-        *            a boolean.
+        *            whether to sign
         * @return {@code this}
         */
        public TagCommand setSigned(boolean signed) {
-               this.signed = signed;
+               checkCallable();
+               this.signed = Boolean.valueOf(signed);
                return this;
        }
 
@@ -268,6 +332,7 @@ public class TagCommand extends GitCommand<Ref> {
         * @return {@code this}
         */
        public TagCommand setTagger(PersonIdent tagger) {
+               checkCallable();
                this.tagger = tagger;
                return this;
        }
@@ -291,14 +356,15 @@ public class TagCommand extends GitCommand<Ref> {
        }
 
        /**
-        * Sets the object id of the tag. If the object id is null, the commit
-        * pointed to from HEAD will be used.
+        * Sets the object id of the tag. If the object id is {@code null}, the
+        * commit pointed to from HEAD will be used.
         *
         * @param id
         *            a {@link org.eclipse.jgit.revwalk.RevObject} object.
         * @return {@code this}
         */
        public TagCommand setObjectId(RevObject id) {
+               checkCallable();
                this.id = id;
                return this;
        }
@@ -321,6 +387,7 @@ public class TagCommand extends GitCommand<Ref> {
         * @return {@code this}
         */
        public TagCommand setForceUpdate(boolean forceUpdate) {
+               checkCallable();
                this.forceUpdate = forceUpdate;
                return this;
        }
@@ -334,18 +401,77 @@ public class TagCommand extends GitCommand<Ref> {
         * @since 3.0
         */
        public TagCommand setAnnotated(boolean annotated) {
-               this.annotated = annotated;
+               checkCallable();
+               this.annotated = Boolean.valueOf(annotated);
                return this;
        }
 
        /**
-        * Whether this will create an annotated command
+        * Whether this will create an annotated tag.
         *
         * @return true if this command will create an annotated tag (default is
         *         true)
         * @since 3.0
         */
        public boolean isAnnotated() {
-               return annotated;
+               boolean setExplicitly = Boolean.TRUE.equals(annotated) || isSigned();
+               if (setExplicitly) {
+                       return true;
+               }
+               // Annotated at default (not set explicitly)
+               return annotated == null;
        }
+
+       /**
+        * Sets the signing key.
+        * <p>
+        * Per spec of {@code user.signingKey}: this will be sent to the GPG program
+        * as is, i.e. can be anything supported by the GPG program.
+        * </p>
+        * <p>
+        * Note, if none was set or {@code null} is specified a default will be
+        * obtained from the configuration.
+        * </p>
+        * <p>
+        * If set to a non-{@code null} value, the tag will be a signed annotated
+        * tag.
+        * </p>
+        *
+        * @param signingKey
+        *            signing key; {@code null} allowed
+        * @return {@code this}
+        * @since 5.11
+        */
+       public TagCommand setSigningKey(String signingKey) {
+               checkCallable();
+               this.signingKey = signingKey;
+               return this;
+       }
+
+       /**
+        * Retrieves the signing key ID.
+        *
+        * @return the key ID set, or {@code null} if none is set
+        * @since 5.11
+        */
+       public String getSigningKey() {
+               return signingKey;
+       }
+
+       /**
+        * Sets a {@link CredentialsProvider}
+        *
+        * @param credentialsProvider
+        *            the provider to use when querying for credentials (eg., during
+        *            signing)
+        * @return {@code this}
+        * @since 5.11
+        */
+       public TagCommand setCredentialsProvider(
+                       CredentialsProvider credentialsProvider) {
+               checkCallable();
+               this.credentialsProvider = credentialsProvider;
+               return this;
+       }
+
 }
index a7daed1318624d72314a4c6fc3adc9c01ab80f90..154f32c25fda85ef57daa87ac8fdcf60eb3a9161 100644 (file)
@@ -646,7 +646,6 @@ public class JGitText extends TranslationBundle {
        /***/ public String shortReadOfOptionalDIRCExtensionExpectedAnotherBytes;
        /***/ public String shortSkipOfBlock;
        /***/ public String signedTagMessageNoLf;
-       /***/ public String signingNotSupportedOnTag;
        /***/ public String signingServiceUnavailable;
        /***/ public String similarityScoreMustBeWithinBounds;
        /***/ public String skipMustBeNonNegative;
index 834fff5dd93afa4a3869184d5e34d160bc068795..2587947c3f5245081b009644b6d09c4d8fa36a74 100644 (file)
@@ -116,14 +116,30 @@ public final class ConfigConstants {
         */
        public static final String CONFIG_COMMIT_SECTION = "commit";
 
+       /**
+        * The "tag" section
+        *
+        * @since 5.11
+        */
+       public static final String CONFIG_TAG_SECTION = "tag";
+
        /**
         * The "gpgSign" key
+        *
         * @since 5.2
         */
        public static final String CONFIG_KEY_GPGSIGN = "gpgSign";
 
+       /**
+        * The "forceSignAnnotated" key
+        *
+        * @since 5.11
+        */
+       public static final String CONFIG_KEY_FORCE_SIGN_ANNOTATED = "forceSignAnnotated";
+
        /**
         * The "hooksPath" key.
+        *
         * @since 5.6
         */
        public static final String CONFIG_KEY_HOOKS_PATH = "hooksPath";
index c1527bc47be887b9b5149f86909a1e3708a43e81..5b43729739de6dab3d7002d514c739ef76c7ca36 100644 (file)
@@ -85,4 +85,29 @@ public class GpgConfig {
                return config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION,
                                ConfigConstants.CONFIG_KEY_GPGSIGN, false);
        }
+
+       /**
+        * Retrieves the value of git config {@code tag.gpgSign}.
+        *
+        * @return the value of {@code tag.gpgSign}; by default {@code false}
+        *
+        * @since 5.11
+        */
+       public boolean isSignAllTags() {
+               return config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
+                               ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+       }
+
+       /**
+        * Retrieves the value of git config {@code tag.forceSignAnnotated}.
+        *
+        * @return the value of {@code tag.forceSignAnnotated}; by default
+        *         {@code false}
+        *
+        * @since 5.11
+        */
+       public boolean isSignAnnotated() {
+               return config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
+                                               ConfigConstants.CONFIG_KEY_FORCE_SIGN_ANNOTATED, false);
+       }
 }