Inject default config from one plugin before other is applied

Hey everyone!

Is it somehow possible from a plugin to inject default configuration for another plugin, before that plugin is applied?

More concrete: I want to specify default config for the axion-release plugin, but keep the possibility to overwrite it from the actual project.

Currently I do something like this:

project.pluginManager.withPlugin(AXION_RELEASE_PLUGIN_ID) { _ ->
	project.extensions.configure(VersionConfig::class.java) { ext ->
		ext.versionIncrementer("branchSpecific", mapOf(
			"^(main|master|HEAD)$" to "incrementMinor",
			"^(release/.+)$" to "incrementMinor",
			".*" to "incrementPatch"
		))

		ext.snapshotCreator { _, position: Any ->
			// configure SNAPSHOT suffixes for different branch types
		}
	}

	val versionConfig = project.extensions.getByType(VersionConfig::class.java)
	project.version = versionConfig.version
}

This works for the default config, but when I now add the scmVersion block in the actual project and overwrite the snapshotCreator, for example, that change has no effect.

I assume the reason to be, that my block here is executed AFTER the plugin is applied. And therefore the block in the actual project is executed before this and overwritten when this config comes into play.

So can I somehow do this configuration BEFORE the plugin is applied? Or can I somehow get hold of the Action<VersionConfig>, that is given to scmVersion in the actual project, so I can apply it afterwards again?

I know I can create an Action<VersionConfig> on a new extension created by my plugin and apply that after the fact. But - if possible - I would like it to stay in the original structure, so that there’s minimal migration effort by the projects, that want to use my plugin.

Thanks in advance!

Best, Max

The configuration of the extension works as you intend.
When the axion plugin is applied, your default values are set.
Then, after that, the build script is evaluated and sets the override.

Your problem is that you eagerly evaluate that propery in your plugin by using project.version = versionConfig.version.
versionConfig.version uses the snapshotCreator to calculate the version.
You need to delay this call as far as possible.
For example by doing

project.version = object {
	override fun toString() = versionConfig.version
}

But be aware, even with this, as soon as any build script or plugin or whatever tries to get the project.version, the evaluation is done with whatever is configured at that time.

So with the code I showed, you might get different values for project.version depending on when it is evaluated.

You could do some caching so that the version stays consistent like e.g.

project.version = object {
	val version by lazy {
		versionConfig.version
	}
	override fun toString() = version
}

but then it will always return the value when first project.version was evaluated after that call, so it might also not really be what you want.

All this is unfortunately mainly caused by project.version not being a Property that can be lazily used, but that’s how it is currently.

Ah, thanks! I somehow thought, that the project.extensions.create in the Axion release plugin already evaluates the config block. Thanks for clarifying that!

I know that using afterEvaluate is discouraged in many cases. But would this maybe be one of the rare occurrences, where it would make sense to use it? I think I would be fine with project.version having no value until everything is evaluated. It would be the same if I set it in the actual project. And it would be better imho, than having a different value depending on the time in the execution I query its value.

Best, Max

Nope. By using afterEvaluate you mainly add timing problems and potential race conditions. What if someone else then uses afterEvaluate to set the value and so on.

If you want to delay the evaluation, the “proper” way is to use the toString method of project.version.

And as long as noone is reading the version too soon in your build, you will be fine.

Even with afterEvaluate, you would get different values depending on call time, either <undefined> before your afterEvaluate, or then the calculated version later. If you want it as non-changing as possible, use the cached value as in my last example and either way you must hope that noone is evaluating the version before the value is set properly.

Ok, then I’ll go with one of those examples.

Thanks a lot!

Best, Max

1 Like