Best practice for dealing with different files per environment

Hello
I have an old (very old) java web project (so ‘war’ plugin) that was previously built with ant. The project contains some files, that differ per environment, within resources/META-INF. With ant those files have just been copied and renamed with three different build tasks (local, dev, prod).
I have searched around a lot but I am not sure what would be the best practice way in up to date gradle versions to deal with such a setup. Copying around files does not feel like the gradle way especially as I would also have to deal with possible duplicates.

What I basically did so far (not sure if it was the best way):

  • removed the files from resources/META-INF (so no duplicates to be placed from build)
  • created three folders like e.g src/env/local; src/env/prod and placed the files in the proper folder removing env parts from file names; so files have the same name now but reside in different folders and there are no default files within META-INF currently to prevent duplicates when env specific files are copied there. A DuplicateStrategy to overwrite files would come in handy. But I think, that copying around files or having to deal with duplicates might be the sign of inproper usage of gradle tasks.
  • register three tasks
['local', 'dev', 'prod'].each { env ->
    tasks.register("buildWar${env.capitalize()}", War) {
        from("src/env/$env") {
            into('WEB-INF/classes/META-INF')
        }
    }
}

This basically works but the questions now are:

  • Is this the best way to deal with?
  • What about the behaviour of the default ‘war’ task?
    – Should the default ‘war’ task be disabled? Will enable=false only prevent execution when calling gradle war? What about tasks that depend on war?
  • How to deal with maven-publish and artifactory publish to specify the correct build, e.g. I might only want to artifactory publish prod build. Do I have to create diffent publications for this also?
    Right now I only have one mavenJava publication.
    But I am definitely not sure whether this is the best approach. Because right now publishing would execute ‘war’-task and none of the specific task with the result that the WEB-INF/classes/META-INF folder is empty because the default war task does not consider any env specific files from src/env right now.
publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.web
        }
    }
}

Thx for any ideas!

A DuplicateStrategy to overwrite files would come in handy. But I think, that copying around files or having to deal with duplicates might be the sign of inproper usage of gradle tasks.

When you copy or sync, then INCLUDE effectively is an overwrite.
If you build an archive that supports multiple entries with the same name, both land in the archive.

But actually, I yet have to see a single use-case where seeing any duplicates strategy is a good idea.
You always just earn uncertainty as this will also hit for all duplicates maybe produced in the future inadvertently and you don’t notice.
It is practically always better to detect and fix the source for the duplicate files so that there actually are no duplicates.

Is this the best way to deal with?

“Best” heavily depends on what you want.
If you for example want to build all three wars within one Gradle run,
having three tasks is probably the only proper way to deal with it.

If in one run you always only want to build one of those wars,
it might also be fine to have just one task where you include one of the three files
depending on the env. You could even keep the files with the env in the name instead of in the parent directory and use rename to rename the one of the three that is actually included to the uniform name.

If you have one task, it of course will not be up-to-date if the env changes and needs to be rebuilt, whereas when you have three tasks, each task can individually be up-to-date, but then needs thrice as much diskspace if you built all three if that is a concern.

Heavily depends on the concrete situation and your concrete needs.

What about the behaviour of the default ‘war’ task?
– Should the default ‘war’ task be disabled? Will enable=false only prevent execution when calling gradle war? What about tasks that depend on war?

Disabling the task disables the task, full-stop.
How it is invoked is irrelevant.
It will just be skipped no matter how you trigger it.

Here again comes in the “depends on your situation” clause from above.
If for example the war is the end-artifact so nothing depends on it, having three tasks is fine.
If the war is consumed by something else that should get the right war according to env, it might again be better to have only the standard war task where you package the env-according file.

Disabling a task you don’t have full control over is seldomly a good idea.
If someone else is depending on that task and you disable it, you might inadvertently break things.

What about tasks that depend on war?

If the war that is declared as output happens to be lying around, it will use that potentially stale (maybe from an earlier run where it was not disabled) file as long as Gradle did not yet add full checks that outputs are only used if the task was actually run in the same build which might come in the future.

Or if the file is not there, will use the absent file which might mean it fails or might mean it ignores it, depending on how it uses it.

How to deal with maven-publish and artifactory publish to specify the correct build, e.g. I might only want to artifactory publish prod build.

Again, heavily depends on your situation.
You could work with one war that changes with env and disable (not define) publication if env is not production.
Or you could work with three wars and just publish the production one.
In the latter case - also for above part of the question - it might make sense to just use the standard war task as the production war and just have two custom war tasks for the other envs.

If you for some reason really want to orphan the built-in war task and create three custom ones and want to publish it with metadata, well you then either have to also create a custom ad-hoc component that you then publish, or you reconfigure the web component to contain your production war instead of the built-in war.