Skip to content

lyio/gradle-git

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gradle-git

A set of plugins to support Git in the Gradle build tool.

Build Status

There are three primary use cases for these plugins:

  • Publishing to a Github Pages website. The github-pages plugin adds support for publishing static files to the gh-pages branch of a Git repository.
  • Managing your release process. The grgit-release plugin adds an opinionated way to manage releases and comply with Semantic Versioning.
  • General Git actions. This plugin JAR depends on, and makes available, grgit which provides a Groovy API for interacting with Git repositories.

For more information see the documentation in the next sections.

API Documentation:

Credit goes to Peter Ledbrook for the initial idea for the github-pages plugin.

Thanks to Zafar Khaja for the very helpful java-semver library.

Adding the Plugins

Add the following lines to your build to use the gradle-git plugins.

buildscript {
  repositories {
    // jcenter()
    mavenCentral()
  }
  dependencies {
    classpath 'org.ajoberstar:gradle-git:<version>'
  }
}

See the Release Notes at the bottom of this README for the available versions.

Using Grgit

For freeform access to a Git repository, you only need the dependency on gradle-git, no additional plugins need to be applied. Import the grgit package and start using a repository.

import org.ajoberstar.grgit.*

ext.repo = Grgit.open(project.file('.'))

version = "1.0.0-${repo.head().abbreviatedId}"

task tagRelease << {
  repo.tag.add {
    name = version
    message = "Release of ${version}"
  }
}

For details on the available methods/properties see the API docs of Grgit. Examples of how to use each operation are provided there.

Authentication

Grgit provides multiple ways to authenticate with remote repositories. If using hardcoded credentials, it is suggested to use the system properties rather than including them in the repository.

See Grgit's AuthConfig docs for information on the various authentication options.

Methods that produce a Grgit instance, which includes clone, init, and open, all take a Credentials instance if you want to provide hardcoded credentials programmatically.

github-pages

apply plugin: 'github-pages'

githubPages {
  repoUri = '...'
  pages {
    from javadoc.outputs.files
  }
}

See the GithubPagesPluginExtension docs for information on the defaults.

The plugin adds a single publishGhPages task that will clone the repository, copy in the files in the pages CopySpec, add all of the changes, commit, and push back to the remote.

grgit-release

Other Options for Gradle Release Plugins

Before describing this plugins functionality I want to point out the other plugins available (that I'm aware of):

  • townsfolk/gradle-release - designed to work like the Maven Release plugin. If that's what you're looking for this is your best option.
  • ari/gradle-release-plugin - Seems purely oriented around versioning the release and tagging it. Versions are generated dynamically based on current branch or tag.
  • stianh/gradle-release-plugin - Similar approach (and created before) the ari plugin. However, as of 4/13/14 it hasn't had any commits in 9 months.

Approach of grgit-release

The grgit-release plugin is intended to be opinionated, and I am not trying to meet all needs. The core principles of this plugin are:

  • Semantic versioning is the ideal way to version code (for most use cases).
  • A Git repository contains enough information to be able to build useful version numbers that comply with semver, with limited direction from the user. The version should not need to be hardcoded into the source code.

The general functionality involves:

  1. Inferring the desired version based on the nearest tagged version(s) (similar to git describe) and the users input of the scope of the changes being made and what stage of development they are in.
  2. Ensuring all changes in the repository have been committed.
  3. Ensuring there aren't any commits in the upstream branch that haven't been merged yet.
  4. Validating that there have been commits since the nearest version. (Don't re-release with a different version.)
  5. Optionally, checking all Java/Groovy files for @since tags. If they exist, ensure that they match a version that has been tagged in the repository or the version that was just inferred.
  6. Execute whatever release tasks the user specified in the plugin configuration. (e.g. build, publishToRepo)
  7. Optionally, tag the release. Can be configured to only tag certain types of versions. (e.g. only final and rc)
  8. Push changes in current branch to the remote. If a tag was made it will also be pushed.

Applying the Plugin

Only the grgit property is mandatory. releaseTasks should be filled in if you want the code built or published. Everything else has defaults, as noted below.

apply plugin: 'grgit-release'

import org.ajoberstar.grgit.*

release {
  grgit = Grgit.open(project.file('.'))
  remote = 'upstream' // default is 'origin'
  prefixTagNameWithV = false // default is true
  releaseTasks = ['build', 'publishGhPages', 'bintrayUpload'] // defaults to []
  enforceSinceTags = true // default is false

  version {
    untaggedStages = ['alpha'] // default is ['dev']
    taggedStages = ['beta'] // default is ['milestone', 'rc']
    useBuildMetadataForStage = { true } // default is { stage -> stage != 'final' }
    createBuildMetadata = { new Date().format('yyyy.MM.dd.hh.mm.ss') } // default is { grgit.head().abbreviatedId }
  }

  generateTagMessage = { version -> // default is "Release of ${version}"
    StringBuilder builder = new StringBuilder()
    builder.append('Release of ')
    builder.append(version)
    builder.append('\n\n')
    grgit.log {
      range "v${version.nearest.normal.toString()}^{commit}", 'HEAD'
    }.inject(builder) { bldr, commit ->
      bldr.append('- ')
      bldr.append(commit.shortMessage)
      bldr.append('\n')
    }
    builder.toString()
  }
}

Using the Plugin

The two main concepts used as part of the version inference are:

  • A normal version in the parlance of semver is <major>.<minor>.<patch> without any pre-release info or build metadata.
  • The nearest version is determined by finding the shortest commit log between a tagged version and the current HEAD. In cases where there are multiple version tags with the same distance, they will be sorted according to semver rules and the one with the highest precedence will be returned.
  • scope - Changes can be either MAJOR, MINOR, or PATCH. These correspond to the portions of a semver version <major>.<minor>.<patch>[.<pre-release>][+<build-metadata>]
  • stage - Correspond to stages of development. The entirety of available stages is the union of untaggedStages and taggedStages with the addition of final. Stages will be used as part of the pre-release info in a version: <stage>.<num>.
    • For untagged stages the num will correspond to the number of commits since the nearest tagged normal version.
    • For tagged stages the num will be incremented from the nearest version if it has the same normal component and stage. Otherwise it will be set to 1.

There are 2 task rules added to handle releases:

  • ready<scope>VersionAs<stage> - (e.g. readyMinorVersionAsRc) This will run before any other tasks in the build.
    • Infers the version. If there have been no commits since the nearest version the task will fail.
    • Checks for changes in the repository. If there are any changes they will be printed out and the task will fail.
    • Fetches from remote.
    • Checks current branch's tracking status to ensure it isn't behind. If it is behind the task will fail.
  • release<scope>VersionAs<stage> - (e.g. releasePatchVersionAsFinal) This will run after any tasks specified in releaseTasks and the corresponding ready<scope>VersionAs<Stage>.
    • Tags the version if stage is either final or in taggedStages.
    • Pushes the current branch and, if created, the release tag to the remote.

Version Inference

Versions will be inferred in the following ways:

  • If a ready* task is executed, the version will be inferred during its execution, using the stage and scope from the name of the task.
  • Otherwise, the version will be inferred as soon as the task graph has been populated. It will assume the scope is PATCH and the stage is the first entry in untaggedStages set.
    • Given untaggedStages is a SortedSet and semver dictates lexicographical sorting of non-numeric components, this should be the stage with the lowest precedence.

If a build tries to access the version before it is inferred an IllegalStateException will be thrown.

When a version is being inferred the first step is to find the nearest tagged version.

  1. All tags in the repository are listed.
  • If the name cannot be parsed as a valid semver version, it is ignored. Prefixed vs will be removed.
  • If the tag is not an ancestor of HEAD, it is ignored.
  1. A commit log is generated between the tag and HEAD.
  2. The tag with the smallest log is returned.
  • In cases where there are multiple tags with the smallest log, they will be ordered according to semver rules and the one with the highest precedence will be returned.

This is completed to determine both the absolute nearest version and the nearest normal version.

The normal component of the inferred version starts from the nearest normal version and the component corresponding to scope is incremented.

e.g. If the nearest normal version is 1.3.2 and the absolute nearest version is 1.4.0-milestone.2. Assume the stage is rc in all cases.

Scope Inferred Version
PATCH 1.3.3-rc.1
MINOR 1.4.0-rc.1
MAJOR 2.0.0-rc.1

The pre-release component is based on the requested stage. Untagged stages are numbered by the count of commits since the nearest normal version. Tagged stages are numbered in incrementing order within a stage.

e.g. If the nearest normal version is 1.3.2, the absolute nearest version is 1.4.0-milestone.2, and there have been 6 commits since 1.3.2. Assume the scope is MINOR in all cases.

Stage Inferred Version
dev 1.4.0-dev.6
milestone 1.4.0-milestone.3
rc 1.4.0-rc.1
final 1.4.0

The build-metadata is determined based on useBuildMetadataForStage and createBuildMetadata. If any is created it is appended to the version after a +. For example, with the defaults: 1.2.3-rc.1+123abcd

Validating @since Tags in Source Code

An additional task (validateSinceTags) is added to enforce @since tags in Javadoc/Groovydoc comments. It defaults to checking all .java or .groovy files in the main source set. This task only runs if release.enforceSinceTags is set to true. By default this task will finalize the read* tasks.

The task will enforce that any @since tags used in the source code correspond to a version considered valid in the repository.

For example, if the repository has the following tags:

  • v0.1.0
  • 0.2.1+gibberish
  • nonsense (for the purposes of version inference this wouldn't be considered, but it is here)

Also assume that the user executed releaseMinorVersionAsRc. The only allowed values for @since tags would be:

  • 0.1.0
  • 0.2.1+gibberish (v prefixes should not be used)
  • nonsense
  • 0.3.0 (can use the target normal version)
  • 0.3.0-rc.1 (or the entire inferred version)
validateSinceTags {
  source += sourceSets.test.allJava // defaults to sourceSets.main.allJava
}

Travis CI Authentication Notes

In case you plan to use Travis-CI make sure to follow the travis guide on encrypted keys to setup an encrypted authentication token like the following:

  1. Create a new "Personal Access Token” on Github at https://github.com/settings/applications and name it whatever fits your needs
  2. Install the travis CLI on your local machine via “gem install travis” (NOTE: ruby needs to be installed locally!!)
  3. Encrypt your Personal Access Token via “travis encrypt GH_TOKEN=”
  4. add the encrypted token to your .travis.yml file like the following
# ...
env:
  global:
  - secure: "E6iGay3wQcbhAUM5S5WkjYUmg6b7oJG9l8T2y0WWRgx50oqR0/jGzCYHpJGCHlb9OOZpB2BnhpYS6fCg09MsPYKcgsMXgjYzozWGBYifBIVNI07zQhDByztWr3fsrwrZc31ifqC3EGL/UEwvN5F093rRufDw2jomGpFQn7gL4Kc="
  1. Adjust your credentials in the build.gradle to something like
githubPages {
  // ...
  credentials {
    username = System.getenv('GH_TOKEN')
    password = ''
  }
}

Release Notes

v0.8.0

  • Updated GithubPagesPluginExtension to allow configuration of commit message used.
  • Added an opinionated release plugin grgit-release. See docs above for info.

v0.7.1

  • Fixed publishGhPages to actually work. (Sorry about that...)

v0.7.0

  • Complete rewrite of the Git layer in the grgit library.
  • Git functionality can be used in a more free form manner, rather than dedicated tasks for each operation.
  • Breaking Changes:
    • The API has almost completely changed. See the Groovydoc of gradle-git and grgit for details.
    • Plugin requires Java 7. Comment on issue 42 if you have any feedback on that.

v0.6.5

  • Fixing GitPush.namesOrSpecs(String...) to avoid NPE.

v0.6.4

  • Adding targetPath property to github-pages plugin to allow pushing to branches besides gh-pages. Contributed by Alexander Heusingfeld
  • Fix to bypass SSHAgentConnector in certain situations where correct libraries aren't in place.

v0.6.3

  • Fixed jsch-agent-proxy support to fall back to other options when agents aren't really available. See #31.

v0.6.2

  • Added GitInit to simply initialize a new local Git repo. Contributed by Rasmus Praestholm

v0.6.1

  • Updated GitPush to support specifying branch names or specs to push. Contributed by Benjamin Muschko

v0.6.0

  • Added support for jsch-agent-proxy. This allows use of sshagent and Pageant to provide ssh credentials.

v0.5.0

  • Added support for checking out tags on GitClone.
  • Fixed a bug with GitAdd that prevented adding individual files from a subdirectory of the repository.

v0.4.0

  • Minor update to GitCommit to allow include/exclude rules for the files to commit, courtesy of Evgeny Shepelyuk.

v0.3.0

  • Added GitBranchCreate, GitBranchList, GitBranchTrackingStatus, GitCheckout, GitStatus tasks courtesy of Evgeny Shepelyuk.

v0.2.3

NOTE: The GitLog tag only supports commit hashes (abbreviated or full). Tag names do not work right now.

v0.2.2

  • Fix: If cloneGhPages does not checkout the gh-pages branch, most likely because it doesn't exist, the task will fail.

v0.2.1

  • Added GitFetch, GitMerge, GitPull, and GitReset tasks contributed by Alex Lixandru.

v0.2.0

This release does contain breaking changes.

  • Consolidated plugins into GithubPagesPlugin. The existing GithubPlugin and GitPlugin provided no useful functionality.
  • Centralized implementation for retrieving authentication.

v0.1.2

  • Added support for SSH connections with the help of Urs

v0.1.1

  • Added GitTag task contributed by Urs Reupke.
  • The repoPath for all Git tasks is defaulted to the root project's directory.

v0.1.0

Initial release.

About

Git plugin for Gradle

Resources

License

Stars

Watchers

Forks

Packages

No packages published