LUCENE-9936: Add gpg signing of the tgz & zip distribution files

This commit is contained in:
Chris Hostetter 2021-05-04 10:20:59 -07:00
parent b5a77de512
commit a6cf46dada
2 changed files with 121 additions and 1 deletions

70
help/gpgSigning.txt Normal file
View File

@ -0,0 +1,70 @@
GPG Signing
===========
GPG Signing of distribution files (typically by a release manager) is done with the 'signDist' command.
The only required configuration property Gradle needs is the 'signing.gnupg.keyName' (aka: the fingerprint) of
the key you wish to use:
./gradlew signDist -Psigning.gnupg.keyName=YOUR_KEY_FINGERPRINT
By default when you run this command, Gradle will delegate to the `gpg2` command for managing the signing of each file, which (should)
in turn use the `gpg-agent` to prompt you for your secret key only as needed using a dialog box specific to your operating system and/or
`gpg-agent` preferences.
You may wish to put the `signing.gnupg.keyName` in your `~/.gradle/gradle.properties` so it is set automatically any time you use gradle
Additional Configuration
------------------------
The following additional properties -- specified either on the command line via `-P...` or in your `~/.gradle/gradle.properties` may be
useful/necessary in your system:
signing.gnupg.useLegacyGpg=true # Changes the default executable from `gpg2` to `gpg` and explicitly sets `--use-agent`
signing.gnupg.executable=gpg # Allows explicit control over what command executable used (ex: `gpg2`, `gpg`, `gpg.exe`, etc...)
signing.gnupg.homeDir=/tmp/gnupg-home # overrides GnuPG's default home directory (ex: `~/.gnupg/`)
signing.gnupg.optionsFile=/tmp/gnupg-home/my.conf # overrides GnuPG's default configuration file
signing.gnupg.passphrase=... # Provide your passphrase to gradle to hand off to gpg. *NOT RECOMMENDED*, see below.
Notes About Error Messages
--------------------------
### `gpg: signing failed: Inappropriate ioctl for device`
This typically happens if your `gpg-agent` is configured (either globally for your operating system, or personally in your
`~/.gnupg/gpg-agent.conf`) to use a `pinentry` command which depends on using the same `tty` as the `gpg` command (ex: `pinentry-curses`,
or `pinentry-tty`, etc...).
`tty` based `pinentry` implementations do not work when Gradle's `SigningPlugin` is attempting to invoke `gpg` -- among other problems:
Gradle is multi-threaded, and we sign multiple artifacts by default; so even if the `SigningPlugin` didn't automatically force `--no-tty` when
running `gpg` you could easily run into problems where a second `pinentry` process wanted to read from the same `tty` in the middle of you
typing in your passphrase to the first process.
Developers are encouraged to configure a *non* `tty` based `pinentry` (ex: `pinentry-gnome`, `pinentry-x11`, `pinentry-qt`, `pinentry-mac`,
`pinentry-wsl-ps1`, etc...) either globally in your operating system, or personally in your `~/.gnupg/gpg-agent.conf`, or in a new
`gpg-agent.conf` file a new GnuPG configuration directory (containing a copy of your private keys) that you direct gradle to via
`signing.gnupg.homeDir`
If none of these options are viable for you, then as a last resort you may wish to consider using the `signing.gnupg.passphrase=...` property.
This will expose your secret passphrase to the Gradle process, which will then pass it directly to each `gpg-agent` instance using
`--pinentry-mode=loopback`.
### `gpg: signing failed: No such file or directory`
This may mean that there is a problem preventing `gpg` from communicating correctly with the `gpg-agent` (and/or invoking your `pinentry`
program) that is independent of gradle. Try running `pkill gpg-agent` and then retrying your `./gradlew` command
### `No value has been specified for property 'signatory.keyId'.`
Do not bother ever attempting to set a command line (or gradle.properties) property named `signatory.keyId`. This is evidently the
name of an internal property that the gradle `SigningPlugin` expects the `GnupgSignatory` plugin we use to provide -- which it does
as long as you have specified a valid value for `signing.gnupg.keyName`
If you see this error, it means you did not properly set `signing.gnupg.keyName` _AND_ you invoked a task which is attempting to use
the `SigningPlugin`, but does not depend on the custom `failUnlessGpgKeyProperty` to report the error correctly. Please file a Jira
noting what `./gradlew` command you attempted to run so we can fix it's dependencies, and try again after setting `signing.gnupg.keyName`.

View File

@ -20,6 +20,7 @@
plugins {
id 'distribution'
id 'signing'
}
description = 'Lucene distribution packaging'
@ -167,4 +168,53 @@ configure(project(":lucene:luke")) {
})
}
}
}
}
configurations {
luceneTgz
luceneZip
}
artifacts {
luceneTgz(distTar)
luceneZip(distZip)
}
// NOTE: we don't use the convinence DSL of the 'signing' extension to define our 'Sign' tasks because
// that (by default) adds our signature files to the 'archives' configuration -- which is what
// assemble & installDist try to copy/sync, so they wouldn't work w/o GPG installed (which would be bad).
//
// We also want to hook in our own property check dependency since the default error message from Sign task
// refers to the wrong (internal only) property name ("signatory.keyId")
signing {
useGpgCmd() // so gpg-agent can be used
}
task failUnlessGpgKeyProperty {
// placeholder that can be depended on by any task needing GPG key which will 'fail fast' if it's not set.
def propName = 'signing.gnupg.keyName'
// This explicitly checks the taskGraph (instead of a simple 'doFirst') so it can fail the user's gradle
// invocation immediately before any unrelated build tasks may run in parallel
if ( ! project.hasProperty(propName) ) {
gradle.taskGraph.whenReady { graph ->
if ( graph.hasTask(failUnlessGpgKeyProperty) ) {
// TODO: it would be really nice if taskGraph was an actual graph and we could report what tasks in (transitive) depend on us
throw new GradleException("'$propName' property must be set for GPG signing, please see help/gpgSigning.txt")
}
}
}
}
task signDistTar(type: Sign) {
dependsOn failUnlessGpgKeyProperty
sign configurations.luceneTgz
}
task signDistZip(type: Sign) {
dependsOn failUnlessGpgKeyProperty
sign configurations.luceneZip
}
task signDist {
group = 'Distribution'
description = 'GPG Signs the main distributions'
dependsOn signDistTar, signDistZip
}