Upload APKs to SFTP with gradle-ssh-plugin


This tutorial will outline how to upload released APKs to a remote server via SFTP/SSH, using gradle-ssh-plugin.

Add the plugin in your project build script:

buildscript {
    dependencies {
        classpath 'org.hidetake:gradle-ssh-plugin:2.8.0'

In your app build script, apply the plugin and configure the remote:

apply plugin: 'org.hidetake.ssh'

remotes {
    ftp {
        user = 'username'
        host = 'my.hostname.com'
        identity = file("${rootDir}/gradle_deploy.key")
ssh.settings {
    knownHosts = allowAnyHosts

We will be using SSH key authentication via the gradle_deploy.key key, in the root project directory. This has been generated via standard ssh-keygen and authorized on the remote machine.

If you project has multiple flavors, you can specify a different remote destination directory for each flavor by using Gradle’s ExtraPropertiesExtension:

productFlavors {
    myflavor {
        project.ext['uploadDestinationPath_myflavor'] = '/pub/client/android/myflavor/'

Next, we will create two tasks:

  • release<Flavor><BuildType>, that will assemble the APK and copy it (and its Proguard mapping file, if any) to the releases/ directory in the project dir
  • upload<Flavor><BuildType>, that will upload the APK created via the release task above to the configured remote

Add the following to your main module build script (app):

android.applicationVariants.all { variant ->
    String today = new Date().format('yyyyMMdd_HHmmss')
    def outBaseName = "${variant.flavorName}_${variant.versionName}_${variant.versionCode}_${today}"
    def isRelease = variant.buildType.name == 'release'
    def suffix = isRelease ? "" : variant.buildType.name.capitalize()

    def destDir = "${rootDir}/releases"
    def apkName = "${outBaseName}.apk"
    def proguardMappingName = "${outBaseName}_mapping.txt"
    def releaseBuildTask = tasks.create(name: "release" + variant.flavorName.capitalize(), type: Copy) {
        group "release"
        description "Packages a release APK for the flavor ${variant.flavorName} and copies the Proguard mapping file into releases/"
        from(variant.outputs[0].outputFile.path) {
            rename '.*', apkName
        into destDir
        if (variant.mappingFile != null) {
            from(variant.mappingFile.path) {
                rename '.*', proguardMappingName
            into destDir
    releaseBuildTask.dependsOn variant.assemble
    def uploadTask = tasks.create(name: "upload" + variant.flavorName.capitalize() + suffix) {
        group "release"
        doLast {
            ssh.run {
                session(remotes.ftp) {
                    def src;
                    def dest = project.ext["uploadDestinationPath_${variant.flavorName}"]
                    if (variant.mappingFile != null) {
                        print "copying ${apkName}, ${proguardMappingName} to ${dest}"
                        src = files("${destDir}/${apkName}", "${destDir}/${proguardMappingName}")
                    } else {
                        print "copying ${apkName} to ${dest}"
                        src = "${destDir}/${apkName}"
                    put from: src, into: dest
    uploadTask.dependsOn releaseBuildTask
comments powered by Disqus