Giter Site home page Giter Site logo

btkelly / gnag Goto Github PK

View Code? Open in Web Editor NEW
125.0 5.0 14.0 1010 KB

A Gradle plugin that helps facilitate GitHub PR checking and automatic commenting of violations.

Home Page: http://gnag.watch

License: Apache License 2.0

Java 69.43% Shell 0.38% Groovy 24.73% Kotlin 5.46%
violations github-pr reporting-violations android android-lint gradle findbugs pmd checkstyle

gnag's Introduction

Gnag Coverage Status Android Arsenal

Gnag is a Gradle plugin that helps facilitate GitHub PR checking and automatic commenting of violations for Android projects.

It can be used in Java-only, Kotlin-only, and mixed Java/Kotlin codebases.

The name is a portmanteau of the words "Gradle" and "nag". The first "g" is silent!

Example Output

Below are examples of output posted to a GitHub PR on a project using Gnag to enforce quality checks.

Violations associated with a specific line in your PR will be posted as comments on that line:

Violations that are not associated with a specific line in your PR will be aggregated and posted in a single top-level PR comment:

Usage

Requires JDK 11 or Higher

Gnag is meant to be simple to use and easy to drop in to any JVM based project. Shown below is the simplest Gnag setup that will report violations to GitHub. By default this config will report PMD, Checkstyle, ktlint, detekt and Android Lint to GitHub.

build.gradle (Groovy)
plugins {
  id "com.btkelly.gnag" version "{current version}"
}

gnag {
    github {
        repoName 'btkelly/repo'
        authToken '0000000000000'
        issueNumber '1'
    }
}
build.gradle.kts (Kotlin)
plugins {
  id("com.btkelly.gnag") version "{current version}"
}

gnag {
    github {
        repoName("btkelly/repo")
        authToken("0000000000000")
        issueNumber("1")
    }
}

This is the simplest way to add automatic PR checking and commenting to your project. The options defined in the github closure can be overridden by passing command line parameters with the same name to your build. This is helpful when using in conjunction with a CI system to allow automated builds.

Tasks

You can use the gnagCheck gradle task to run Gnag locally and generate an HTML report in the build directory.

./gradlew clean gnagCheck

You can use the gnagReport task which will first run gnagCheck and then report detected violations to the GitHub issue specified. In this example the issue number and authtoken for the comment user are passed as commandline arguments.

./gradlew clean gnagReport -PissueNumber=11 -PauthToken=iu2n3iu2nfjknfjk23nfkj23nk

Customization

build.gradle (Groovy)
gnag {
    enabled true
    failOnError true

    checkstyle {
        enabled true
        toolVersion "8.45.1"
        reporterConfig project.file('config/checkstyle.xml')
    }

    pmd {
        enabled true
        toolVersion "6.22.0"
        reporterConfig project.file('config/pmd.xml')
    }

    ktlint {
        enabled true
        toolVersion "0.42.0"
    }

    detekt {
        enabled true
        reporterConfig project.file('config/detekt.yml')
        toolVersion "1.17.1"
    }

    androidLint {
        enabled true
        severity 'Error'
    }

    github {
        rootUrl 'https://my.githubinstall.com/repos/'
        repoName 'btkelly/repo'
        authToken '0000000000000'
        issueNumber '1'
        setCommentInline true
        setCommentOnSuccess true
        useGitHubStatuses true
    }
}
build.gradle.kts (Kotlin)
gnag {
    isEnabled = true
    setFailOnError(true)

    checkstyle {
        isEnabled = true
        toolVersion("8.45.1")
        reporterConfig(project.file("config/checkstyle.xml"))
    }

    pmd {
        isEnabled = true
        toolVersion("6.22.0")
        reporterConfig(project.file("config/pmd.xml"))
    }

    ktlint {
        isEnabled = true
        toolVersion("0.35.0")
    }

    detekt {
        isEnabled = true
        reporterConfig(project.file("config/detekt.yml"))
    }

    androidLint {
        isEnabled = true
        severity("Error")
    }

    github {
        rootUrl("https://my.githubinstall.com/repos/")
        repoName("btkelly/repo")
        authToken("0000000000000")
        issueNumber("1")
        setCommentInline(true)
        setCommentOnSuccess(true)
        useGitHubStatuses(true)
    }
}

NOTE: All reporters are enabled by default

  • enabled - easily disable Gnag in specific situations

  • failOnError - should violations cause the build to fail or just generate a report; if set to false, you may need to add the following to prevent your build still failing from Android Lint errors:

    build.gradle (Groovy)
    android {
        lintOptions {
            abortOnError false
        }
    }
    
    build.gradle.kts (Kotlin)
    android {
        lintOptions {
            abortOnError(false)
        }
    }
    
  • checkstyle - block to customize the checkstyle reporter

    • enabled - set if checkstyle should execute
    • toolVersion - override the checkstyle version
    • reporterConfig - provide a custom checkstyle config (see the default config here)
  • pmd - block to customize the PMD reporter

    • enabled - set if PMD should execute
    • toolVersion - override the PMD version
    • reporterConfig - provide a custom PMD config (see the default config here)
  • ktlint - block to customize the ktlint reporter

    • enabled - set if ktlint should execute
    • toolVersion - override the ktlint version
  • detekt - block to customize the detekt reporter

    • enabled - set if detekt should execute
    • reporterConfig - provide a custom detekt config
    • toolVersion - override the detekt version
  • androidLint - block to customize the android lint reporter

    • enabled - set if the android lint reporter should look for a lint report
    • severity - can be 'Error' or 'Warning' (case insensitive) depending on which severity you want Gnag to check
  • github - block to customize GitHub reporting (only used during the gnagReport task

    • rootUrl - root URL to use when communicating with the GitHub API (must include trailing slash), if not provided will default to "https://api.github.com/repos/"
    • repoName - account and repo name to report violations to
    • authToken - a GitHub token for a user that has access to comment on issues to the specified repo
    • issueNumber - the issue or PR number currently being built
    • setCommentInline - whether or not comments posted to GitHub should be placed inline where possible
    • setCommentOnSuccess - whether or not a comment should be posted to GitHub when no violations exist
    • useGitHubStatuses - should report GitHub status on each module in the PR or just fail if failOnError enabled

Multi-Module Projects

To enforce the same quality checks across multiple Gradle modules, apply and configure Gnag in your root build.gradle file as follows (you can remove the equivalent code from submobule build.gradle files):

build.gradle (Groovy)
plugins {
  id "com.btkelly.gnag" version "{current version}" apply false
}

subprojects {
    plugins {
      id "com.btkelly.gnag"
    }

    gnag {
        // Standard Gnag configuration goes here.
        //
        // Reference tool configuration files using the rootProject:
        //   reporterConfig rootProject.file('config/toolrules.xml')
    }
}
build.gradle.kts (Kotlin)
plugins {
  id("com.btkelly.gnag") version "{current version}" apply false
}

subprojects {
    plugins {
      id("com.btkelly.gnag")
    }

    configure<com.btkelly.gnag.extensions.GnagPluginExtension> {
        // Standard Gnag configuration goes here.
        //
        // Reference tool configuration files using the rootProject:
        //   reporterConfig(rootProject.file("config/toolrules.xml"))
    }
}

You may need to keep the Android lint portions of your configuration in submodule build.gradle files if your project includes non-Android submodules. In this case, you should also use the rootProject to reference any shared lint configuration file.

Example Travis CI Usage

Travis is a continuous integration service and is free for open source projects. Below is an example of how to configure Gnag to run on Travis.

You must set an environment variable on your Travis instance for the PR_BOT_AUTH_TOKEN used to post comments back to GitHub.

.travis.yml

language: android
android:
  components:
  - platform-tools
  - tools
  - build-tools-25.0.3
  - android-25
jdk:
  - openjdk8
branches:
  only:
  - master
script: "./travis-build.sh"

travis-build.sh

#!/bin/bash
set -ev

if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
	./gradlew clean gnagCheck
else
	./gradlew clean gnagReport -PauthToken="${PR_BOT_AUTH_TOKEN}" -PissueNumber="${TRAVIS_PULL_REQUEST}"
fi

License

Copyright 2016 Bryan Kelly

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

gnag's People

Contributors

amatkivskiy avatar bmunzenb avatar btkelly avatar friederbluemle avatar hanspeide avatar ilmazgumerov avatar janani-subbiah avatar rstockbridge avatar stkent avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

gnag's Issues

Exception while parsing Android lint output

Gnag version: 1.1
Gnag configuration:

gnag {
    pmd {
        enabled false
    }

    github {
        repoName 'redacted/redacted'
    }
}

Stack trace (recorded output when running ./gradlew clean gnagCheck):

Error reading line number from Android Lint violations.
java.lang.NumberFormatException: For input string: ""
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Integer.parseInt(Integer.java:592)
        at java.lang.Integer.valueOf(Integer.java:766)
        at org.codehaus.groovy.runtime.StringGroovyMethods.toInteger(StringGroovyMethods.java:3333)
        at org.codehaus.groovy.runtime.StringGroovyMethods.toInteger(StringGroovyMethods.java:3342)
        at groovy.util.slurpersupport.GPathResult.toInteger(GPathResult.java:281)
        at sun.reflect.GeneratedMethodAccessor97.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1210)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
        at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:151)
        at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:42)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
        at com.btkelly.gnag.reporters.AndroidLintViolationDetector$_getDetectedViolations_closure2.doCall(AndroidLintViolationDetector.groovy:65)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
        at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
        at groovy.lang.Closure.call(Closure.java:426)
        at groovy.lang.Closure.call(Closure.java:442)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2030)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2015)
        at org.codehaus.groovy.runtime.dgm$158.doMethodInvoke(Unknown Source)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1210)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
        at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:151)
        at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:42)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
        at com.btkelly.gnag.reporters.AndroidLintViolationDetector.getDetectedViolations(AndroidLintViolationDetector.groovy:60)
        at com.btkelly.gnag.tasks.GnagCheck.lambda$executeGnagCheck$2(GnagCheck.java:87)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
        at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
        at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
        at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
        at com.btkelly.gnag.tasks.GnagCheck.executeGnagCheck(GnagCheck.java:81)
        at com.btkelly.gnag.tasks.GnagCheck.taskAction(GnagCheck.java:71)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:75)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:227)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:220)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:209)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:585)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:568)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:203)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:185)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:66)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:50)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:25)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:110)
        at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
        at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
        at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
        at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
        at org.gradle.initialization.DefaultGradleLauncher$4.run(DefaultGradleLauncher.java:154)
        at org.gradle.internal.Factories$1.create(Factories.java:22)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:52)
        at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:151)
        at org.gradle.initialization.DefaultGradleLauncher.access$200(DefaultGradleLauncher.java:32)
        at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:99)
        at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:93)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:62)
        at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:93)
        at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:82)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:94)
        at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
        at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:43)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:28)
        at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:75)
        at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:45)
        at org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter.execute(DaemonUsageSuggestingBuildActionExecuter.java:51)
        at org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter.execute(DaemonUsageSuggestingBuildActionExecuter.java:28)
        at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:43)
        at org.gradle.internal.Actions$RunnableActionAdapter.execute(Actions.java:170)
        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:237)
        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:210)
        at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:35)
        at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:24)
        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:206)
        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:169)
        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
        at org.gradle.launcher.Main.doAction(Main.java:33)
        at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:54)
        at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:35)
        at org.gradle.launcher.GradleMain.main(GradleMain.java:23)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.gradle.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:30)
        at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:129)
        at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)

Lint output (check the errors of type InvalidPackage; they are missing line/column numbers):

<?xml version="1.0" encoding="UTF-8"?>
<issues format="4" by="lint 25.1.7">

    <issue
        id="InlinedApi"
        severity="Warning"
        message="Field requires API level 23 (current min is 16): `android.view.View#SYSTEM_UI_FLAG_LIGHT_STATUS_BAR`"
        category="Correctness"
        priority="6"
        summary="Using inlined constants on older versions"
        explanation="This check scans through all the Android API field references in the application and flags certain constants, such as static final integers and Strings, which were introduced in later versions. These will actually be copied into the class files rather than being referenced, which means that the value is available even when running on older devices. In some cases that&apos;s fine, and in other cases it can result in a runtime crash or incorrect behavior. It depends on the context, so consider the code carefully and device whether it&apos;s safe and can be suppressed or whether the code needs tbe guarded.

If you really want to use this API and don&apos;t need to support older devices just set the `minSdkVersion` in your `build.gradle` or `AndroidManifest.xml` files.
If your code is *deliberately* accessing newer APIs, and you have ensured (e.g. with conditional execution) that this code will only ever be called on a supported platform, then you can annotate your class or method with the `@TargetApi` annotation specifying the local minimum SDK to apply, such as `@TargetApi(11)`, such that this check considers 11 rather than your manifest file&apos;s minimum SDK as the required API level.
"
        errorLine1="import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;"
        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        quickfix="studio,adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/java/com/detroitlabs/redacted/BaseActivity.java"
            line="20"
            column="15"/>
    </issue>

    <issue
        id="InvalidPackage"
        severity="Error"
        message="Invalid package reference in library; not included in Android: `java.nio.file`. Referenced from `okio.Okio`."
        category="Correctness"
        priority="6"
        summary="Package not included in Android"
        explanation="This check scans through libraries looking for calls to APIs that are not included in Android.

When you create Android projects, the classpath is set up such that you can only access classes in the API packages that are included in Android. However, if you add other projects to your libs/ folder, there is no guarantee that those .jar files were built with an Android specific classpath, and in particular, they could be accessing unsupported APIs such as java.applet.

This check scans through library jars and looks for references to API packages that are not included in Android and flags these. This is only an error if your code calls one of the library classes which wind up referencing the unsupported package.">
        <location
            file="/Users/stkent/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.8.0/5ea7af56cc7c567ed9856d99efb30740e9b17ff/okio-1.8.0.jar"/>
    </issue>

    <issue
        id="InvalidPackage"
        severity="Error"
        message="Invalid package reference in library; not included in Android: `java.lang.invoke`. Referenced from `retrofit2.Platform.Java8`."
        category="Correctness"
        priority="6"
        summary="Package not included in Android"
        explanation="This check scans through libraries looking for calls to APIs that are not included in Android.

When you create Android projects, the classpath is set up such that you can only access classes in the API packages that are included in Android. However, if you add other projects to your libs/ folder, there is no guarantee that those .jar files were built with an Android specific classpath, and in particular, they could be accessing unsupported APIs such as java.applet.

This check scans through library jars and looks for references to API packages that are not included in Android and flags these. This is only an error if your code calls one of the library classes which wind up referencing the unsupported package.">
        <location
            file="/Users/stkent/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.0.2/43eeae0b9fb087bb3194ba59ab63a38a32fbf3e/retrofit-2.0.2.jar"/>
    </issue>

    <issue
        id="OldTargetApi"
        severity="Warning"
        message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the android.os.Build.VERSION_CODES javadoc for details."
        category="Correctness"
        priority="6"
        summary="Target SDK attribute is not targeting latest version"
        explanation="When your application runs on a version of Android that is more recent than your `targetSdkVersion` specifies that it has been tested with, various compatibility modes kick in. This ensures that your application continues to work, but it may look out of place. For example, if the `targetSdkVersion` is less than 14, your app may get an option button in the UI.

To fix this issue, set the `targetSdkVersion` to the highest available value. Then test your app to make sure everything works correctly. You may want to consult the compatibility notes to see what changes apply to each version you are adding support for: http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
        url="http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
        urls="http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
        errorLine1="        targetSdkVersion 23"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~"
        quickfix="studio">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/build.gradle"
            line="42"
            column="9"/>
    </issue>

    <issue
        id="UnusedAttribute"
        severity="Warning"
        message="Attribute `elevation` is only used in API level 21 and higher (current min is 16)"
        category="Correctness"
        priority="6"
        summary="Attribute unused on older versions"
        explanation="This check finds attributes set in XML files that were introduced in a version newer than the oldest version targeted by your application (with the `minSdkVersion` attribute).

This is not an error; the application will simply ignore the attribute. However, if the attribute is important to the appearance of functionality of your application, you should consider finding an alternative way to achieve the same result with only available attributes, and then you can optionally create a copy of the layout in a layout-vNN folder which will be used on API NN or higher where you can take advantage of the newer attribute.

Note: This check does not only apply to attributes. For example, some tags can be unused too, such as the new `&lt;tag>` element in layouts introduced in API 21."
        errorLine1="            android:elevation=&quot;0dp&quot;"
        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~"
        quickfix="studio">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/activity_login.xml"
            line="56"
            column="13"/>
    </issue>

    <issue
        id="UnusedAttribute"
        severity="Warning"
        message="Attribute `stateListAnimator` is only used in API level 21 and higher (current min is 16)"
        category="Correctness"
        priority="6"
        summary="Attribute unused on older versions"
        explanation="This check finds attributes set in XML files that were introduced in a version newer than the oldest version targeted by your application (with the `minSdkVersion` attribute).

This is not an error; the application will simply ignore the attribute. However, if the attribute is important to the appearance of functionality of your application, you should consider finding an alternative way to achieve the same result with only available attributes, and then you can optionally create a copy of the layout in a layout-vNN folder which will be used on API NN or higher where you can take advantage of the newer attribute.

Note: This check does not only apply to attributes. For example, some tags can be unused too, such as the new `&lt;tag>` element in layouts introduced in API 21."
        errorLine1="            android:stateListAnimator=&quot;@null&quot;"
        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        quickfix="studio">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/activity_login.xml"
            line="57"
            column="13"/>
    </issue>

    <issue
        id="GradleDependency"
        severity="Warning"
        message="A newer version of com.android.support:design than 23.4.0 is available: 24.0.0"
        category="Correctness"
        priority="4"
        summary="Obsolete Gradle Dependency"
        explanation="This detector looks for usages of libraries where the version you are using is not the current stable release. Using older versions is fine, and there are cases where you deliberately want to stick with an older version. However, you may simply not be aware that a more recent version is available, and that is what this lint check helps find."
        errorLine1="    compile &quot;com.android.support:design:23.4.0&quot;"
        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        quickfix="studio">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/build.gradle"
            line="78"
            column="5"/>
    </issue>

    <issue
        id="GradleDynamicVersion"
        severity="Warning"
        message="Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (io.fabric.tools:gradle:1.+)"
        category="Correctness"
        priority="4"
        summary="Gradle Dynamic Version"
        explanation="Using `+` in dependencies lets you automatically pick up the latest available version rather than a specific, named version. However, this is not recommended; your builds are not repeatable; you may have tested with a slightly different version than what the build server used. (Using a dynamic version as the major version number is more problematic than using it in the minor version position.)"
        errorLine1="        classpath &apos;io.fabric.tools:gradle:1.+&apos;"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        quickfix="studio">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/build.gradle"
            line="12"
            column="9"/>
    </issue>

    <issue
        id="AllowBackup"
        severity="Warning"
        message="On SDK version 23 and up, your app data will be automatically backed up and restored on app install. Consider adding the attribute `android:fullBackupContent` to specify an `@xml` resource which configures which files to backup. More info: https://developer.android.com/preview/backup/index.html"
        category="Security"
        priority="3"
        summary="AllowBackup/FullBackupContent Problems"
        explanation="The `allowBackup` attribute determines if an application&apos;s data can be backed up and restored. It is documented at http://developer.android.com/reference/android/R.attr.html#allowBackup

By default, this flag is set to `true`. When this flag is set to `true`, application data can be backed up and restored by the user using `adb backup` and `adb restore`.

This may have security consequences for an application. `adb backup` allows users who have enabled USB debugging to copy application data off of the device. Once backed up, all application data can be read by the user. `adb restore` allows creation of application data from a source specified by the user. Following a restore, applications should not assume that the data, file permissions, and directory permissions were created by the application itself.

Setting `allowBackup=&quot;false&quot;` opts an application out of both backup and restore.

To fix this warning, decide whether your application should support backup, and explicitly set `android:allowBackup=(true|false)&quot;`.

If not set to false, and if targeting API 23 or later, lint will also warn that you should set `android:fullBackupContent` to configure auto backup."
        url="https://developer.android.com/preview/backup/index.html"
        urls="https://developer.android.com/preview/backup/index.html,http://developer.android.com/reference/android/R.attr.html#allowBackup"
        errorLine1="    &lt;application"
        errorLine2="    ^"
        includedVariants="debug"
        excludedVariants="release"
        quickfix="studio,adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/debug/AndroidManifest.xml"
            line="7"
            column="5"/>
    </issue>

    <issue
        id="AllowBackup"
        severity="Warning"
        message="On SDK version 23 and up, your app data will be automatically backed up and restored on app install. Consider adding the attribute `android:fullBackupContent` to specify an `@xml` resource which configures which files to backup. More info: https://developer.android.com/preview/backup/index.html"
        category="Security"
        priority="3"
        summary="AllowBackup/FullBackupContent Problems"
        explanation="The `allowBackup` attribute determines if an application&apos;s data can be backed up and restored. It is documented at http://developer.android.com/reference/android/R.attr.html#allowBackup

By default, this flag is set to `true`. When this flag is set to `true`, application data can be backed up and restored by the user using `adb backup` and `adb restore`.

This may have security consequences for an application. `adb backup` allows users who have enabled USB debugging to copy application data off of the device. Once backed up, all application data can be read by the user. `adb restore` allows creation of application data from a source specified by the user. Following a restore, applications should not assume that the data, file permissions, and directory permissions were created by the application itself.

Setting `allowBackup=&quot;false&quot;` opts an application out of both backup and restore.

To fix this warning, decide whether your application should support backup, and explicitly set `android:allowBackup=(true|false)&quot;`.

If not set to false, and if targeting API 23 or later, lint will also warn that you should set `android:fullBackupContent` to configure auto backup."
        url="https://developer.android.com/preview/backup/index.html"
        urls="https://developer.android.com/preview/backup/index.html,http://developer.android.com/reference/android/R.attr.html#allowBackup"
        errorLine1="    &lt;application"
        errorLine2="    ^"
        quickfix="studio,adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/AndroidManifest.xml"
            line="8"
            column="5"/>
    </issue>

    <issue
        id="AllowBackup"
        severity="Warning"
        message="Should explicitly set `android:allowBackup` to `true` or `false` (it&apos;s `true` by default, and that can have some security implications for the application&apos;s data)"
        category="Security"
        priority="3"
        summary="AllowBackup/FullBackupContent Problems"
        explanation="The `allowBackup` attribute determines if an application&apos;s data can be backed up and restored. It is documented at http://developer.android.com/reference/android/R.attr.html#allowBackup

By default, this flag is set to `true`. When this flag is set to `true`, application data can be backed up and restored by the user using `adb backup` and `adb restore`.

This may have security consequences for an application. `adb backup` allows users who have enabled USB debugging to copy application data off of the device. Once backed up, all application data can be read by the user. `adb restore` allows creation of application data from a source specified by the user. Following a restore, applications should not assume that the data, file permissions, and directory permissions were created by the application itself.

Setting `allowBackup=&quot;false&quot;` opts an application out of both backup and restore.

To fix this warning, decide whether your application should support backup, and explicitly set `android:allowBackup=(true|false)&quot;`.

If not set to false, and if targeting API 23 or later, lint will also warn that you should set `android:fullBackupContent` to configure auto backup."
        url="https://developer.android.com/preview/backup/index.html"
        urls="https://developer.android.com/preview/backup/index.html,http://developer.android.com/reference/android/R.attr.html#allowBackup"
        errorLine1="    &lt;application"
        errorLine2="    ^"
        quickfix="studio,adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/AndroidManifest.xml"
            line="8"
            column="5"/>
    </issue>

    <issue
        id="Overdraw"
        severity="Warning"
        message="Possible overdraw: Root element paints background `@color/brightBlue` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
        category="Performance"
        priority="3"
        summary="Overdraw: Painting regions more than once"
        explanation="If you set a background drawable on a root view, then you should use a custom theme where the theme background is null. Otherwise, the theme background will be painted first, only to have your custom background completely cover it; this is called &quot;overdraw&quot;.

NOTE: This detector relies on figuring out which layouts are associated with which activities based on scanning the Java code, and it&apos;s currently doing that using an inexact pattern matching algorithm. Therefore, it can incorrectly conclude which activity the layout is associated with and then wrongly complain that a background-theme is hidden.

If you want your custom background on multiple pages, then you should consider making a custom theme with your custom background and just using that theme instead of a root element background.

Of course it&apos;s possible that your custom drawable is translucent and you want it to be mixed with the background. However, you will get better performance if you pre-mix the background with your drawable and use that resulting image or color as a custom theme background instead.
"
        errorLine1="    android:background=&quot;@color/brightBlue&quot;>"
        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/activity_login.xml"
            line="8"
            column="5"/>
    </issue>

    <issue
        id="UnusedResources"
        severity="Warning"
        message="The resource `R.color.darkBlue` appears to be unused"
        category="Performance"
        priority="3"
        summary="Unused resources"
        explanation="Unused resources make applications larger and slow down builds."
        errorLine1="    &lt;color name=&quot;darkBlue&quot;>#067CAD&lt;/color>"
        errorLine2="           ~~~~~~~~~~~~~~~">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/values/colors.xml"
            line="7"
            column="12"/>
    </issue>

    <issue
        id="UnusedResources"
        severity="Warning"
        message="The resource `R.dimen.default_screen_padding` appears to be unused"
        category="Performance"
        priority="3"
        summary="Unused resources"
        explanation="Unused resources make applications larger and slow down builds."
        errorLine1="    &lt;dimen name=&quot;default_screen_padding&quot;>16dp&lt;/dimen>"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/values/dimens.xml"
            line="3"
            column="12"/>
    </issue>

    <issue
        id="GoogleAppIndexingWarning"
        severity="Warning"
        message="App is not indexable by Google Search; consider adding at least one Activity with an ACTION-VIEW intent-filler. See issue explanation for more details."
        category="Usability"
        priority="5"
        summary="Missing support for Google App Indexing"
        explanation="Adds URLs to get your app into the Google index, to get installs and traffic to your app from Google Search."
        url="https://g.co/AppIndexing/AndroidStudio"
        urls="https://g.co/AppIndexing/AndroidStudio"
        errorLine1="    &lt;application"
        errorLine2="    ^"
        includedVariants="debug"
        excludedVariants="release"
        quickfix="studio">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/debug/AndroidManifest.xml"
            line="7"
            column="5"/>
    </issue>

    <issue
        id="GoogleAppIndexingWarning"
        severity="Warning"
        message="App is not indexable by Google Search; consider adding at least one Activity with an ACTION-VIEW intent-filler. See issue explanation for more details."
        category="Usability"
        priority="5"
        summary="Missing support for Google App Indexing"
        explanation="Adds URLs to get your app into the Google index, to get installs and traffic to your app from Google Search."
        url="https://g.co/AppIndexing/AndroidStudio"
        urls="https://g.co/AppIndexing/AndroidStudio"
        errorLine1="    &lt;application"
        errorLine2="    ^"
        quickfix="studio">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/AndroidManifest.xml"
            line="8"
            column="5"/>
    </issue>

    <issue
        id="ContentDescription"
        severity="Warning"
        message="[Accessibility] Missing `contentDescription` attribute on image"
        category="Accessibility"
        priority="3"
        summary="Image without `contentDescription`"
        explanation="Non-textual widgets like ImageViews and ImageButtons should use the `contentDescription` attribute to specify a textual description of the widget such that screen readers and other accessibility tools can adequately describe the user interface.

Note that elements in application screens that are purely decorative and do not provide any content or enable a user action should not have accessibility content descriptions. In this case, just suppress the lint warning with a tools:ignore=&quot;ContentDescription&quot; attribute.

Note that for text fields, you should not set both the `hint` and the `contentDescription` attributes since the hint will never be shown. Just set the `hint`. See http://developer.android.com/guide/topics/ui/accessibility/checklist.html#special-cases."
        errorLine1="    &lt;ImageView"
        errorLine2="    ^"
        quickfix="studio,adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/activity_login.xml"
            line="10"
            column="5"/>
    </issue>

    <issue
        id="HardcodedText"
        severity="Warning"
        message="[I18N] Hardcoded string &quot;Email Address&quot;, should use `@string` resource"
        category="Internationalization"
        priority="5"
        summary="Hardcoded text"
        explanation="Hardcoding text attributes directly in layout files is bad for several reasons:

* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)

* The application cannot be translated to other languages by just adding new translations for existing string resources.

In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup."
        errorLine1="            android:hint=&quot;Email Address&quot;"
        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        quickfix="adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/activity_login.xml"
            line="30"
            column="13"/>
    </issue>

    <issue
        id="HardcodedText"
        severity="Warning"
        message="[I18N] Hardcoded string &quot;Password&quot;, should use `@string` resource"
        category="Internationalization"
        priority="5"
        summary="Hardcoded text"
        explanation="Hardcoding text attributes directly in layout files is bad for several reasons:

* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)

* The application cannot be translated to other languages by just adding new translations for existing string resources.

In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup."
        errorLine1="            android:hint=&quot;Password&quot;"
        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~"
        quickfix="adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/activity_login.xml"
            line="41"
            column="13"/>
    </issue>

    <issue
        id="HardcodedText"
        severity="Warning"
        message="[I18N] Hardcoded string &quot;SIGN IN&quot;, should use `@string` resource"
        category="Internationalization"
        priority="5"
        summary="Hardcoded text"
        explanation="Hardcoding text attributes directly in layout files is bad for several reasons:

* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)

* The application cannot be translated to other languages by just adding new translations for existing string resources.

In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup."
        errorLine1="            android:text=&quot;SIGN IN&quot;"
        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~"
        quickfix="adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/activity_login.xml"
            line="52"
            column="13"/>
    </issue>

    <issue
        id="HardcodedText"
        severity="Warning"
        message="[I18N] Hardcoded string &quot;\?&quot;, should use `@string` resource"
        category="Internationalization"
        priority="5"
        summary="Hardcoded text"
        explanation="Hardcoding text attributes directly in layout files is bad for several reasons:

* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)

* The application cannot be translated to other languages by just adding new translations for existing string resources.

In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup."
        errorLine1="            android:text=&quot;\?&quot;"
        errorLine2="            ~~~~~~~~~~~~~~~~~"
        quickfix="adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/widget_service_interval_progress.xml"
            line="27"
            column="13"/>
    </issue>

    <issue
        id="HardcodedText"
        severity="Warning"
        message="[I18N] Hardcoded string &quot;\?&quot;, should use `@string` resource"
        category="Internationalization"
        priority="5"
        summary="Hardcoded text"
        explanation="Hardcoding text attributes directly in layout files is bad for several reasons:

* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)

* The application cannot be translated to other languages by just adding new translations for existing string resources.

In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup."
        errorLine1="        android:text=&quot;\?&quot;"
        errorLine2="        ~~~~~~~~~~~~~~~~~"
        quickfix="adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/widget_service_interval_progress.xml"
            line="51"
            column="9"/>
    </issue>

    <issue
        id="HardcodedText"
        severity="Warning"
        message="[I18N] Hardcoded string &quot;\?&quot;, should use `@string` resource"
        category="Internationalization"
        priority="5"
        summary="Hardcoded text"
        explanation="Hardcoding text attributes directly in layout files is bad for several reasons:

* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)

* The application cannot be translated to other languages by just adding new translations for existing string resources.

In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup."
        errorLine1="        android:text=&quot;\?&quot;"
        errorLine2="        ~~~~~~~~~~~~~~~~~"
        quickfix="adt">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/widget_service_interval_progress.xml"
            line="61"
            column="9"/>
    </issue>

    <issue
        id="RtlHardcoded"
        severity="Warning"
        message="Consider adding `android:layout_alignParentStart=&quot;true&quot;` to better support right-to-left layouts"
        category="Internationalization:Bidirectional Text"
        priority="5"
        summary="Using left/right instead of start/end attributes"
        explanation="Using `Gravity#LEFT` and `Gravity#RIGHT` can lead to problems when a layout is rendered in locales where text flows from right to left. Use `Gravity#START` and `Gravity#END` instead. Similarly, in XML `gravity` and `layout_gravity` attributes, use `start` rather than `left`.

For XML attributes such as paddingLeft and `layout_marginLeft`, use `paddingStart` and `layout_marginStart`. *NOTE*: If your `minSdkVersion` is less than 17, you should add *both* the older left/right attributes *as well as* the new start/right attributes. On older platforms, where RTL is not supported and the start/right attributes are unknown and therefore ignored, you need the older left/right attributes. There is a separate lint check which catches that type of error.

(Note: For `Gravity#LEFT` and `Gravity#START`, you can use these constants even when targeting older platforms, because the `start` bitmask is a superset of the `left` bitmask. Therefore, you can use `gravity=&quot;start&quot;` rather than `gravity=&quot;left|start&quot;`.)"
        errorLine1="        android:layout_alignParentLeft=&quot;true&quot;"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/widget_service_interval_progress.xml"
            line="48"
            column="9"/>
    </issue>

    <issue
        id="RtlHardcoded"
        severity="Warning"
        message="Consider adding `android:layout_alignParentEnd=&quot;true&quot;` to better support right-to-left layouts"
        category="Internationalization:Bidirectional Text"
        priority="5"
        summary="Using left/right instead of start/end attributes"
        explanation="Using `Gravity#LEFT` and `Gravity#RIGHT` can lead to problems when a layout is rendered in locales where text flows from right to left. Use `Gravity#START` and `Gravity#END` instead. Similarly, in XML `gravity` and `layout_gravity` attributes, use `start` rather than `left`.

For XML attributes such as paddingLeft and `layout_marginLeft`, use `paddingStart` and `layout_marginStart`. *NOTE*: If your `minSdkVersion` is less than 17, you should add *both* the older left/right attributes *as well as* the new start/right attributes. On older platforms, where RTL is not supported and the start/right attributes are unknown and therefore ignored, you need the older left/right attributes. There is a separate lint check which catches that type of error.

(Note: For `Gravity#LEFT` and `Gravity#START`, you can use these constants even when targeting older platforms, because the `start` bitmask is a superset of the `left` bitmask. Therefore, you can use `gravity=&quot;start&quot;` rather than `gravity=&quot;left|start&quot;`.)"
        errorLine1="        android:layout_alignParentRight=&quot;true&quot;"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="/Users/stkent/dev/detroit_labs/apps/redacted/app/src/main/res/layout/widget_service_interval_progress.xml"
            line="58"
            column="9"/>
    </issue>

</issues>

Exception if no custom PMD rulesConfig is defined

Gnag version: 1.1
Gnag configuration:

gnag {
    github {
        repoName 'redacted/redacted'
    }
}

Java info:

java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

Output of ./gradlew clean gnagCheck --stacktrace:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:gnagCheck'.
> Can't find resource 'jar:file:/Users/stkent/.gradle/caches/modules-2/files-2.1/com.btkelly/gnag/1.1/95b5b84f9e8950943e0d4d09ae93e48f2ba8b089/gnag-1.1.jar!/pmd.xml' for rule 'null'.  Make sure the resource is a valid file or URL and is on the CLASSPATH. Here's the current classpath: /Users/stkent/dev/detroit_labs/apps/redacted/gradle/wrapper/gradle-wrapper.jar

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:gnagCheck'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:203)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:185)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:66)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:50)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:25)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:110)
        at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
        at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
        at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
        at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
        at org.gradle.initialization.DefaultGradleLauncher$4.run(DefaultGradleLauncher.java:154)
        at org.gradle.internal.Factories$1.create(Factories.java:22)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:52)
        at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:151)
        at org.gradle.initialization.DefaultGradleLauncher.access$200(DefaultGradleLauncher.java:32)
        at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:99)
        at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:93)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:62)
        at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:93)
        at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:82)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:94)
        at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
        at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:43)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:28)
        at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:75)
        at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:45)
        at org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter.execute(DaemonUsageSuggestingBuildActionExecuter.java:51)
        at org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter.execute(DaemonUsageSuggestingBuildActionExecuter.java:28)
        at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:43)
        at org.gradle.internal.Actions$RunnableActionAdapter.execute(Actions.java:170)
        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:237)
        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:210)
        at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:35)
        at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:24)
        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:206)
        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:169)
        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
        at org.gradle.launcher.Main.doAction(Main.java:33)
        at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
        at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:54)
        at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:35)
        at org.gradle.launcher.GradleMain.main(GradleMain.java:23)
        at org.gradle.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:30)
        at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:129)
        at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)
Caused by: Can't find resource 'jar:file:/Users/stkent/.gradle/caches/modules-2/files-2.1/com.btkelly/gnag/1.1/95b5b84f9e8950943e0d4d09ae93e48f2ba8b089/gnag-1.1.jar!/pmd.xml' for rule 'null'.  Make sure the resource is a valid file or URL and is on the CLASSPATH. Here's the current classpath: /Users/stkent/dev/detroit_labs/apps/redacted/gradle/wrapper/gradle-wrapper.jar
        at net.sourceforge.pmd.ant.internal.PMDTaskImpl.doTask(PMDTaskImpl.java:121)
        at net.sourceforge.pmd.ant.internal.PMDTaskImpl.execute(PMDTaskImpl.java:267)
        at net.sourceforge.pmd.ant.PMDTask.execute(PMDTask.java:47)
        at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
        at org.apache.tools.ant.Task.perform(Task.java:348)
        at org.apache.tools.ant.Task$perform.call(Unknown Source)
        at com.btkelly.gnag.reporters.PMDViolationDetector.executeReporter(PMDViolationDetector.groovy:54)
        at com.btkelly.gnag.tasks.GnagCheck.lambda$executeGnagCheck$2(GnagCheck.java:84)
        at com.btkelly.gnag.tasks.GnagCheck.executeGnagCheck(GnagCheck.java:81)
        at com.btkelly.gnag.tasks.GnagCheck.taskAction(GnagCheck.java:71)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:75)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:227)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:220)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:209)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:585)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:568)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
        ... 60 more
Caused by: net.sourceforge.pmd.RuleSetNotFoundException: Can't find resource 'jar:file:/Users/stkent/.gradle/caches/modules-2/files-2.1/com.btkelly/gnag/1.1/95b5b84f9e8950943e0d4d09ae93e48f2ba8b089/gnag-1.1.jar!/pmd.xml' for rule 'null'.  Make sure the resource is a valid file or URL and is on the CLASSPATH. Here's the current classpath: /Users/stkent/dev/detroit_labs/apps/redacted/gradle/wrapper/gradle-wrapper.jar
        at net.sourceforge.pmd.RuleSetReferenceId.getInputStream(RuleSetReferenceId.java:404)
        at net.sourceforge.pmd.RuleSetFactory.createRuleSet(RuleSetFactory.java:195)
        at net.sourceforge.pmd.RuleSetFactory.createRuleSet(RuleSetFactory.java:190)
        at net.sourceforge.pmd.RuleSetFactory.createRuleSets(RuleSetFactory.java:154)
        at net.sourceforge.pmd.RuleSetFactory.createRuleSets(RuleSetFactory.java:138)
        at net.sourceforge.pmd.ant.internal.PMDTaskImpl.doTask(PMDTaskImpl.java:117)
        ... 77 more

Interrupting CI build prevents Gnag status from ever resolving?

Probable steps to reproduce

  1. Start CI build that includes Gnag. Gnag posts in progress status to GitHub PR.
  2. Cancel CI build (not sure how specific the timing of this step needs to be).

Expected: Gnag posts some sort of resolved status to GitHub PR.
Actual: Gnag check status remains in progress indefinitely.

Notes

Starting/completing a new CI build in which Gnag goes through a full check cycle and succeeds does not clear the pending status (see screenshot):

screen shot 2016-06-28 at 11 21 37 am

This is a problem for protected branches, since PRs into such branches cannot be merged until all status checks succeed.

Add a "Contributing" section

Given that I want to contribute fixes and enhancements to Gnag;
When I find the time;
Then I would appreciate guidance on local setup.

Possible inclusions:

  • tasks to run to validate changes
  • how to deploy locally and test features

Report tested commit sha when reporting success to GitHub

Coveralls does this, and it's helpful for figuring out which status comment corresponds to which commit. Message could read something like:

Congrats! No ๐Ÿ’ฉ code found in commit abc1234, this PR is safe to merge.

Sample showing Coveralls comments:

screen shot 2016-06-06 at 10 08 27 pm

Improve log output when violation detector creation fails due to invalid config

Example:

:app:gnagCheck FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:gnagCheck'.
> Unable to create a Checker: configLocation {/Users/stkent/dev/detroit_labs/apps/herbie-android/app/config/checkstyle.xml}, classpath {null}.

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:gnagCheck'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:66)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:203)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:185)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:66)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:50)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:25)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:110)
        at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
        at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
        at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
        at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
        at org.gradle.initialization.DefaultGradleLauncher$4.run(DefaultGradleLauncher.java:153)
        at org.gradle.internal.Factories$1.create(Factories.java:22)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:53)
        at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:150)
        at org.gradle.initialization.DefaultGradleLauncher.access$200(DefaultGradleLauncher.java:32)
        at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:98)
        at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:92)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:63)
        at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:92)
        at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:83)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:99)
        at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
        at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:48)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:30)
        at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:81)
        at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:46)
        at org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter.execute(DaemonUsageSuggestingBuildActionExecuter.java:51)
        at org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter.execute(DaemonUsageSuggestingBuildActionExecuter.java:28)
        at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:43)
        at org.gradle.internal.Actions$RunnableActionAdapter.execute(Actions.java:173)
        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:239)
        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:212)
        at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:35)
        at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:24)
        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:205)
        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:169)
        at org.gradle.launcher.Main.doAction(Main.java:33)
        at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
        at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:55)
        at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:36)
        at org.gradle.launcher.GradleMain.main(GradleMain.java:23)
        at org.gradle.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:30)
        at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:129)
        at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)
Caused by: Unable to create a Checker: configLocation {/Users/stkent/dev/detroit_labs/apps/herbie-android/app/config/checkstyle.xml}, classpath {null}.
        at com.puppycrawl.tools.checkstyle.ant.CheckstyleAntTask.createChecker(CheckstyleAntTask.java:425)
        at com.puppycrawl.tools.checkstyle.ant.CheckstyleAntTask.realExecute(CheckstyleAntTask.java:320)
        at com.puppycrawl.tools.checkstyle.ant.CheckstyleAntTask.execute(CheckstyleAntTask.java:303)
        at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
        at org.apache.tools.ant.Task.perform(Task.java:348)
        at org.apache.tools.ant.Task$perform.call(Unknown Source)
        at com.btkelly.gnag.reporters.CheckstyleViolationDetector.executeReporter(CheckstyleViolationDetector.groovy:53)
        at com.btkelly.gnag.tasks.GnagCheck.lambda$executeGnagCheck$0(GnagCheck.java:84)
        at com.btkelly.gnag.tasks.GnagCheck.executeGnagCheck(GnagCheck.java:81)
        at com.btkelly.gnag.tasks.GnagCheck.taskAction(GnagCheck.java:71)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:75)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:228)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:221)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:210)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:621)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:604)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
        ... 60 more
Caused by: com.puppycrawl.tools.checkstyle.api.CheckstyleException: EmptyLineSeparator is not allowed as a child in Checker
        at com.puppycrawl.tools.checkstyle.Checker.setupChild(Checker.java:423)
        at com.puppycrawl.tools.checkstyle.api.AutomaticBean.configure(AutomaticBean.java:138)
        at com.puppycrawl.tools.checkstyle.ant.CheckstyleAntTask.createChecker(CheckstyleAntTask.java:422)
        ... 77 more

Fix checkAndReport failure Github status

There seems to be a regression in the checkAndReport task where the failure status is not being set correctly on a PR. A quick fix was to throw a GradleException to fail the build but a Github status of failure would be more appropriate. This was working at one point and would set the Gnag status on the Github PR to failure.

Break same PMD violations into multiple lines

When a PMD violation is found with the same type in the same file it will group them. Either improve the formatting or split each line violation into it's own rule violation in the comment reporter.

screen shot 2015-11-23 at 2 34 33 pm

Tweak violation location representation in comments

With known line number

Before:

File: app/src/test/java/com/gnag/example/ExampleUnitTest.java
Line: 27

After:

Location: app/src/test/java/com/gnag/example/ExampleUnitTest.java:27

Without known line number

Before:

File: app/src/test/java/com/gnag/example/ExampleUnitTest.java

After:

File: app/src/test/java/com/gnag/example/ExampleUnitTest.java

Add note about configuring the Android linter

Since the linter is not wrapped by Gnag (unlike the other violation detectors), all configuration of Android lint rules must be done using the built-in mechanisms within the android { lintOptions { ... } } closure. We should call this out explicitly since it's different from the other three detectors.

Format code in Android lint comments

Example comment for an AllowBackup violation:

Notes: On SDK version 23 and up, your app data will be automatically backed up and restored on app install. Consider adding the attribute android:fullBackupContent to specify an @xml resource which configures which files to backup. More info: https://developer.android.com/preview/backup/index.html

The backticked code is not formatted using pre tags in reports. It could be!

Double reports?

I recently opened a PR and edited the description while the original Travis build was running. gnag commented on the PR twice:

screen shot 2016-01-24 at 3 53 11 pm

Not sure if Travis issue, or gnag issue...

Add check local task

Add another Gradle task that will run all the checks and fail the build locally without trying to report. This is helpful for checking before creating a PR. Maybe a local HTML report of sorts?

Be more careful about building relative file location from detector output

The typical issue generated by the Android linter has the following structure:

    <issue
        id="OldTargetApi"
        severity="Warning"
        message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the android.os.Build.VERSION_CODES javadoc for details."
        category="Correctness"
        priority="6"
        summary="Target SDK attribute is not targeting latest version"
        explanation="When your application runs on a version of Android that is more recent than your `targetSdkVersion` specifies that it has been tested with, various compatibility modes kick in. This ensures that your application continues to work, but it may look out of place. For example, if the `targetSdkVersion` is less than 14, your app may get an option button in the UI.

To fix this issue, set the `targetSdkVersion` to the highest available value. Then test your app to make sure everything works correctly. You may want to consult the compatibility notes to see what changes apply to each version you are adding support for: http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
        url="http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
        urls="http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
        errorLine1="        targetSdkVersion 22"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~"
        quickfix="studio">
        <location
            file="/Users/stuart/dev/personal/libraries/bugshaker-android/bugshaker/build.gradle"
            line="54"
            column="9"/>
    </issue>

When attempting to generate a file path relative to the source folder /Users/stuart/dev/personal/libraries/bugshaker-android/, we currently (Gnag 1.1) use the following code in the AndroidLintViolationDetector class:

sanitize((String) violation.location.@file.text()).replace(project.rootDir.absolutePath + "/", "")

However, it is possible for the linter to report violations at completely different paths (this probably indicates a misconfigured linter; however, we should be defensive in Gnag):

    <issue
        id="InvalidPackage"
        severity="Error"
        message="Invalid package reference in library; not included in Android: `java.nio.file`. Referenced from `okio.Okio`."
        category="Correctness"
        priority="6"
        summary="Package not included in Android"
        explanation="This check scans through libraries looking for calls to APIs that are not included in Android.

When you create Android projects, the classpath is set up such that you can only access classes in the API packages that are included in Android. However, if you add other projects to your libs/ folder, there is no guarantee that those .jar files were built with an Android specific classpath, and in particular, they could be accessing unsupported APIs such as java.applet.

This check scans through library jars and looks for references to API packages that are not included in Android and flags these. This is only an error if your code calls one of the library classes which wind up referencing the unsupported package.">
        <location
            file="/Users/stkent/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.8.0/5ea7af56cc7c567ed9856d99efb30740e9b17ff/okio-1.8.0.jar"/>
    </issue>

In this case, we should detect that the file string does not start with the path we are expecting to replace, and therefore treat the file location as unknown/unknowable.

Handle multiple URLs associated with a violation

As of 1.2.1, we handle 0 or 1 URLs gracefully. We should update to handle any number of URLs.

Examples from Android lint:

0 URLs

<issue
    id="GradleDependency"
    severity="Warning"
    message="A newer version of com.android.support:appcompat-v7 than 23.3.0 is available: 24.0.0"
    category="Correctness"
    priority="4"
    summary="Obsolete Gradle Dependency"
    explanation="This detector looks for usages of libraries where the version you are using is not the current stable release. Using older versions is fine, and there are cases where you deliberately want to stick with an older version. However, you may simply not be aware that a more recent version is available, and that is what this lint check helps find."
    errorLine1="    compile &apos;com.android.support:appcompat-v7:23.3.0&apos;"
    errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    quickfix="studio">
    <location
        file="/Users/stkent/dev/personal/libraries/gnag/example/app/build.gradle"
        line="41"
        column="5"/>
</issue>

1 URL

<issue
    id="OldTargetApi"
    severity="Warning"
    message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the android.os.Build.VERSION_CODES javadoc for details."
    category="Correctness"
    priority="6"
    summary="Target SDK attribute is not targeting latest version"
    explanation="When your application runs on a version of Android that is more recent than your `targetSdkVersion` specifies that it has been tested with, various compatibility modes kick in. This ensures that your application continues to work, but it may look out of place. For example, if the `targetSdkVersion` is less than 14, your app may get an option button in the UI.

To fix this issue, set the `targetSdkVersion` to the highest available value. Then test your app to make sure everything works correctly. You may want to consult the compatibility notes to see what changes apply to each version you are adding support for: http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
    url="http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
    urls="http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"
    errorLine1="        targetSdkVersion 23"
    errorLine2="        ~~~~~~~~~~~~~~~~~~~"
    quickfix="studio">
    <location
        file="/Users/stkent/dev/personal/libraries/gnag/example/app/build.gradle"
        line="26"
        column="9"/>
</issue>

2 URLs

<issue
    id="AllowBackup"
    severity="Warning"
    message="On SDK version 23 and up, your app data will be automatically backed up and restored on app install. Consider adding the attribute `android:fullBackupContent` to specify an `@xml` resource which configures which files to backup. More info: https://developer.android.com/preview/backup/index.html"
    category="Security"
    priority="3"
    summary="AllowBackup/FullBackupContent Problems"
    explanation="The `allowBackup` attribute determines if an application&apos;s data can be backed up and restored. It is documented at http://developer.android.com/reference/android/R.attr.html#allowBackup

By default, this flag is set to `true`. When this flag is set to `true`, application data can be backed up and restored by the user using `adb backup` and `adb restore`.

This may have security consequences for an application. `adb backup` allows users who have enabled USB debugging to copy application data off of the device. Once backed up, all application data can be read by the user. `adb restore` allows creation of application data from a source specified by the user. Following a restore, applications should not assume that the data, file permissions, and directory permissions were created by the application itself.

Setting `allowBackup=&quot;false&quot;` opts an application out of both backup and restore.

To fix this warning, decide whether your application should support backup, and explicitly set `android:allowBackup=(true|false)&quot;`.

If not set to false, and if targeting API 23 or later, lint will also warn that you should set `android:fullBackupContent` to configure auto backup."
    url="https://developer.android.com/preview/backup/index.html"
    urls="https://developer.android.com/preview/backup/index.html,http://developer.android.com/reference/android/R.attr.html#allowBackup"
    errorLine1="    &lt;application"
    errorLine2="    ^"
    quickfix="studio,adt">
    <location
        file="/Users/stkent/dev/personal/libraries/gnag/example/app/src/main/AndroidManifest.xml"
        line="5"
        column="5"/>
</issue>

Fix logged path to local report file

Expected output:

One or more violation detectors has found violations. Check the report at /Users/stkent/dev/personal/libraries/gnag/example/app/build/outputs/gnag/gnag.html for details.

Actual output:

One or more violation detectors has found violations. Check the report at /Users/stkent/dev/personal/libraries/gnag/example/app/build/outputs/gnaggnag.html for details.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.