Background
Every software project starts with the setup of a local development environment: a JDK, the preferred IDE and build tool, a local database and application server and so forth. Everything you and your team needs to be productive from day one. Time is valuable, so you take the quick route and reuse a development environment from a previous project. Broken windows from day one! With the first required changes things usually start to go wrong. The individual environments start to diverge and problems during the build or local execution of your software are inevitable.
How can you do better? The short answer is: with SEU-as-code. (SEU; that is the German acronym for software development environment). It is a lightweight approach and tool based on Gradle that helps to alleviate and automate the provisioning of developers and their local software development environments. The idea behind SEU-as-code is simple: describe and implement your software development environment as Gradle build file. Required software packages become dependencies. Shell scripts are implemented in Groovy, additional Gradle tasks or in JavaScript when using Nashorn from the command line. Everything that makes up your development environment is version controlled just as any other source code. This way you can go back to a previous state of your SEU easily. Also you are able to reproduce the exact same result on every developer machine. And as a bonus you get full IDE support for all the scripts and code that make up your SEU.
Quickstart
If you are still reading this documentation than you have decided to build the SEU for your next project as-code. Excellent choice! The first version of your SEU is up and running in 15 minutes.
-
Download the latest SEU-as-code Archetype distribution from either Bintray or GitHub.
-
Extract the archive to a custom location and rename the template project directory, e.g.
seubase
. For optimal isolation, you can use a virtual hard drive (VHD) on Windows or a sparse image disk file for Mac OSX. -
Customize the
build.gradle
file. Adjust theext
configuration section and set theseuRoot
directory path and theseuName
accordingly. -
Adjust the
dependencies
section and add the desired software packages using thesoftware
orhome
configurations. For a complete list of available packages have a look at the SEU-as-code Package repository or the Bintray repository. -
At this point you should add your SEU project to a version control system of your choice.
-
Finally, open a console and issue the following command to create your SEU:
gradlew bootstrapSeu
Plugins
This section of the documentation is dedicated to the SEU-as-code plugins. They do the heavy lifting when used in a Gradle build. All of the plugins can be used independently, some of them can even be used standalone outside of an SEU context.
Base Plugin
A Gradle plugin to create SEU installations from code. It provides basic tasks to
bootstrap and update the SEU and to install and update the software packages. The
SEU can be configured and customized using the seuAsCode
extension.
Basic Usage
Build script snippet for use in all Gradle versions, using the Bintray Maven repository:
buildscript {
repositories {
mavenCentral()
maven {
url 'https://dl.bintray.com/seu-as-code/gradle-plugins'
}
}
dependencies {
classpath 'de.qaware.seu.as.code:seuac-base-plugin:2.4.0'
}
}
apply plugin: 'de.qaware.seu.as.code.base'
Build script snippet for new, incubating, plugin mechanism introduced in Gradle 2.1:
plugins {
id 'de.qaware.seu.as.code.base' version '2.4.0'
}
Tasks
The plugin defines the following tasks:
Task name | Depends on | Type | Description |
---|---|---|---|
|
|
- |
Initial tasks to create the SEU from scratch. |
|
|
- |
Updates a the SEU installation and applies the latest configuration. |
|
- |
DestroySeuTask |
Destroy the complete SEU and deletes all directories. So be careful! |
|
- |
CreateSeuacLayoutTask |
Creates the basic directory layout for the SEU. |
|
- |
ApplyConfigurationTask |
Apply the software configuration and install packages. New dependencies are installed, obsolete software will be removed. |
|
- |
ApplyConfigurationTask |
Apply the home configuration and install packages. New dependencies are installed, obsolete software will be removed. |
|
- |
RunHooksTask |
Runs any software hooks after the installation. |
|
- |
RunHooksTask |
Runs any home hooks after the installation. |
|
- |
CreateAsciiBannerTask |
Creates the ASCII banner file. |
|
- |
StoreSeuacDbTask |
Store the current SEU software package configuration. |
Configurations
The plugin defines the following configurations:
Task name | Description |
---|---|
|
Used for dependencies that need to be put in the root classloader, e.g. SQL drivers |
|
Used for software dependencies that will be installed in the software directory. |
|
Used for dependencies that will be installed in the home directory. |
Extra Properties
The plugin defines the following extra properties, that may be used for platform specific behavior. This functionality will soon be removed from this plugin in favor of the platform plugin.
Task name | Description |
---|---|
|
The OS family, either |
|
The OS classifier, either |
|
The OS architecture, either |
Extension
The plugin defines the following extension properties in the seuAsCode
closure:
Property name | Type | Default value | Description |
---|---|---|---|
|
String |
- |
The home directory for this SEU. Can be a VHD or any other valid directory. |
|
String |
- |
The project name for this SEU. |
|
SeuacLayout |
- |
Optional. Defines the directory layout for this SEU. |
|
SeuacDatastore |
- |
Optional. Defines the datastore used to persist the SEU configuration. Currently H2 (use jdbc:h2:seuac as URL) and MapDB (use file:mapdb:seuac as URL) are supported. |
|
SeuacBanner |
- |
Optional. Defines the ASCII banner configuration. |
Examples
Defining basic SEU properties
seuAsCode {
// these two properties are mandatory
seuHome 'S:'
projectName 'SEU-as-code'
// the other properties are optional
layout {
codebase 'S:/codebase/'
docbase 'S:/docbase/'
home 'S:/home/'
repository 'S:/repository/'
software 'S:/software/'
temp 'S:/temp/'
}
datastore {
// this is for backwards compatibility to H2 1.3.x
// for latest H2 1.4.x you can enable the MVStore
url 'jdbc:h2:./seuac;mv_store=false'
// use this URL if you want to store things with MapDB
// url 'file:mapdb:./seuac'
user 'sa'
password 'sa'
}
banner {
font 'slant'
reflection 'no'
adjustment 'center'
stretch 'yes'
width 80
}
}
Install software as dependencies
The plugin configurations are used to determine the installation directory of the software package dependencies. You have to specify a Maven repository where these dependencies are located, in this case we used Bintray and JCenter.
repositories {
jcenter()
maven { url 'https://dl.bintray.com/seu-as-code/maven' }
}
dependencies {
// dependencies for the Groovy root classloader
seuac 'org.codehaus.groovy.modules.scriptom:scriptom:1.6.0'
seuac 'com.h2database:h2:1.4.188'
// mandatory dependencies for basic SEU setup
home 'de.qaware.seu.as.code:seuac-home:2.0.0'
software 'de.qaware.seu.as.code:seuac-environment:2.0.0:jdk8'
// additional dependencies for a Groovy development environment
software 'org.gradle:gradle:2.14'
software 'org.groovy-lang:groovy:2.4.4'
}
Building platform specific SEUs
In case you want to build platform specific SEUs with a lot of differences you can use the platform closure with its nested closures for each supported platform. This functionality will soon be removed from this plugin in favor of the platform plugin.
platform {
win { // add Windows specific code like dependencies or tasks here }
mac { // add MacOS specific code like dependencies or tasks here }
unix { // add Unix or Linux specific stuff like dependencies or tasks here }
x86 { // add 32-bit specific stuff like dependencies or tasks here }
x86_64 { // add 64-bit specific code like dependencies or tasks here }
}
Credentials Plugin
A Gradle plugin for the secure storage of your credentials either using the Windows Data Protection API (DPAPI) or the MacOS keychain mechanism. Storing any credentials in clear text in your sources or your build file is not only bad practice, it is just plain stupid since this makes it really easy for a potential attacker to access your systems.
Basic Usage
Build script snippet for use in all Gradle versions, using the Bintray Maven repository:
buildscript {
repositories {
mavenCentral()
maven {
url 'https://dl.bintray.com/seu-as-code/gradle-plugins'
}
}
dependencies {
classpath 'de.qaware.seu.as.code:seuac-credentials-plugin:2.4.0.RC1'
}
}
apply plugin: 'de.qaware.seu.as.code.credentials'
Build script snippet for new, incubating, plugin mechanism introduced in Gradle 2.1:
plugins {
id 'de.qaware.seu.as.code.credentials' version '2.4.0.RC1'
}
Tasks
The plugin defines the following tasks:
Task name | Depends on | Type | Description |
---|---|---|---|
|
- |
SetCredentialsTask |
Sets the credentials for a service. Invoke with |
|
- |
DisplayCredentialsTask |
Displays the credentials for a service of a credential. Invoke with |
|
- |
ClearCredentialsTask |
Clears the credentials for a service. Invoke with |
Extra Properties
The plugin defines the following extra extension properties:
Property name | Type | Default value | Description |
---|---|---|---|
|
Credentials |
- |
Object to query credentials. Invoke the |
Extension
The plugin defines the following closures in the credentials
extension:
Property name | Type | Default value | Description |
---|---|---|---|
|
String |
|
The custom location for the MacOS keychain file to use. |
|
String |
|
The custom location of the secure properties file under Windows. |
Examples
Command line usage
First add the credentials for the Nexus
service by invoking one of the following
Gradle tasks, you will be asked for the username and password on the Console:
$ ./gradlew setCredentials --service Nexus
$ ./gradlew setCredentials --service Nexus --username fooUser
You can also display and clear the credentials from the command line using the following Gradle tasks:
$ ./gradlew displayCredentials --service Nexus
$ ./gradlew clearCredentials --service Nexus
Using credentials in a build script
Once you have set the credentials via the command line you can then use this
credential information in your build script, e.g. in the repositories section,
via the credentials
extra property:
repositories {
mavenCentral()
maven {
url nexusUrl
credentials {
// use array type access to credentials via service name
username project.credentials['Nexus'].username
password project.credentials['Nexus'].password
// use getter access to credentials via service name
username project.credentials.get('Nexus').username
password project.credentials.get('Nexus').password
// or use string interpolation
username "${credentials['Nexus'].username}"
password "${credentials['Nexus'].password}"
}
}
}
Custom keychain or property file location
The plugin comes with sensible default values where the credentials are stored.
On MacOS this will be the user’s default login keychain, and on Windows the
secure-credentials.properties
file is stored in the user’s Gradle home dir.
In case you want to override these locations you can define these using the
credentials
extension in your Gradle build file.
credentials {
keychainFile = "$projectDir/SEU-as-code.keychain"
propertyFile = "$projectDir/secure-credentials.properties"
}
Git Plugin
A Gradle plugin for handling Git repositories. It provides basic tasks to init, clone commit, push and push Git repositories. This plugin does not require a Git CLI since it uses the JGit library under the hood. The repositories can be configured using the plugin extension.
Basic Usage
Build script snippet for use in all Gradle versions, using the Bintray Maven repository:
buildscript {
repositories {
mavenCentral()
maven {
url 'https://dl.bintray.com/seu-as-code/gradle-plugins'
}
}
dependencies {
classpath 'de.qaware.seu.as.code:seuac-git-plugin:2.3.0.RC2'
}
}
apply plugin: 'de.qaware.seu.as.code.git'
Build script snippet for new, incubating, plugin mechanism introduced in Gradle 2.1:
plugins {
id 'de.qaware.seu.as.code.git' version '2.3.0.RC2'
}
Tasks
The plugin defines the following tasks:
Task name | Depends on | Type | Description |
---|---|---|---|
|
all |
- |
Performs a Git init for all defined repositories. |
|
all |
- |
Performs a Git clone for all defined repositories. |
|
all |
- |
Performs a Git push for all defined repositories. |
|
all |
- |
Performs a Git pull for all defined repositories. |
|
all |
- |
Performs a Git status for all defined repositories. |
|
- |
GitInitTask |
Performs a Git init for the named Git repository. |
|
- |
GitCloneTask |
Performs a Git clone for the named Git repository. |
|
- |
GitStatusTask |
Performs a Git status for the named Git repository. |
|
- |
GitCommitTask |
Performs a Git commit for the named Git repository. Override message project property. |
|
- |
GitPushTask |
Performs a Git push for the named Git repository to remote origin. |
|
- |
GitPullTask |
Performs a Git pull for the named Git repository from remote origin. |
Extension
The plugin defines the following extension properties in the git
closure:
Property name | Type | Default value | Description |
---|---|---|---|
|
NamedDomainObjectContainer<GitRepository> |
- |
Contains the named Git repository definitions. |
|
String |
- |
The URL of the named Git repository. Include username and password in the URL. |
|
File |
- |
The local directory of the named Git repository. |
|
String |
- |
The branch name to use. Defaults to HEAD.
If |
|
String |
- |
The username used for authentication. |
|
String |
- |
The password used for authentication. |
|
GitOptions |
- |
The Git command options. |
Examples
Defining Git repositories
The following example defines the Git repository of the SEU-as-code plugins repo. The example does not hardcode the username and password properties, instead you should use either project properties or the SEU-as-code credentials plugin.
git {
SeuAsCodePlugins {
url 'https://github.com/seu-as-code/seu-as-code.plugins.git'
directory file("$seuHome/codebase/seu-as-code.plugins/")
branch 'HEAD'
username gitUsername
password gitPassword
options {
clone {
singleBranch = false
cloneSubmodules = true
noCheckout = false
timeout = 300
}
pull {
rebase = true
timeout = 600
}
push {
dryRun = false
pushAll = true
pushTags = true
timeout = 200
force = true
}
}
}
}
Working with Git repositories
Once you have defined one or more Git repositories using the plugin extension, you can perform the support operations by calling the associated tasks. Most of the command options from the configuration extension can also be set as command line options.
$ ./gradlew gitCloneSeuAsCodePlugins
$ ./gradlew gitPullAll --rebase true
...
$ ./gradlew gitCommitSeuAsCodePlugins --message "New feature added."
$ ./gradlew gitPushAll --all
Platform Plugin
A basic Gradle plugin that allows to apply platform specific configurations in a Gradle build file. Originally, this plugin has been developed to enable multi-platform SEUs. In a mixed team you sometimes have team members that develop under Windows, MacOS or Linux. But you want to support all these platform via one Gradle build file. But usually you need to use different dependency versions between these platforms or you may require different implementations of the same task depending on the platform.
Basic Usage
Build script snippet for use in all Gradle versions, using the Bintray Maven repository:
buildscript {
repositories {
mavenCentral()
maven {
url 'https://dl.bintray.com/seu-as-code/gradle-plugins'
}
}
dependencies {
classpath 'de.qaware.seu.as.code:seuac-platform-plugin:1.0.0'
}
}
apply plugin: 'de.qaware.seu.as.code.platform'
Build script snippet for new, incubating, plugin mechanism introduced in Gradle 2.1:
plugins {
id 'de.qaware.seu.as.code.platform' version '1.0.0'
}
Extra Properties
The plugin defines the following extra properties, that may be used for platform specific behaviour:
Task name | Description |
---|---|
|
The OS family, either |
|
The OS classifier, either |
|
The OS architecture, either |
Extension
The plugin defines the following closures in the platform
extension:
Property name | Type | Default value | Description |
---|---|---|---|
|
Closure |
- |
Apply configuration to project if running on Windows. |
|
Closure |
- |
Apply configuration to project if running on MacOS. |
|
Closure |
- |
Apply configuration to project if running on Linux or Unix. |
|
Closure |
- |
Apply configuration to project if running on x86 system. |
|
Closure |
- |
Apply configuration to project if running on x86_64 system. |
The following example shows the full extension configuration in code:
platform {
win { // add Windows specific code like dependencies or tasks here }
mac { // add MacOS specific code like dependencies or tasks here }
unix { // add Unix or Linux specific stuff like dependencies or tasks here }
x86 { // add 32-bit specific stuff like dependencies or tasks here }
x86_64 { // add 64-bit specific code like dependencies or tasks here }
}
Examples
Basic extension configuration
The following example uses the extension configuration to add platform specific dependencies as well as platform specific task definitions.
platform {
win {
dependencies {
software 'io.github.msysgit:git:1.9.5'
software 'org.gradle:gradle:2.13'
}
task helloSeuAsCode(group: 'Example') << {
println 'Hello SEU-as-code on Windows.'
}
}
mac {
dependencies {
software 'org.gradle:gradle:2.14'
}
task helloSeuAsCode(group: 'Example') << {
println 'Hello SEU-as-code on MacOS.'
}
}
}
Platform specific dependencies
The following example uses the $osClassifier
extra property as classifier to
add a platform specific dependency.
dependencies {
software "de.qaware.seu.as.code:seuac-environment:2.3.0:$osClassifier"
}
Platform specific tasks
This example uses static methods from the Platform
class to enable tasks based
on the current platform the build is running on.
import static de.qaware.seu.as.code.plugins.platform.Platform.isWindows
import static de.qaware.seu.as.code.plugins.platform.Platform.isMacOs
task helloWorldOnWindows(group: 'Example') {
enabled = isWindows()
doLast {
println 'Hello World on Windows.'
}
}
task helloWorldOnlyIfMac(group: 'Example') {
onlyIf { isMacOs() }
doLast {
println 'Hello World only if Mac.'
}
}
SVN Plugin
A Gradle plugin for handling SVN repositories. Provides basic tasks to checkout SVN repositories and update local directories. The repositories can be configured using an extension.
Basic Usage
Build script snippet for use in all Gradle versions, using the Bintray Maven repository:
buildscript {
repositories {
mavenCentral()
maven {
url 'https://dl.bintray.com/seu-as-code/gradle-plugins'
}
}
dependencies {
classpath 'de.qaware.seu.as.code:seuac-svn-plugin:2.1.1'
}
}
apply plugin: 'de.qaware.seu.as.code.svn'
Build script snippet for new, incubating, plugin mechanism introduced in Gradle 2.1:
plugins {
id 'de.qaware.seu.as.code.svn' version '2.1.1'
}
Tasks
The plugin defines the following tasks:
Task name | Depends on | Type | Description |
---|---|---|---|
|
all |
- |
Performs a SVN checkout of all defined repositories. |
|
all |
- |
Performs a SVN update of all defined repositories. |
|
- |
SvnCheckoutTask |
Performs a SVN checkout of the named SVN repository. |
|
- |
SvnUpdateTask |
Performs a SVN update of the named SVN repository. |
Extension
The plugin defines the following extension properties in the subversion
closure:
Property name | Type | Default value | Description |
---|---|---|---|
|
NamedDomainObjectContainer<SvnRepository> |
- |
Contains the named SVN repository definitions. |
|
String |
- |
The URL of the named SVN repository. |
|
File |
- |
The local checkout directory of the named SVN repository. |
|
String |
- |
The username used to authenticate. |
|
String |
- |
The password used to authenticate. |
Examples
Defining SVN repositories
The following example defines the SVN repository for the SEU-as-code plugins repo. The example does not hardcode the username and password properties, instead you should either use project properties or the SEU-as-code credentials plugin.
subversion {
SeuAsCodePlugins {
url 'https://github.com/seu-as-code/seu-as-code.plugins'
directory file("$seuHome/codebase/seu-as-code.plugins/")
username svnUsername
password svnPassword
}
}
Working with SVN repositories
Once you have defined one or more SVN repositories using the plugin extension, you can perform a SVN checkout and update on each repository individually or on all defined repos.
$ ./gradlew svnCheckoutSeuAsCodePlugins
$ ./gradlew svnUpdateAll
Packages
The idea of SEU-as-code is pretty useless without software packages. Instead of inventing somthing totally new from scratch we decided to go for a more simpler solution. The installation of software packages using SEU-as-code is basically a simple copy deployment. The artifacts are based on the original open source release artifacts of the original project, spiced up with some shell scripts or some additional customization scripts.
Building Packages
Building packages is not hard. Again, we mainly use Gradle to build the software packages. A good way to start is to have a look at the package builds that are available at the SEU-as-code Packages GitHub repository. Basically, the process of building your packages consists of five simple steps:

The first step is usually downloading the original software package artifact from its original site. An archive format is preferred, like ZIP, TAR or GZIP so that it can be easily extracted in the next step. If there is no archive available, then download the MSI or DMG or whatever, install it locally once, repackage as archive and continue from here.
plugins {
id 'de.undercouch.download' version '1.2'
}
import de.undercouch.gradle.tasks.download.Download
task downloadArchive(type: Download) {
src "https://services.gradle.org/distributions/gradle-2.14-bin.zip"
dest "$buildDir"
}
Next, we extract the previously downloaded artifact into the build directory. Thanks
to the power of Gradle this is an easy one using the Copy
task type together with
the zipTree
function.
task extractArchive(type: Copy, dependsOn: downloadArchive) {
from { zipTree("$buildDir/gradle-2.14-bin.zip") }
into "$buildDir/files"
}
Now it is time to customize the package. Usually this implies added additional shell scripts or setting specific environment variables. Once this is done we can repackage the downloaded and extracted files together with our customizations.
task buildPackageBin(type: Jar, dependsOn: extractArchive) {
baseName project.name
version version
extension 'jar'
destinationDir buildDir
from "$buildDir/files"
from "files"
}
Finally, we can publish the created software artifact to an artifact repository such as Nexus. For this we use the standard Maven publishing plugin.
apply plugin: 'maven-publish'
publishing {
publications {
gradle(MavenPublication) {
artifact buildPackage
}
}
repositories {
// set the properties via -P to publish to your company repo
maven {
url = project.hasProperty('nexusUrl') ? project.nexusUrl : ''
credentials {
username = project.hasProperty('nexusUsername') ? project.nexusUsername : ''
password = project.hasProperty('nexusPassword') ? project.nexusPassword : ''
}
}
}
}
Package Repositories
Since SEU-as-code packages are basically ordinary Maven artifacts, a package repo is required to host the packages. Most of the open source packages in the GutHub packages repository are hosted at Bintray.
Use a dedicated repository. Don’t mix software packages with ordinary Maven artifacts. |
But you probably soon find that you may need some customizations that are very specific to your project or even your company. So where do you host your private software package artifacts? You have two options.
-
Private Company Repository: this option is suitable for generic, company wide artifacts you want to share. For example, we have a custom package for IntelliJ Ultimate deployed at our repo that is used in all of our project SEUs. So probably you also have an artifact repository server like Nexus or Artifactory available at your company you can use.
repositories {
maven {
url 'https://your.company/repo/seu-as-code'
}
}
-
Local SEU Repository: this options is most useful for small and very SEU or project specific artifacts and customizations. Those artifacts are part of your SEU and should be versioned with the other SEU files.
repositories {
flatDir { dirs 'repo' }
}