Developing modules for OpenTofu
Traditional module development means one project per module and scripting a number of tools to be able to test, push to Git and tag. No longer so, because due to the power of Gradle, we can manage a number of modules within one project and also publish to a variety of sources.
Declaring and configuring a module
opentofu {
modules {
myModule { (1)
overrideModuleNamePostfix('hismodule') (2)
providerName = 'aws' (3)
failOnExtraDirectories = true (4)
allowSubdirectories 'json-templates', 'yaml-templates' (5)
failOnMissingLicense = true (6)
includeTestsWhenPublishing = false (7)
srcDir = 'src/somewhere/else' (8)
secondarySources 'src/module-common1', 'src/module-common2' (9)
environment FOO: 'BAR' (10)
addEnvironmentProvider project.provider { -> [FOO: 'BAR'] } (11)
}
}
}
1 | Creates a module which in Gradle is called myModule . |
2 | When published the module name postfix is derived from the module name, but lowercased — i.e., it will be mymodule .
It is possible to override that postfix with something else should it be needed. |
3 | The name of the main provider that this applies to. If a module references more than one provider, convention it to use the provider that has the most resources in the module. |
4 | Fail validation if any directories other than the test directory, modules , and examples exist in the root of the source directory.
Default is true . |
5 | Add additional directories which are allowed and thus will not cause the validation described above to fail. |
6 | Whether to check for a missing LICENSE file.
Even if this is set to false a module repository might require this to be true and thus the validation can still fail.
Default is true . |
7 | Whether to include the test directory and test files that are in the module root when publishing to a repository.
Default is true . |
8 | Convention is for the source directory to be located below src/tf-modules and be named the same as the module name — i.e., src/tf-modules/myModule .
If needed, the source directory can be changed. |
9 | Additional files and directories that affect the up-to-date status of tasks related to the module. |
10 | Additional environment variables to be passed when testing the module. |
11 | Additional environment variables can also be lazy-evaluated via a provider. |
Testing a module
Write tests as per normal for the module and then run the appropriate test task.
$ ./gradlew tofuModuleMyModuleTest
See Tests for more details on writing and executing tests.
Publishing a module
A module can be published to more than one target, and these targets and declared in a moduleRepositories
block.
There are two types of configuration.
The first is what globally applies to the target.
The second is configuration specific to the pairing of a target repository and a module.
import org.ysb33r.gradle.opentofu.publisher.OpenTofuRegistry
import org.ysb33r.gradle.opentofu.publisher.GenericGit
import org.ysb33r.gradle.opentofu.publisher.Gitlab
import org.ysb33r.gradle.opentofu.publisher.Local
opentofu {
moduleRepositories {
publicRegistry(OpenTofuRegistry) { (1)
prefixVersionWithV = false (2)
credentials { (3)
}
module('myModule') { (4)
projectNameSpace = 'ysb33r/my-awesome-module' (5)
preferSshOverHttps = true (6)
branch = 'main' (7)
commitMessage = 'my awesome update' (8)
preserve { (9)
include 'foo'
}
tagOnPush( 'my tag message') (10)
tagOnPush( 'myTag', 'my-tag-message') (11)
failOnTagExists = true (12)
}
}
myCustomGit(GenericGit) { (13)
prefixVersionWithV = true
credentials { (14)
}
module('myModule') {
repoURI = 'https://my.server.example/foo/bar.git' (15)
branch = 'main'
commitMessage = 'my awesome update'
preserve {
}
tagOnPush( 'my tag message')
tagOnPush( 'myTag', 'my-tag-message')
failOnTagExists = true
}
}
myGitlab(Gitlab) { (16)
prefixVersionWithV = true
credentials { (17)
}
module('myModule') {
gitlabHost = 'gitlab.com.example' (18)
projectNameSpace = 'ysb33r/my-awesome-module' (19)
preferSshOverHttps = true
branch = 'main'
commitMessage = 'my awesome update'
preserve {
}
tagOnPush( 'my tag message')
tagOnPush( 'myTag', 'my-tag-message')
failOnTagExists = true
}
}
aLocalFileServer(Local) { (20)
destinationDir = '/path/to/root/of/repository' (21)
prefixVersionWithV = false
module 'myModule' (22)
}
}
}
1 | Ability to publish to the public registry. This takes care of publishing to the repository on Github. The module author is still responsible for registering the module with the public registry. Public registration adds strict checks to what is allowed and required in the module source. |
2 | Whether version string should be prefixed with a v .
Default is false .
This setting can be applied to any repository. |
3 | Configures Github credentials. This is exactly the same as for the Gradle Git plugin suite. |
4 | Associates a module with the public registry. |
5 | Where the project is located on Github. |
6 | Whether to use ssh (true ) or https (false ). |
7 | Branch to publish to. |
8 | The commit message. Is lazy-evaluated and can be set from a provider. |
9 | Files to preserve between commits. The default is to remove the previous commit’s files and replace them with the new set. There may be cases where this is useful. |
10 | Whether a push should also tag the branch.
This option derives the tag directly from the module version and
will also prefix it with v if so configured.
The tag message is lazy-evaluated and can be set from a provider.
It can be used to set a longer release message.
It is not allowed to be empty. |
11 | Alternative form for enabling a tag on push. This option can be used to manipulate the tag and not use the default. |
12 | Whether to prevent the push if a tag already exists.
If this is set to false , the remote tag will be deleted and then moved to match the latest commit. |
13 | Ability to publish to any Git repository. |
14 | Configures Git credentials. This is exactly the same as for the Gradle Git plugin suite. |
15 | Sets the remote URI of the repository |
16 | Ability to publish a Git repository on Gitlab. |
17 | Configures Gitlab credentials. This is exactly the same as for the Gradle Git plugin suite. |
18 | The Gitlab host if not gitlab.com . |
19 | The project namespace on Gitlab i.e., ysb33rOrg/myGroup/my-project . |
20 | Ability to publish to a local file server. This has primarily been developed for testing this plugin, but might be useful to some. |
21 | Path to the root of the repository.
The module will be published below this in a subfolder of format <MODULE-NAME>/<MODULE-VERSION> . |
22 | Associates a module with a local publication. Unlike most other module repositories, no configuration is required. |
You can publish a module to a specific target
$ ./gradlew tofuModuleMyModulePublishPublicRegistry (1)
1 | The name of the repository forms the last part of the task names |
You can also publish a module to all the targets
$ ./gradlew tofuModuleMyModulePublishAll (1)
Also, you can publish all modules to all linked repositories:
$ ./gradlew tofuModulePublishAll