Repository: intuit/auto
Pull request #924 (2020-01-31 00:32:38)

Gradle Release Plugin
# What Changed

Added new capability to build Gradle projects using The Gradle Release Plugin.

# Why

So that we could build Gradle Projects that support

Todo:

- [X] Add tests
- [X] Add docs



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b85932a..0a46f90e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,29 +1,3 @@
-# v9.8.3 (Thu Jan 30 2020)
-
-#### 🐛  Bug Fix
-
-- `@auto-it/core`
-  - move ICommit to "core" to fix external plugin TS build errors [#921](https://github.com/intuit/auto/pull/921) ([@hipstersmoothie](https://github.com/hipstersmoothie))
-
-#### Authors: 1
-
-- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
-
----
-
-# v9.8.2 (Thu Jan 30 2020)
-
-#### 🐛  Bug Fix
-
-- `@auto-it/npm`
-  - add fallback to get lerna json [#920](https://github.com/intuit/auto/pull/920) ([@hipstersmoothie](https://github.com/hipstersmoothie))
-
-#### Authors: 1
-
-- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
-
----
-
 # v9.8.1 (Wed Jan 29 2020)
 
 #### 🐛  Bug Fix
diff --git a/Formula/auto.rb b/Formula/auto.rb
index 408770c5..3ea6d0ad 100644
--- a/Formula/auto.rb
+++ b/Formula/auto.rb
@@ -1,8 +1,8 @@
 class Auto < Formula
   desc "Generate releases based on semantic version labels on pull requests."
   homepage "https://intuit.github.io/auto/home.html"
-  url "https://github.com/intuit/auto/releases/download/v9.8.3/auto-macos.gz"
-  sha256 "0c247f65c7140874a351287eed4f594bd145b912b4a35a5ede308f2f0921d880"
+  url "https://github.com/intuit/auto/releases/download/v9.8.1/auto-macos.gz"
+  sha256 "99f93578b41a60b5ef0a143a7ed2eb6150c09f4aef5d6da0864db20250698f64"
 
   def install
     libexec.install Dir["*"]
diff --git a/docs/pages/build-platforms/jenkins.md b/docs/pages/build-platforms/jenkins.md
index 351be3ca..fb4a2e4f 100644
--- a/docs/pages/build-platforms/jenkins.md
+++ b/docs/pages/build-platforms/jenkins.md
@@ -16,7 +16,6 @@ You must use some sort of step that implements `skip ci` functionality. Otherwis
 pipeline {
   environment {
     NPM_TOKEN = credentials('NPM_TOKEN')
-    GH_USER = credentials('GH_TOKEN')
     GH_TOKEN = credentials('GH_TOKEN')
   }
   stages {
@@ -35,7 +34,7 @@ pipeline {
     stage('Auth') {
       steps {
         sh '''
-          echo "https://${GH_USER}:${GH_TOKEN}@github.com" >> /tmp/gitcredfile
+          echo "https://${GITHUB_TOKEN}@github.com" >> /tmp/gitcredfile
           git config --global user.name "Andrew Lisowski"
           git config --global user.email "lisowski54@gmail.com"
           git config --global credential.helper "store --file=/tmp/gitcredfile"
diff --git a/lerna.json b/lerna.json
index f08e1155..1e93ea22 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
 {
-  "version": "9.8.3",
+  "version": "9.8.1",
   "npmClient": "yarn",
   "useWorkspaces": true,
   "packages": [
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 1164a5f9..4d092041 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -2,7 +2,7 @@
   "name": "auto",
   "bin": "dist/bin/auto.js",
   "description": "CLI tools to help facilitate semantic versioning based on GitHub PR labels",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "author": {
     "name": "Andrew Lisowski",
     "email": "lisowski54@gmail.com"
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index bef9a22b..8e642821 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,15 +1,3 @@
-# v9.8.3 (Thu Jan 30 2020)
-
-#### 🐛  Bug Fix
-
-- move ICommit to "core" to fix external plugin TS build errors  ([@hipstersmoothie](https://github.com/hipstersmoothie))
-
-#### Authors: 1
-
-- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
-
----
-
 # v9.8.1 (Wed Jan 29 2020)
 
 #### 🐛  Bug Fix
diff --git a/packages/core/package.json b/packages/core/package.json
index 34b31a52..a9b7ff0c 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/core",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "description": "Node API for using auto.",
   "main": "dist/auto.js",
   "author": {
diff --git a/packages/core/src/git.ts b/packages/core/src/git.ts
index 33d43906..64b12b77 100644
--- a/packages/core/src/git.ts
+++ b/packages/core/src/git.ts
@@ -4,7 +4,7 @@ import path from 'path';
 import { retry } from '@octokit/plugin-retry';
 import { throttling } from '@octokit/plugin-throttling';
 import Octokit from '@octokit/rest';
-import gitlogNode from 'gitlog';
+import gitlogNode, { ICommit } from 'gitlog';
 import HttpsProxyAgent from 'https-proxy-agent';
 import tinyColor from 'tinycolor2';
 import { promisify } from 'util';
@@ -16,7 +16,6 @@ import { ILabelDefinition } from './release';
 import execPromise from './utils/exec-promise';
 import { dummyLog, ILogger } from './utils/logger';
 import { gt } from 'semver';
-import { ICommit } from './log-parse';
 
 const gitlog = promisify(gitlogNode);
 
@@ -307,7 +306,7 @@ export default class Git {
   @memoize()
   async getGitLog(start: string, end = 'HEAD'): Promise<ICommit[]> {
     try {
-      const log = await gitlog<ICommit>({
+      const log = await gitlog({
         repo: process.cwd(),
         number: Number.MAX_SAFE_INTEGER,
         fields: ['hash', 'authorName', 'authorEmail', 'rawBody'],
diff --git a/packages/core/src/log-parse.ts b/packages/core/src/log-parse.ts
index 40bac3a0..673c4737 100644
--- a/packages/core/src/log-parse.ts
+++ b/packages/core/src/log-parse.ts
@@ -1,3 +1,4 @@
+import { ICommit } from 'gitlog';
 import { AsyncSeriesBailHook, AsyncSeriesWaterfallHook } from 'tapable';
 import { makeLogParseHooks } from './utils/make-hooks';
 
@@ -21,37 +22,6 @@ export interface IPullRequest {
   body?: string;
 }
 
-export interface ICommit {
-  /**
-   *
-   */
-hash: string;
-  /**
-   *
-   */
-authorName?: string;
-  /**
-   *
-   */
-authorEmail?: string;
-  /**
-   *
-   */
-subject: string;
-  /**
-   *
-   */
-rawBody?: string;
-  /**
-   *
-   */
-labels?: string[];
-  /**
-   *
-   */
-files: string[];
-}
-
 export type IExtendedCommit = ICommit & {
   /** The authors that contributed to the pull request */
   authors: ICommitAuthor[];
diff --git a/plugins/all-contributors/package.json b/plugins/all-contributors/package.json
index 80cbd8f9..8bd6818a 100644
--- a/plugins/all-contributors/package.json
+++ b/plugins/all-contributors/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/all-contributors",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Automatically add contributors as changelogs are produced.",
   "author": {
diff --git a/plugins/chrome/package.json b/plugins/chrome/package.json
index 4dc32ad7..42939242 100644
--- a/plugins/chrome/package.json
+++ b/plugins/chrome/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/chrome",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Chrome publishing plugin for auto",
   "author": {
diff --git a/plugins/conventional-commits/package.json b/plugins/conventional-commits/package.json
index 7ada3f1a..3f770739 100644
--- a/plugins/conventional-commits/package.json
+++ b/plugins/conventional-commits/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/conventional-commits",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Conventional commit plugin for auto",
   "author": {
diff --git a/plugins/crates/package.json b/plugins/crates/package.json
index 101aba7d..574e87e5 100644
--- a/plugins/crates/package.json
+++ b/plugins/crates/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/crates",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Deploy Rust crates to crates.io",
   "author": {
diff --git a/plugins/first-time-contributor/package.json b/plugins/first-time-contributor/package.json
index 8bf1bd32..e6cac33b 100644
--- a/plugins/first-time-contributor/package.json
+++ b/plugins/first-time-contributor/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/first-time-contributor",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Thank first time contributors for their work right in your release notes.",
   "author": {
diff --git a/plugins/git-tag/package.json b/plugins/git-tag/package.json
index 40a5376c..19f92c88 100644
--- a/plugins/git-tag/package.json
+++ b/plugins/git-tag/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/git-tag",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Manage your projects version through just a git tag",
   "author": {
diff --git a/plugins/gradle/README.md b/plugins/gradle/README.md
new file mode 100644
index 00000000..723e4f6a
--- /dev/null
+++ b/plugins/gradle/README.md
@@ -0,0 +1,72 @@
+# Gradle Plugin
+
+Release a Java project using [gradle](https://gradle.org/).
+
+## Installation
+
+This plugin is not included with the `auto` CLI installed via NPM. To install:
+
+```sh
+npm i --save-dev @auto-it/gradle
+# or
+yarn add -D @auto-it/gradle
+```
+
+## Usage
+
+```json
+{
+  "plugins": [
+    [
+      "gradle", {
+        // An optional gradle binary cmd/path relative to your project
+        // @default /usr/bin/gradle
+        "gradleCommand": "./gradlew",
+
+        // An optional properties file where the gradle release plugin will read/write versions from.to.
+        // @default ./gradle.properties
+        "versionFile": "./gradle.properties",
+
+        // An optional gradle argument list -- IE any gradle option allowed for the version
+        // of gradle you're using
+        // @default []
+        "gradleOptions": ["-P someProp=someVal"]
+      }
+    ]
+    // other plugins
+  ]
+}
+```
+
+## Gradle Project Configuration
+
+Your project must be using the gradle release plugin. Make sure the the latest `gradle-release-plugin` is in your `build.gradle`.
+
+```groovy
+import java.util.regex.Matcher
+
+plugins {
+  id "org.sonarqube" version "2.7.1"
+  id 'net.researchgate.release' version '2.6.0' // gradle release plugin
+}
+
+task build {}
+build.dependsOn('app:build')
+build.dependsOn('app:assembleRelease')
+
+release {
+    failOnCommitNeeded = false
+    buildTasks = ['build']
+    versionPatterns = [
+        /(\d+)([^\d]*$)/: { Matcher m, Project p -> m.replaceAll("${(m[0][1] as int) + 1}${m[0][2]}")}
+    ]
+....
+```
+
+You will also need all of the following configuration blocks for all parts of `auto` to function:
+
+1. Version defined inside `versionFile`
+
+```java-properties
+version=1.0.0
+```
\ No newline at end of file
diff --git a/plugins/gradle/package.json b/plugins/gradle/package.json
new file mode 100644
index 00000000..a19cc8c5
--- /dev/null
+++ b/plugins/gradle/package.json
@@ -0,0 +1,46 @@
+{
+  "name": "@auto-it/gradle",
+  "version": "7.16.3",
+  "main": "dist/index.js",
+  "description": "A plugin that calls gradle-release-plugin with versioning information.",
+  "author": {
+    "name": "Brandon Miller",
+    "email": "unknownerror404@gmail.com"
+  },
+  "publishConfig": {
+    "registry": "https://registry.npmjs.org/",
+    "access": "public"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/intuit/auto"
+  },
+  "files": [
+    "dist"
+  ],
+  "keywords": [
+    "automation",
+    "semantic",
+    "release",
+    "github",
+    "labels",
+    "automated",
+    "continuos integration",
+    "changelog"
+  ],
+  "scripts": {
+    "build": "tsc -b",
+    "start": "npm run build -- -w",
+    "lint": "eslint src --ext .ts",
+    "test": "jest --maxWorkers=2 --config ../../package.json"
+  },
+  "dependencies": {
+    "@auto-it/core": "link:../../packages/core",
+    "dot-properties": "^0.3.2",
+    "fs-extra": "^8.1.0",
+    "tslib": "1.10.0"
+  },
+  "devDependencies": {
+    "@types/fs-extra": "^8.0.1"
+  }
+}
diff --git a/plugins/gradle/src/index.ts b/plugins/gradle/src/index.ts
new file mode 100644
index 00000000..20f93d84
--- /dev/null
+++ b/plugins/gradle/src/index.ts
@@ -0,0 +1,122 @@
+import { Auto, IPlugin, execPromise } from '@auto-it/core';
+import { IExtendedCommit } from '@auto-it/core/dist/log-parse';
+
+import path from 'path';
+import fs from 'fs-extra';
+import { inc, ReleaseType } from 'semver';
+import { parse } from 'dot-properties';
+
+/** Global functions for usage in module */
+const logPrefix = '[Gradle-Release-Plugin]';
+
+export interface IGradleReleasePluginPluginOptions {
+  /** The file that contains the version string in it. */
+  versionFile?: string;
+
+  /** The gradle binary to release the project with */
+  gradleCommand?: string;
+
+  /** A list of gradle command customizations to pass to gradle */
+  gradleOptions?: Array<string>;
+}
+
+/** Retrieves a previous version from gradle.properties */
+async function getPreviousVersion(path: string): Promise<string> {
+  try {
+    const data = await fs.readFile(path, 'utf-8');
+    const { version } = parse(data);
+
+    if (version) {
+      return version;
+    }
+  } catch (error) {}
+
+  throw new Error('No version was found inside version-file.');
+}
+
+/** A plugin to release java projects with gradle */
+export default class GradleReleasePluginPlugin implements IPlugin {
+  /** The name of the plugin */
+  name = 'Gradle Release Plugin';
+
+  /** The options of the plugin */
+  readonly options: Required<IGradleReleasePluginPluginOptions>;
+
+  /** Initialize the plugin with it's options */
+  constructor(options: IGradleReleasePluginPluginOptions = {}) {
+    this.options = {
+      versionFile: options?.versionFile
+        ? path.join(process.cwd(), options.versionFile)
+        : path.join(process.cwd(), './gradle.properties'),
+      gradleCommand: options?.gradleCommand
+        ? path.join(process.cwd(), options.gradleCommand)
+        : '/usr/bin/gradle',
+      gradleOptions: options.gradleOptions || [] 
+    };
+  }
+
+  /** Tap into auto plugin points. */
+  apply(auto: Auto) {
+    auto.hooks.beforeRun.tap(this.name, () => {
+      auto.logger.log.warn(`${logPrefix} BeforeRun`);
+      // validation
+      if (!fs.existsSync(this.options.versionFile)) {
+        auto.logger.log.error(
+          `${logPrefix} The version-file does not exist on disk.`
+        );
+        process.exit(1)
+      }
+    });
+
+    auto.hooks.onCreateLogParse.tap(this.name, logParse => {
+      logParse.hooks.omitCommit.tap(this.name, (commit: IExtendedCommit) => {
+        if (commit.subject.includes('[Gradle Release Plugin]')) {
+          return true;
+        }
+      });
+    });
+
+    auto.hooks.getPreviousVersion.tapPromise(this.name, () => {
+      return getPreviousVersion(this.options.versionFile);
+    });
+
+    auto.hooks.version.tapPromise(this.name, async (version: string) => {
+      const previousVersion = await getPreviousVersion(
+        this.options.versionFile
+      );
+      const newVersion = inc(previousVersion, version as ReleaseType) || '';
+      if (!newVersion) {
+        throw new Error(
+          `Could not increment previous version: ${previousVersion}`
+        );
+      }
+
+      await execPromise(this.options.gradleCommand, [
+        'release',
+        '-Prelease.useAutomaticVersion=true',
+        `-Prelease.releaseVersion=${previousVersion}`,
+        `-Prelease.newVersion=${newVersion}`,
+        '-x createReleaseTag',
+        '-x preTagCommit',
+        '-x commitNewVersion',
+        ...this.options.gradleOptions
+      ]);
+
+      await execPromise('git', ['add', 'gradle.properties']);
+      await execPromise('git', [
+        'commit',
+        '-m',
+        `"Bump version to: ${newVersion} [skip ci]"`,
+        '--no-verify'
+      ]);
+
+      await execPromise('git', [
+        'push',
+        '--follow-tags',
+        '--set-upstream',
+        'origin',
+        auto.baseBranch
+      ]);
+    });
+  }
+}
diff --git a/plugins/gradle/tsconfig.json b/plugins/gradle/tsconfig.json
new file mode 100644
index 00000000..bfbef6fc
--- /dev/null
+++ b/plugins/gradle/tsconfig.json
@@ -0,0 +1,16 @@
+{
+  "extends": "../../tsconfig.json",
+  "include": ["src/**/*", "../../typings/**/*"],
+
+  "compilerOptions": {
+    "outDir": "./dist",
+    "rootDir": "./src",
+    "composite": true
+  },
+
+  "references": [
+    {
+      "path": "../../packages/core"
+    }
+  ]
+}
diff --git a/plugins/jira/package.json b/plugins/jira/package.json
index 981e67db..984dc919 100644
--- a/plugins/jira/package.json
+++ b/plugins/jira/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/jira",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Jira plugin for auto",
   "author": {
diff --git a/plugins/maven/README.md b/plugins/maven/README.md
index a517703e..308b2f51 100644
--- a/plugins/maven/README.md
+++ b/plugins/maven/README.md
@@ -28,7 +28,7 @@ Your project must be using the maven release plugin. Make sure the the latest `m
 <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-release-plugin</artifactId>
-  <version>3.0.0-M1</version>
+  <version>2.5.3</version>
   <configuration>
     <preparationGoals>initialize</preparationGoals>
     <goals>deploy</goals>
@@ -53,15 +53,11 @@ You will also need all of the following configuration blocks for all parts of `a
 
    ```xml
    <scm>
-     <connection>scm:git:https://${env.GH_USER}:${env.GH_TOKEN}@github.com/Fuego-Tools/java-test-project.git</connection>
-     <developerConnection>scm:git:https://${env.GH_USER}:${env.GH_TOKEN}@github.com/Fuego-Tools/java-test-project.git</developerConnection>
+     <connection>scm:git:https://github.com/Fuego-Tools/java-test-project.git</connection>
      <url>https://github.com/Fuego-Tools/java-test-project</url>
      <tag>HEAD</tag>
    </scm>
    ```
-  ::: message is-info
-  Don't forget to set enviornment variables `GH_USER`, `GH_TOKEN`
-  :::
 
 3. Version
 
diff --git a/plugins/maven/package.json b/plugins/maven/package.json
index c147a843..dfa9ade7 100644
--- a/plugins/maven/package.json
+++ b/plugins/maven/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/maven",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Maven publishing plugin for auto",
   "author": {
diff --git a/plugins/npm/CHANGELOG.md b/plugins/npm/CHANGELOG.md
index 65fd9daf..64ee3aa2 100644
--- a/plugins/npm/CHANGELOG.md
+++ b/plugins/npm/CHANGELOG.md
@@ -1,16 +1,3 @@
-# v9.8.2 (Thu Jan 30 2020)
-
-#### 🐛  Bug Fix
-
-- fix test  ([@hipstersmoothie](https://github.com/hipstersmoothie))
-- add fallback to get lerna json  ([@hipstersmoothie](https://github.com/hipstersmoothie))
-
-#### Authors: 1
-
-- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
-
----
-
 # v9.8.0 (Mon Jan 27 2020)
 
 #### 🐛  Bug Fix
diff --git a/plugins/npm/package.json b/plugins/npm/package.json
index 6850a1f1..d193651a 100644
--- a/plugins/npm/package.json
+++ b/plugins/npm/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/npm",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "NPM publishing plugin for auto",
   "author": {
diff --git a/plugins/npm/src/index.ts b/plugins/npm/src/index.ts
index 5cfb3ac6..aef6a5bb 100644
--- a/plugins/npm/src/index.ts
+++ b/plugins/npm/src/index.ts
@@ -22,7 +22,7 @@ import { gt, gte, inc, ReleaseType } from 'semver';
 
 import getConfigFromPackageJson from './package-config';
 import setTokenOnCI from './set-npm-token';
-import { loadPackageJson, writeFile } from './utils';
+import { loadPackageJson, readFile, writeFile } from './utils';
 
 const { isCi } = envCi();
 const VERSION_COMMIT_MESSAGE = '"Bump version to: %s [skip ci]"';
@@ -200,13 +200,7 @@ interface INpmConfig {
 }
 
 /** Parse the lerna.json file. */
-const getLernaJson = () => {
-  try {
-    return JSON.parse(fs.readFileSync('lerna.json', 'utf8'));
-  } catch (error) {
-    return {};
-  }
-};
+const getLernaJson = () => JSON.parse(fs.readFileSync('lerna.json', 'utf8'));
 
 /** Render a list of string in markdown */
 const markdownList = (lines: string[]) =>
@@ -220,7 +214,8 @@ async function getPreviousVersion(auto: Auto, prereleaseBranch: string) {
     auto.logger.veryVerbose.info(
       'Using monorepo to calculate previous release'
     );
-    const monorepoVersion = getLernaJson().version;
+    const monorepoVersion = JSON.parse(await readFile('lerna.json', 'utf-8'))
+      .version;
 
     if (monorepoVersion === 'independent') {
       previousVersion =
diff --git a/plugins/omit-commits/package.json b/plugins/omit-commits/package.json
index 1ba98318..8f687354 100644
--- a/plugins/omit-commits/package.json
+++ b/plugins/omit-commits/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/omit-commits",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Omit commits plugin for auto",
   "author": {
diff --git a/plugins/omit-release-notes/package.json b/plugins/omit-release-notes/package.json
index eec984c9..335566dc 100644
--- a/plugins/omit-release-notes/package.json
+++ b/plugins/omit-release-notes/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/omit-release-notes",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Omit release notes plugin for auto",
   "author": {
diff --git a/plugins/released/package.json b/plugins/released/package.json
index b44f124a..684c4aeb 100644
--- a/plugins/released/package.json
+++ b/plugins/released/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/released",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Released plugin for auto. Comments with version + extra",
   "author": {
diff --git a/plugins/s3/package.json b/plugins/s3/package.json
index a24cebc3..73b405d7 100644
--- a/plugins/s3/package.json
+++ b/plugins/s3/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/s3",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Post your built artifacts to s3",
   "author": {
diff --git a/plugins/slack/package.json b/plugins/slack/package.json
index a59caac4..a8d3a4c7 100644
--- a/plugins/slack/package.json
+++ b/plugins/slack/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/slack",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Slack plugin for auto",
   "author": {
diff --git a/plugins/twitter/package.json b/plugins/twitter/package.json
index 1fda05e0..57cbcb46 100644
--- a/plugins/twitter/package.json
+++ b/plugins/twitter/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/twitter",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Twitter plugin for auto",
   "author": {
diff --git a/plugins/upload-assets/package.json b/plugins/upload-assets/package.json
index 6005de25..1ca04861 100644
--- a/plugins/upload-assets/package.json
+++ b/plugins/upload-assets/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@auto-it/upload-assets",
-  "version": "9.8.3",
+  "version": "9.8.1",
   "main": "dist/index.js",
   "description": "Upload assets plugin for auto",
   "author": {
diff --git a/typings/dot-properties.d.ts b/typings/dot-properties.d.ts
new file mode 100644
index 00000000..cba60bf4
--- /dev/null
+++ b/typings/dot-properties.d.ts
@@ -0,0 +1,3 @@
+declare module 'dot-properties' {
+  export function parse(file: string): { version?: string };
+}
diff --git a/typings/gitlog.d.ts b/typings/gitlog.d.ts
index c15a132d..1415698d 100644
--- a/typings/gitlog.d.ts
+++ b/typings/gitlog.d.ts
@@ -1,34 +1,26 @@
 declare module 'gitlog' {
+  export interface ICommit {
+    hash: string;
+    authorName?: string;
+    authorEmail?: string;
+    subject: string;
+    rawBody?: string;
+    labels?: string[];
+    files: string[];
+  }
+
   interface IGitlogOptions {
-    /**
-     *
-     */
-repo: string;
-    /**
-     *
-     */
-fields: string[];
-    /**
-     *
-     */
-branch: string;
-    /**
-     *
-     */
-number: number;
-    /**
-     *
-     */
-execOptions: {
-      /**
-       *
-       */
-maxBuffer: number;
+    repo: string;
+    fields: string[];
+    branch: string;
+    number: number;
+    execOptions: {
+      maxBuffer: number;
     };
   }
 
-  export default function gitlog<T>(
+  export default function gitlog(
     options: IGitlogOptions,
-    callback: (err: Error, res: T[]) => void
+    callback: (err: Error, res: ICommit[]) => void
   ): void;
 }
diff --git a/yarn.lock b/yarn.lock
index ffd44ca0..876fa652 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8,7 +8,7 @@
   integrity sha512-m2OzlTDbhvzK4hgswH9Lx0cdpq1AntXu53Klz5RGkFfuoDvKsnlU7tmtVfNWtUiP0zZbGtrb/BZYH7aB6TRnMA==
 
 "@auto-it/core@link:packages/core":
-  version "9.4.1"
+  version "9.8.1"
   dependencies:
     "@octokit/core" "2.2.0"
     "@octokit/graphql" "4.3.1"
@@ -37,7 +37,7 @@
     url-join "^4.0.0"
 
 "@auto-it/npm@link:plugins/npm":
-  version "9.4.1"
+  version "9.8.1"
   dependencies:
     "@auto-it/core" "link:packages/core"
     env-ci "^5.0.1"
@@ -52,7 +52,7 @@
     user-home "^2.0.0"
 
 "@auto-it/released@link:plugins/released":
-  version "9.4.1"
+  version "9.8.1"
   dependencies:
     "@auto-it/core" "link:packages/core"
     deepmerge "^4.0.0"
@@ -2543,6 +2543,13 @@
   resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
   integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
 
+"@types/fs-extra@^8.0.1":
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.0.1.tgz#a2378d6e7e8afea1564e44aafa2e207dadf77686"
+  integrity sha512-J00cVDALmi/hJOYsunyT52Hva5TnJeKP5yd1r+mH/ZU0mbYZflR0Z5kw5kITtKTRYMhm1JMClOFYdHnQszEvqw==
+  dependencies:
+    "@types/node" "*"
+
 "@types/glob@^7.1.1":
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
@@ -5876,6 +5883,11 @@ dot-prop@^4.1.0, dot-prop@^4.1.1, dot-prop@^4.2.0:
   dependencies:
     is-obj "^1.0.0"
 
+dot-properties@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/dot-properties/-/dot-properties-0.3.2.tgz#aa3efa3c8ca4343701eeac697b17dd426a7c8e40"
+  integrity sha512-1JmMpYEOP4ASJPlAejX3IOVErPyZ1Dqfqiy3jxgOk/9xPgoOWS6p5EHWETgWsIrG0VcIA4Tzyu0zBFzyj9oe7Q==
+
 dotenv@*, dotenv@^8.0.0:
   version "8.2.0"
   resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"


Test patch:
diff --git a/plugins/gradle/__tests__/gradle.test.ts b/plugins/gradle/__tests__/gradle.test.ts
new file mode 100644
index 00000000..b63b27a1
--- /dev/null
+++ b/plugins/gradle/__tests__/gradle.test.ts
@@ -0,0 +1,146 @@
+import fs from 'fs-extra';
+
+import * as Auto from '@auto-it/core';
+import { dummyLog } from '@auto-it/core/dist/utils/logger';
+import { makeHooks } from '@auto-it/core/dist/utils/make-hooks';
+import GradleReleasePlugin, { IGradleReleasePluginPluginOptions } from '../src';
+
+const mockRead = (result: string) =>
+  jest
+    .spyOn(fs, 'readFile')
+    // @ts-ignore
+    .mockReturnValueOnce(result);
+
+describe('Gradle Plugin', () => {
+  let hooks: Auto.IAutoHooks;
+  const options: IGradleReleasePluginPluginOptions = {};
+
+  beforeEach(() => {
+    const plugin = new GradleReleasePlugin(options);
+    hooks = makeHooks();
+    plugin.apply({ hooks, logger: dummyLog() } as Auto.Auto);
+  });
+
+  describe('getPreviousVersion', () => {
+    test('should get previous version from version.json', async () => {
+      mockRead('version=1.0.0');
+      expect(await hooks.getPreviousVersion.promise()).toBe('1.0.0');
+    });
+
+    test('should throw when no in version.json', async () => {
+      mockRead('');
+      await expect(hooks.getPreviousVersion.promise()).rejects.toThrowError(
+        'No version was found inside version-file.'
+      );
+    });
+
+    test('should throw when no version.json', async () => {
+      await expect(hooks.getPreviousVersion.promise()).rejects.toThrowError(
+        'No version was found inside version-file.'
+      );
+    });
+  });
+
+  describe('version', () => {
+    test('should version release - patch version', async () => {
+      mockRead('version=1.0.0');
+      const spy = jest.fn();
+      jest.spyOn(Auto, 'execPromise').mockImplementation(spy);
+
+      await hooks.version.promise(Auto.SEMVER.patch);
+
+      expect(spy).toHaveBeenCalledWith(
+        expect.stringMatching('gradle'),
+        [
+          'release',
+          '-Prelease.useAutomaticVersion=true',
+          '-Prelease.releaseVersion=1.0.0',
+          '-Prelease.newVersion=1.0.1',
+          '-x createReleaseTag',
+          '-x preTagCommit',
+          '-x commitNewVersion'
+        ]
+      );
+    });
+
+    test('should version release - major version', async () => {
+      mockRead('version=1.0.0');
+      const spy = jest.fn();
+      jest.spyOn(Auto, 'execPromise').mockImplementation(spy);
+
+      await hooks.version.promise(Auto.SEMVER.major);
+
+      expect(spy).toHaveBeenCalledWith(
+        expect.stringMatching('gradle'),
+        [
+          'release',
+          '-Prelease.useAutomaticVersion=true',
+          '-Prelease.releaseVersion=1.0.0',
+          '-Prelease.newVersion=2.0.0',
+          '-x createReleaseTag',
+          '-x preTagCommit',
+          '-x commitNewVersion'
+        ]
+      );
+    });
+
+    test('should version release - minor version', async () => {
+      mockRead('version=1.1.0');
+      const spy = jest.fn();
+      jest.spyOn(Auto, 'execPromise').mockImplementation(spy);
+
+      await hooks.version.promise(Auto.SEMVER.minor);
+
+      expect(spy).toHaveBeenCalledWith(
+        expect.stringMatching('gradle'),
+        [
+          'release',
+          '-Prelease.useAutomaticVersion=true',
+          '-Prelease.releaseVersion=1.1.0',
+          '-Prelease.newVersion=1.2.0',
+          '-x createReleaseTag',
+          '-x preTagCommit',
+          '-x commitNewVersion'
+        ]
+      );
+    });
+  });
+});
+
+describe('Gradle Plugin - Custom Command', () => {
+  let hooks: Auto.IAutoHooks;
+  const options: IGradleReleasePluginPluginOptions = {
+    gradleCommand: './gradlew',
+    gradleOptions: ['-P prop=val']
+  };
+
+  beforeEach(() => {
+    const plugin = new GradleReleasePlugin(options);
+    hooks = makeHooks();
+    plugin.apply({ hooks, logger: dummyLog() } as Auto.Auto);
+  });
+
+  describe('version', () => {
+    test('should version release - patch version - with custom gradle command', async () => {
+      mockRead('version=1.0.0');
+      const spy = jest.fn();
+      jest.spyOn(Auto, 'execPromise').mockImplementation(spy);
+
+      await hooks.version.promise(Auto.SEMVER.patch);
+
+      expect(spy).toHaveBeenCalledWith(
+        expect.stringMatching('gradlew'),
+        [
+          'release',
+          '-Prelease.useAutomaticVersion=true',
+          '-Prelease.releaseVersion=1.0.0',
+          '-Prelease.newVersion=1.0.1',
+          '-x createReleaseTag',
+          '-x preTagCommit',
+          '-x commitNewVersion',
+          '-P prop=val'
+        ]
+      );
+    });
+  });
+});
\ No newline at end of file
diff --git a/plugins/npm/__tests__/npm.test.ts b/plugins/npm/__tests__/npm.test.ts
index ae64b4dc..4230db3a 100644
--- a/plugins/npm/__tests__/npm.test.ts
+++ b/plugins/npm/__tests__/npm.test.ts
@@ -314,12 +314,12 @@ describe('getPreviousVersion', () => {
       prefixRelease: str => str
     } as Auto.Auto);
 
-    readFileSync.mockReturnValueOnce(`
+    readResult = `
       {
         "name": "test",
         "version": "2.0.0"
       }
-    `);
+    `;
     expect(await hooks.getPreviousVersion.promise()).toBe('2.0.0');
   });
 
@@ -330,12 +330,12 @@ describe('getPreviousVersion', () => {
     // isMonorepo
     existsSync.mockReturnValueOnce(true);
     monorepoPackages.mockReturnValueOnce(monorepoPackagesResult);
-    readFileSync.mockReturnValueOnce(`
+    readResult = `
       {
         "name": "test",
         "version": "0.0.1"
       }
-    `);
+    `;
     // published version of test package
     exec.mockReturnValueOnce('0.1.2');