Add apiKey and apiSecret to pluginBundle extension for plugin-publish-plugin

Ask: Please provide the apiKey and apiSecret as options in the plugin-publish-plugin’s pluginBundle extension

Context:

Just integrated the plugin-publish plugin to upload my gradle plugin to the community plugin portal.

The way the plugin consumes the api key and secret makes it difficult to use on build machine line travis-ci.

Travis allows me to supply environment variables that can then be used securely in my build scripts. For example, i would add a BINTRAYKEY value in travis with the api key value. I would then supply this value to the bintray publish plugin configuration as System.env.BINTRAYKEY.

bintray {
    key = System.env.BINTRAYTOKEN
   .....
}

For the plugin-publish-plugin, i had to do the following due to the limitation

/**
 * Grabs the values from the env and writes to a file that can be used by the plugin-publish plugin.
 * This allows us define the API and secrete securely in travis-ci
 */
task setupPluginUpload << {
    def file = file("build/plugin-publish-keys.properties")
    def key=System.env.gradlePublishKey
    def secret = System.env.gradlePublishSecret

    if( !key || !secret)
    {
        throw new RuntimeException("gradlePublishKey and/or gradlePublishSecret are not defined environment variables")
    }

    def props = new Properties()
    props.setProperty("gradle.publish.key", key)
    props.setProperty("gradle.publish.secret", secret)
    props.store(file.newWriter(), "Generated for use with plugin-publish-plugin")
    
    // Used by the plugin-publish plugin to retrieve the keys.
    System.properties.setProperty("com.gradle.login.properties.file", file.absolutePath)
}

tasks.publishPlugins.dependsOn tasks.setupPluginUpload
1 Like

The Publishing Plugin is just looking for a project property with that name. If you want to use an environment variable instead you can set the property to the environment variable value using extra properties. No need to create a task to do this. In fact, this is considered configuration, which should never be done at execution time.

ext['gradle.publish.key'] = System.env.gradlePublishKey

This won’t work (and yes, i tried it). According to the source code for plugin-publishing-plugin, you have the following options

  1. keys must exist in the gradle.properties in the root of your project
  2. Keys must exist in the ~/.gradle/gradle.properties
  3. Provide an override properties file to read via the system property “com.gradle.login.properties.file”
  4. Set as system properties

Code from https://plugins.gradle.org/m2/com/gradle/publish/plugin-publish-plugin/0.9.0/plugin-publish-plugin-0.9.0-sources.jar

//TODO: we should not do this!
//TODO: MUST FIX properties storage and loading!
static Properties loadProperties(File propertiesFile) throws IOException {
	LayoutPreservingProperties props = new LayoutPreservingProperties(System.getProperties());
    props.setRemoveComments(true);
    if (propertiesFile == null || !propertiesFile.exists()) {
        return props;
    }
    InputStream in = null;
    try {
        in = new FileInputStream(propertiesFile);
        props.load(in);
        return props;
    } finally {
        if (in != null) in.close();
    }
}

static File getDefaultPropertiesFile(Project project) {
    String overridePropertiesFileProp = System.getProperty(PROPERTIES_FILE_LOCATION);
    if (overridePropertiesFileProp != null) {
        return new File(overridePropertiesFileProp);
    } else {
        File projectPropFile = new File(project.getProjectDir(), project.GRADLE_PROPERTIES);
        if (projectPropFile != null && projectPropFile.exists()) {
            return projectPropFile;
        } else {
            File defaultGradleDir = project.getGradle().getGradleUserHomeDir();
            File gradleHomeFile = new File(defaultGradleDir, project.GRADLE_PROPERTIES);
            return gradleHomeFile;
        }
    }
}
1 Like

This is probably a better workaround for now, since it doesn’t involve creating a properties file. But it would be great if it was an option in the pluginBundle extension

task setupPluginUpload << {

    def key=System.env.gradlePublishKey
    def secret = System.env.gradlePublishSecret

    if( !key || !secret)
    {
        throw new RuntimeException("gradlePublishKey and/or gradlePublishSecret are not defined environment variables")
    }

    System.properties.setProperty("gradle.publish.key", key)
    System.properties.setProperty("gradle.publish.secret", secret)
}

tasks.publishPlugins.dependsOn tasks.setupPluginUpload
2 Likes

I agree that this should be settable in the pluginBundle DSL.

In the meantime, is there a reason why you can’t pass the variables through to your script using a -D argument in the .travis.yml to set the system properties? I can’t remember whether the Travis UI masks secure variables in the output. I thought it did, but can’t find an example of a build with that happening right now.

Another, much quicker way to create the properties file is to do it in the .travis.yml file itself - shell is built for this kind of thing:

echo -e "gradle.publish.key=$APIKEY\ngradle.publish.secret=$APISECRET" > gradle.properties

Do that as a before_install command and you should be good.

Yeah, that’s also a good solution. Hope this turns into a trackable bug to
add the keys into the DSL.

Added as GRADLE-3273

This solution may work securely for Travis but not for all build systems. Jenkins, for example, provides full access to the job workspace post-build, even people without direct read-access to credential storage. From a corporate security standpoint, the credentials should never be written to any long term storage medium in unencrypted form. The easiest solution to satisfy everyone is to allow the gradle script to supply the credentials directly, then it is up to the user and their security requirements to figure out how to get them there. For many, that will be environment variables but it could be a database query or some other mechanism.

1 Like