Bintray is dead! long live Sonatype!
Bintray is dead… F##k!
On February the 3rd, JFrog announced the EOL (End of Life) of Bintray. I rekon that at the time I didn’t believe it could be true. For me Bintray shutting down was like if Github was shutting down, highly improbable.
For many JVM developers Bintay had become the way of publishing jars in the wild. Even Gradle dependency resolution treated Bintray as eternal. But as the song says, nothing lasts forever… and the day has come, and all these projects need to change the way they’re published publicly. How ? What was going to be the alternative ?
Index
This is not supposed to be a detailed guide on how to publish your artefacts, but a general view of the requirements you will need to accomplish in order to be able to publish your artefacts via Sonatype.
Sonatype
After spending a couple of hours looking for an alternative, I found how Sonatype enables you to publish to Maven Central. Sonatype is well known by its artifact repository management tool Nexus, but also is a way to deploy your Java artefacts to maven central. Basically the whole process will go through the following steps:
-
Create a Sonatype’s JIRA account
-
Opening a Jira Ticket asking for adding your project space in the public repository
-
Sonatype will challenge you to make sure you are the owner of the open source repository you claim you own.
-
Once they confirm you are who you claim to be, you can start uploading your artifacts, but only after signing the project artifacts with your PGP key.
-
Publish your signed artifacts to Sonatype and sync with Maven central
A good place to start is to follow the steps at reviewing Sonatype’s documentation about how to publish new artefacts.
PGP
One of the last steps before configuring your building tool to publish your artifacts is to create your PGP keys. These keys will be used later on to sign your artifacts. Artifacts won’t be publicly available until they are properly signed.
Creating keys
One of the requirements to deploy artifacts to Sonatype is to be able to sign those artefacts with a PGP key. Moreover the public key should be available in a public server so that everybody can verify the artifacts' signature. If you’ve got a Linux OS in your system PGP is probably already installed. To create a new PGP key just execute:
gpg --gen-key
You will be asked to enter the name and email address the key is linked to.
john@john-doe:~/Development/repositories$ gpg --gen-key
gpg (GnuPG) 2.2.20; Copyright (C) 2020 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
gpg: directory '/home/john/.gnupg' created
gpg: keybox '/home/john/.gnupg/pubring.kbx' created
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
GnuPG needs to construct a user ID to identify your key.
Real name: John Doe
Email address: john.doe@gmail.com
You selected this USER-ID:
"John Doe <john.doe@gmail.com>"
Change (N)ame, (E)mail, or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /home/john/.gnupg/trustdb.gpg: trustdb created
gpg: key 72AC5723BDF6A21E marked as ultimately trusted
gpg: directory '/home/john/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/john/.gnupg/openpgp-revocs.d/5D6F1DECC2C80E320732DADC72AC5723BDF6A21E.rev'
public and secret key created and signed.
pub rsa3072 2021-05-09 [SC] [expires: 2023-05-09]
5D6F1DECC2C80E320732DADC72AC5723BDF6A21E
uid John Doe <john.doe@gmail.com>
sub rsa3072 2021-05-09 [E] [expires: 2023-05-09]
Creating subkeys
Instead of using our primary key everytime, is better to create a subkey to use it and avoid compromising the primary key. I’ve found a great tutorial to create subkeys here. The idea is that once the subkey has been created we can create a backup for our primary key, then remove it from the local pubring, publish the subkey to public servers and finally start using the subkey to start signing our artifacts.
First lets take the primary key id:
john@john-doe:~/myprojects$ gpg --list-secret-keys --keyid-format short
/home/john/.gnupg/pubring.kbx
------------------------------
sec rsa3072/99708DF2 2021-05-10 [SC] [expires: 2023-05-10]
10DA4D2A8C65A037ADBE2DF0E45B5B8199708DF2
uid [ultimate] John Doe <john.doe@gmail.com>
ssb rsa3072/17ECACBF 2021-05-10 [E] [expires: 2023-05-10]
With the id 99708DF2 we can now add a new subkey with the parameter --edit-key:
john@john-doe:~/myprojects$ gpg --edit-key 99708DF2
gpg (GnuPG) 2.2.20; Copyright (C) 2020 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret key is available.
sec rsa3072/E45B5B8199708DF2
created: 2021-05-10 expires: 2023-05-10 usage: SC
trust: ultimate validity: ultimate
ssb rsa3072/58CD43FC17ECACBF
created: 2021-05-10 expires: 2023-05-10 usage: E
[ultimate] (1). John Doe <john.doe@gmail.com>
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(14) Existing key from card
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 6m
Key expires at sáb 06 nov 2021 09:15:10 CET
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa3072/E45B5B8199708DF2
created: 2021-05-10 expires: 2023-05-10 usage: SC
trust: ultimate validity: ultimate
ssb rsa3072/58CD43FC17ECACBF
created: 2021-05-10 expires: 2023-05-10 usage: E
ssb rsa4096/B73C6F790D7D2738
created: 2021-05-10 expires: 2021-11-06 usage: S
[ultimate] (1). John Doe <john.doe@gmail.com>
gpg> save
Now in order to keep the primary key safe we need to apply a few more steps:
-
BACKUP of our .gnupg directory
-
REMOVE the entry of your private key at $HOME/.gnupg/private-keys-v1.d/KEYGRIP
To remove your master private key from the local registry we need to look for an specific entry at $HOME/.gnupg/private-keys-v1.d/KEYGRIP where KEYGRIP value can be found by executing:
gpg --with-keygrip --list-key
john@john-doe:~/myprojects$ gpg --with-keygrip --list-key
/home/john/.gnupg/pubring.kbx
------------------------------
pub rsa3072 2021-05-26 [SC] [expires: 2023-05-26]
3E1848F2436069CE448356EDE4E85A9D556DD9BF
Keygrip = CBB7481937CA046D81CBB9DC8D7730828F9419D8
uid [ultimate] John Doe <john.doe@gmail.com>
sub rsa3072 2021-05-26 [E] [expires: 2023-05-26]
Keygrip = 7CCE676066967B78D602267DD0E7612F65FF3857
sub rsa4096 2021-05-26 [S] [expires: 2021-11-22]
Keygrip = DCE1C14FC7815969DD7FB11EAD9A62D92616D49D
The master keygrip is the one ending in …419D8, so lets go to $HOME/.gnupg/private-keys-v1.d/ list all keys, and delete the master secret key.
john@john-doe:~/myprojects$ cd $HOME/.gnupg/private-keys-v1.d/
john@john-doe:~/myprojects$ ls -l
total 12
-rw------- 1 john john 1624 may 26 19:21 7CCE676066967B78D602267DD0E7612F65FF3857.key
-rw------- 1 john john 1608 may 26 19:21 CBB7481937CA046D81CBB9DC8D7730828F9419D8.key
-rw------- 1 john john 2056 may 26 19:24 DCE1C14FC7815969DD7FB11EAD9A62D92616D49D.key
john@john-doe:~/myprojects$ rm CBB7481937CA046D81CBB9DC8D7730828F9419D8.key
Now if you execute:
john@john-doe:~/myprojects$ gpg --list-secret-keys --keyid-format short
/home/john/.gnupg/pubring.kbx
------------------------------
sec# rsa3072/556DD9BF 2021-05-26 [SC] [expires: 2023-05-26]
3E1848F2436069CE448356EDE4E85A9D556DD9BF
uid [ultimate] John Doe <john.doe@gmail.com>
ssb rsa3072/8FD152E3 2021-05-26 [E] [expires: 2023-05-26]
ssb rsa4096/E37A78A2 2021-05-26 [S] [expires: 2021-11-22]
You can see that the secret key appears with sec# meaning it’s not present.
-
CHANGE the passphrase of the remaining keys
To add another layer of security, lets change the passphrase so that the primary key has a different passphrase other than the one used for the rest of the keys. To change the rest of the keys passphrase:
john@john-doe:~/myprojects$ gpg --list-secret-keys --keyid-format short
/home/john/.gnupg/pubring.kbx
------------------------------
sec# rsa3072/556DD9BF 2021-05-26 [SC] [expires: 2023-05-26]
3E1848F2436069CE448356EDE4E85A9D556DD9BF
uid [ultimate] John Doe <john.doe@gmail.com>
ssb rsa3072/8FD152E3 2021-05-26 [E] [expires: 2023-05-26]
ssb rsa4096/E37A78A2 2021-05-26 [S] [expires: 2021-11-22]
john@john-doe:~/myprojects$ gpg --edit-key 556DD9BF
gpg (GnuPG) 2.2.20; Copyright (C) 2020 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret subkeys are available.
pub rsa3072/E4E85A9D556DD9BF
created: 2021-05-26 expires: 2023-05-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa3072/0F75EB9F8FD152E3
created: 2021-05-26 expires: 2023-05-26 usage: E
ssb rsa4096/B651C5F5E37A78A2
created: 2021-05-26 expires: 2021-11-22 usage: S
[ultimate] (1). John Doe <john.doe@gmail.com>
gpg> passwd
gpg: key E4E85A9D556DD9BF/E4E85A9D556DD9BF: error changing passphrase: No secret key
Don’t bother with the last error, it’s because it’s applying the new passphrase to all keys and subkeys, and it can’t apply it to the primary key because is no longer there.
Upload keys to a public keyring
Although you’ve removed the private key all subkeys contain implicitly the original public key. So if you’re wondering whether you should upload your subkeys or your primary key, the answer is that either way your master public key will be published. Your secret key will remain safe wherever you decided to store it.
To upload your public keys to a public server you can just execute:
gpg --keyserver https://keyserver.ubuntu.com --send-keys 8FD152E3
In fact, because you can upload your keys to any public server, instead of waiting them to sync each other you can directly upload your keys to the most important ones (see "Which servers does gradle use by default?" below).
Just change they key (--send-keys) with they key you want to publish and the server (-keyserver) with the server you want to upload your key to. From now on everyone who wants to check your artifacts' signatures, they can use your published public keys to do so.
Gradle
Because I’m using Gradle as my build tool of choice for JVM related projects, I need to know how to configure my Gradle project to sign my artifacts (with my previously created PGP keys) and publish them to Maven Central. There’re some options but lately I’ve found that using the Kordamp Gradle plugins makes Gradle configuration easier and cleaner, but of course, using directly the Gradle signing plugin is good enough.
This project provides a set of Gradle plugins that follow an opinionated way to build Java and Groovy projects. The conventions define by these plugins closely follow common practices observed in many Open Source projects.
Creating a Gradle/Kordamp project
First I’m creating a Gradle project:
john@john-doe:~/myprojects$ mkdir mc
john@john-doe:~/myprojects$ cd mc
john@john-doe:~/myprojects$ gradle init
Select type of project to generate:
1: basic
2: application
3: library
4: Gradle plugin
Enter selection (default: basic) [1..4] 1
Select build script DSL:
1: Groovy
2: Kotlin
Enter selection (default: Groovy) [1..2] 1
Project name (default: gradleinit): mc
> Task :init
Get more help with your project: Learn more about Gradle by exploring our samples at https://docs.gradle.org/6.8/samples
Next, I’m creating a structure with two modules that I want to be published to Maven Central: mc-core and mc-micronaut:
mc
|
+---modules
| |
| +---mc-core
| | |
| | +----src
| | | |
| | | +----main
| | | |
| | | +------groovy
| | |
| | +----mc-core.gradle
| | |
| +---mc-micronaut
| | |
| | +----src
| | | |
| | | +----main
| | | |
| | | +------groovy
| | |
| | +----mc-micronaut.gradle
|
+---build.gradle
|
+---settings.gradle
Then, following the Kordamp guide, I’m configuring my project with Kordamp so that Gradle understands my project structure. First I need to change my settings.gradle
buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath 'org.kordamp.gradle:settings-gradle-plugin:0.45.0'
}
}
apply plugin: 'org.kordamp.gradle.settings'
rootProject.name = 'mc'
projects {
directories = ['modules']
}
And finally I’m configuring the top build.gradle file and the modules mc-*.gradle. First the build.gradle file. Here we are adding basic information about our project and some common behaviors among all modules such as:
-
general info: name, description, where is the scm, website…
-
licensing: which is the source license
-
allprojects: common configuration for all projects. In this case I’m declaring that all modules should be using maven central to resolve their dependencies.
plugins {
id 'org.kordamp.gradle.groovy-project' version '0.45.0'
}
config {
info {
name = 'mc'
vendor = 'mc'
description = 'mc does this and that'
inceptionYear = '2021'
tags = ['great stuff']
links {
website = 'https://github.com/somewhere/mc'
issueTracker = 'https://github.com/somewhere/mc/issues'
scm = 'https://github.com/somewhere/mc.git'
}
}
licensing {
enabled = false
includes = ['**/*.groovy', '**/*.gradle']
licenses {
license {
id = 'Apache-2.0'
}
}
}
}
allprojects {
repositories {
mavenCentral()
}
}
Later on I’m editing build.gradle for adding publishing and signing related information for all modules. For the time being lets finish with the modules .gradle files. These files only are declaring enough information to compile Groovy code.
plugins {
id "groovy"
}
plugins {
id "groovy"
}
Finally there’re some properties that I wan’t to keep tracked by SCM such as:
-
group: is the group id of the artifacts
-
version: the version of the artifacts
Dependencies in the Java world can be located by their group id, the artifact name and the artifact version. For example if you are using Maven, you will declare an external dependency as:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
In Gradle, dependencies can be declared with different strategies, for example groupId, artifactId and version can be declared explicitly or using a string convention "groupId:artifactId:version":
dependencies {
testImplementation "junit:junit:4.11"
}
So our modules should declare the group they belong to, their artifact name and their current version.
Here the artifact name by default corresponds with the module name. So we only have to declare manually the missing properties: version and group. You can hardcode them in the build.gradle file directly or in the project’s gradle.properties file.
group = com.github.mc
version = 0.1.0
gradle.properties file in the project’s root directory is only applied to the project whereas the gradle.properties file at $HOME/.gradle/gradle.properties is applied to all Gradle projects in your system, so make sure you are setting group and version in your project, not in the shared properties file. |
When the artifact are published, you will be able, for example to reference mc-core as an external dependency in your project as:
dependencies {
implementation "com.github.mc:mc-core:0.1.0"
}
Signing artifacts
I’ve considered two ways of signing and deploying your artifacts.
Using private ring
The first one is to sign and upload your artifacts from your local machine using the private keyring. First of all we need to enable artifact signing in build.gradle:
config {
info {
// add this after licensing block
publishing {
signing {
enabled = true
}
}
}
}
Then we need to get the key we’re using for signing our artefacts:
john@john-doe:~/myprojects$ gpg --list-keys --keyid-format short
/home/john/.gnupg/pubring.kbx
------------------------------
pub rsa3072/29F2E1CA 2021-05-08 [SC] [expires: 2023-05-08]
642D639C538EF46CC4FA04164B41DF7F29F2E1CA
uid [ultimate] johndoe <john.doe@gmail.com>
sub rsa3072/EC8AFB99 2021-05-08 [E] [expires: 2023-05-08]
The way the Kordamp Gradle plugin picks up the secret PGP key requires one more step. I need to export my secret subkey to a secret ring.
gpg --export-secret-keys -o secring.gpg
From PGP 2.1 this file has to be created explicitly. The way I’m configuring the Gradle signing plugin needs the secret ring to be present, but there’re other options as we’ll see in a moment for the CI mode.
Because credentials MUST NOT be hardcoded in the build.gradle file I’m keeping credentials values in the $HOME/.gradle/gradle.properties file so that Gradle execution can read them:
# to sign artifacts
signing.keyId=29F2E1CA
signing.password=mypassphrase
signing.secretKeyRingFile=/home/john/.gnupg/secring.gpg
Again, don’t confuse $HOME/.gradle/gradle.properties with the project’s own gradle.properties file |
This variable name convention comes the official Gradle signing plugin. Now before publishing to Sonatype we can test that signing the artifacts is properly configured by publishing artifacts locally:
./gradlew publishToMavenLocal
Go to your maven local directory and check whether artifacts have been signed or not. You must find some .asc files along with your normal artifacts. For example, our project has two modules, lets check how mc-core has been published to local maven repository:
john@john-doe:~/myprojects$ ls -l ~/.m2/repository/com/github/mc/mc-core/0.1.0
-rw-rw-r-- 1 john john 261 may 30 18:43 mc-core-0.1.0-groovydoc.jar
-rw-rw-r-- 1 john john 821 may 30 18:43 mc-core-0.1.0-groovydoc.jar.asc
-rw-rw-r-- 1 john john 1310 may 30 18:43 mc-core-0.1.0.jar
-rw-rw-r-- 1 john john 821 may 30 18:43 mc-core-0.1.0.jar.asc
-rw-rw-r-- 1 john john 261 may 30 18:43 mc-core-0.1.0-javadoc.jar
-rw-rw-r-- 1 john john 821 may 30 18:43 mc-core-0.1.0-javadoc.jar.asc
-rw-rw-r-- 1 john john 798 may 30 18:43 mc-core-0.1.0.pom
-rw-rw-r-- 1 john john 821 may 30 18:43 mc-core-0.1.0.pom.asc
-rw-rw-r-- 1 john john 261 may 30 18:43 mc-core-0.1.0-sources.jar
-rw-rw-r-- 1 john john 821 may 30 18:43 mc-core-0.1.0-sources.jar.asc
Ok everything seems ok, every file produced by Gradle has been signed, now we could start publishing our artifacts to Sonatype. But before that, lets see another way of signing our artifacts.
Using armored key
Another way to use our key is creating an ASCII version of the key, an armored key. This way of signing our artifacts is specially convenient in a continuous integration environment where uploading your private ring is not an option. The idea is to use a configuration file to set anything we need in order to sign the artifacts without depending on any key file.
To do that I’m going to export a signing subkey (instead of the primary key) as ASCII text, and use that text as a value in a properties file along with the subkey password and keyId. That should allowed me to use that strategy in a continuous integration environment.
Before setting the armored key, I’m setting the key id, and the password of the signing key (the signing subkey we created earlier) in the common gradle.properties file:
signingKeyId = 261EB37A
signingPassword = passwordyousetforthesubkey
leave a new line after signingPassword because the next step will add the secret subkey at the end of the file. |
As I mentioned an armored key is an plain ASCII version of a key. In this case I’m getting the armored version of the subkey I want to use to sign my artifacts and setting the signingKey variable value in my common gradle.properties. Because in order to be able to use it as a property value I need to escape newline characters, I’m using the following snippet I found at stackoverflow.
gpg --armor --export-secret-subkeys 261EB37A \
| awk 'NR == 1 { print "signingKey=" } 1' ORS='\\n' \
>> $HOME/.gradle/gradle.properties
You should see something like the following:
signingKeyId = 261EB37A
signingPassword = passwordyousetforthesubkey
signingKey = signingKey=\n-----BEGIN PGP PRIVATE KEY BLOCK-----\n\nlQGVBGCzu5MMMMMdupzgGgmK4YenNnswpzRELAHWdRSUjus4f...
Then I’m changing build.gradle in order Gradle to find these values with the name I used in the properties file.
publishing {
signing {
enabled = true
keyId = findProperty("signingKeyId")
secretKey = findProperty("signingKey")
password = findProperty("signingPassword")
}
}
And finally execute the gradle task to publish signed artifacts to local maven repository to check that the artifacts have been signed:
./gradlew publishToMavenLocal
publishToMavenLocal task signs the artifacts when publishing because I set the variable "enabled=true" within the publising→signing configuration block inside build.gradle
Publishing to Sonatype
Ok I’ve got my artifacts properly signed it’s time to share them with the rest of the world uploading them to Sonatype. In order to do that, first I need to add the remote repositories where my artifacts are going to be uploaded to the build.gradle file:
config {
info {
repositories {
repository {
name = 'releases'
url = 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/'
credentials {
username = findProperty("maven.username")
password = findProperty("maven.password")
}
}
repository {
name = 'snapshots'
url = 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
credentials {
username = findProperty("maven.username")
password = findProperty("maven.password")
}
}
}
}
}
And complete the publishing block:
config {
info {
publishing {
enabled = true
releasesRepository = 'releases'
snapshotsRepository = 'snapshots'
signing {
enabled = !version.endsWith("-SNAPSHOT")
keyId = findProperty("signingKeyId")
secretKey = findProperty("signingKey")
password = findProperty("signingPassword")
}
}
}
}
Instead of using your username/password, Sonatype allows you to get an alternative username/password. That way we can avoid using our master username/password everytime a new artifact is signed and deployed. You can reset these credentials at any time, so it may be suitable for example for CI environments. There’s a video tutorial on how to do that.
Remember that the URL to get your new credentials is not https://oss.sonatype.org but https://s01.oss.sonatype.org |
Again use $HOME/.gradle/gradle.properties file to store your variable names:
maven.username=username
maven.password=alternativepassword
If you’d like to start uploading your artifacts as snapshots to test your settings, change your version to '0.1.0-SNAPSHOT' and publish to Sonatype as:
./gradlew publishAllPublicationsToSnapshotsRepository
And once your are completely sure you’d like to release an stable version (remember you can’t remove an already published release from maven central), you can execute:
./gradlew publish
If everything went ok, all our signed artifacts will be uploaded to Sonatype and eventually synchronized to Maven Central.
Checking published artifacts
To check that your files have been uploaded properly you can go to https://s01.oss.sonatype.org with your JIRA credentials.
When you created your namespace in Sonatype, according to the mail the send you (see below), you should comment to that email to tell them to synchronize the repo to maven central, but sometimes, depending on your publishing configuration, after a couple minutes it’s not necessary, and your artifacts can be found at central.
Conclusions
Writing this article I realized how Bintray made our lifes easier, specially not having to deal with security issues such as managing our own PGP keys. But hey! somebody said that with a great power comes a great responsibility, that’s why, besides the building tool you are going to use for publishing your artifacts, I think is worth remembering the following tips:
-
PGP KEYS
-
Use decent passwords for your keys: Don’t leave keys without password or with an obvious one like admin and alike.
-
Keep master and subkeys separated: It will make it easier to keep the primary key safe.
-
Always use a subkey: if your lose your subkey or it is stolen you can always revoke it so that everybody knows that key is no longer valid.
-
Set a reasonable expiration date: you can always change expiration time later on.
-
Backup your keys: Redundancy
-
-
SONATYPE CREDENTIALS
-
use alternative username/password: you can always revoke them using the original username/passwords.
-
-
GRADLE
-
Don’t include your credentials inside your project’s files: make sure your credentials stay away from your source control system (Git, Subversion…etc). To store your credentials, you can use $HOME/.gradle/gradle.properties which is outside your project, but still visible for Gradle when building your project. You can also set temporary local environment variables.
-
-
CONTINUOUS INTEGRATION
-
use armored keys: armored keys content can be easily used in a CI environment as variable value.
-
use CI features for protecting sensitive variables: Don’t expose your credentials as plain text variables. Environments like Github or Gitlab can set sensitive variables.
-
always use subkeys: don’t use your primary key in a public environment, keep your primary key safe. If a subkey gets exposed, you can always create a revocation key with your primary subkey.
-
RESOURCES
Here are most, if not all the resources I’ve checked to create the current article.
Gradle/Maven
-
How to setup variables: use of gradle.properties
-
alternative Gradle plugin for signing artifacts having support for setting all types of parameters in gradle.properties by convention.
-
Como publicar en Maven Central (Spanish): Una buenisima guia en español para publicar tus artefactos en Sonatype y que sincroncen en Maven Central de la mano del gran Jorge Aguilera.
GNUPG
Buff, that was a lot to read, I hope it helps.