leiningen-semantic-release-test-clojars

A hello world project to test the leiningen-semantic-release plugin



Documentation

leiningen-semantic-release-test-clojars

CircleCI Clojars Project

Summary

A library which exists only to test the leiningen-semantic-release plugin for semantic-release.

The interesting bits are:

  • project.clj - The project configuration which includes required keys for deployment to clojars.
  • .releaserc.json - The semantic-release configuration file that specified the steps to be followed for release.
  • .circleci/config.yml - The build script that builds and deploys the library automatically using semantic-release.

The code under the src and test directories is not at all interesting.

Envrionment Variables

The environment variables that are needed for this build to work are:

  • GH_TOKEN - A Github personal access token to push updates to Github.
  • GPG_KEY - The secret key to import into gpg and sign the artifact with before uploading to clojars.
  • GPG_PASSPHRASE - The passphrase to access the key in GPG_KEY.
  • LEIN_USERNAME - The username used to upload to clojars.
  • LEIN_PASSWORD - THe password used to upload to clojars.

Signing

There were a few things that needed to be done to get the gpg signing working on the CircleCI builder with leiningen.

Exporting the signing key

If you run the following command, the secret key will be dumped to the terminal

$ gpg -a --export-secret-keys 3D061752D6187B0730E33033902AF4D92DF99864 | cat -e | sed 's/\$/\\n/g'
-----BEGIN PGP PRIVATE KEY BLOCK-----\n
\n
lQPGBF2WZIYBCADdaMf43c6I18CzYFR4cBSNhV9f089WMJwglO98tA0ErVMwjTqz\n
....
hV6eQGcvb7VabZCbAdKb/evQVN7qSsWERQ/3rhCm+Pa+E4v7a4e+1RxqTWm6y1Gq\n
yrycUQ==\n
=K9Rc\n
-----END PGP PRIVATE KEY BLOCK-----\n

Copy the entire multiline output. When you add the environment variable GPG_KEY in the CircleCI console paste the copied text as the value. It will be compressed down to a single line automatically, no need to do it yourself.

Make sure you run the command in bash, I tried it in fish and the \n characters were not printed literally and were not in the copied text. You need the \n characters in the text so the multiline text can be reconstructed by echo -e.

Disable any interactive GPG features

To avoid the Error: gpg: signing failed: Inappropriate ioctl for device error, you can add these options to gpg.conf to prevent any interactive prompts from gpg.

echo "no-tty" >> ~/.gnupg/gpg.conf
echo "batch" >> ~/.gnupg/gpg.conf

This will cause these options to be added to every gpg command (even the ones run by leiningen).

It is suggested in a lot of places that GPG_TTY=$(tty) will fix this issue but when I tried it I just got a Required environment variable not set” error.

Cache the passphrase

It doesn't seem like we can make leiningen pass the --passphrase switch to gpg at signing time (although maybe I just can't find it).

In the meantime you can do the following to get gpg-agent to cache the passphrase for you before you run lein deploy. That way, when lein deploy runs it doesn't need to prompt for it.

First enable gpg-agent.

# Makes gpg use the agent
$ echo use-agent >> ~/.gnupg/gpg.conf
# Make sure that the agent doesn't try to do anything interactive
$ echo pinentry-mode loopback >> ~/.gnupg/gpg.conf
$ echo allow-loopback-pinentry >> ~/.gnupg/gpg-agent.conf
# Make sure the agent is using the latest config.
$ echo RELOADAGENT | gpg-connect-agent

Then to force gpg-agent to cache the passphrase for a key we just sign some test data. It doesn't matter what the test data is, in this case the string "test" is being signed. The output is also piped to /dev/null because we don't need it.

$ command: echo "test" | gpg --sign --passphrase "$GPG_PASSPHRASE" -o /dev/null

The default expiry for passphrase caching is 10 minutes so if you run this command before a long running build step it may expire. You should probably run it just before signing time.

Future work

Things that need to be verified:

  • Is there an way to get leiningen to pass the passphrase to gpg?
  • Is there a way to get leiningen to pass arguments like --no-tty for us?
  • Is there a way to cache a passphrase without actually invoking a sign command?