Channels ▼
RSS

Security

Driving Continuous Integration from Git


Most CI servers don't expose code coverage data through their remote APIs. But there's an easy work-around: pulling down the code coverage report. To do this, the build must be configured to publish the report as a shared artifact, both on master and on the branch build. (Notice how automatically cloning build configs for development branches comes in handy here: set it up for master, and get it on the branch for free!) Once published, you can get the latest coverage report from master by a call to the CI server. For branch coverage, you can fetch the coverage report either from the latest build, or for builds related to the reference (commit) being merged, as shown here for the code coverage tool Clover.

#!/usr/bin/env ruby
 
 # Ref update hook for asserting the code coverage of a 
 # topic branch being merged into a 
 # protected branch (e.g. master) is the same or better
 #
 # requires Ruby 1.9.3+ 
 
 require_relative 'ci-util'
 require 'rexml/document'
 
 include REXML
 
 # Determine the code coverage for a particular commit by 
 # parsing Clover artifacts
 def find_coverage(bamboo, commit)
     # grab the clover.xml artifact from the build. 
     # This (assumes a shared artifact named 
     # 'clover' with 'clover.xml' at the root).
     # Change this for your coverage tool’s report name.
     clover_xml = shared_artifact_for_commit(bamboo, commit,
         bamboo["coverage_key"], "clover/clover.xml")
     doc = Document.new clover_xml
 
     # parse out the project metrics element from the response
     metrics = XPath.first(doc, "coverage/project/metrics")
 
     # Use algorithm similar to Clover 
     # (https://confluence.atlassian.com/x/LoHEB) for 
     # determining coverage percentage
     covered_elements = 
         metrics.attribute("coveredconditionals").value.to_i
     covered_elements += 
         metrics.attribute("coveredmethods").value.to_i
     covered_elements += 
         metrics.attribute("coveredstatements").value.to_i
 
     elements = metrics.attribute("conditionals").value.to_i
     elements += metrics.attribute("methods").value.to_i
     elements += metrics.attribute("statements").value.to_i
 
     coverage = 0
     if (elements > 0)
         coverage = covered_elements / elements
     end 
     coverage
 end
 
 # parse args supplied by git: <ref_name> <old_sha> <new_sha>
 ref = simple_branch_name ARGV[0]
 prevCommit = ARGV[1]
 newCommit = ARGV[2]
 
 # test if the updated ref is one we want to enforce 
 # green builds for  exit_if_not_protected_ref(ref)
 
 # get the tip of the most recently merged branch
 tip_of_merged_branch = 
     find_newest_non_merge_commit(prevCommit, newCommit)
 
 # parse our bamboo server config
 bamboo = read_config("bamboo", 
     ["url", "username", "password", "coverage_key"])
 
 # calculate code coverage for the old and new commits
 prev_coverage = find_coverage(bamboo, prevCommit)
 new_coverage = find_coverage(bamboo, tip_of_merged_branch)
 
 # if the coverage has dropped for the new commit, block the update
 if prev_coverage > new_coverage
     abort "Code coverage for #{shortSha(tip_of_merged_branch)} is 
         only #{new_coverage}! #{ref} is currently at #{prev_coverage}." 
 else 
     # if the coverage has increased, TFCIT
     puts "Nice work! Code coverage for #{ref} has 
         increased by #{new_coverage - prev_coverage}."
 end

Enforce Good Coding Style

Tests are something no self-respecting software project can do without, but they only tell part of the story. Open source tools such as Checkstyle and Findbugs scour your codebase and provide reports on stylistic violations — anything from duplicated code to excessively long methods to the use of deprecated methods. These are hard-won guidelines, and they exist for a reason: Ignoring them can result in code being harder to understand, harder to maintain, and more vulnerable to runtime problems.

As with code coverage, each team has a different level of tolerance for unstylish code. But introducing more style violations is almost universally agreed-upon as undesirable. In this, Git hooks come to the rescue. Build artifacts come into play here as well since you can easily retrieve the violations report. (No CI server we're aware of exposes static analysis data via remote access API.) So you can create another pre-receive hook that checks violations for master and the dev branch, and rejects the push if it would introduce additional errors into master.

#!/usr/bin/env ruby
 
 # Ref update hook for asserting that a topic branch 
 # being merged into a protected
 # branch (e.g. master) does not introduce an increase in 
 # checkstyle violations
 #
 # requires Ruby 1.9.3+ 
 
 require_relative 'ci-util'
 require 'rexml/document'
 
 include REXML
 
 # This example 
 def count_checkstyle_violations(bamboo, commit)
     # grab the checkstyle.xml artifact from the 
     # build (assumes a shared artifact named 
     # 'checkstyle' with 'checkstyle-result.xml' at the root)
     checkstyle_xml = 
         shared_artifact_for_commit(bamboo, commit, 
         bamboo["checkstyle_key"],
        "checkstyle/checkstyle-result.xml")
     doc = Document.new checkstyle_xml
     # could go to town on the comparison here - but let's just count  
     # the raw number of errors for the time being
     XPath.match(doc, "//error").length
 end
 
 # parse args supplied by git: <ref_name> <old_sha> <new_sha>
 ref = simple_branch_name ARGV[0]
 prevCommit = ARGV[1]
 newCommit = ARGV[2]
 
 # test if the updated ref is one we want to enforce green builds for
 exit_if_not_protected_ref(ref)
 
 # get the tip of the most recently merged branch
 tip_of_merged_branch = 
    find_newest_non_merge_commit(prevCommit, newCommit)
 
 # parse our bamboo server config
 bamboo = read_config("bamboo", 
    ["url", "username", "password", "checkstyle_key"])
 
 # calculate number of checkstyle violations for 
 #the old and new commits
 prev_violations = 
     count_checkstyle_violations(bamboo, prevCommit)
 new_violations = 
     count_checkstyle_violations(bamboo, tip_of_merged_branch)
 
 # if the number of checkstyle violations has increased, block the update
 if prev_violations > new_violations
     abort "#{shortSha(tip_of_merged_branch)} 
        has #{new_violations} checkstyle violations! #{ref} 
        currently has only #{prev_violations}." 
 else 
     # if the number of checkstyle violations has 
     # decreased, send kudos to the dev
     puts "Nice work! #{ref} has #{new_violations - prev_violations} 
         fewer checkstyle violations than before."
 end

To get the original source code and surrounding config files for all the server-side hooks you've seen here, clone the repo at: https://bitbucket.org/tpettersen/git-ci-hooks.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video