First I’d like to say that I’m new to Gradle and I use it in an Android project, within Android Studio (0.2.7 at this moment).

Moreover I couldn’t reproduce the problem after fixing it which may indicate that there were other things going on under.

And last but not least, I’d like to apologize to Luke Daley for my harsh tweet. I had been struggling with this awkward behaviour for more than hour and when I thought I found the solution I couldn’t believe it was a Convention over Configuration (CoC) problem.

The problem

Today I’ve stumbled across a weird behaviour from Gradle. It just couldn’t create a task of a certain type.

bdusauso@tu-150:~/devel/android/someproject ➔  gradle --daemon tasks
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/bdusauso/devel/android/someproject/build.gradle' line: 108

* What went wrong:
A problem occurred evaluating root project 'someproject'.
> Cannot create task of type 'TestFlightUploadTask' as it does not implement the Task interface.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 1.063 secs

And here was the incriminated task :

class TestFlightUploadTask extends DefaultTask {

    static final String URL = 'http://testflightapp.com/api/builds.json'
    static final String CONTENT_TYPE = 'multipart/form-data'

    @Input String apkFilename
    @Input String apiToken
    @Input String teamToken
    @Input String notes

    @TaskAction
    void upload() {
        def http = new HTTPBuilder(URL)

        http.request(Method.POST, CONTENT_TYPE) { req ->

            MultipartEntity entity = new MultipartEntity()
            entity.addPart("file", new FileBody(new File(apkFilename)))
            entity.addPart("apiToken", new StringBody(apiToken))
            entity.addPart("teamToken", new StringBody(teamToken))
            entity.addPart("notes", new StringBody(notes))

            req.entity = entity
        }
    }
}

task uploadToTestFlight(
        type: TestFlightUploadTask,
        group: 'deploy',
        description: 'Upload Staging APK to TestFlight',
        dependsOn: assembleStaging) {
    apkFilename = '******'
    apiToken    = '******'
    teamToken   = '******'
    notes       = '******'
}

No matter what I did to the code Gradle seemed to be reluctant to correctly do its work.

As a last resort I decided to change the class name from TestFlightUploadTaskto FlightUploadTask - don’t ask me why though, just an intuition - and … magically it solved the problem, hence my rant about Convention over configuration going a bit too far.

Epilogue

As I said earlier, unfortunately I couldn’t reproduce the problem, but trust me I’ll try to do it. I’ll update this post.