```def props = ...read profs from file appAutomate...
# community-support
v
Copy code
def props = ...read profs from file
appAutomateUpload {
    username = props["username"]
    password = props["password"]
}
I have a plugin which needs credentails. I have those in a file. How would I best set this up so the properties are not read every build? Is this a bad design on plugin part? Should it be working expecting a file path and read the file lazily when actually running the task? Or can I lazy configure a extension?
c
The overhead of reading/parsing a (presumably small) file is likely insignificant and may not be worth optimizing for. If you do wish to avoid reading this on every build, the best bet is to use Gradle’s configuration cache.
v
I mean it went from this
Copy code
loop all android variants
	...
	tasks.register("appAutomateUpload$variant") {
		def props = .... read prefs from file
		username = props["username"]
		password = props["password"]
	}
(as you can see props are read lazily) with task body being in buildSrc, to creating a plugin which will be moved out to a separate repo now it feels like a downgrade
and in my experience config cache is so not ready and broken often (even new kotlin breaks it)
c
agreed re: cc - it’s great when it works, but…
have you looked at ProviderFactory.fileContents?
providers.fileContents(file)
that allows for lazy reading of a file.
v
I noticed deploy plugins take in files mostly, or have this dual api
Copy code
play {
    serviceAccountCredentials.set(file("your-key.json"))
}
ill like into that, have no clue about it
btw stupid question, wont this actually do the job?
Copy code
appAutomateUpload {
	def props = ...read profs from file
    username = props["username"]
    password = props["password"]
}
just moving the reading into the extension? not sure when are those resolved tbh
c
that will be done at configuration time, which usually isn’t desirable. You’ll also need to check that the file isn’t read/parsed more than once (for optimal performance).
v
yea so I can only pass the file handle — but then the layout of the properties file needs to be documented since its hardcoded internally in the plugin now
c
your plugin can provide a Provider<Map<String,String>> that handles reading the file, parsing it into a map (lazily - only when resolved, and only once).
Copy code
provider {
def props = ...read profs from file
props
}  // assuming props is a Map<String,String>
v
but what would it return, the map? then the expected map keys need to be documented
guess only making it type safe is safe and self documenting, and will probably blow up groovy 😄
c
it return a provider to a map, yes. When resolved it’s used as in your examples
props["username"]
. Yea, type safety and groovy ugh…
v
Copy code
appAutomateUpload {
    credentials {
    	def props = ...read profs from file
    	Credentials(props["username"], props["password"])
    }
}
something like this?
c
be sure not to resolve the provider until in the execution phase - inside the tasks that need the props, generally.
v
whats a provider in gradle speak, just a plain lambda to defer stuff?
c
no, that isn’t right. If it’s a credentials object you are after, create a Provider<Credentials> and a receiver property Property<Credentials> on the extensions. A Provider is a specific interface in the Gradle API; more context available here.
v
im a bit lost. Credentials is my type or gradle type
c
Something like this (Kotlin): Extension:
Copy code
public abstract class AppAutomateUploadExtension {
    @get:Input
    public abstract val credentials : Property<Credentials>
}
Copy code
val credProvider = providers.provider {
            val props = readFile(...)
            return Credentials(props["username"], props["password"])
        }
…Credentials can be your type, if that exists and is what you are modelling.
v
interesting but isnt that the same as I did?
appAutomateUpload { credentials = { def props = ...read profs from file Credentials(props[“username”], props[“password”]) } }
or since for some reason I cannot use my own lambdas, I need to use the
Provider
, hence I need the
providers.provider
factory?
c
Not the same. It’s a Property/Provider, which is the key difference. That represents a supplier/factory for getting the credentials at a later time. That is then passed to where the credentials are needed (presumably a task), which then resolves the provider via
.get
.
oh, I see, the Groovy assignment of the lambda thing. similar, as long as credentials is a lambda type, and resolved later.
v
yea but I still want to be idiomatic, so Provider is the way to go right?
c
Yes. All the values on your extension should be Property objects (or List/Set/MapProperty as needed), as should all the attributes on your tasks.
you should only call
.get()
on a property/provider inside the execution phase, usually in a task action.
v
yes Ill only resolve those in execution phase. Now I’m just figuring out if my api or the
providers.provider
factory is nicer
c
the Providers API also offers mapping via
.map()
. Say you have a Provider<Map<String,String>> and want to pass one specific map entry into a task - you can go
prop.map { this["username"] }
to turn that into a Provider<String>. also available are conventions (defaults) for properties via
.convention()
.
for example
project.layout.projectDir.file("myFile.properties").map { // process file }
v
yea, interesting that I never heard of this yet. usually its just file handles
thank you for your help!
c
no worries, hope it works out for you.
e
if you use
providers
, then
Copy code
tasks.register("myTask") {
    val credentials = project.providers.credentials(PasswordCredentials::class, name)
    doLast {
        val credentials = credentials.get()
        println("${credentials.username}:${credentials.password}")
    }
}
lets you provide it via any of
Copy code
# gradle.properties
myTaskUsername = foo
myTaskPassword = bar
or
Copy code
./gradlew myTask -PmyTaskUsername=foo -PmyTaskPassword=bar
or
Copy code
env ORG_GRADLE_PROJECT_myTaskUsername=foo ORG_GRADLE_PROJECT_myTaskPassword=bar ./gradlew myTask
without requiring credentials if the task isn't run