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.

  1. Download the latest SEU-as-code Archetype distribution from either Bintray or GitHub.

  2. 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.

  3. Customize the build.gradle file. Adjust the ext configuration section and set the seuRoot directory path and the seuName accordingly.

  4. Adjust the dependencies section and add the desired software packages using the software or home configurations. For a complete list of available packages have a look at the SEU-as-code Package repository or the Bintray repository.

  5. At this point you should add your SEU project to a version control system of your choice.

  6. 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

bootstrapSeu

createSeuacLayout, installSoftware

-

Initial tasks to create the SEU from scratch.

updateSeu

installSoftware

-

Updates a the SEU installation and applies the latest configuration.

destroySeu

-

DestroySeuTask

Destroy the complete SEU and deletes all directories. So be careful!

createSeuacLayout

-

CreateSeuacLayoutTask

Creates the basic directory layout for the SEU.

applySoftware

-

ApplyConfigurationTask

Apply the software configuration and install packages. New dependencies are installed, obsolete software will be removed.

applyHome

-

ApplyConfigurationTask

Apply the home configuration and install packages. New dependencies are installed, obsolete software will be removed.

runSoftwareHooks

-

RunHooksTask

Runs any software hooks after the installation.

runHomeHooks

-

RunHooksTask

Runs any home hooks after the installation.

createAsciiBanner

-

CreateAsciiBannerTask

Creates the ASCII banner file.

storeSeuacDb

-

StoreSeuacDbTask

Store the current SEU software package configuration.

Configurations

The plugin defines the following configurations:

Task name Description

seuac

Used for dependencies that need to be put in the root classloader, e.g. SQL drivers

software

Used for software dependencies that will be installed in the software directory.

home

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

osFamily

The OS family, either windows, macos, unix or unknown

osClassifier

The OS classifier, either win, mac, unix or ???

osArch

The OS architecture, either x86_64 or x86

Extension

The plugin defines the following extension properties in the seuAsCode closure:

Property name Type Default value Description

seuHome

String

-

The home directory for this SEU. Can be a VHD or any other valid directory.

projectName

String

-

The project name for this SEU.

layout

SeuacLayout

-

Optional. Defines the directory layout for this SEU.

datastore

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.

banner

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

setCredentials

-

SetCredentialsTask

Sets the credentials for a service. Invoke with --service [Name of service] parameter.

displayCredentials

-

DisplayCredentialsTask

Displays the credentials for a service of a credential. Invoke with --service [Name of service] parameter.

clearCredentials

-

ClearCredentialsTask

Clears the credentials for a service. Invoke with --service [Name of service] parameter.

Extra Properties

The plugin defines the following extra extension properties:

Property name Type Default value Description

credentials

Credentials

-

Object to query credentials. Invoke the String get(String service) method to get the credentials with the service name service.

Extension

The plugin defines the following closures in the credentials extension:

Property name Type Default value Description

keychainFile

String

null

The custom location for the MacOS keychain file to use.

propertyFile

String

null

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

gitInitAll

all gitInit<RepositoryName> tasks

-

Performs a Git init for all defined repositories.

gitCloneAll

all gitClone<RepositoryName> tasks

-

Performs a Git clone for all defined repositories.

gitPushAll

all gitPush<RepositoryName> tasks

-

Performs a Git push for all defined repositories.

gitPullAll

all gitPull<RepositoryName> tasks

-

Performs a Git pull for all defined repositories.

gitStatusAll

all gitStatus<RepositoryName> tasks

-

Performs a Git status for all defined repositories.

gitInit<RepositoryName>

-

GitInitTask

Performs a Git init for the named Git repository.

gitClone<RepositoryName>

-

GitCloneTask

Performs a Git clone for the named Git repository.

gitStatus<RepositoryName>

-

GitStatusTask

Performs a Git status for the named Git repository.

gitCommit<RepositoryName>

-

GitCommitTask

Performs a Git commit for the named Git repository. Override message project property.

gitPush<RepositoryName>

-

GitPushTask

Performs a Git push for the named Git repository to remote origin.

gitPull<RepositoryName>

-

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

git

NamedDomainObjectContainer<GitRepository>

-

Contains the named Git repository definitions.

url

String

-

The URL of the named Git repository. Include username and password in the URL.

directory

File

-

The local directory of the named Git repository.

branch

String

-

The branch name to use. Defaults to HEAD. If singleBranch is true this must be a valid refspec like refs/heads/BRANCHNAME.

username

String

-

The username used for authentication.

password

String

-

The password used for authentication.

options

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

osFamily

The OS family, either windows, macos, unix or unknown

osClassifier

The OS classifier, either win, mac, unix or ???

osArch

The OS architecture, either x86_64 or x86

Extension

The plugin defines the following closures in the platform extension:

Property name Type Default value Description

win

Closure

-

Apply configuration to project if running on Windows.

mac

Closure

-

Apply configuration to project if running on MacOS.

unix

Closure

-

Apply configuration to project if running on Linux or Unix.

x86

Closure

-

Apply configuration to project if running on x86 system.

x86_64

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

svnCheckoutAll

all svnCheckout<RepositoryName> tasks

-

Performs a SVN checkout of all defined repositories.

svnUpdateAll

all svnUpdate<RepositoryName> tasks

-

Performs a SVN update of all defined repositories.

svnCheckout<RepositoryName>

-

SvnCheckoutTask

Performs a SVN checkout of the named SVN repository.

svnUpdate<RepositoryName>

-

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

subversion

NamedDomainObjectContainer<SvnRepository>

-

Contains the named SVN repository definitions.

url

String

-

The URL of the named SVN repository.

directory

File

-

The local checkout directory of the named SVN repository.

username

String

-

The username used to authenticate.

password

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:

5 Steps Packaging Process

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' }
}