gradle-buildscan-recipes
gradle-buildscan-recipes copied to clipboard
A Gradle plugin for Build Scans
= Build Scans Recipes Gradle Plugin :buildscan-version: 1.10.1 :plugin-version: 0.2.3
image:http://img.shields.io/travis/melix/gradle-buildscan-recipes/master.svg["Build Status (travis)", link="https://travis-ci.org/melix/gradle-buildscan-recipes"] image:http://img.shields.io/badge/license-ASF2-blue.svg["Apache License 2", link="http://www.apache.org/licenses/LICENSE-2.0.txt"]
This plugin enhances the information published in https://scans.gradle.com[Build Scans] with custom values and tags.
This https://scans.gradle.com/s/wjgfuwn447g2o[sample Build Scan] was generated using this plugin, for example, on Travis CI.
This https://scans.gradle.com/s/sburrg2uho64k[other one] was generated locally, using this plugin too: you will notice
that it is marked as dirty
and shows the git status.
== Usage
Build script snippet for use in all Gradle versions: [source,groovy] [subs="attributes"] .build.gradle
buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } }
dependencies { classpath "com.gradle:build-scan-plugin:{buildscan-version}" classpath 'me.champeau.gradle:buildscan-recipes-plugin:{plugin-version}' } }
apply plugin: 'com.gradle.build-scan' apply plugin: me.champeau.gradle.buildscans.RecipesPlugin
buildScan { licenseAgreementUrl = 'https://gradle.com/terms-of-service' licenseAgree = 'yes' }
buildScanRecipes { recipes 'git-commit', 'git-status', 'teamcity', 'gc-stats' }
Build script snippet for new, incubating, plugin mechanism introduced in Gradle 2.1: [source,groovy] [subs="attributes"] .build.gradle
plugins { id 'com.gradle.build-scan' version '{buildscan-version}' id 'me.champeau.buildscan-recipes' version '{plugin-version}' }
== Configuration
The plugin provides a number of recipes that can be used to automatically add tags and custom values to Build Scans. The following recipes are available:
-
git-commit
: Tries to determine the Git commit ID for the build -
git-status
: Outputs the result ofgit status
, and tags the build asdirty
if there are uncommitted or changes files locally -
git-diff-to-gist
: Pushes the result ofgit diff
to a Gist and creates a link in the build scan to this diff -
teamcity
: Detects a build running under https://www.jetbrains.com/teamcity/[TeamCity], tags it asCI
, and adds a link to the build -
travis-ci
: Detects a build running under https://travis-ci.org[Travis CI], tags it asCI
and outputs information about the Travis environment -
disk-usage
: Shows information about Gradle disk usage. It will scan your$HOME/.gradle
directory as well as your project directory. Note that depending on your disk type, OS, disk usage and file system, this can turn to be very slow and add a non negligeable time at build start. You should therefore use this recipe with care. This recipe only works on Linux and Mac OS. -
gc-stats
: Shows statistics about garbage collection happening during your build. -
gist
: Allows to use a recipe found in a GitHub Gist -
remote
: Allows to use a recipe found in any remote URL -
file-leak-detection
: Attemps to identify tasks which leak file handles (experimental)
=== Recipe specific parameters
==== git-commit
The git-commit
recipe accepts an optional baseUrl
parameter. If specified, a link to the commit will also be created. For example:
[source,groovy]
buildScanRecipes { recipe 'git-commit', baseUrl: 'https://github.com/melix/gradle-buildscan-recipes/tree' }
==== git-diff-to-gist
This recipe will take the result of the git diff
command and paste it as a Gist. By default, this will create a private
Gist, but still be aware than anyone with the link can view the diff. For this recipe to work, you need a GitHub account
and an https://github.com/settings/tokens[API Token]. We strongly recommend that you create a token specifically for
this recipe, limited to the gist
access rights. The recipe takes 3 optional parameters:
-
user
is your GitHub username -
token
is your API token. If not specified explicitly, will look for a project property named "gistToken" -
public
(by default"false"
) tells if the Gist should be public ("true"
) or private ("false"
).
All those arguments are optional. For security reasons, we recommend not setting the user
and token
directly in your
build file. For this reason, the recipe will by default search for a project property gistUsername
for the username
and gistToken
for the token. You can set those properties in your home directory gradle.properties
file:
.~/gradle.properties
gistUsername=buildscansrulez
gistToken=github-generated-api-token
If you do this, the simples configuration looks like this:
buildScanRecipes {
...
recipes 'git-diff-to-gist'
}
In addition, if you don't want to systematically publish the diff, you can run with -PnoGist
:
./gradlew -PnoGist someTask
==== teamcity
The teamcity
recipe accepts 2 optional parameters:
-
baseUrl
needs to be set to the CI server base URL -
guest
, if set, will generate a URL with anonymous login
==== file-leak-detection
This recipe will attempt to detect which tasks leak file handles (a leaking file handle is often referred to a file which is open but never closed). If a file is opened during a build, but never closed, subsequent builds may fail, dependending on your operating system, because of those. Typically, your build could fail removing a jar file because the previous build opened it, but never closed it. This is problematic in Gradle if you use the daemon, which is now on by default. So it is important to track which tasks do not properly close their files, so that you can either report to the plugin author (for tasks defined in a plugin) or fix your build.
The recipe works by loading http://file-leak-detector.kohsuke.org/[a Java agent] that will track file handles. If leaking files
are detected, the build scan will report a LEAKING FILE HANDLES
tag, and in custom values you will see each file that leaks a
file, with the files and details as to where it happens (for debugging).
For example, imagine the following build file:
[source,groovy]
task foo1 { doLast { def fos = file('/tmp/foo1.txt').newOutputStream() } }
task foo2 { doLast { def fos = file('/tmp/foo2.txt').newOutputStream() fos.close() } }
The foo1
task is opening a file, but never closes it. The foo2
task, on the contrary, closes it properly. Here's the
resulting https://scans.gradle.com/s/dpvxomrln43bo/custom-values[build scan]. The details file contains the stack trace to
the file open, so you could see:
...snip...
at build_733t8o56jehucx5ms8s6ul86j$_run_closure6$_closure11.doCall(/home/cchampeau/DEV/PROJECTS/GITHUB/buildscan-recipes-plugin/test/build.gradle:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...snip...
And therefore find out where the file is opened and never closed.
This recipe is still experimental, as it could potentially detect files opened by Gradle, which are closed after a build is finished.
If the recipe is applied, it is still possible to avoid loading the agent and paying the overhead of detection by using -PdisableLeakDetection
.
=== External recipes
In addition to the bundled recipes, it is possible to use recipes on external resources. The first recipe allows to reference another recipe found in a GitHub Gist:
==== gist
buildScanRecipes {
recipe 'gist', user: 'melix',
id: '5944cb701d6c9650ecaccccd4642ea5f',
rev: '4b40b45559929ee2baaa7599e29dd78e51c3843a',
recipe: 'my-recipe',
// external recipe parameters
name: 'Bob'
}
The gist
recipe accepts the following parameters:
-
user
: username of the gist owner -
id
id of the gist -
recipe
name of the file containing the gist -
rev
revision of the gist. If absent, the compiled recipe will not be cached.
Any additional parameter will be passed to the remote recipe (here, the name
parameter).
If the rev
parameter is present, we're pointing at
a specific version of the Gist, so the recipe will fetch it only once, compile it and cache it in the
$USER_HOME/.gradle/buildScanRecipes
directory. If it is absent, it is going to point to HEAD
, meaning that
each time the recipe is called, it's going to fetch it remotely, compile it, but it will not cache the result.
==== remote
As an alternative to the gist
recipe, you can simply reference any remote URL, using the remote
recipe:
recipe 'remote',
url: 'https://gist.githubusercontent.com/melix/5944cb701d6c9650ecaccccd4642ea5f/raw//my-recipe.groovy',
cache: 'true',
// external recipe parameters below:
name: 'Bob'
This recipe will fetch the remote recipe, compile it, and cache it if and only if the cache
flag is set to true
. The
recipe accepts 2 parameters:
-
url
: the URL of the script, pointing at a Groovy recipe script -
cache
: iftrue
, the URL will only be fetched the first time, then it will compile the script and subsequent executions will reuse the result, avoiding a network call and compile phase.
Any additional parameter will be passed to the remote recipe (here, the name
parameter).
It's worth noting that this recipe can be used to compile local recipes too, or to test recipes before you publish them on a Gist or anywhere else:
buildScanRecipes {
recipe 'remote',
url: file('recipes/my-awesome-recipe.groovy').toURL(),
// local recipe parameters below
name: 'Bob'
}
== Adding recipes
Recipes are written in Groovy and can be found in the https://github.com/melix/gradle-buildscan-recipes/tree/master/src/recipes[recipes] directory. Note that the rules are statically compiled and expose 2 variables:
-
buildScan
, of typeBuildScanExtension
, providing ability to tag a build scan, add a link, or add custom values -
gradle
, giving access to theGradle
instance of the build -
params
, aMap<String, String>
of parameters (non-null, but maybe empty)
Recipes are bundled with this plugin.