Grails & Hudson part 1: CodeNarc

Grails has a rich plugin eco-system with over 400 hundred plugins – so it’s easy to miss something useful. If you’re serious about software craftsmanship, then using static code analysis tools should be part of your quality regime as it gives further insight into the code base (and if you insist, yes it’ll help with your Technical Debt management).

CodeNarc provides static code analysis for Groovy and the CodeNarc plugin for Grails allows you to perform this analysis with the “grails codenarc” script. Behind the scenes this uses the CodeNarc ant task and settings from grails-app/conf/Config.groovy and produces an HTML report by default.

Until recently, if you used the codenarc target within a continuous integration server such as Hudson – then the HTML report would be generated and sit in the workspace waiting for a diligent developer to check it. You can imagine how often that happens in practice with all the other demands of a project!

However, I’ve now integrated the CodeNarc XML output with the Hudson Violations plugin so that an overview trend line is shown against the Hudson job. Then the team quickly fixed the violations…

You can also get a breakdown by priority:

And in-context views of the violations so you know what to fix:

This is how you do it…

Grails config.groovy

codenarc {
 reportName = 'target/test-reports/CodeNarcReport.xml'
 reportType = 'xml'
 // any further settings like maxPriority1Violations=0
}

Hudson

Set up your Grails build step – normally you’d add the ‘codenarc’ target:

The codenarc.properties file can be used to configure specific exclusions, the location of this file can be passed in as a system property (shown above).

You need to have version 0.7.7 (or later) of the violations plugin installed (Manage Hudson > Manage Plugins > Available and search for Violations).
As this is a recent addition, I’m using a patched version of the Violations plugin, though the patch has been integrated into trunk (I’ll update when it is released).
Configure violations:

Note the ‘Faux project path’ – you may need to set this to get an in-context view working properly due to path (e.g. if your code is checked out to workspace/trunk)

I also had a contribution to CodeNarc accepted at the weekend to add an inlineXml report type – this will, with a minor tweak to the CodeNarc parser, allow the Hudson Violations plugin to give the rule description on the pop-up message.

[image here]
About these ads

19 responses to “Grails & Hudson part 1: CodeNarc

  1. Just want to keep track of when your changes would be implemented in the Hudson Violations plug in. Thanks!!

  2. So my puppy is still up and running and at the heart of your groovy and grails development? Great to know!

  3. The CodeNarc support is in version 0.7.7 of the Violations plugin which was released last week.

  4. Hi! Thanks for the integration of codenarc, I’m using the 0.7.7 version of Violations plugin, Hudson ver. 1.380, but i’m getting this error: Publisher hudson.plugins.violations.ViolationsPublisher aborted due to exception
    java.lang.NullPointerException

    My target is: clean test-app codenarc

    The stacktrace is:

    Base Directory: /home/tek/.hudson/jobs/tekapp2/workspace
    Resolving dependencies…
    Dependencies resolved in 4787ms.
    Running script /home/tek/.grails/1.3.5/projects/workspace/plugins/codenarc-0.7/scripts/Codenarc.groovy
    Environment set to development
    Running CodeNarc …
    CodeNarc completed: (p1=0; p2=1; p3=0) 8114ms
    CodeNarc finished; report generated: target/test-reports/CodeNarcReport.xml
    ERROR: Publisher hudson.plugins.violations.ViolationsPublisher aborted due to exception
    java.lang.NullPointerException
    at hudson.plugins.violations.generate.OutputFileModel.doViolation(OutputFileModel.java:198)
    at hudson.plugins.violations.generate.OutputFileModel.createLimited(OutputFileModel.java:113)
    at hudson.plugins.violations.generate.OutputFileModel.execute(OutputFileModel.java:80)
    at hudson.plugins.violations.generate.ExecuteFilePath.execute(ExecuteFilePath.java:45)
    at hudson.plugins.violations.generate.GenerateXML.execute(GenerateXML.java:47)
    at hudson.plugins.violations.ViolationsCollector.invoke(ViolationsCollector.java:124)
    at hudson.plugins.violations.ViolationsCollector.invoke(ViolationsCollector.java:27)
    at hudson.FilePath.act(FilePath.java:753)
    at hudson.FilePath.act(FilePath.java:735)
    at hudson.plugins.violations.ViolationsPublisher.perform(ViolationsPublisher.java:74)
    at hudson.tasks.BuildStepMonitor$3.perform(BuildStepMonitor.java:36)
    at hudson.model.AbstractBuild$AbstractRunner.perform(AbstractBuild.java:601)
    at hudson.model.AbstractBuild$AbstractRunner.performAllBuildSteps(AbstractBuild.java:580)
    at hudson.model.AbstractBuild$AbstractRunner.performAllBuildSteps(AbstractBuild.java:558)
    at hudson.model.Build$RunnerImpl.post2(Build.java:157)
    at hudson.model.AbstractBuild$AbstractRunner.post(AbstractBuild.java:528)
    at hudson.model.Run.run(Run.java:1303)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:46)
    at hudson.model.ResourceController.execute(ResourceController.java:88)
    at hudson.model.Executor.run(Executor.java:140)
    Finished: FAILURE

    • I haven’t been able to reproduce this – just tried on a clean install of Hudson (1.379), Violations 0.7.7, Grails 1.3.5, CodeNarc plugin 0.7 (CodeNarc 0.10). Following the instructions in the post and it all worked as expected.

      Is there any more stack trace info as none of those classes are CodeNarc specific?
      Can you check your CodeNarc config & the Violations config?

      • Thank you very much. I’ve installed again Hudson version 1.379 from the scratch with and Grails, Codenarc and Violations versions that you specified (I’m using Debian Lenny), and I’ve followed the post instructions, but the problem persist. I think that the problem is related to my grails-app/conf/Config.groovy, because the problem appeared when I add:

        codenarc {
        reportName = ‘target/test-reports/CodeNarcReport.xml’
        reportType = ‘xml’
        }

        I’m adding this fragment at the end of the file, it is ok? Could you show me your Config.groovy?

        Thanks in advance, best regards.

      • We have a Debian build server running the Debian packaged Hudson (1.352), with a snapshot release of violations 0.7.7 – this is from the end of a Config.groovy from one of the Grails projects that it builds:


        codenarc {
        reportName = 'target/test-reports/CodeNarcReport.xml'
        reportType = 'xml'
        maxPriority1Violations=0
        maxPriority2Violations=25
        maxPriority3Violations=10
        }

  5. It still show me: “codenarc No reports”. This time the stacktrace seems to be diferent. Thanks a lot.

    Started by user anonymous
    [workspace] $ grails clean
    Welcome to Grails 1.3.5 – http://grails.org/
    Licensed under Apache Standard License 2.0
    Grails home is set to: /opt/install/grails

    Base Directory: /home/tek/.hudson/jobs/myapp/workspace
    Resolving dependencies…
    Dependencies resolved in 4305ms.
    Running script /opt/install/grails/scripts/Clean.groovy
    Environment set to development
    [delete] Deleting: /home/tek/.grails/1.3.5/projects/workspace/resources/web.xml
    [delete] Deleting directory /home/tek/.hudson/jobs/myapp/workspace/target/classes
    [delete] Deleting directory /home/tek/.grails/1.3.5/projects/workspace/plugin-classes
    [delete] Deleting directory /home/tek/.grails/1.3.5/projects/workspace/resources
    [delete] Deleting directory /home/tek/.hudson/jobs/myapp/workspace/target/test-classes
    [workspace] $ grails test-app
    Welcome to Grails 1.3.5 – http://grails.org/
    Licensed under Apache Standard License 2.0
    Grails home is set to: /opt/install/grails

    Base Directory: /home/tek/.hudson/jobs/myapp/workspace
    Resolving dependencies…
    Dependencies resolved in 4245ms.
    Running script /opt/install/grails/scripts/TestApp.groovy
    Environment set to test
    [mkdir] Created dir: /home/tek/.grails/1.3.5/projects/workspace/plugin-classes
    [groovyc] Compiling 32 source files to /home/tek/.grails/1.3.5/projects/workspace/plugin-classes
    Note: Some input files use unchecked or unsafe operations.
    Note: Recompile with -Xlint:unchecked for details.
    [mkdir] Created dir: /home/tek/.hudson/jobs/myapp/workspace/target/classes
    [groovyc] Compiling 8 source files to /home/tek/.hudson/jobs/myapp/workspace/target/classes
    [copy] Copied 4 empty directories to 2 empty directories under /home/tek/.grails/1.3.5/projects/workspace/resources
    [copy] Copied 7 empty directories to 6 empty directories under /home/tek/.grails/1.3.5/projects/workspace/resources
    [copy] Copied 2 empty directories to 2 empty directories under /home/tek/.grails/1.3.5/projects/workspace/resources
    [copy] Copied 2 empty directories to 2 empty directories under /home/tek/.grails/1.3.5/projects/workspace/resources
    [mkdir] Created dir: /home/tek/.hudson/jobs/myapp/workspace/target/test-reports/html
    [mkdir] Created dir: /home/tek/.hudson/jobs/myapp/workspace/target/test-reports/plain

    Starting unit test phase …
    [mkdir] Created dir: /home/tek/.hudson/jobs/myapp/workspace/target/test-classes/unit

    Starting integration test phase …
    [copy] Copying 1 file to /home/tek/.hudson/jobs/myapp/workspace/target/test-classes/integration
    [copy] Copying 1 file to /home/tek/.hudson/jobs/myapp/workspace/target/test-classes
    [mkdir] Created dir: /home/tek/.grails/1.3.5/projects/workspace/resources/grails-app/i18n
    [native2ascii] Converting 13 files from /home/tek/.hudson/jobs/myapp/workspace/grails-app/i18n to /home/tek/.grails/1.3.5/projects/workspace/resources/grails-app/i18n
    [mkdir] Created dir: /home/tek/.grails/1.3.5/projects/workspace/resources/plugins/codenarc-0.7/grails-app/i18n
    [native2ascii] Converting 1 file from /home/tek/.grails/1.3.5/projects/workspace/plugins/codenarc-0.7/grails-app/i18n to /home/tek/.grails/1.3.5/projects/workspace/resources/plugins/codenarc-0.7/grails-app/i18n
    [mkdir] Created dir: /home/tek/.grails/1.3.5/projects/workspace/resources/plugins/code-coverage-1.1.8/grails-app/i18n
    [native2ascii] Converting 1 file from /home/tek/.grails/1.3.5/projects/workspace/plugins/code-coverage-1.1.8/grails-app/i18n to /home/tek/.grails/1.3.5/projects/workspace/resources/plugins/code-coverage-1.1.8/grails-app/i18n
    [copy] Copying 1 file to /home/tek/.hudson/jobs/myapp/workspace/target/classes

    Starting other test phase …
    [junitreport] Processing /home/tek/.hudson/jobs/myapp/workspace/target/test-reports/TESTS-TestSuites.xml to /tmp/null210907909
    [junitreport] Loading stylesheet /opt/install/grails/lib/junit-frames.xsl
    [junitreport] Transform time: 5022ms
    [junitreport] Deleting: /tmp/null210907909

    Tests PASSED – view reports in target/test-reports
    Application context shutting down…
    Application context shutdown.
    [workspace] $ grails codenarc
    Welcome to Grails 1.3.5 – http://grails.org/
    Licensed under Apache Standard License 2.0
    Grails home is set to: /opt/install/grails

    Base Directory: /home/tek/.hudson/jobs/myapp/workspace
    Resolving dependencies…
    Dependencies resolved in 4777ms.
    Running script /home/tek/.grails/1.3.5/projects/workspace/plugins/codenarc-0.7/scripts/Codenarc.groovy
    Environment set to development
    Running CodeNarc …
    CodeNarc completed: (p1=0; p2=1; p3=58) 7422ms
    Error executing script Codenarc: : Exceeded maximum number of priority 3 violations: (p1=0; p2=1; p3=58)
    : Exceeded maximum number of priority 3 violations: (p1=0; p2=1; p3=58)
    at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:391)
    at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
    at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
    at gant.Gant.withBuildListeners(Gant.groovy:427)
    at gant.Gant.this$2$withBuildListeners(Gant.groovy)
    at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
    at gant.Gant.dispatch(Gant.groovy:415)
    at gant.Gant.this$2$dispatch(Gant.groovy)
    at gant.Gant.invokeMethod(Gant.groovy)
    at gant.Gant.executeTargets(Gant.groovy:590)
    at gant.Gant.executeTargets(Gant.groovy:589)
    Caused by: : Exceeded maximum number of priority 3 violations: (p1=0; p2=1; p3=58)
    at org.codenarc.ant.CodeNarcTask.checkMaxViolationForPriority(CodeNarcTask.groovy:135)
    at org.codenarc.ant.CodeNarcTask.this$4$checkMaxViolationForPriority(CodeNarcTask.groovy)
    at org.codenarc.ant.CodeNarcTask.checkMaxViolations(CodeNarcTask.groovy:130)
    at org.codenarc.ant.CodeNarcTask.this$4$checkMaxViolations(CodeNarcTask.groovy)
    at org.codenarc.ant.CodeNarcTask.execute(CodeNarcTask.groovy:88)
    at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
    at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
    at Codenarc.runCodenarc(Codenarc:47)
    at Codenarc.this$4$runCodenarc(Codenarc)
    at Codenarc$_run_closure1.doCall(Codenarc:25)
    at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
    … 10 more
    — Nested Exception —
    : Exceeded maximum number of priority 3 violations: (p1=0; p2=1; p3=58)
    at org.codenarc.ant.CodeNarcTask.checkMaxViolationForPriority(CodeNarcTask.groovy:135)
    at org.codenarc.ant.CodeNarcTask.this$4$checkMaxViolationForPriority(CodeNarcTask.groovy)
    at org.codenarc.ant.CodeNarcTask.checkMaxViolations(CodeNarcTask.groovy:130)
    at org.codenarc.ant.CodeNarcTask.this$4$checkMaxViolations(CodeNarcTask.groovy)
    at org.codenarc.ant.CodeNarcTask.execute(CodeNarcTask.groovy:88)
    at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
    at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
    at Codenarc.runCodenarc(Codenarc:47)
    at Codenarc.this$4$runCodenarc(Codenarc)
    at Codenarc$_run_closure1.doCall(Codenarc:25)
    at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
    at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
    at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
    at gant.Gant.withBuildListeners(Gant.groovy:427)
    at gant.Gant.this$2$withBuildListeners(Gant.groovy)
    at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
    at gant.Gant.dispatch(Gant.groovy:415)
    at gant.Gant.this$2$dispatch(Gant.groovy)
    at gant.Gant.invokeMethod(Gant.groovy)
    at gant.Gant.executeTargets(Gant.groovy:590)
    at gant.Gant.executeTargets(Gant.groovy:589)
    Error executing script Codenarc: : Exceeded maximum number of priority 3 violations: (p1=0; p2=1; p3=58)
    ERROR: Publisher hudson.plugins.violations.ViolationsPublisher aborted due to exception
    java.lang.NullPointerException
    at hudson.plugins.violations.generate.OutputFileModel.doViolation(OutputFileModel.java:198)
    at hudson.plugins.violations.generate.OutputFileModel.createLimited(OutputFileModel.java:113)
    at hudson.plugins.violations.generate.OutputFileModel.execute(OutputFileModel.java:80)
    at hudson.plugins.violations.generate.ExecuteFilePath.execute(ExecuteFilePath.java:45)
    at hudson.plugins.violations.generate.GenerateXML.execute(GenerateXML.java:47)
    at hudson.plugins.violations.ViolationsCollector.invoke(ViolationsCollector.java:124)
    at hudson.plugins.violations.ViolationsCollector.invoke(ViolationsCollector.java:27)
    at hudson.FilePath.act(FilePath.java:753)
    at hudson.FilePath.act(FilePath.java:735)
    at hudson.plugins.violations.ViolationsPublisher.perform(ViolationsPublisher.java:74)
    at hudson.tasks.BuildStepMonitor$3.perform(BuildStepMonitor.java:36)
    at hudson.model.AbstractBuild$AbstractRunner.perform(AbstractBuild.java:601)
    at hudson.model.AbstractBuild$AbstractRunner.performAllBuildSteps(AbstractBuild.java:580)
    at hudson.model.AbstractBuild$AbstractRunner.performAllBuildSteps(AbstractBuild.java:558)
    at hudson.model.Build$RunnerImpl.post2(Build.java:157)
    at hudson.model.AbstractBuild$AbstractRunner.post(AbstractBuild.java:528)
    at hudson.model.Run.run(Run.java:1303)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:46)
    at hudson.model.ResourceController.execute(ResourceController.java:88)
    at hudson.model.Executor.run(Executor.java:137)
    Finished: FAILURE

    • Hi,
      The code has 58 priority 3 violations, so it has exceeded the configured threshold of 10 (assuming you tried maxPriority3Violations=10) – this will cause CodeNarc to fail the build.

      Might be easier to raise a Jira (at http://issues.hudson-ci.org/browse/HUDSON) and attach the:

      1. – full Hudson job console output stack trace
      2. – full CodeNarcReport.xml
      3. hudson.plugins.violations.ViolationsPublisher portion of the Hudson job configuration.

      and let me know the issue number.

  6. I’m having the same problem tek is:
    I followed every step. I’ve already checked the steps many times but I keep getting the same exception. Is there any update about how to fix this? Thanks!

    ERROR: Publisher hudson.plugins.violations.ViolationsPublisher aborted due to exception
    hudson.util.IOException2: Cannot parse exemplo_grails/target/test-reports/CodeNarcReport.xml
    at hudson.plugins.violations.parse.AbstractTypeParser.parse(AbstractTypeParser.java:64)
    at hudson.plugins.violations.ViolationsCollector.doType(ViolationsCollector.java:189)
    at hudson.plugins.violations.ViolationsCollector.invoke(ViolationsCollector.java:116)
    at hudson.plugins.violations.ViolationsCollector.invoke(ViolationsCollector.java:27)
    at hudson.FilePath.act(FilePath.java:753)
    at hudson.FilePath.act(FilePath.java:735)
    at hudson.plugins.violations.ViolationsPublisher.perform(ViolationsPublisher.java:74)
    at hudson.tasks.BuildStepMonitor$3.perform(BuildStepMonitor.java:36)
    at hudson.model.AbstractBuild$AbstractRunner.perform(AbstractBuild.java:601)
    at hudson.model.AbstractBuild$AbstractRunner.performAllBuildSteps(AbstractBuild.java:580)
    at hudson.model.AbstractBuild$AbstractRunner.performAllBuildSteps(AbstractBuild.java:558)
    at hudson.model.Build$RunnerImpl.post2(Build.java:157)
    at hudson.model.AbstractBuild$AbstractRunner.post(AbstractBuild.java:528)
    at hudson.model.Run.run(Run.java:1303)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:46)
    at hudson.model.ResourceController.execute(ResourceController.java:88)
    at hudson.model.Executor.run(Executor.java:139)
    Caused by: java.lang.NullPointerException
    at hudson.plugins.violations.model.Violation.compareTo(Violation.java:192)
    at hudson.plugins.violations.model.Violation.compareTo(Violation.java:8)
    at java.util.TreeMap.put(TreeMap.java:545)
    at java.util.TreeSet.add(TreeSet.java:238)
    at hudson.plugins.violations.model.FullFileModel.addViolation(FullFileModel.java:24)
    at hudson.plugins.violations.types.codenarc.CodenarcParser.parseFileElement(CodenarcParser.java:90)
    at hudson.plugins.violations.types.codenarc.CodenarcParser.execute(CodenarcParser.java:47)
    at hudson.plugins.violations.parse.AbstractTypeParser.parse(AbstractTypeParser.java:59)
    … 16 more

    • Unfortunately without more detail it’s not possible to diagnose – raise a Jira (at http://issues.hudson-ci.org/browse/HUDSON) and attach the:

      1. – full Hudson job console output stack trace
      2. – full CodeNarcReport.xml
      3. – hudson.plugins.violations.ViolationsPublisher portion of the Hudson job configuration
      4. – CodeNarc configuration (which rulesets are being used, override properties etc.)

      and let me know the issue number.

      • Hi,

        I’ve got the same parse error, and since a jira issue did not exist I created one, #8046, but the problem is that I can not attach files and add new comments to this issue in jira. What should I do to be able to do that?

        Btw, I’ve registered on dev.java.net just before creating this issue, is it possible that new users don’t have permissions for that?

        Best Regards,
        Peter

      • Have finally managed to get some time to look into this problem.

        The change checked in for HUDSON-7271 broke the code – it looks as though the codenarc.source.patch was only partially applied.
        The original code set source then message, as of Subversion revision 36264 it no longer set the message on the violation object:


        ret.setSource(getString("ruleName"));
        setSeverity(ret, getString("priority"));
        getParser().next();
        // get the contents of the embedded SourceLine or Message element
        try {
        expectNextTag("SourceLine");
        } catch (IOException ioe) {
        expectNextTag("Message");
        }
        ret.setSource(getNextText("Missing SourceLine or Message"));

        As message is null, it causes a null pointer exception on this piece of OutputFileModel:


        printAttr("message", v.getMessage().trim());
        printAttr("severity-level", v.getSeverityLevel());
        if (v.getPopupMessage() != null) {
        printAttr("popup-message", v.getPopupMessage().trim());
        }

        The GitHub commit linked in a comment on HUDSON-8046:
        https://github.com/hudson/hudson-plugins/commit/ec5b03d72a8bcf510960867707fab625e9b807e8


        - fixed NPE. It's not clear if the Violation class is supposed to have null message or not, but it doesn't hurt to be defensive.
        - added a test case that reproduces NPE on message.
        - Codenarc parser shouldn't override the 'source' information by a pointless copy of the source code.
        git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/plugins@36380 71c3de6d-444a-0410-be80-ed276b4c234a
        kohsuke (author)
        October 29, 2010

        Obviously this change needs a closer look to see if it is actually the correct fix.

  7. Hi! Sorry for the late. I’ve created the issue: http://issues.hudson-ci.org/browse/HUDSON-7902

    Thanks!

  8. Hi, am I right in saying this should work now? I am getting this error but the JIRA says it was fixed in October. Which version of the codenarc plugin should I be using?