Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building versionName/versionCode from data external (to lambda) #14

Open
Wrywulf opened this issue Jan 11, 2021 · 6 comments
Open

Building versionName/versionCode from data external (to lambda) #14

Wrywulf opened this issue Jan 11, 2021 · 6 comments

Comments

@Wrywulf
Copy link

Wrywulf commented Jan 11, 2021

I guess this is more of a question.. How would it be possible to customize the versionName/versionCode using data not provided as input to the lambda (GitTag, ProviderFactory, VariantInfo)?

Context: I'm building an opinionated wrapper plugin around app-versioning and would like to declare my own plugin extension that app projects populate. Those extension properties would be used in the overrideVersionName lambda to calculate the version name.

If I try to use my custom extension property in the overrideVersionName lambda like this:

class WrapperPlugin : Plugin<Project> {
   override fun apply(project: Project) {
        project.plugins.withType<AppPlugin> {

            // exposes a "myFilter" Property<String>
            val wrapperExtension: WrapperExtension = project.extensions.create(
                "wrapper", WrapperExtension::class.java
            )
            project.plugins.apply("io.github.reactivecircus.app-versioning")
            val extension = project.extensions.getByType(AppVersioningExtension::class.java)

            // mapping the myFilter extension property to the app-versioning property works fine
            extension.tagFilter.set(wrapperExtension.myFilter.map { "*+${it}" })

            // accessing the myFilter extension property here fails
            extension.overrideVersionName  { gitTag, providerFactory, variantInfo ->
               //do some logic based on the myFilter value
                wrapperExtension.myFilter.get()
            }
       }

I get the error:

Property 'kotlinVersionNameCustomizer' with value '(io.github.reactivecircus.appversioning.GitTag, org.gradle.api.provider.ProviderFactory, io.github.reactivecircus.appversioning.VariantInfo) -> kotlin.String' cannot be serialized

Am I missing something obvious here?

Basically, I guess I would like "myFilter" to be a part of the input to the GenerateAppVersionInfo task.

@ychescale9
Copy link
Member

This is tricky.

The cannot be serialized error is probably due to the Extensions (WrapperExtension) being dynamic object in gradle which can't be serialized.

If you try to get the value of wrapperExtension.myfilter.get() before overrideVersionName, you'll get Cannot query the value of extension 'wrapper' property 'myFilter' because it has no value available. as the values won't be available before the project is evaluated.

The only workaround I can think of right now is:

class WrapperPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.plugins.withType<AppPlugin> {
            val wrapperExtension = project.extensions.create("wrapper", WrapperExtension::class.java)
            project.plugins.apply(AppVersioningPlugin::class.java)
            val extension = project.extensions.getByType(AppVersioningExtension::class.java)
            
            val myFilter = AtomicReference<String>(null)
            project.afterEvaluate {
                myFilter.set(wrapperExtension.myFilter.get())
            }

            extension.overrideVersionName { gitTag, providerFactory, variantInfo ->
                myFilter.get()
            }
        }
    }
}

I could change the plugin to add tagFilter to the overrideVersionCode|Name lambda parameters (maybe wrap them in a Metadata object), but it feels weird to just add tagFilter and not the others.

The other option is to add a val metadata = objects.property<Any>().convention(null) extension which is passed into the overrrideVersionCode|Name lambda by the plugin. But I'm not sure if being able to customize the versionCode/name from the extension of another plugin is a common use case.

@Wrywulf
Copy link
Author

Wrywulf commented Jan 13, 2021

Thanks for the input.

It seems even the

            project.afterEvaluate {
                myFilter.set(wrapperExtension.myFilter.get())
            }

cannot be done (it triggers Cannot query the value of extension 'wrapper' property 'myFilter' because it has no value available)
I've sort of paused the idea for now.

I understand that this is probably a niche use case so it makes sense to not pollute the API with these specific requirements.

However, I think it the metadata idea could make sense at some point. Perhaps as an optionally implemented "overloaded" lambda with this extra parameter. It would definitely lift the current constraints and make it flexible for use cases similar to mine (hydrating the lambda with external data).

@ychescale9
Copy link
Member

cannot be done (it triggers Cannot query the value of extension 'wrapper' property 'myFilter' because it has no value available)
I've sort of paused the idea for now.

Hmm, I was able to do get this to work locally. How are you defining your WrapperExtension?

This is how I defined it:

open class WrapperExtension internal constructor(objects: ObjectFactory) {
    val myFilter = objects.property<String>().convention(null)
}

@Wrywulf
Copy link
Author

Wrywulf commented Jan 13, 2021

This is how my extension is defined:

open class WrapperExtension @Inject constructor(objectFactory: ObjectFactory) {
    val myFilter: Property<String> = objectFactory.property(String::class)
}

perhaps the convention is the reason?

@ychescale9
Copy link
Member

That doesn't seem to make a difference. Unless you're not actually setting myFilter in your custom plugin configuration?

Here are all the changes I have locally:

In buildSrc/build.gradle.kts:

dependencies {
    ...
    implementation("io.github.reactivecircus.appversioning:app-versioning-gradle-plugin:0.8.1")
}

gradlePlugin {
    plugins {
        register("wrapper") {
            id = "wrapper-plugin"
            implementationClass = "io.github.reactivecircus.streamlined.WrapperPlugin"
        }
    }
}

In my buildSrc/WrapperPlugin.kt:

package io.github.reactivecircus.streamlined

import com.android.build.gradle.internal.plugins.AppPlugin
import io.github.reactivecircus.appversioning.AppVersioningExtension
import io.github.reactivecircus.appversioning.AppVersioningPlugin
import java.util.concurrent.atomic.AtomicReference
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
import org.gradle.kotlin.dsl.property
import org.gradle.kotlin.dsl.withType

class WrapperPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.plugins.withType<AppPlugin> {
            val wrapperExtension = project.extensions.create("wrapper", WrapperExtension::class.java)
            project.plugins.apply(AppVersioningPlugin::class.java)
            val extension = project.extensions.getByType(AppVersioningExtension::class.java)

            val myFilter = AtomicReference<String>(null)
            project.afterEvaluate {
                myFilter.set(wrapperExtension.myFilter.get())
            }

            extension.overrideVersionName { gitTag, providerFactory, variantInfo ->
                myFilter.get()
            }
        }
    }
}

open class WrapperExtension internal constructor(objects: ObjectFactory) {
    val myFilter = objects.property<String>().convention(null)
}

Finally in the app/build.gradle.kts:

plugins {
    `wrapper-plugin`
    id("com.android.application")
    kotlin("android")
    ...
}

wrapper {
    myFilter.set("0-9]*.[0-9]*.[0-9]*")
}

...

Running ./gradlew generateAppVersionInfoForDebug produces:

> Task :app:generateAppVersionInfoForDebug
Generated app version code: 300.
Generated app version name: "0-9]*.[0-9]*.[0-9]*".

@Wrywulf
Copy link
Author

Wrywulf commented Jan 13, 2021

Thanks a lot for looking into this. I'll try to replicate what you did and figure out why it fails on my side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants