# Setting project.version using a plugin

**URL:** <https://discuss.gradle.org/t/setting-project-version-using-a-plugin/45475>\
**Category:** Help/Discuss\
**Created:** [April 24, 2023, 8:44pm UTC](https://discuss.gradle.org/t/setting-project-version-using-a-plugin/45475 "2023-04-24T20:44:14Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![mos20](https://avatars.discourse-cdn.com/v4/letter/m/ecd19e/32.png) [@mos20](https://discuss.gradle.org/u/mos20)\
**Post date:** [April 24, 2023, 8:44pm UTC](https://discuss.gradle.org/t/setting-project-version-using-a-plugin/45475/1 "2023-04-24T20:44:14Z")

</div>

I’m looking for advice on how best to set `project.version` from a plugin.

To set the version when the logic is simple, I’d write something like this directly in the build script:

```plaintext
version = System.getenv("FOO") ?: "bar"

```

But what about when the logic is more complicated, and involves doing some non-trivial work, such as interacting with my release infrastructure?

I could set `project.version` in the `apply` method of a plugin, but I suspect this is bad practice:

```plaintext
class VersionPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.version = System.getenv("FOO") ?: "bar"
    }
}

```

I see a couple of issues with this approach:

1. It risks being hard to follow: If my plugin lives in a separate project, finding what is setting the version is difficult.
2. If the calculation of the version takes some appreciable time, the configuration time for every task takes a hit, even when executing tasks that don’t use the version.

If I understand correctly, `version` isn’t compatible with [lazy properties](https://docs.gradle.org/current/userguide/lazy_configuration.html#lazy_properties), because `project.setVersion` just uses the `toString()` of whatever it is passed. Is there some other way a plugin can expose a property that the version can be set to in such a way that the evaluation is deferred?

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [April 24, 2023, 10:00pm UTC](https://discuss.gradle.org/t/setting-project-version-using-a-plugin/45475/2 "2023-04-24T22:00:16Z")

</div>

`toString()` is not called on `setVersion`.  
`toString()` needs to be called whenever you use the version.  
So to calculate it lazily, you can set any object as version that calculates the version on first `toString()` call and caches the result for further calls.

But please strongly consider whether you really want to do that.  
Imho any version calculation by release infrastructure, VCS, or whatever dynamic source is bad.  
That way you do not get reproducible builds, and you also cannot build outside that infrastructure.  
If you e.g. use JGit to get the last tag and calculate the version from it, then you are already lost when you try to build from a secondary Git worktree as JGit cannot handle them. Same for shallow clones where the base for the calculation might not be available, …

---

<div class="post-metadata">

**Author:** ![mos20](https://avatars.discourse-cdn.com/v4/letter/m/ecd19e/32.png) [@mos20](https://discuss.gradle.org/u/mos20)\
**Post date:** [April 25, 2023, 11:07am UTC](https://discuss.gradle.org/t/setting-project-version-using-a-plugin/45475/3 "2023-04-25T11:07:27Z")

</div>

Thanks for the speedy response, @Vampire .

I agree with you that reproducible builds are important, but that leads to some awkwardness for version that I’m not sure how best to tackle.

In my case, I’m using the `maven-publish` plugin, so the version ends up in the names and metadata of the published artifacts. This breaks reproducibility because the Maven repository won’t allow the same artifact to be published twice (in particular, if there’s a transient problem, like the upload of one artifact timing out, the build can’t be rerun; a new version number is needed). In a sense, the `maven-publish` plugin causes the release infrastructure to be involved in version calculation, because it enables the release infrastructure to reject versions.

My current solution is to pass the version to Gradle from my CI/CD pipeline (which is the only place where the version really matters for me, and where I have easy access to information like commit hash and build number), but the result is quite limited. It would be possible for the pipeline to generate a more meaningful version using shell scripting, but given that the output of the shell script is immediately going to be passed to Gradle which has the full power of the JVM available, I’m tempted to substitute the shell script with a Gradle plugin. Would this be unwise? Perhaps I’m abusing version here, and I should configure the publish task separately?

BTW, thanks for your suggestion about using `toString()` to defer calculation. It works perfectly:

```plaintext
class LazyVersion {
    private val version: String by lazy {
        System.getenv("FOO") ?: "bar"
    }
    override fun toString() = version
}

open class VersionPluginExtension {
    val version = LazyVersion()
}

class VersionPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.extensions.create<VersionPluginExtension>("versionPluginExtension")
    }
}

apply<VersionPlugin>()

version = the<VersionPluginExtension>().version

```

I have a slight concern that future versions of Gradle might call `toString()` more eagerly, but though this might slow my build, it can’t have any functional impact, so it’s not a big deal.

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [April 25, 2023, 3:19pm UTC](https://discuss.gradle.org/t/setting-project-version-using-a-plugin/45475/4 "2023-04-25T15:19:07Z")

</div>

It could actually be any plugin you apply, not only Gradle itself, that might need the version and thus call `toString()`. If it is needed, it is needed. 🙂

> In a sense, the `maven-publish` plugin causes the release infrastructure to be involved in version calculation, because it enables the release infrastructure to reject versions.

Well, not really. It is up to the discretion of your repository software whether it allows to overwrite version artefacts or not. Gradle would be perfectly happy with that. 🙂

So yeah, if you need such automatic failure recovery, maybe for you such version calculation is the way to go. I just personally don’t like it. A “publish” does imho not have to be reproducible if the version is already published and the repository does not allow overwriting.
