Introduction

I’ve been using GitHub for the past couple of years, but it was not until last year that my contribution activity increased. Some PRs here and some others here and there.

I have created an organization, started sponsoring, and switched from HTTP to SSH following this useful guide.

But yesterday a user’s commit Verified label caught my attention, so I decided to grab a cup of coffee and learn about managing commit signature verification, and after following all the necessary steps I was able to generate my GPG key and upload it to GitHub, but there was an issue, which is the main topic of this post.

The problem

At the Signing commits step I was about to sign my first commit but then tragedy struck.

$ git commit -S -m "First signed commit"
error: gpg failed to sign the data
fatal: failed to write commit object

I was not sure what was going, so I retraced my steps, and I even found some possible solutions, but none of them worked. And I even deleted my keys, and started again but then, similar to this user’s symptoms, I got the following:

$ gpg --full-generate-key
...
gpg: agent_genkey failed: No such file or directory
Key generation failed: No such file or directory

What is this? Let’s see what $GPG_TTY has to say:

$ echo $GPG_TTY
not a tty

What? That doesn’t make sense! – I thought to myself.

But finally, I saw the light.

The solution

I found this great guide on how to set up GPG, and some useful Homebrew formulae, courtesy of Erik Samuelsson.

But all of the guides that I found did not take into account my current setup; I am using Powerlevel10k by Roman Perepelitsa, and he had the answer to my problem.

tty command requires that stdin is attached to a terminal. When using Powerlevel10k, stdin is redirected from /dev/null when Instant Prompt is activated and until Zsh is fully initialized. This is explained in more detail in Powerlevel10k FAQ.

To solve this problem you can either move export GPG_TTY=$(tty) to the top of ~/.zshrc so that it executes before Instant Prompt is activated, or (better!) use export GPG_TTY=$TTY. The latter version will work anywhere and it’s over 1000 times faster. TTY is a special parameter set by Zsh very early during initialization. It gives you access to the terminal even when stdin might be redirected.

answered Sep 11 ‘20 at 7:28 by Roman Perepelitsa

So there it was. Instead of GPG_TTY=$(tty) somewhere in my .zshrc, all I needed was GPG_TTY=$TTY.

$ exec "$SHELL"
$ echo $GPG_TTY
/dev/ttys000

And ever since, all my commits are signed by updating my git settings:

git config --global user.signiningkey XXXXXXXXXXXXXXXX
git config --global commit.gpgsign true

And after installing pinentry-mac, I don’t have to enter my passphrase every single time.

echo 'pinentry-program /usr/local/bin/pinentry-mac' >> ~/.gnupg/gpg-agent.conf
killall gpg-agent

And done!

Hope this helps.