<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Personal Programming Notes]]></title>
  <link href="http://tdongsi.github.io/atom.xml" rel="self"/>
  <link href="http://tdongsi.github.io/"/>
  <updated>2022-04-05T09:27:45-07:00</updated>
  <id>http://tdongsi.github.io/</id>
  <author>
    <name><![CDATA[Cuong Dong-Si]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Closures in Loop]]></title>
    <link href="http://tdongsi.github.io/blog/2021/07/24/closures-in-loop/"/>
    <updated>2021-07-24T22:12:43-07:00</updated>
    <id>http://tdongsi.github.io/blog/2021/07/24/closures-in-loop</id>
    <content type="html"><![CDATA[<p>In this post, we&rsquo;ll look at a nasty gotcha with closures that can ensare even the most experienced programmers.
This problem can happen to any programming language that has <strong>closures</strong>.</p>

<!--more-->


<p>Recently, while working in Jenkinsfile, I got stuck with this piece of Groovy code:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">deployedApps</span> <span class="o">=</span> <span class="o">[</span> <span class="s1">&#39;app1&#39;</span><span class="o">,</span> <span class="s1">&#39;app2&#39;</span><span class="o">,</span> <span class="s1">&#39;app3&#39;</span><span class="o">]</span>
</span><span class='line'><span class="kt">def</span> <span class="n">jobs</span> <span class="o">=</span> <span class="o">[:]</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// ERROR: what&#39;s wrong with this?</span>
</span><span class='line'><span class="k">for</span> <span class="o">(</span><span class="n">String</span> <span class="nl">app:</span> <span class="n">deployedApps</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">jobs</span><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="o">=</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span><span class="o">(</span><span class="s2">&quot;Deploy $app&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// DEBUG: execute the closure to debug.</span>
</span><span class='line'><span class="k">for</span> <span class="o">(</span><span class="n">k</span> <span class="k">in</span> <span class="n">deployedApps</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">jobs</span><span class="o">[</span><span class="n">k</span><span class="o">]()</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>In the above code, the <code>jobs</code> variable is a mapping from String to <a href="https://groovy-lang.org/closures.html">Groovy Closure</a> objects.
It is intended for <a href="https://www.jenkins.io/doc/pipeline/steps/workflow-cps/#parallel-execute-in-parallel"><code>parallel</code> step</a> to programmatically create a multi-fork stage in Jenkins.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="c1">// Constructing &quot;jobs&quot; variable as above.</span>
</span><span class='line'>
</span><span class='line'><span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Deploy&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">parallel</span> <span class="n">jobs</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The stage will look like this in BlueOcean interface:</p>

<p><img class="center" src="http://tdongsi.github.io/images/jenkins/parallel.png" title="Parallel jobs" ></p>

<p>As you can probably guess, the intention is to concurrently deploy/print mulitple distinct applications, colorfully named as <code>app1</code> <code>app2</code> <code>app3</code>, in a Jenkins stage &ldquo;Deploy&rdquo;.
However, it does not work, as shown in the console log output below (NOTE: the deployment code has been replaced with <code>println</code> for simplicity).</p>

<figure class='code'><figcaption><span>Console log</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[Pipeline] stage
</span><span class='line'>[Pipeline] { (Deploy)
</span><span class='line'>[Pipeline] parallel
</span><span class='line'>[Pipeline] { (Branch: app1)
</span><span class='line'>[Pipeline] { (Branch: app2)
</span><span class='line'>[Pipeline] { (Branch: app3)
</span><span class='line'>[Pipeline] echo
</span><span class='line'>Deploy app3
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] echo
</span><span class='line'>Deploy app3
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] echo
</span><span class='line'>Deploy app3
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] // parallel
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] // stage</span></code></pre></td></tr></table></div></figure>


<p>Although the keys (used for display names) are correct, the values, which are Closure objects for actual execution such as deployment or simple prints, are wrong.
The bug is subtle and puzzling: only the <strong>last element</strong> in the application list, regardless of its size and content, will be deployed or printed out (<code>app3</code> in this example).
As we look further into it, we&rsquo;ll see that this problem has nothing to do with Map or Groovy.
It can happen to any language that has closures.
For example: The same above problem can be simplified with list <a href="https://groovyconsole.appspot.com/script/5140979879247872">in Groovy</a>:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="c1">// List version</span>
</span><span class='line'><span class="kt">def</span> <span class="n">closures</span> <span class="o">=</span> <span class="o">[]</span>
</span><span class='line'><span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">closures</span> <span class="o">+=</span> <span class="o">{</span> <span class="n">println</span> <span class="n">i</span> <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">closures</span><span class="o">.</span><span class="na">each</span><span class="o">{</span> <span class="n">it</span><span class="o">()</span> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The same problem can be seen in <a href="https://play.golang.org/p/OHhJkCwTGQ8">Go language</a>:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='go'><span class='line'><span class="kd">var</span> <span class="nx">closures</span> <span class="p">[]</span><span class="kd">func</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Functions to Print from 0 to 4</span>
</span><span class='line'><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">closures</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">closures</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span>
</span><span class='line'>    <span class="p">})</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Now call them</span>
</span><span class='line'><span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">f</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">closures</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">f</span><span class="p">()</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>or in JavaScript:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>
</span><span class='line'>    <span class="p">},</span> <span class="mi">0</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>In these List-based examples, &ldquo;5&rdquo; (the last values of the list) is always printed 5 times.</p>

<p>It turns out that this surprise problem is quite common.
In fact, it is so common that the &ldquo;Go Programming Language&rdquo; book dedicates <a href="https://www.oreilly.com/library/view/the-go-programming/9780134190570/ebook_split_047.html">a whole section</a> (5.6.1: Caveat: Capturing iteration variables) in its Chapter 5 to discuss this gotcha.
The reason is related to scope rule: as we iterate through closures and use iteration variable (<code>i</code> in the three list examples), all the Closure objects created in this loop &ldquo;capture&rdquo; and share the same variable <code>i</code> (i.e., same addressable memory location) - not its value at that particular iteration (such as 0 in the first iteration).
At the end of the loop, the variable <code>i</code> has been updated several times and has the final value <code>5</code>.
Thus, the values that are used by all individual Closure objects when executed are all <code>5</code>&rsquo;s instead of 0-4 for each.</p>

<p>Now that we understand what went wrong, the fix is pretty simple: we simply declare a new variable within the loop body before using it in the closure.
By doing so, each Closure object will have a separate variable (with distinct memory address) and value.</p>

<figure class='code'><figcaption><span>Groovy fix</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="c1">// Map version</span>
</span><span class='line'><span class="kt">def</span> <span class="n">deployedApps</span> <span class="o">=</span> <span class="o">[</span> <span class="s1">&#39;app1&#39;</span><span class="o">,</span> <span class="s1">&#39;app2&#39;</span><span class="o">,</span> <span class="s1">&#39;app3&#39;</span><span class="o">]</span>
</span><span class='line'><span class="kt">def</span> <span class="n">jobs</span> <span class="o">=</span> <span class="o">[:]</span>
</span><span class='line'>
</span><span class='line'><span class="k">for</span> <span class="o">(</span><span class="n">String</span> <span class="nl">e:</span> <span class="n">deployedApps</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">app</span> <span class="o">=</span> <span class="n">e</span>  <span class="c1">// TRICKY: necessary!</span>
</span><span class='line'>    <span class="n">jobs</span><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="o">=</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span><span class="o">(</span><span class="s2">&quot;Deploy $app&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// List version</span>
</span><span class='line'><span class="kt">def</span> <span class="n">closures</span> <span class="o">=</span> <span class="o">[]</span>
</span><span class='line'><span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">e</span> <span class="o">=</span> <span class="n">i</span>  <span class="c1">// TRICKY: necessary!</span>
</span><span class='line'>    <span class="n">closures</span> <span class="o">+=</span> <span class="o">{</span> <span class="n">println</span> <span class="n">e</span> <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">closures</span><span class="o">.</span><span class="na">each</span><span class="o">{</span> <span class="n">it</span><span class="o">()</span> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Go fix</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='go'><span class='line'><span class="kd">var</span> <span class="nx">closures</span> <span class="p">[]</span><span class="kd">func</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Functions to Print from 0 to 4</span>
</span><span class='line'><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">e</span> <span class="o">:=</span> <span class="nx">i</span>  <span class="c1">// TRICKY: necessary!</span>
</span><span class='line'>    <span class="nx">closures</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">closures</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span>
</span><span class='line'>    <span class="p">})</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>JavaScript fix</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="kd">let</span> <span class="nx">e</span> <span class="o">=</span> <span class="nx">i</span><span class="p">;</span>  <span class="c1">// TRICKY: necessary!</span>
</span><span class='line'>    <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span><span class='line'>    <span class="p">},</span> <span class="mi">0</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>In general, I would recommend adding the comment <code>TRICKY: necessary!</code>.
This would caution another team member, out of desire for <a href="https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil">premature optimization</a>, from accidentally remove the apparently useless line and produce the subtly incorrect variants as seen above.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Apple News in 2020 Election]]></title>
    <link href="http://tdongsi.github.io/blog/2020/12/20/apple-news-in-2020-election/"/>
    <updated>2020-12-20T02:08:21-08:00</updated>
    <id>http://tdongsi.github.io/blog/2020/12/20/apple-news-in-2020-election</id>
    <content type="html"><![CDATA[<p>Some positive feedback about Apple News during 2020 election.</p>

<p><img class="center" src="http://tdongsi.github.io/images/apple/Apple_News.png" title="Cover" ></p>

<!--more-->


<p>The feedback is overall positive.</p>

<p><img class="center" src="http://tdongsi.github.io/images/apple/News_2020_2.jpeg" title="Feedback" ></p>

<p>Some are outright hilarious.</p>

<p><img class="center" src="http://tdongsi.github.io/images/apple/News_2020_1.png" title="Feedback" ></p>

<p>Twitter users generally like the clear, digestable format on Apple News.
Others also start appreciating human-curated contents on Apple News, especially in the context of &ldquo;fake news&rdquo; and deeply divided 2020 election.</p>

<p>After the election, it is <a href="https://www.cnn.com/videos/business/2020/12/06/what-does-bidens-news-diet-reveal-about-him.cnn/video/playlists/business-reliable-sources/">reported</a> that the President-elect Joe Biden “relies on Apple News to help him get headlines from other reputable media sources.”
<a href="https://www.politico.com/newsletters/politico-nightly/2020/12/07/what-joe-biden-reads-and-watches-491081">Politico</a> also mentioned Biden’s media diet, noting he is a “devoted fan” of Apple News and has news app notifications on his iPhone on to keep up with stories throughout the day.</p>

<p>P.S.: I&rsquo;m just a little proud of my work&rsquo;s impact. This post is not meant to express my political views.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Minikube in Corporate VPN]]></title>
    <link href="http://tdongsi.github.io/blog/2018/12/31/minikube-in-corporate-vpn/"/>
    <updated>2018-12-31T12:40:08-08:00</updated>
    <id>http://tdongsi.github.io/blog/2018/12/31/minikube-in-corporate-vpn</id>
    <content type="html"><![CDATA[<p>If you are connected to corporate VPN via Cisco&rsquo;s AnyConnect client, you might have problem with starting Minikube.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>tdongsi$ minikube start --disk-size=50g --kubernetes-version=v1.8.0
</span><span class='line'>Starting local Kubernetes v1.8.0 cluster...
</span><span class='line'>Starting VM...
</span><span class='line'>Downloading Minikube ISO
</span><span class='line'> 140.01 MB / 140.01 MB [============================================] 100.00% 0s
</span><span class='line'>
</span><span class='line'>^C</span></code></pre></td></tr></table></div></figure>




<!--more-->


<p>The issue has been extensively discussed in <a href="https://github.com/kubernetes/minikube/issues/1099">this bug report</a>.
<a href="https://github.com/kubernetes/minikube/pull/1329">This pull request</a> supposedly fixes the issue, in v0.19.0 release.
However, I&rsquo;m still occasionally seeing the issue.
I have attempted different approaches but they have different degrees of convenience and success in different networks.</p>

<ol>
<li>Use OpenConnect for VPN access rather than Cisco&rsquo;s AnyConnect client.</li>
<li>Set port forwarding to forward port 8443 on 127.0.0.1 to port 8443 in the minikube VM.</li>
<li>Use <code>--host-only-cidr</code> option in <code>minikube start</code>.</li>
</ol>


<p>In this post, we will look into each approach in more details.</p>

<h3>Using OpenConnect</h3>

<p><a href="http://www.infradead.org/openconnect/">OpenConnect</a> is a CLI client alternative for Cisco&rsquo;s AnyConnect VPN.
Here&rsquo;s how you setup OpenConnect on Mac OSX:</p>

<ol>
<li><p>OpenConnect can be installed via <a href="http://mxcl.github.com/homebrew/">homebrew</a>:</p>

<pre><code class="` plain"> brew update
 brew install openconnect
</code></pre></li>
<li>Install the <a href="http://tuntaposx.sourceforge.net/">Mac OS X TUN/TAP</a> driver</li>
<li><p>Connect. The only thing you should be prompted for is your VPN password.</p>

<pre><code class="` plain"> sudo openconnect --user=&lt;VPN username&gt; &lt;your vpn hostname&gt;
</code></pre></li>
<li>To disconnect, just Ctrl-C in the window where you started the VPN connection.</li>
</ol>


<h3>Port forwarding localhost:xxx -> minikube_IP:xxx</h3>

<p>This approach is the more convenient and more reliable in my experience.
All you need to do is to set up a list of port forwarding rules for minikube&rsquo;s VirtualBox:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>VBoxManage controlvm minikube natpf1 k8s-apiserver,tcp,127.0.0.1,8443,,8443
</span><span class='line'>VBoxManage controlvm minikube natpf1 k8s-dashboard,tcp,127.0.0.1,30000,,30000
</span><span class='line'>VBoxManage controlvm minikube natpf1 jenkins,tcp,127.0.0.1,30080,,30080
</span><span class='line'>VBoxManage controlvm minikube natpf1 docker,tcp,127.0.0.1,2376,,2376</span></code></pre></td></tr></table></div></figure>


<p>Then, you can set up a new Kubernetes context for working with VPN:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>kubectl config set-cluster minikube-vpn --server=https://127.0.0.1:8443 --insecure-skip-tls-verify
</span><span class='line'>kubectl config set-context minikube-vpn --cluster=minikube-vpn --user=minikube</span></code></pre></td></tr></table></div></figure>


<p>When working on VPN, you can set <code>kubectl</code> to switch to the new context:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>kubectl config use-context minikube-vpn</span></code></pre></td></tr></table></div></figure>


<p>All Minikube URLs now must be accessed through <code>localhost</code> in browser.
For example, the standard Kubernetes dashboard URL such as:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>tdongsi$ minikube dashboard --url
</span><span class='line'>http://192.168.99.100:30000</span></code></pre></td></tr></table></div></figure>


<p>must now be accessed via <code>localhost:30000</code>.
Similar applies to other services that are deployed to minikube, such as <code>jenkins</code> shown above.</p>

<p>In addition, the <code>eval $(minikube docker-env)</code> standard pattern to reuse minikube&rsquo;s Docker deamon would not work anymore.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>tdongsi$ minikube docker-env
</span><span class='line'>export DOCKER_TLS_VERIFY="1"
</span><span class='line'>export DOCKER_HOST="tcp://192.168.99.100:2376"
</span><span class='line'>export DOCKER_CERT_PATH="/Users/tdongsi/.minikube/certs"
</span><span class='line'>export DOCKER_API_VERSION="1.23"
</span><span class='line'># Run this command to configure your shell:
</span><span class='line'># eval $(minikube docker-env)
</span><span class='line'>
</span><span class='line'>tdongsi$ echo $DOCKER_HOST
</span><span class='line'>tcp://192.168.99.100:2376
</span><span class='line'>tdongsi$ docker images
</span><span class='line'>Cannot connect to the Docker daemon at tcp://192.168.99.100:2376. Is the docker daemon running?</span></code></pre></td></tr></table></div></figure>


<p>Instead, you have to adjust DOCKER_HOST accordingly and use <code>docker --tlsverify=false ...</code>.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>tdongsi$ export DOCKER_HOST="tcp://127.0.0.1:2376"
</span><span class='line'>tdongsi$ alias dockervpn="docker --tlsverify=false"
</span><span class='line'>
</span><span class='line'>tdongsi$ dockervpn images
</span><span class='line'>...</span></code></pre></td></tr></table></div></figure>


<p>Finally, when not working on VPN, you can set <code>kubectl</code> to switch back to the old context:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>kubectl config use-context minikube</span></code></pre></td></tr></table></div></figure>


<h3>Use <code>--host-only-cidr</code> option</h3>

<p>This approach is the most simple but it also has less success than I hoped.
The idea of this approach is that AnyConnect VPN client likely routes <code>192.168.96.0/19</code> through its tunnel.
This may conflict with the default Minikube network of <code>192.168.99.0/24</code>.
Therefore, we use <code>minikube start --host-only-cidr 10.254.254.1/24</code> to instruct minikube to use a different, unused arbitrary network.
It is worth a try but it often does not work in my experience.</p>

<h3>Reference</h3>

<ul>
<li><a href="https://github.com/kubernetes/minikube/issues/1099">Bug report &amp; discussion</a></li>
<li><a href="https://gist.github.com/moklett/3170636">OpenConnect instructions</a></li>
<li><a href="https://www.virtualbox.org/manual/ch08.html#vboxmanage-controlvm">VBoxManage</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Github REST API Cookbook]]></title>
    <link href="http://tdongsi.github.io/blog/2018/08/06/github-rest-api/"/>
    <updated>2018-08-06T21:37:20-07:00</updated>
    <id>http://tdongsi.github.io/blog/2018/08/06/github-rest-api</id>
    <content type="html"><![CDATA[<p>The blog post shows some useful snippets for interacting with Github API.
Jenkins pipelines regularly interacts with Github (public or Enterprise) API to perform some query/posting, for example, regarding the current pull request.
For that reason, some of the following snippets are either in Groovy or <code>curl</code> commands embedded in Groovy-based Jenkinsfile code with some Jenkinsfile DSLs.</p>

<!--more-->


<h3>Working with Pull Requests</h3>

<h4>Extracting Pull Request details</h4>

<figure class='code'><figcaption><span>Get PR body text</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">groovy.json.JsonSlurper</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="nf">getPrBody</span><span class="o">(</span><span class="n">String</span> <span class="n">githubUsername</span><span class="o">,</span> <span class="n">String</span> <span class="n">githubToken</span><span class="o">,</span> <span class="n">String</span> <span class="n">repo</span><span class="o">,</span> <span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">String</span> <span class="n">GITHUB_API</span> <span class="o">=</span> <span class="s1">&#39;https://git.enterprise.com/api/v3/repos&#39;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">String</span> <span class="n">url</span> <span class="o">=</span> <span class="s2">&quot;${GITHUB_API}/${githubUsername}/${repo}/pulls/${id}&quot;</span>
</span><span class='line'>  <span class="n">println</span> <span class="s2">&quot;Querying ${url}&quot;</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">text</span> <span class="o">=</span> <span class="n">url</span><span class="o">.</span><span class="na">toURL</span><span class="o">().</span><span class="na">getText</span><span class="o">(</span><span class="nl">requestProperties:</span> <span class="o">[</span><span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="s2">&quot;token ${githubToken}&quot;</span><span class="o">])</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">json</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonSlurper</span><span class="o">().</span><span class="na">parseText</span><span class="o">(</span><span class="n">text</span><span class="o">)</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">bodyText</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="na">body</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="n">bodyText</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The equivalent <code>curl</code> command is as follows, with JSON processing is done in <code>jq</code>:</p>

<figure class='code'><figcaption><span>Equivalent curl | jq command in Jenkisfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">sh</span> <span class="s2">&quot;&quot;&quot;</span>
</span><span class='line'><span class="s2">curl -s -H &quot;</span><span class="nl">Authorization:</span> <span class="n">token</span> <span class="n">$</span><span class="o">{</span><span class="n">env</span><span class="o">.</span><span class="na">GITHUB_TOKEN</span><span class="o">}</span><span class="s2">&quot; ${GITHUB_API}/${org}/${repo}/pulls/${env.CHANGE_ID} | jq &#39;.body&#39; -r &gt; pr_body.txt</span>
</span><span class='line'><span class="s2">&quot;&quot;&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Posting comment on the Pull Request</h4>

<p>Reference: <a href="https://developer.github.com/v3/issues/comments/#create-a-comment">Create a comment</a>.</p>

<figure class='code'><figcaption><span>Equivalent curl in Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">sh</span> <span class="s2">&quot;&quot;&quot;</span>
</span><span class='line'><span class="s2">curl -s -X POST -H &quot;</span><span class="nl">Authorization:</span> <span class="n">token</span> <span class="n">$</span><span class="o">{</span><span class="n">env</span><span class="o">.</span><span class="na">GITHUB_TOKEN</span><span class="o">}</span><span class="s2">&quot; --data &#39;{&quot;</span><span class="n">body</span><span class="s2">&quot;: &quot;</span><span class="n">$</span><span class="o">{</span><span class="n">comment</span><span class="o">}</span><span class="s2">&quot;}&#39; ${GITHUB_API}/${org}/${repo}/issues/${env.CHANGE_ID}/comments</span>
</span><span class='line'><span class="s2">&quot;&quot;&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Merge Pull Request</h4>

<p>Based on <a href="http://www.cloudypoint.com/Tutorials/discussion/jenkins-solved-how-to-merge-a-successful-build-of-a-pull-request-using-a-jenkinsfile/">this article</a>.</p>

<figure class='code'><figcaption><span>Merge pull request</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">stage</span> <span class="o">(</span><span class="s2">&quot;Merge PR&quot;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">steps</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">withCredentials</span><span class="o">([</span><span class="n">usernamePassword</span><span class="o">(</span><span class="nl">credentialsId:</span> <span class="s1">&#39;credential-value&#39;</span><span class="o">,</span> <span class="nl">usernameVariable:</span> <span class="s1">&#39;ACCESS_TOKEN_USERNAME&#39;</span><span class="o">,</span> <span class="nl">passwordVariable:</span> <span class="s1">&#39;ACCESS_TOKEN_PASSWORD&#39;</span><span class="o">,)])</span> <span class="o">{</span>
</span><span class='line'>            <span class="kt">def</span> <span class="n">GITHUB</span> <span class="o">=</span> <span class="s1">&#39;https://github.ibm.com/api/v3/repos&#39;</span>
</span><span class='line'>            <span class="n">sh</span> <span class="s2">&quot;curl -X PUT -d &#39;{\&quot;commit_title\&quot;: \&quot;Merge pull request\&quot;}&#39; ${GITHUB}/org-name/repo-name/pulls/${env.CHANGE_ID}/merge?access_token=${env.ACCESS_TOKEN_PASSWORD}&quot;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The Jenkins-provided environment variable <code>$CHANGE_ID</code>, in the case of a pull request, is the pull request number.</p>

<h3>Working with Branches</h3>

<h4>Getting email of branch maintainer</h4>

<p>At the end of a Jenkins build for a feature branch (NOT <code>develop</code>/<code>master</code>), you may want to email some developer of its status, as opposed to blasting a whole distribution list.
Note that in Git, there is no such metadata for branch creator, as discussed <a href="https://stackoverflow.com/questions/12055198/find-out-git-branch-creator/19135644">here</a>.
Instead, it makes more sense to notify the latest/active committer which is likely the owner of the branch.</p>

<!--
Furthermore, while most of the branches in Git has short lifetime, some branches such as `master` and `develop` can stay around for a long time.
That person may be not active or leave the project entirely.
-->




<figure class='code'><figcaption><span>Get email of branch maintainer.</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="nf">getBranchCreator</span><span class="o">(</span><span class="n">String</span> <span class="n">githubUsername</span><span class="o">,</span> <span class="n">String</span> <span class="n">githubToken</span><span class="o">,</span> <span class="n">String</span> <span class="n">repo</span><span class="o">,</span> <span class="n">String</span> <span class="n">branch</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">String</span> <span class="n">GITHUB_API</span> <span class="o">=</span> <span class="s1">&#39;https://git.enterprise.com/api/v3/repos&#39;</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">String</span> <span class="n">url</span> <span class="o">=</span> <span class="s2">&quot;${GITHUB_API}/${githubUsername}/${repo}/branches/${branch}&quot;</span>
</span><span class='line'>    <span class="n">println</span> <span class="s2">&quot;Querying ${url}&quot;</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">text</span> <span class="o">=</span> <span class="n">url</span><span class="o">.</span><span class="na">toURL</span><span class="o">().</span><span class="na">getText</span><span class="o">(</span><span class="nl">requestProperties:</span> <span class="o">[</span><span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="s2">&quot;token ${githubToken}&quot;</span><span class="o">])</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">json</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonSlurper</span><span class="o">().</span><span class="na">parseText</span><span class="o">(</span><span class="n">text</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Get last committer.</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">creator</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="na">commit</span><span class="o">.</span><span class="na">commit</span><span class="o">.</span><span class="na">committer</span><span class="o">.</span><span class="na">email</span>
</span><span class='line'>    <span class="c1">// TRICKY: json.commit.commit.committer.email is not valid if someone commits from Github web interface.</span>
</span><span class='line'>    <span class="c1">// In the case, committer name is &#39;GitHub Enterprise&#39;.</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">json</span><span class="o">.</span><span class="na">commit</span><span class="o">.</span><span class="na">commit</span><span class="o">.</span><span class="na">committer</span><span class="o">.</span><span class="na">name</span> <span class="o">==</span> <span class="s1">&#39;GitHub Enterprise&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="c1">// Use author&#39;s email instead</span>
</span><span class='line'>    <span class="n">creator</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="na">commit</span><span class="o">.</span><span class="na">commit</span><span class="o">.</span><span class="na">author</span><span class="o">.</span><span class="na">email</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="c1">// TRICKY: the following can return inconsistent data, including &quot;null&quot;.</span>
</span><span class='line'>    <span class="c1">// def author = json.author</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">creator</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Calling from Jenkinsfile</span>
</span><span class='line'><span class="n">withCredentials</span><span class="o">([</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;UsernamePasswordMultiBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;my-credentials&#39;</span><span class="o">,</span>
</span><span class='line'>        <span class="nl">passwordVariable:</span> <span class="s1">&#39;GITHUB_PASSWORD&#39;</span><span class="o">,</span> <span class="nl">usernameVariable:</span> <span class="s1">&#39;GITHUB_USERNAME&#39;</span><span class="o">]</span>
</span><span class='line'><span class="o">])</span> <span class="o">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">env</span><span class="o">.</span><span class="na">BRANCH_NAME</span> <span class="o">==~</span> <span class="s">/PR-\d+/</span> <span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="c1">// If it is a PR build, use some distribution list</span>
</span><span class='line'>        <span class="n">email</span> <span class="o">=</span> <span class="s1">&#39;someemail@enterprise.com&#39;</span>
</span><span class='line'>    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class='line'>        <span class="c1">// NOTE: Replace env.GITHUB_USERNAME with the correct Github org name.</span>
</span><span class='line'>        <span class="n">email</span> <span class="o">=</span> <span class="n">getBranchCreator</span><span class="o">(</span><span class="n">env</span><span class="o">.</span><span class="na">GITHUB_USERNAME</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">GITHUB_PASSWORD</span><span class="o">,</span> <span class="s1">&#39;my_repo&#39;</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">BRANCH_NAME</span><span class="o">)</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Deleting a branch</h4>

<p>Searching how to delete a branch in Github API&rsquo;s <a href="https://developer.github.com/v3/repos/branches/">Branches reference</a> does not return anything.
In fact, to delete a branch, we have to delete its HEAD reference as shown <a href="https://developer.github.com/v3/git/refs/#delete-a-reference">here</a>.</p>

<figure class='code'><figcaption><span>Deleting a branch</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>DELETE /repos/octocat/Hello-World/git/refs/heads/feature-a</span></code></pre></td></tr></table></div></figure>


<h3>More tips on Github API</h3>

<p>1) When processing data from Github API, note that any commit has an author and a committer, as shown below.</p>

<figure class='code'><figcaption><span>Example commit data</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='json'><span class='line'>    <span class="s2">&quot;commit&quot;</span><span class="err">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nt">&quot;author&quot;</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>            <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Cuong Dong-Si&quot;</span><span class="p">,</span>
</span><span class='line'>            <span class="nt">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;tdongsi@example.com&quot;</span><span class="p">,</span>
</span><span class='line'>            <span class="nt">&quot;date&quot;</span><span class="p">:</span> <span class="s2">&quot;2017-08-17T05:33:46Z&quot;</span>
</span><span class='line'>        <span class="p">},</span>
</span><span class='line'>        <span class="nt">&quot;committer&quot;</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>            <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Tue-Cuong Dong-Si&quot;</span><span class="p">,</span>
</span><span class='line'>            <span class="nt">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;tdongsi@example.com&quot;</span><span class="p">,</span>
</span><span class='line'>            <span class="nt">&quot;date&quot;</span><span class="p">:</span> <span class="s2">&quot;2017-08-17T05:33:46Z&quot;</span>
</span><span class='line'>        <span class="p">},</span>
</span><span class='line'>        <span class="nt">&quot;message&quot;</span><span class="p">:</span> <span class="s2">&quot;@JIRA-4214772@: Add function.&quot;</span><span class="p">,</span>
</span><span class='line'>        <span class="nt">&quot;tree&quot;</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>            <span class="nt">&quot;sha&quot;</span><span class="p">:</span> <span class="s2">&quot;xxx&quot;</span><span class="p">,</span>
</span><span class='line'>            <span class="nt">&quot;url&quot;</span><span class="p">:</span> <span class="s2">&quot;xxx&quot;</span>
</span><span class='line'>        <span class="p">},</span>
</span><span class='line'>        <span class="nt">&quot;url&quot;</span><span class="p">:</span> <span class="s2">&quot;xxx&quot;</span><span class="p">,</span>
</span><span class='line'>        <span class="nt">&quot;comment_count&quot;</span><span class="p">:</span> <span class="mi">0</span>
</span><span class='line'>    <span class="p">}</span><span class="err">,</span>
</span></code></pre></td></tr></table></div></figure>


<p>While the two fields are usually the same in normal commits (with same associated email and timestamp), they have different meanings.
In summary, the author is the one who created the content, and the committer is the one who committed it.
The two fields can be different in some common Github workflows:</p>

<ul>
<li>Commit a change from Github web interface: The author is the logged-in user (e.g., tdongsi) but the &ldquo;committer&rdquo; field usually has the Github default name and email, e.g., &ldquo;Github Enterprise&rdquo; and &ldquo;<a href="&#109;&#97;&#105;&#x6c;&#x74;&#x6f;&#58;&#x6e;&#x6f;&#x2d;&#114;&#101;&#112;&#108;&#x79;&#x40;&#103;&#x69;&#116;&#104;&#117;&#98;&#x2e;&#99;&#x6f;&#109;">&#110;&#x6f;&#x2d;&#114;&#x65;&#112;&#x6c;&#121;&#x40;&#103;&#x69;&#x74;&#x68;&#117;&#98;&#x2e;&#99;&#111;&#x6d;</a>&rdquo;.</li>
<li>Make and/or merge a pull request from Github: For example, Alice submitted a pull request which was accepted and then merged by Betty (the repository owner). In that case, the author is Alice and the committer is Betty.</li>
</ul>


<p>Due to that subtle difference in committer and author in different scenarios, one has to be careful when using data sent by Github API in a Jenkins pipeline.
For example, you want to send email to the repository owner (committer) at the end of a Pull Request build, but what if someone adds a commit via Github web interface (commiter email would be &ldquo;<a href="&#x6d;&#x61;&#x69;&#108;&#116;&#x6f;&#58;&#110;&#x6f;&#x2d;&#114;&#x65;&#x70;&#x6c;&#x79;&#64;&#x67;&#x69;&#x74;&#x68;&#117;&#x62;&#46;&#99;&#x6f;&#109;">&#110;&#111;&#45;&#x72;&#101;&#x70;&#108;&#x79;&#64;&#103;&#x69;&#116;&#x68;&#117;&#x62;&#x2e;&#x63;&#x6f;&#109;</a>&rdquo; which is not helpful).</p>

<p>2) There is an API rate limit for the free public Github API (note &ldquo;X-RateLimit-Limit&rdquo; and &ldquo;X-RateLimit-Remaining&rdquo; in output below).</p>

<figure class='code'><figcaption><span>Github API limit</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>tdongsi-mac:dev tdongsi$ curl -i https://api.github.com/users/tdongsi
</span><span class='line'>HTTP/1.1 200 OK
</span><span class='line'>Date: Fri, 09 Jun 2017 16:16:49 GMT
</span><span class='line'>Content-Type: application/json; charset=utf-8
</span><span class='line'>Content-Length: 1236
</span><span class='line'>Server: GitHub.com
</span><span class='line'>Status: 200 OK
</span><span class='line'>X-RateLimit-Limit: 60
</span><span class='line'>X-RateLimit-Remaining: 55
</span><span class='line'>X-RateLimit-Reset: 1497025098
</span><span class='line'>Cache-Control: public, max-age=60, s-maxage=60
</span><span class='line'>Vary: Accept
</span><span class='line'>ETag: "4d7770cf5c2478bf64d23bc908494172"
</span><span class='line'>Last-Modified: Thu, 01 Jun 2017 01:09:00 GMT
</span><span class='line'>X-GitHub-Media-Type: github.v3; format=json
</span><span class='line'>Access-Control-Expose-Headers: ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
</span><span class='line'>Access-Control-Allow-Origin: *
</span><span class='line'>Content-Security-Policy: default-src 'none'
</span><span class='line'>Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
</span><span class='line'>X-Content-Type-Options: nosniff
</span><span class='line'>X-Frame-Options: deny
</span><span class='line'>X-XSS-Protection: 1; mode=block
</span><span class='line'>X-Runtime-rack: 0.030687
</span><span class='line'>Vary: Accept-Encoding
</span><span class='line'>X-Served-By: 62cdcc2d03a2f173f1c58590d1a90077
</span><span class='line'>Vary: Accept-Encoding
</span><span class='line'>X-GitHub-Request-Id: FADF:2CB6E:44F743B:56EDC6F:593AC9F1
</span><span class='line'>
</span><span class='line'>...</span></code></pre></td></tr></table></div></figure>


<p>You are likely to hit this rate limit quickly if you are polling the repos for updates.
Instead of polling from your CI (e.g., Jenkins) system, it is recommended to use <a href="https://developer.github.com/webhooks/creating/">Github webhooks</a>.</p>

<h3>Reference</h3>

<ul>
<li><a href="http://tdongsi.github.io/blog/2015/08/04/curl-cookbook/">curl cookbook</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Jsonnet Code Recipes]]></title>
    <link href="http://tdongsi.github.io/blog/2018/07/12/jsonnet-snippets/"/>
    <updated>2018-07-12T14:24:32-07:00</updated>
    <id>http://tdongsi.github.io/blog/2018/07/12/jsonnet-snippets</id>
    <content type="html"><![CDATA[<p>If you are sending/working with lots of JSON data in files such as AWS CloudFormation templates, <a href="http://jsonnet.org/"><code>Jsonnet</code> tool</a> can help reducing the hassle of maintaining.
Using Jsonnet templates, it is easier to organize data and reduce repeated code present in such JSON data.
This post goes over a few common Jsonnet code recipes for generating JSON data.</p>

<!--more-->


<h3>Automated tests</h3>

<p>At least, make sure your jsonnet template files can compile.
The following example bash script will find all the manifest files and try to compile that:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="c"># Provide a superset of required parameters</span>
</span><span class='line'><span class="k">for</span> i in <span class="sb">`</span>find Project1/manifests Project2/manifests   -name <span class="s2">&quot;*.jsonnet&quot;</span><span class="sb">`</span><span class="p">;</span>
</span><span class='line'><span class="k">do</span>
</span><span class='line'>  jsonnet -V <span class="nv">param1</span><span class="o">=</span><span class="m">1</span> -V <span class="nv">param2</span><span class="o">=</span>dummy -V <span class="nv">param3</span><span class="o">=</span><span class="m">1</span> <span class="s2">&quot;${i}&quot;</span> &gt;&gt; /dev/null
</span><span class='line'><span class="k">done</span><span class="p">;</span>
</span><span class='line'><span class="c"># This will lint-test the files, including libsonnet files.</span>
</span><span class='line'><span class="k">for</span> i in <span class="sb">`</span>find Project1 Project2 -name <span class="s2">&quot;*.*sonnet&quot;</span><span class="sb">`</span><span class="p">;</span>
</span><span class='line'><span class="k">do</span>
</span><span class='line'>  jsonnet fmt -i -n <span class="m">2</span> <span class="s2">&quot;${i}&quot;</span> --test
</span><span class='line'><span class="k">done</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Conditional add</h3>

<p>Conditionally adding items to a list.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='json'><span class='line'>  <span class="err">ports:</span> <span class="p">[</span>
</span><span class='line'>           <span class="p">{</span>
</span><span class='line'>             <span class="err">port:</span> <span class="err">$.httpPort,</span>
</span><span class='line'>             <span class="err">targetport:</span> <span class="err">$.httpTargetPort,</span>
</span><span class='line'>           <span class="p">},</span>
</span><span class='line'>           <span class="p">{</span>
</span><span class='line'>             <span class="err">port:</span> <span class="err">$.jnlpPort,</span>
</span><span class='line'>             <span class="err">targetport:</span> <span class="err">15372,</span>
</span><span class='line'>           <span class="p">},</span>
</span><span class='line'>         <span class="p">]</span> <span class="err">+</span>
</span><span class='line'>         <span class="err">(if</span> <span class="err">$.sshEnabled</span> <span class="err">then</span>
</span><span class='line'>            <span class="p">[{</span>
</span><span class='line'>              <span class="err">port:</span> <span class="err">$.sshPort,</span>
</span><span class='line'>              <span class="err">targetport:</span> <span class="err">15373,</span>
</span><span class='line'>            <span class="p">}]</span> <span class="err">else</span> <span class="p">[]</span><span class="err">),</span>
</span></code></pre></td></tr></table></div></figure>


<p>Conditionally adding attributes to an object/map.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='json'><span class='line'>  <span class="err">defaultContainerEnv::</span> <span class="p">{</span>
</span><span class='line'>    <span class="err">LOGGING_STDERR_LEVEL:</span> <span class="nt">&quot;ALL&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="err">JENKINS_USER:</span> <span class="nt">&quot;jenkins&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span> <span class="err">+</span> <span class="err">(if</span> <span class="err">$.sshEnabled</span> <span class="err">then</span> <span class="p">{</span>
</span><span class='line'>          <span class="err">SSH_PORT:</span> <span class="err">7012,</span>
</span><span class='line'>        <span class="p">}</span> <span class="err">else</span> <span class="p">{}</span>
</span><span class='line'>      <span class="err">)</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Jenkins Pipeline Unit Testing]]></title>
    <link href="http://tdongsi.github.io/blog/2018/06/07/jenkins-pipeline-unit-testing/"/>
    <updated>2018-06-07T22:33:46-07:00</updated>
    <id>http://tdongsi.github.io/blog/2018/06/07/jenkins-pipeline-unit-testing</id>
    <content type="html"><![CDATA[<p><a href="https://jenkins.io/doc/book/pipeline/shared-libraries/">Jenkins shared library</a> is a powerful way for sharing Groovy code between multiple Jenkins pipelines.
However, when many Jenkins pipelines, including mission-critical deployment pipelines, depend on such shared libraries, automated testing becomes necessary to prevent regressions whenever new changes are introduced into shared librariers.
Despite its drawbacks, the third-party <a href="https://github.com/jenkinsci/JenkinsPipelineUnit">Pipeline Unit Testing framework</a> satisfies some of automated testing needs.
It would allow you to do mock execution of pipeline steps and checking for expected behaviors before actually running in Jenkins.
However, documentation for this third-party framework is severely lacking (mentioned briefly <a href="https://jenkins.io/doc/book/pipeline/development/#unit-test">here</a>) and it is one of many reasons that unit testing for Jenkins shared libraries is usually an after-thought, instead of being integrated early.
In this blog post, we will see how to do unit testing for Jenkins shared library with the Pipeline Unit Testing framework.</p>

<!--more-->


<h3>Testing Jenkins shared library</h3>

<h4>Example Groovy file</h4>

<p>For this tutorial, we look at the following Groovy build wrapper as the example under test:</p>

<figure class='code'><figcaption><span>buildWrapper.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="nf">call</span><span class="o">(</span><span class="n">Closure</span> <span class="n">body</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">config</span> <span class="o">=</span> <span class="o">[:]</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="o">(</span><span class="n">body</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">body</span><span class="o">.</span><span class="na">resolveStrategy</span> <span class="o">=</span> <span class="n">Closure</span><span class="o">.</span><span class="na">DELEGATE_FIRST</span>
</span><span class='line'>    <span class="n">body</span><span class="o">.</span><span class="na">delegate</span> <span class="o">=</span> <span class="n">config</span>
</span><span class='line'>    <span class="n">body</span><span class="o">()</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="kt">def</span> <span class="n">settings</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">settings</span> <span class="o">?:</span> <span class="s2">&quot;settings.xml&quot;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">node</span><span class="o">(</span><span class="s1">&#39;java-agent&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Checkout&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Main&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>      <span class="c1">// Test Python setup</span>
</span><span class='line'>      <span class="n">sh</span><span class="o">(</span><span class="nl">script:</span> <span class="s1">&#39;python -c &quot;import requests&quot;&#39;</span><span class="o">,</span> <span class="nl">returnStatus:</span> <span class="kc">true</span><span class="o">)</span>
</span><span class='line'>      <span class="c1">// Test Docker setup</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s1">&#39;docker version&#39;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Post&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>      <span class="c1">// Print info of standard tools</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s1">&#39;ls -al&#39;</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s1">&#39;java -version&#39;</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s2">&quot;mvn -s $settings -version&quot;</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s1">&#39;python -V&#39;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>After the shared library is set up properly, you can call the above Groovy build wrapper in Jenkinsfile as follows to use default parameters:</p>

<figure class='code'><figcaption><span>Jenkinsfile for first use case </span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">buildWrapper</span> <span class="o">{</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>or you can set the parameters in the wrapper&rsquo;s body as follows:</p>

<figure class='code'><figcaption><span>Jenkinsfile for second use case</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">buildWrapper</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">settings</span> <span class="o">=</span> <span class="s2">&quot;dummy.xml&quot;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>In the next section, we will look into automated testing of both use cases using JenkinsPipelineUnit.</p>

<h4>Using JenkinsPipelineUnit</h4>

<p>To use JenkinsPipelineUnit, it is recommended to set up IntelliJ following <a href="http://tdongsi.github.io/blog/2018/02/09/intellij-setup-for-jenkins-shared-library-development/">this tutorial</a>.</p>

<p>To test the above <code>buildWrapper.groovy</code> using the Jenkins Pipeline Unit, you can start with a unit test for the second use case as follows:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>  <span class="cm">/**</span>
</span><span class='line'><span class="cm">   * Represent the call:</span>
</span><span class='line'><span class="cm">   *     buildWrapper {</span>
</span><span class='line'><span class="cm">   *       settings = &quot;dummy.xml&quot;</span>
</span><span class='line'><span class="cm">   *     }</span>
</span><span class='line'><span class="cm">   *</span>
</span><span class='line'><span class="cm">   * @throws Exception</span>
</span><span class='line'><span class="cm">   */</span>
</span><span class='line'>  <span class="nd">@Test</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">configured</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">script</span> <span class="o">=</span> <span class="n">loadScript</span><span class="o">(</span><span class="s1">&#39;vars/buildWrapper.groovy&#39;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">script</span><span class="o">.</span><span class="na">call</span><span class="o">({</span>
</span><span class='line'>      <span class="n">settings</span> <span class="o">=</span> <span class="s2">&quot;dummy.xml&quot;</span>
</span><span class='line'>    <span class="o">})</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">printCallStack</span><span class="o">()</span>
</span><span class='line'>  <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Unfortunately, when executing that unit test, it is very likely that you will get various errors that are not well-explained by JenkinsPipelineUnit documentation.</p>

<figure class='code'><figcaption><span>Stack trace</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>groovy.lang.MissingPropertyException: No such property: scm for class: demoWrapper
</span><span class='line'>
</span><span class='line'>  at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:66)
</span><span class='line'>  at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51)
</span><span class='line'>  at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:310)
</span><span class='line'>  at demoWrapper$_call_closure1$_closure2.doCall(demoWrapper.groovy:19)</span></code></pre></td></tr></table></div></figure>


<p>The short explanation is that the mock execution environment is not properly set up.
First, we need to call <code>setUp()</code> from the base class BaseRegressionTest of JenkinsPipelineUnit to set up the mock execution environment.
In addition, since most Groovy scripts will have this statement <code>checkout scm</code>, we need to <a href="https://github.com/jenkinsci/JenkinsPipelineUnit#mock-jenkins-variables">mock the Jenkins global variable</a> <code>scm</code>, which represents the SCM state (e.g., Git commit) associated with the current Jenkinsfile.
The most simple way to mock it is to set it to empty state as follows:</p>

<figure class='code'><figcaption><span>Mocking Jenkins variable scm</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">binding</span><span class="o">.</span><span class="na">setVariable</span><span class="o">(</span><span class="s1">&#39;scm&#39;</span><span class="o">,</span> <span class="o">[:])</span>
</span></code></pre></td></tr></table></div></figure>


<p>We can also set it to a more meaningful value such as a Git branch as follows:</p>

<figure class='code'><figcaption><span>Mocking Jenkins variable scm</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">binding</span><span class="o">.</span><span class="na">setVariable</span><span class="o">(</span><span class="s1">&#39;scm&#39;</span><span class="o">,</span> <span class="o">[</span>
</span><span class='line'>    <span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;GitSCM&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">branches:</span> <span class="o">[[</span><span class="nl">name:</span> <span class="s1">&#39;master&#39;</span><span class="o">]],</span>
</span><span class='line'>    <span class="nl">doGenerateSubmoduleConfigruations:</span> <span class="kc">false</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">extensions:</span> <span class="o">[],</span>
</span><span class='line'>    <span class="nl">submoduleCfg:</span> <span class="o">[],</span>
</span><span class='line'>    <span class="nl">userRemoteConfigs:</span> <span class="o">[[</span><span class="nl">url:</span> <span class="s2">&quot;/var/git-repo&quot;</span><span class="o">]]</span>
</span><span class='line'><span class="o">])</span>
</span></code></pre></td></tr></table></div></figure>


<p>However, an empty <code>scm</code> will usually suffice.
Besides Jenkins variables, we can also <a href="https://github.com/jenkinsci/JenkinsPipelineUnit#mock-jenkins-commands">register different Jenkins steps/commands</a> as follows:</p>

<figure class='code'><figcaption><span>Mocking Jenkins step library</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">helper</span><span class="o">.</span><span class="na">registerAllowedMethod</span><span class="o">(</span><span class="s1">&#39;library&#39;</span><span class="o">,</span> <span class="o">[</span><span class="n">String</span><span class="o">.</span><span class="na">class</span><span class="o">],</span> <span class="kc">null</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>After going through the setup steps above, you should have the following setup method like this:</p>

<figure class='code'><figcaption><span>Minimum setup method</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">com.lesfurets.jenkins.unit.BaseRegressionTest</span>
</span><span class='line'>
</span><span class='line'><span class="kd">class</span> <span class="nc">DemoTest</span> <span class="kd">extends</span> <span class="n">BaseRegressionTest</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="nd">@Override</span>
</span><span class='line'>  <span class="nd">@Before</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setUp</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">super</span><span class="o">.</span><span class="na">setUp</span><span class="o">();</span>
</span><span class='line'>    <span class="n">binding</span><span class="o">.</span><span class="na">setVariable</span><span class="o">(</span><span class="s1">&#39;scm&#39;</span><span class="o">,</span> <span class="o">[:])</span>
</span><span class='line'>    <span class="n">helper</span><span class="o">.</span><span class="na">registerAllowedMethod</span><span class="o">(</span><span class="s1">&#39;library&#39;</span><span class="o">,</span> <span class="o">[</span><span class="n">String</span><span class="o">.</span><span class="na">class</span><span class="o">],</span> <span class="kc">null</span><span class="o">)</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">...</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Rerunning the above unit test will show the full stack of execution:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>   buildWrapper.call(groovy.lang.Closure)
</span><span class='line'>      buildWrapper.node(java-agent, groovy.lang.Closure)
</span><span class='line'>         buildWrapper.stage(Checkout, groovy.lang.Closure)
</span><span class='line'>            buildWrapper.checkout({})
</span><span class='line'>         buildWrapper.stage(Main, groovy.lang.Closure)
</span><span class='line'>            buildWrapper.sh({script=python -c "import requests", returnStatus=true})
</span><span class='line'>            buildWrapper.sh(docker version)
</span><span class='line'>         buildWrapper.stage(Post, groovy.lang.Closure)
</span><span class='line'>            buildWrapper.sh(ls -al)
</span><span class='line'>            buildWrapper.sh(java -version)
</span><span class='line'>            buildWrapper.sh(mvn -s dummy.xml -version)
</span><span class='line'>            buildWrapper.sh(python -V)</span></code></pre></td></tr></table></div></figure>


<p>For automated detection of regression, we need to save the expected call stack above into a file into a location known to JenkinsPipelineUnit.
You can specify the location of such call stacks by overriding the field <code>callStackPath</code> of BaseRegressionTest in <code>setUp</code> method.
The file name should follow the convention <code>${ClassName}_${subname}.txt</code> where <code>subname</code> is specified by <code>testNonRegression</code> method in each test case.
Then, you can update the above test case to perform regression check as follows:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>  <span class="nd">@Test</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">configured</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">script</span> <span class="o">=</span> <span class="n">loadScript</span><span class="o">(</span><span class="s1">&#39;vars/demoWrapper.groovy&#39;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">script</span><span class="o">.</span><span class="na">call</span><span class="o">({</span>
</span><span class='line'>      <span class="n">settings</span> <span class="o">=</span> <span class="s2">&quot;dummy.xml&quot;</span>
</span><span class='line'>    <span class="o">})</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// printCallStack()</span>
</span><span class='line'>    <span class="n">testNonRegression</span><span class="o">(</span><span class="s2">&quot;configured&quot;</span><span class="o">)</span>
</span><span class='line'>  <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>In this example, the above call stack should be saved into <code>DemoTest_configured.txt</code> file at the location specified by <code>callStackPath</code>.
Similarly, you can also have another unit test for the other use case of <code>buildWrapper</code>.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>  <span class="cm">/**</span>
</span><span class='line'><span class="cm">   * Represent the call:</span>
</span><span class='line'><span class="cm">   * buildWrapper {</span>
</span><span class='line'><span class="cm">   * }</span>
</span><span class='line'><span class="cm">   *</span>
</span><span class='line'><span class="cm">   * @throws Exception</span>
</span><span class='line'><span class="cm">   */</span>
</span><span class='line'>  <span class="nd">@Test</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">default_value</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">script</span> <span class="o">=</span> <span class="n">loadScript</span><span class="o">(</span><span class="s1">&#39;vars/buildWrapper.groovy&#39;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">script</span><span class="o">.</span><span class="na">call</span><span class="o">({})</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// printCallStack()</span>
</span><span class='line'>    <span class="n">testNonRegression</span><span class="o">(</span><span class="s2">&quot;default&quot;</span><span class="o">)</span>
</span><span class='line'>  <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Any change in <code>buildWrapper.groovy</code> will be detected as test failures, as shown in the screen shot below.</p>

<p><img src="http://tdongsi.github.io/images/idea/screen09.png" title="Difference" alt="Screeshot" /></p>

<p>In IntelliJ, we can click on <em>Click to see difference</em> link to compare the actual call stack versus the expected one that was saved in the text file.</p>

<p><img src="http://tdongsi.github.io/images/idea/screen10.png" title="Compare" alt="Screeshot" /></p>

<p>This <a href="https://github.com/tdongsi/jenkins-steps-override/blob/master/test/vars/BuildWrapperTest.groovy">test class</a> shows a complete example, together with <a href="https://github.com/tdongsi/jenkins-steps-override/tree/master/test/vars/callstacks">files of expected call stacks</a>.</p>

<h3>Other usage</h3>

<p>You can also use PipelineUnitTests to test Jenkinsfile.
In most cases, testing Jenkinsfile will be similar to testing Groovy files in <code>vars</code> folder, as explained above, since they are quite similar.</p>

<figure class='code'><figcaption><span>Example Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">node</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Checkout&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>    <span class="n">sh</span> <span class="s1">&#39;git clean -xdf&#39;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Build and test&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">sh</span> <span class="s1">&#39;./gradlew build&#39;</span>
</span><span class='line'>    <span class="n">junit</span> <span class="s1">&#39;build/test-results/test/*.xml&#39;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The process is very similar: you need to mock out some global variables and functions corresponding to Jenkins pipeline steps.
You will need to <code>printCallStack</code> to obtain the expected output and save it into some text file.
Then, you can use <code>testNonRegression</code> for automated verification of no-regression in Jenkinsfile.
<a href="https://github.com/tdongsi/jenkins-steps-override/blob/master/test/vars/JenkinsfileTest.groovy">This test class</a> shows an example of testing Jenkinsfile using PipelineUnitTests.</p>

<p>Note that, unlike Groovy files in <code>vars</code> folder, Jenkinsfiles are regularly updated and usually NOT depended/used by any other codes.
Therefore, automated tests for Jenkinsfile are not very common because of the cost/effort required.</p>

<h3>References</h3>

<ul>
<li><a href="https://github.com/jenkinsci/JenkinsPipelineUnit">JenkinsPipelineUnit</a></li>
<li><a href="https://www.youtube.com/watch?v=RmrpUtbVR7o">The talk at Jenkins World 17</a></li>
<li><a href="https://github.com/tdongsi/jenkins-steps-override">Example</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[IntelliJ Setup for Jenkins Development]]></title>
    <link href="http://tdongsi.github.io/blog/2018/02/09/intellij-setup-for-jenkins-shared-library-development/"/>
    <updated>2018-02-09T21:32:59-08:00</updated>
    <id>http://tdongsi.github.io/blog/2018/02/09/intellij-setup-for-jenkins-shared-library-development</id>
    <content type="html"><![CDATA[<p>This posts will show how to setup IntelliJ for development of Jenkins <a href="http://tdongsi.github.io/blog/2017/12/30/groovy-hook-script-and-jenkins-configuration-as-code/">Groovy Init Scripts</a> and <a href="https://jenkins.io/doc/book/pipeline/shared-libraries/">shared libraries</a>, including auto-complete for <a href="https://jenkins.io/doc/pipeline/steps/">Jenkins pipeline steps</a>.
This is based on my original write-up in <a href="https://github.com/tdongsi/jenkins-config/blob/develop/docs/IDE.md">this project</a>.</p>

<!--more-->


<p>NOTE: this setup is NOT intended for Jenkins plugin or core development.</p>

<h3>Start a new Gradle project</h3>

<p>It is best to start a new project:</p>

<ol>
<li>Select <strong>File | New Project</strong></li>
<li>Select <strong>Gradle</strong></li>
<li>Select <strong>Java</strong> AND <strong>Groovy</strong>
<img src="http://tdongsi.github.io/images/idea/screen01.png" title="Start" alt="Screeshot" /></li>
<li>Choose <strong>GroupId</strong> and <strong>ArtifactId</strong>
<img src="http://tdongsi.github.io/images/idea/screen02.png" title="Project Name" alt="Screeshot" /></li>
<li>Enter path to Gradle. For Gradle on Mac installed via Homebrew, the Gradle home is like this:
<img src="http://tdongsi.github.io/images/idea/screen03.png" title="Configure Gradle" alt="Screeshot" />
NOTE: For Gradle installed on a Mac via Homebrew, the path &ldquo;/usr/local/opt/gradle/libexec&rdquo; may be preferrable to &ldquo;/usr/local/Cellar/gradle/X.X/libexec&rdquo; since the former will stay the same after Gradle version upgrades.
In addition, if you work extensively with Grails/Gradle/Groovy, you may prefer installing via <a href="https://sdkman.io/install"><code>sdk</code> tool</a> instead of Homebrew.</li>
<li>Choose <strong>Project name</strong> and <strong>Project Location</strong>
<img src="http://tdongsi.github.io/images/idea/screen04.png" title="Project location" alt="Screeshot" /></li>
<li>Finish
<img src="http://tdongsi.github.io/images/idea/screen05.png" title="Finish" alt="Screeshot" /></li>
</ol>


<h3>Configure IDEA</h3>

<p>Set up for Jenkins Plugins files which are of types <strong>.hpi</strong> or <strong>.jpi</strong>.</p>

<ol>
<li>Select <strong>IntelliJ IDEA | Preferences | Editor | File Types</strong></li>
<li>Select <strong>Archive</strong></li>
<li>Select <strong>+</strong> at the bottom left corner</li>
<li>Add both <strong>.hpi</strong> and <strong>.jpi</strong></li>
<li>Select <strong>OK</strong></li>
</ol>


<p><img src="http://tdongsi.github.io/images/idea/screen06.png" title="Configure plugin files" alt="Screeshot" /></p>

<p>Modify <strong>build.gradle</strong> to add the following lines.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">compile</span> <span class="s1">&#39;org.jenkins-ci.main:jenkins-core:2.23&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Jenkins plugins</span>
</span><span class='line'><span class="n">compile</span> <span class="nl">group:</span> <span class="s1">&#39;org.jenkins-ci.plugins&#39;</span><span class="o">,</span> <span class="nl">name:</span> <span class="s1">&#39;credentials&#39;</span><span class="o">,</span> <span class="nl">version:</span> <span class="s1">&#39;2.1.13&#39;</span><span class="o">,</span> <span class="nl">ext:</span> <span class="s1">&#39;jar&#39;</span>
</span><span class='line'><span class="n">compile</span> <span class="nl">group:</span> <span class="s1">&#39;org.jenkins-ci.plugins&#39;</span><span class="o">,</span> <span class="nl">name:</span> <span class="s1">&#39;matrix-auth&#39;</span><span class="o">,</span> <span class="nl">version:</span> <span class="s1">&#39;1.6&#39;</span><span class="o">,</span> <span class="nl">ext:</span> <span class="s1">&#39;jar&#39;</span>
</span><span class='line'><span class="n">compile</span> <span class="nl">group:</span> <span class="s1">&#39;org.jenkins-ci.plugins.workflow&#39;</span><span class="o">,</span> <span class="nl">name:</span> <span class="s1">&#39;workflow-cps&#39;</span><span class="o">,</span> <span class="nl">version:</span> <span class="s1">&#39;2.39&#39;</span><span class="o">,</span> <span class="nl">ext:</span> <span class="s1">&#39;jar&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// TRICKY: The lib folder contains all other plugins *JAR* files</span>
</span><span class='line'><span class="c1">// if not found in Maven</span>
</span><span class='line'><span class="n">compile</span> <span class="nf">fileTree</span><span class="o">(</span><span class="nl">dir:</span> <span class="s1">&#39;lib&#39;</span><span class="o">,</span> <span class="nl">include:</span> <span class="o">[</span><span class="s1">&#39;*.jar&#39;</span><span class="o">])</span>
</span></code></pre></td></tr></table></div></figure>


<p>The above example will grab Jenkins core libraries, Matrix Authorization Plugin hpi, other plugin dependencies and javadocs for all imported libraries.
Having these libraries imported will enable code auto-completion, syntax checks, easy refactoring when working with Groovy scripts for Jenkins.
It will be a great productivity boost.</p>

<p>NOTE 1: The last line <code>compile fileTree</code> is the last resort for any Jenkins plugins that you cannot find the right group ID and artifact ID.
It is rare these days but such cases cannot be completely ruled out.</p>

<p>NOTE 2: The <code>ext: 'jar'</code> is VERY important to ensure that <code>jar</code> files, instead of <code>hpi</code>/<code>jpi</code> files, are being downloaded and understood by IntellJ.
Without that <code>ext</code> option specified, IntellJ won&rsquo;t find JAR files nested in <code>hpi</code>/<code>jpi</code> files which is the default binaries for Jenkins plugins.</p>

<p>The final <strong>build.gradle</strong> will look like <a href="https://github.com/tdongsi/jenkins-steps-override/blob/master/build.gradle">this</a>.
All of the above setup should suffice for working with <a href="http://tdongsi.github.io/blog/2017/12/30/groovy-hook-script-and-jenkins-configuration-as-code/">Groovy Init Scripts</a>.
For working with Jenkins Shared Pipeline Libraries, we should take one extra step shown as follows.</p>

<h3>Setup for Jenkins pipeline library</h3>

<p>All Groovy files in Jenkins shared library for Pipelines have to follow this directory structure:</p>

<figure class='code'><figcaption><span>Directory structure of a Shared Library repository</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='text'><span class='line'>(root)
</span><span class='line'>+- src                     # Groovy source files
</span><span class='line'>|   +- org
</span><span class='line'>|       +- foo
</span><span class='line'>|           +- Bar.groovy  # for org.foo.Bar class
</span><span class='line'>+- vars
</span><span class='line'>|   +- foo.groovy          # for global &#39;foo&#39; variable
</span><span class='line'>|   +- foo.txt             # help for &#39;foo&#39; variable
</span><span class='line'>+- resources               # resource files (external libraries only)
</span><span class='line'>|   +- org
</span><span class='line'>|       +- foo
</span><span class='line'>|           +- bar.json    # static helper data for org.foo.Bar
</span></code></pre></td></tr></table></div></figure>


<p>Note that the Groovy code can be in both <a href="http://tdongsi.github.io/blog/2017/12/26/class-in-jenkins-shared-library/"><code>src</code></a>
and <a href="http://tdongsi.github.io/blog/2017/03/17/jenkins-pipeline-shared-libraries/"><code>vars</code></a> folders.
Therefore, you need to add the following lines in <code>build.gradle</code> to inform Gradle locations of Groovy source codes:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">sourceSets</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">main</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">groovy</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">srcDirs</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;vars&#39;</span><span class="o">,</span> <span class="s1">&#39;src&#39;</span><span class="o">]</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">test</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">groovy</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">srcDirs</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;test&#39;</span><span class="o">]</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Optionally, for unit testing Jenkins shared library, we have to add the following dependencies into our <strong>build.gradle</strong> file.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">testCompile</span> <span class="nl">group:</span> <span class="s1">&#39;com.lesfurets&#39;</span><span class="o">,</span> <span class="nl">name:</span> <span class="s1">&#39;jenkins-pipeline-unit&#39;</span><span class="o">,</span> <span class="nl">version:</span> <span class="s1">&#39;1.1&#39;</span>
</span><span class='line'><span class="n">testCompile</span> <span class="nl">group:</span> <span class="s1">&#39;org.spockframework&#39;</span><span class="o">,</span> <span class="nl">name:</span> <span class="s1">&#39;spock-core&#39;</span><span class="o">,</span> <span class="nl">version:</span> <span class="s1">&#39;1.1-groovy-2.4&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Please see <a href="http://tdongsi.github.io/blog/2018/06/07/jenkins-pipeline-unit-testing/">this blog post</a> for more details on unit testing.
The final <strong>build.gradle</strong> will look like <a href="https://github.com/tdongsi/jenkins-steps-override/blob/master/build.gradle">this</a>.</p>

<h4>Auto-completion for Jenkins Pipeline</h4>

<p>IntelliJ can&rsquo;t auto-complete <a href="https://jenkins.io/doc/pipeline/steps/">Jenkins pipeline steps</a> such as <code>echo</code> or <code>sh</code> out of the box.
We have to make it aware of those Jenkins pipeline DSLs, via a generic process explained <a href="https://confluence.jetbrains.com/display/GRVY/Scripting+IDE+for+DSL+awareness">here</a>.
Fortunately, it is much easier than it looks and you don&rsquo;t have to actually write GroovyDSL script for tens of Jenkins pipeline steps.
Jenkins make it easy by auto-generating the GroovyDSL script and it is accessible via &ldquo;IntelliJ IDEA GDSL&rdquo; link, as shown in screenshot below.</p>

<p><img src="http://tdongsi.github.io/images/idea/screen08.png" title="GroovyDSL" alt="Screeshot" /></p>

<p>The &ldquo;IntelliJ IDEA GDSL&rdquo; link can be found by accessing &ldquo;Pipeline Syntax&rdquo; section, which is visible in the left navigation menu of any Pipeline-based job (e.g., &ldquo;Admin&rdquo; job in the example above).
After clicking on the &ldquo;IntelliJ IDEA GDSL&rdquo; link, you will be able to download a plain text file with content starting like this:</p>

<figure class='code'><figcaption><span>IntelliJ IDEA GDSL</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="c1">//The global script scope</span>
</span><span class='line'><span class="kt">def</span> <span class="n">ctx</span> <span class="o">=</span> <span class="n">context</span><span class="o">(</span><span class="nl">scope:</span> <span class="n">scriptScope</span><span class="o">())</span>
</span><span class='line'><span class="n">contributor</span><span class="o">(</span><span class="n">ctx</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'><span class="n">method</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;build&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;Object&#39;</span><span class="o">,</span> <span class="nl">params:</span> <span class="o">[</span><span class="nl">job:</span><span class="s1">&#39;java.lang.String&#39;</span><span class="o">],</span> <span class="nl">doc:</span> <span class="s1">&#39;Build a job&#39;</span><span class="o">)</span>
</span><span class='line'><span class="n">method</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;build&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;Object&#39;</span><span class="o">,</span> <span class="nl">namedParams:</span> <span class="o">[</span><span class="n">parameter</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;job&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;java.lang.String&#39;</span><span class="o">),</span> <span class="n">parameter</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;parameters&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;Map&#39;</span><span class="o">),</span> <span class="n">parameter</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;propagate&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;boolean&#39;</span><span class="o">),</span> <span class="n">parameter</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;quietPeriod&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;java.lang.Integer&#39;</span><span class="o">),</span> <span class="n">parameter</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;wait&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;boolean&#39;</span><span class="o">),</span> <span class="o">],</span> <span class="nl">doc:</span> <span class="s1">&#39;Build a job&#39;</span><span class="o">)</span>
</span><span class='line'><span class="n">method</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;echo&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;Object&#39;</span><span class="o">,</span> <span class="nl">params:</span> <span class="o">[</span><span class="nl">message:</span><span class="s1">&#39;java.lang.String&#39;</span><span class="o">],</span> <span class="nl">doc:</span> <span class="s1">&#39;Print Message&#39;</span><span class="o">)</span>
</span><span class='line'><span class="n">method</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;error&#39;</span><span class="o">,</span> <span class="nl">type:</span> <span class="s1">&#39;Object&#39;</span><span class="o">,</span> <span class="nl">params:</span> <span class="o">[</span><span class="nl">message:</span><span class="s1">&#39;java.lang.String&#39;</span><span class="o">],</span> <span class="nl">doc:</span> <span class="s1">&#39;Error signal&#39;</span><span class="o">)</span>
</span><span class='line'><span class="o">...</span>
</span></code></pre></td></tr></table></div></figure>


<p>As you can see, it is a GroovyDSL file that describes the known pipeline steps such as <code>echo</code> and <code>error</code>.
Note that GDSL files can be different for different Jenkins instances, depending on Pipeline-supported plugins currently installed on individual Jenkins instance.
To make IntelliJ aware of the current Jenkins pipeline steps available on our Jenkins, we need to place that GDSL file into a location known to source folders.
As shown in the last section, anywhere in both <code>vars</code> and <code>src</code> folders are eligible as such although I personally prefer to put the GDSL file into <code>vars</code> folder (<a href="https://github.com/tdongsi/jenkins-steps-override/tree/master/vars">for example</a>).</p>

<p>After installing the GDSL file into a proper location, IntelliJ may complain with the following message <em>DSL descriptor file has been change and isn’t currently executed</em> and you have to click <strong>Activate back</strong> to get the IntelliJ aware of the current DSLs.
After that, you can enjoy auto-completion as well as documentation of the Jenkine Pipeline DSLs.</p>

<h3>More information</h3>

<ul>
<li><a href="https://github.com/tdongsi/jenkins-steps-override">Example of final setup</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Design Patterns for Container-based Distributed Systems]]></title>
    <link href="http://tdongsi.github.io/blog/2018/01/14/design-patterns-for-container-based-distributed-systems/"/>
    <updated>2018-01-14T14:56:56-08:00</updated>
    <id>http://tdongsi.github.io/blog/2018/01/14/design-patterns-for-container-based-distributed-systems</id>
    <content type="html"><![CDATA[<p>In early and mid-1990s, object-oriented programming (OOP) revolutionized software development by dividing applications into modular components as objects and classes.
Today, the rise of microservice architectures and containerization technologies enable a similar revolution in developing distributed systems and SaaS/PaaS products.
&ldquo;Containers&rdquo; and &ldquo;container images&rdquo; are analogus to &ldquo;objects&rdquo; and &ldquo;classes&rdquo; in OOP, respectively, as a unit of development and deployment in distributed systems.
In <a href="https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/45406.pdf">this paper</a>, the authors argued that just like Design Patterns for Object-Oriented Programming, codified in the famous <a href="https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/ref=sr_1_3?ie=UTF8&amp;qid=1516232505&amp;sr=8-3&amp;keywords=design+patterns">&ldquo;Gang of Four&rdquo; book</a>, some Design Patterns have emerged for Container-based Distributed Systems.
In the same paper, the authors attempted to do something similar, documenting the most common patterns and their usage.</p>

<!--more-->


<h3>General ideas</h3>

<p>The container and the container image should be the abstractions for the development of distributed systems.
Similar to what objects and classes did for OOP, thinking in term of containers abstracts away the low-level details of code and allows us to think in higher-level design patterns.
Based on how containers interact with other containers and get deployed into actual underlying machines, the authors divide the patterns in to three main groups:</p>

<ul>
<li>Single-container patterns: How to expose interface of application in container (similar to effective design of object interface).

<ul>
<li>Upward direction: expose application info/metrics such as <code>/health</code> endpoint.</li>
<li>Downward direction: Formal life cycle agreed between application and management system (similar to Android Activity model).</li>
</ul>
</li>
<li>Single-node multi-container patterns: Basically, how to design a pod in Kubernetes (pod = group of symbiotic containers)

<ul>
<li>Sidecar pattern:

<ul>
<li>Sidecar containers will extend and enhance the main container.</li>
<li>Example: Log forwarding sidecar that collects logs from main container from local disk and stream to a cluster storage system.</li>
</ul>
</li>
<li>Ambassador pattern:

<ul>
<li>Ambassador containers will proxy communication to and from the main container.</li>
<li>Ambassador simplifies and standardizes the outside world to the main container.</li>
<li>Example: Redis proxy ambassador that will discover dependent services for the main container.</li>
</ul>
</li>
<li>Adapter pattern:

<ul>
<li>Adapter containers will standardize and normalize the output of the main container.</li>
<li>In contrast to ambassador, adapter simplifies and normalizes the main app to outside world.</li>
<li>Example: Adapters to ensure all containers have the same monitoring interface to hook to central monitoring system.</li>
</ul>
</li>
</ul>
</li>
<li>Multi-node patterns

<ul>
<li>Leader election pattern

<ul>
<li>When we have many replicas, but only one of them is active at a time.</li>
</ul>
</li>
<li>Work queue pattern

<ul>
<li>One coordinator and many workers distributed to as many nodes for processing.</li>
</ul>
</li>
<li>Scatter/Gather pattern

<ul>
<li>Similar to &ldquo;Work queue&rdquo; pattern, except one coordinator scatter partial works to many slaves, then gather/merge partial outcomes from those slaves.</li>
<li>Example: A search engine service (e.g., Google) will multicast a request (search query) to all workers. Each worker will compute a local result and send it back to a gatherer who will consolidate into a single presentable response.</li>
</ul>
</li>
</ul>
</li>
</ul>


<h3>Why multiple containers on single node</h3>

<p>The more contentious patterns are probably single-node multi-container patterns, especially the sidecar pattern.
The most common anti-pattern is that we try to merge the functionality of the sidecar container into the main container.
Analogously, we also have seen a similar anti-pattern in OOP that ends up with a large class that tries to do many things at once.
There are several benefits to use separate containers:</p>

<ul>
<li>Container is the unit of resource allocation and accounting.

<ul>
<li>In the sidecar example above, the log forwarding container is configured to scavenge spare CPU cycles when the web server is not busy.</li>
</ul>
</li>
<li>Container is the unit of packaging.

<ul>
<li>Two separate teams can work independently on log forwarding and web application.</li>
</ul>
</li>
<li>Container is the unit of reuse.

<ul>
<li>The log forwarding sidecar image can be paired with other &ldquo;main&rdquo; containers.</li>
</ul>
</li>
<li>Container provides failure containment boundary.

<ul>
<li>If the log forwarding container fails, the application container continues serving.</li>
</ul>
</li>
<li>Container is the unit of deployment.

<ul>
<li>Each component can be functionally upgraded or rolled back independently.</li>
</ul>
</li>
</ul>


<h3>References</h3>

<ul>
<li><a href="https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/45406.pdf">Original Paper</a></li>
<li><a href="http://blog.kubernetes.io/2015/06/the-distributed-system-toolkit-patterns.html">Single-node multi-container patterns</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Groovy Hook Script and Jenkins Configuration as Code]]></title>
    <link href="http://tdongsi.github.io/blog/2017/12/30/groovy-hook-script-and-jenkins-configuration-as-code/"/>
    <updated>2017-12-30T21:02:48-08:00</updated>
    <id>http://tdongsi.github.io/blog/2017/12/30/groovy-hook-script-and-jenkins-configuration-as-code</id>
    <content type="html"><![CDATA[<p>This post discusses <a href="https://wiki.jenkins.io/display/JENKINS/Groovy+Hook+Script">Groovy Hook Scripts</a> and how to use them for full configuration-as-code in Jenkins with Docker, Pipeline.
This can help us to set up local environment for developing Jenkins Pipeline libraries and to evaluate various Jenkins features.</p>

<!--more-->


<h3>Groovy Hook Scripts</h3>

<p>These scripts are written in Groovy, and get executed inside the same JVM as Jenkins, allowing full access to the domain model of Jenkins.
For a given hook <code>HOOK</code>, the following locations are searched:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>WEB-INF/HOOK.groovy in jenkins.war
</span><span class='line'>WEB-INF/HOOK.groovy.d/*.groovy in the lexical order in jenkins.war
</span><span class='line'>$JENKINS_HOME/HOOK.groovy
</span><span class='line'>$JENKINS_HOME/HOOK.groovy.d/*.groovy in the lexical order</span></code></pre></td></tr></table></div></figure>


<p>The <code>init</code> is the most commonly used hook (i.e., <code>HOOK=init</code>).
The following sections show how some of the most common tasks and configurations in Jenkins can be achieved by using such Groovy scripts.
For example, in <a href="https://github.com/tdongsi/jenkins-config">this project</a>, many of such scripts are added into a Dockerized Jenkins master and executed when
starting a container to replicate configurations of the Jenkins instance in production.
It will give us ability to quickly spin up local Jenkins instances for development or troubleshooting issues in production Jenkins.</p>

<p>On a side note, IntelliJ IDEA is probably the best development tool for working with these Groovy Scripts.
Check out <a href="https://github.com/tdongsi/jenkins-config/blob/develop/docs/IDE.md">these instructions on how to set it up in IntelliJ</a>.
<em>UPDATED ON 2018/09/29</em>: More on IntelliJ setup is discussed in <a href="http://tdongsi.github.io/blog/2018/02/09/intellij-setup-for-jenkins-shared-library-development/">this blog post</a>.</p>

<h3>Authorization</h3>

<p>This section shows how to enable different authorization strategies in Groovy code.</p>

<figure class='code'><figcaption><span>"Logged-in users can do anything"</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">jenkins.model.*</span>
</span><span class='line'><span class="kt">def</span> <span class="n">instance</span> <span class="o">=</span> <span class="n">Jenkins</span><span class="o">.</span><span class="na">getInstance</span><span class="o">()</span>
</span><span class='line'>
</span><span class='line'><span class="kn">import</span> <span class="nn">hudson.security.*</span>
</span><span class='line'><span class="kt">def</span> <span class="n">realm</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HudsonPrivateSecurityRealm</span><span class="o">(</span><span class="kc">false</span><span class="o">)</span>
</span><span class='line'><span class="n">instance</span><span class="o">.</span><span class="na">setSecurityRealm</span><span class="o">(</span><span class="n">realm</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">strategy</span> <span class="o">=</span> <span class="k">new</span> <span class="n">hudson</span><span class="o">.</span><span class="na">security</span><span class="o">.</span><span class="na">FullControlOnceLoggedInAuthorizationStrategy</span><span class="o">()</span>
</span><span class='line'><span class="n">strategy</span><span class="o">.</span><span class="na">setAllowAnonymousRead</span><span class="o">(</span><span class="kc">false</span><span class="o">)</span>
</span><span class='line'><span class="n">instance</span><span class="o">.</span><span class="na">setAuthorizationStrategy</span><span class="o">(</span><span class="n">strategy</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">instance</span><span class="o">.</span><span class="na">save</span><span class="o">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>Matrix-based authorization: Gives all authenticated users admin access:</p>

<figure class='code'><figcaption><span>Matrix-based authorization</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">jenkins.model.*</span>
</span><span class='line'><span class="kt">def</span> <span class="n">instance</span> <span class="o">=</span> <span class="n">Jenkins</span><span class="o">.</span><span class="na">getInstance</span><span class="o">()</span>
</span><span class='line'>
</span><span class='line'><span class="kn">import</span> <span class="nn">hudson.security.*</span>
</span><span class='line'><span class="kt">def</span> <span class="n">realm</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HudsonPrivateSecurityRealm</span><span class="o">(</span><span class="kc">false</span><span class="o">)</span>
</span><span class='line'><span class="n">instance</span><span class="o">.</span><span class="na">setSecurityRealm</span><span class="o">(</span><span class="n">realm</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">strategy</span> <span class="o">=</span> <span class="k">new</span> <span class="n">hudson</span><span class="o">.</span><span class="na">security</span><span class="o">.</span><span class="na">GlobalMatrixAuthorizationStrategy</span><span class="o">()</span>
</span><span class='line'><span class="n">strategy</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">Jenkins</span><span class="o">.</span><span class="na">ADMINISTER</span><span class="o">,</span> <span class="s1">&#39;authenticated&#39;</span><span class="o">)</span>
</span><span class='line'><span class="n">instance</span><span class="o">.</span><span class="na">setAuthorizationStrategy</span><span class="o">(</span><span class="n">strategy</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">instance</span><span class="o">.</span><span class="na">save</span><span class="o">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>For importing GlobalMatrixAuthorizationStrategy class, make sure that <a href="https://wiki.jenkins.io/display/JENKINS/Matrix+Authorization+Strategy+Plugin"><code>matrix-auth</code> plugin</a> is installed.
For full list of standard permissions in the matrix, see <a href="https://gist.github.com/jnbnyc/c6213d3d12c8f848a385">this code snippet</a>.
Note that the matrix can be different if different plugins are installed.
For example, the &ldquo;Replay&rdquo; permission for Runs is not simply <code>hudson.model.Run.REPLAY</code> since there is no such static constant.
Such permission is only available after <a href="https://github.com/jenkinsci/workflow-cps-plugin">Workflow CPS plugin</a> is installed.
Therefore, we can only set &ldquo;Replay&rdquo; permission for Runs with the following:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">strategy</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">org</span><span class="o">.</span><span class="na">jenkinsci</span><span class="o">.</span><span class="na">plugins</span><span class="o">.</span><span class="na">workflow</span><span class="o">.</span><span class="na">cps</span><span class="o">.</span><span class="na">replay</span><span class="o">.</span><span class="na">ReplayAction</span><span class="o">.</span><span class="na">REPLAY</span><span class="o">,</span><span class="n">USER</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>References</strong></p>

<ul>
<li><a href="https://gist.github.com/jnbnyc/c6213d3d12c8f848a385">Matrix-based Authorizaiton</a></li>
<li><a href="https://github.com/oleg-nenashev/demo-jenkins-config-as-code">Jenkins config as code</a></li>
</ul>


<h3>Basic Jenkins security</h3>

<p>In addition to enable authorization strategy, we should also set some basic configurations for hardening Jenkins.
Those includes various options that you see in Jenkins UI when going to Manage Jenkins > Configure Global Security.</p>

<ul>
<li><a href="https://support.cloudbees.com/hc/en-us/articles/234709648-Disable-Jenkins-CLI">Disable Jenkins CLI</a></li>
<li>Limit Jenkins agent protocols.</li>
<li>&ldquo;Enable Slave -> Master Access Control&rdquo;</li>
<li>&ldquo;Prevent Cross Site Request Forgery exploits&rdquo;</li>
</ul>


<figure class='code'><figcaption><span>Basic Jenkins security</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">hudson.security.csrf.DefaultCrumbIssuer</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">jenkins.model.Jenkins</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">jenkins.model.JenkinsLocationConfiguration</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">jenkins.security.s2m.AdminWhitelistRule</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">org.kohsuke.stapler.StaplerProxy</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">hudson.tasks.Mailer</span>
</span><span class='line'>
</span><span class='line'><span class="nf">println</span><span class="o">(</span><span class="s2">&quot;--- Configuring Remoting (JNLP4 only, no Remoting CLI)&quot;</span><span class="o">)</span>
</span><span class='line'><span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">getDescriptor</span><span class="o">(</span><span class="s2">&quot;jenkins.CLI&quot;</span><span class="o">).</span><span class="na">get</span><span class="o">().</span><span class="na">setEnabled</span><span class="o">(</span><span class="kc">false</span><span class="o">)</span>
</span><span class='line'><span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">agentProtocols</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashSet</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;([</span><span class="s2">&quot;JNLP4-connect&quot;</span><span class="o">])</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span><span class="o">(</span><span class="s2">&quot;--- Enable Slave -&gt; Master Access Control&quot;</span><span class="o">)</span>
</span><span class='line'><span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">getExtensionList</span><span class="o">(</span><span class="n">StaplerProxy</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
</span><span class='line'>    <span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">AdminWhitelistRule</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
</span><span class='line'>    <span class="o">.</span><span class="na">masterKillSwitch</span> <span class="o">=</span> <span class="kc">false</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span><span class="o">(</span><span class="s2">&quot;--- Checking the CSRF protection&quot;</span><span class="o">)</span>
</span><span class='line'><span class="k">if</span> <span class="o">(</span><span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">crumbIssuer</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s2">&quot;CSRF protection is disabled, Enabling the default Crumb Issuer&quot;</span>
</span><span class='line'>    <span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">crumbIssuer</span> <span class="o">=</span> <span class="k">new</span> <span class="n">DefaultCrumbIssuer</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span><span class="o">(</span><span class="s2">&quot;--- Configuring Quiet Period&quot;</span><span class="o">)</span>
</span><span class='line'><span class="c1">// We do not wait for anything</span>
</span><span class='line'><span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">quietPeriod</span> <span class="o">=</span> <span class="mi">0</span>
</span><span class='line'><span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">save</span><span class="o">()</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span><span class="o">(</span><span class="s2">&quot;--- Configuring Email global settings&quot;</span><span class="o">)</span>
</span><span class='line'><span class="n">JenkinsLocationConfiguration</span><span class="o">.</span><span class="na">get</span><span class="o">().</span><span class="na">adminAddress</span> <span class="o">=</span> <span class="s2">&quot;admin@non.existent.email&quot;</span>
</span><span class='line'><span class="n">Mailer</span><span class="o">.</span><span class="na">descriptor</span><span class="o">().</span><span class="na">defaultSuffix</span> <span class="o">=</span> <span class="s2">&quot;@non.existent.email&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Some are not working for versions before 2.46, according to <a href="https://support.cloudbees.com/hc/en-us/articles/234709648-Disable-Jenkins-CLI">this</a>.
For disabling Jenkins CLI, you can simply add the java argument <code>-Djenkins.CLI.disabled=true</code> on Jenkins startup.</p>

<p><strong>References</strong></p>

<ul>
<li><a href="https://support.cloudbees.com/hc/en-us/articles/234709648-Disable-Jenkins-CLI">Disable Jenkins CLI: different versions</a></li>
<li><a href="https://wiki.jenkins.io/display/JENKINS/Slave+To+Master+Access+Control">Slave to Master Access Control</a></li>
</ul>


<h3>Create Jobs and Items</h3>

<figure class='code'><figcaption><span>Create "Pipeline script from SCM" job</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">hudson.plugins.git.*</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">scm</span> <span class="o">=</span> <span class="k">new</span> <span class="n">GitSCM</span><span class="o">(</span><span class="s2">&quot;git@github.com:dermeister0/Tests.git&quot;</span><span class="o">)</span>
</span><span class='line'><span class="n">scm</span><span class="o">.</span><span class="na">branches</span> <span class="o">=</span> <span class="o">[</span><span class="k">new</span> <span class="n">BranchSpec</span><span class="o">(</span><span class="s2">&quot;*/develop&quot;</span><span class="o">)];</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">flowDefinition</span> <span class="o">=</span> <span class="k">new</span> <span class="n">org</span><span class="o">.</span><span class="na">jenkinsci</span><span class="o">.</span><span class="na">plugins</span><span class="o">.</span><span class="na">workflow</span><span class="o">.</span><span class="na">cps</span><span class="o">.</span><span class="na">CpsScmFlowDefinition</span><span class="o">(</span><span class="n">scm</span><span class="o">,</span> <span class="s2">&quot;Jenkinsfile&quot;</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">parent</span> <span class="o">=</span> <span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span>
</span><span class='line'><span class="kt">def</span> <span class="n">job</span> <span class="o">=</span> <span class="k">new</span> <span class="n">org</span><span class="o">.</span><span class="na">jenkinsci</span><span class="o">.</span><span class="na">plugins</span><span class="o">.</span><span class="na">workflow</span><span class="o">.</span><span class="na">job</span><span class="o">.</span><span class="na">WorkflowJob</span><span class="o">(</span><span class="n">parent</span><span class="o">,</span> <span class="s2">&quot;New Job&quot;</span><span class="o">)</span>
</span><span class='line'><span class="n">job</span><span class="o">.</span><span class="na">definition</span> <span class="o">=</span> <span class="n">flowDefinition</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li><a href="https://stackoverflow.com/questions/16963309/how-create-and-configure-a-new-jenkins-job-using-groovy">Stackoverflow thread</a></li>
<li><a href="https://github.com/linagora/james-jenkins/blob/master/create-dsl-job.groovy">More example</a></li>
</ul>


<h3>Create different kinds of Credentials</h3>

<p>Adding Credentials to a new, local Jenkins for development or troubleshooting can be a daunting task.
However, with the following scripts and the right setup (NEVER commit your secrets into VCS), developers can automate adding the required Credentials into the new Jenkins.</p>

<figure class='code'><figcaption><span>Preamble</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">org.jenkinsci.plugins.plaincredentials.impl.FileCredentialsImpl</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">com.cloudbees.plugins.credentials.domains.Domain</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">com.cloudbees.plugins.credentials.CredentialsScope</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">jenkins.model.Jenkins</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">hudson.util.Secret</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">com.cloudbees.plugins.credentials.SecretBytes</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">domain</span> <span class="o">=</span> <span class="n">Domain</span><span class="o">.</span><span class="na">global</span><span class="o">()</span>
</span><span class='line'><span class="kt">def</span> <span class="n">store</span> <span class="o">=</span> <span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">getExtensionList</span><span class="o">(</span><span class="s1">&#39;com.cloudbees.plugins.credentials.SystemCredentialsProvider&#39;</span><span class="o">)[</span><span class="mi">0</span><span class="o">].</span><span class="na">getStore</span><span class="o">()</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>"Username with Password" type</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">githubAccount</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UsernamePasswordCredentialsImpl</span><span class="o">(</span>
</span><span class='line'>        <span class="n">CredentialsScope</span><span class="o">.</span><span class="na">GLOBAL</span><span class="o">,</span> <span class="s2">&quot;test-github&quot;</span><span class="o">,</span> <span class="s2">&quot;Test Github Account&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="s2">&quot;testuser&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="s2">&quot;testpassword&quot;</span>
</span><span class='line'><span class="o">)</span>
</span><span class='line'><span class="n">store</span><span class="o">.</span><span class="na">addCredentials</span><span class="o">(</span><span class="n">domain</span><span class="o">,</span> <span class="n">githubAccount</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>"Secret text" type</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">secretString</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringCredentialsImpl</span><span class="o">(</span>
</span><span class='line'>        <span class="n">CredentialsScope</span><span class="o">.</span><span class="na">GLOBAL</span><span class="o">,</span> <span class="s2">&quot;test-secret-string&quot;</span><span class="o">,</span> <span class="s2">&quot;Test Secret String&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="n">Secret</span><span class="o">.</span><span class="na">fromString</span><span class="o">(</span><span class="s2">&quot;testpassword&quot;</span><span class="o">)</span>
</span><span class='line'><span class="o">)</span>
</span><span class='line'><span class="n">store</span><span class="o">.</span><span class="na">addCredentials</span><span class="o">(</span><span class="n">domain</span><span class="o">,</span> <span class="n">secretString</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>"Secret file" type</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="c1">// Text file</span>
</span><span class='line'><span class="kt">def</span> <span class="n">secret</span> <span class="o">=</span> <span class="s1">&#39;&#39;&#39;Hi,</span>
</span><span class='line'><span class="s1">This is the content of the file.</span>
</span><span class='line'><span class="s1">&#39;&#39;&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">secretBytes</span> <span class="o">=</span> <span class="n">SecretBytes</span><span class="o">.</span><span class="na">fromBytes</span><span class="o">(</span><span class="n">secret</span><span class="o">.</span><span class="na">getBytes</span><span class="o">())</span>
</span><span class='line'><span class="kt">def</span> <span class="n">secretFile</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileCredentialsImpl</span><span class="o">(</span>
</span><span class='line'>  <span class="n">CredentialsScope</span><span class="o">.</span><span class="na">GLOBAL</span><span class="o">,</span>
</span><span class='line'>  <span class="s1">&#39;text-secret-file&#39;</span><span class="o">,</span>
</span><span class='line'>  <span class="s1">&#39;description&#39;</span><span class="o">,</span>
</span><span class='line'>  <span class="s1">&#39;file.txt&#39;</span><span class="o">,</span>
</span><span class='line'>  <span class="n">secretBytes</span><span class="o">)</span>
</span><span class='line'><span class="n">store</span><span class="o">.</span><span class="na">addCredentials</span><span class="o">(</span><span class="n">domain</span><span class="o">,</span> <span class="n">secretFile</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Binary file</span>
</span><span class='line'><span class="n">Path</span> <span class="n">fileLocation</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s2">&quot;/path/to/some/file.tar&quot;</span><span class="o">);</span>
</span><span class='line'><span class="kt">def</span> <span class="n">secretBytes</span> <span class="o">=</span> <span class="n">SecretBytes</span><span class="o">.</span><span class="na">fromBytes</span><span class="o">(</span><span class="n">Files</span><span class="o">.</span><span class="na">readAllBytes</span><span class="o">(</span><span class="n">fileLocation</span><span class="o">))</span>
</span><span class='line'><span class="kt">def</span> <span class="n">secretFile</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileCredentialsImpl</span><span class="o">(</span>
</span><span class='line'>  <span class="n">CredentialsScope</span><span class="o">.</span><span class="na">GLOBAL</span><span class="o">,</span>
</span><span class='line'>  <span class="s1">&#39;binary-secret-file&#39;</span><span class="o">,</span>
</span><span class='line'>  <span class="s1">&#39;description&#39;</span><span class="o">,</span>
</span><span class='line'>  <span class="s1">&#39;file.tar&#39;</span><span class="o">,</span>
</span><span class='line'>  <span class="n">secretBytes</span><span class="o">)</span>
</span><span class='line'><span class="n">store</span><span class="o">.</span><span class="na">addCredentials</span><span class="o">(</span><span class="n">domain</span><span class="o">,</span> <span class="n">secretFile</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>"SSH Username with private key" type</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">String</span> <span class="n">keyfile</span> <span class="o">=</span> <span class="s2">&quot;/var/jenkins_home/.ssh/id_rsa&quot;</span>
</span><span class='line'><span class="kt">def</span> <span class="n">privateKey</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BasicSSHUserPrivateKey</span><span class="o">(</span>
</span><span class='line'>        <span class="n">CredentialsScope</span><span class="o">.</span><span class="na">GLOBAL</span><span class="o">,</span>
</span><span class='line'>        <span class="s2">&quot;jenkins_ssh_key&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="s2">&quot;git&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="k">new</span> <span class="n">BasicSSHUserPrivateKey</span><span class="o">.</span><span class="na">FileOnMasterPrivateKeySource</span><span class="o">(</span><span class="n">keyfile</span><span class="o">),</span>
</span><span class='line'>        <span class="s2">&quot;&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="s2">&quot;&quot;</span>
</span><span class='line'><span class="o">)</span>
</span><span class='line'><span class="n">store</span><span class="o">.</span><span class="na">addCredentials</span><span class="o">(</span><span class="n">domain</span><span class="o">,</span> <span class="n">privateKey</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>"Certificate" type</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">String</span> <span class="n">minikubeKeyfile</span> <span class="o">=</span> <span class="s2">&quot;/var/jenkins_home/secret_data/minikube.pfx&quot;</span>
</span><span class='line'><span class="kt">def</span> <span class="n">minikubeCreds</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CertificateCredentialsImpl</span><span class="o">(</span>
</span><span class='line'>        <span class="n">CredentialsScope</span><span class="o">.</span><span class="na">GLOBAL</span><span class="o">,</span>
</span><span class='line'>        <span class="s2">&quot;minikube&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="s2">&quot;Minikube client certificate&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="s2">&quot;secret&quot;</span><span class="o">,</span>
</span><span class='line'>        <span class="k">new</span> <span class="n">CertificateCredentialsImpl</span><span class="o">.</span><span class="na">FileOnMasterKeyStoreSource</span><span class="o">(</span><span class="n">minikubeKeyfile</span><span class="o">))</span>
</span><span class='line'><span class="n">store</span><span class="o">.</span><span class="na">addCredentials</span><span class="o">(</span><span class="n">domain</span><span class="o">,</span> <span class="n">minikubeCreds</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li><a href="https://support.cloudbees.com/hc/en-us/articles/217708168-create-credentials-from-groovy">CloudBees tutorial</a></li>
<li><a href="https://github.com/tdongsi/jenkins-config/blob/develop/init_scripts/src/main/groovy/scripts/Credentials.groovy">Examples</a></li>
</ul>


<h3>Notifications</h3>

<figure class='code'><figcaption><span>Configure Slack</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">jenkins.model.*</span>
</span><span class='line'><span class="kt">def</span> <span class="n">instance</span> <span class="o">=</span> <span class="n">Jenkins</span><span class="o">.</span><span class="na">getInstance</span><span class="o">()</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// configure slack</span>
</span><span class='line'><span class="kt">def</span> <span class="n">slack</span> <span class="o">=</span> <span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">getExtensionList</span><span class="o">(</span>
</span><span class='line'>  <span class="n">jenkins</span><span class="o">.</span><span class="na">plugins</span><span class="o">.</span><span class="na">slack</span><span class="o">.</span><span class="na">SlackNotifier</span><span class="o">.</span><span class="na">DescriptorImpl</span><span class="o">.</span><span class="na">class</span>
</span><span class='line'><span class="o">)[</span><span class="mi">0</span><span class="o">]</span>
</span><span class='line'><span class="kt">def</span> <span class="n">params</span> <span class="o">=</span> <span class="o">[</span>
</span><span class='line'>  <span class="nl">slackTeamDomain:</span> <span class="s2">&quot;domain&quot;</span><span class="o">,</span>
</span><span class='line'>  <span class="nl">slackToken:</span> <span class="s2">&quot;token&quot;</span><span class="o">,</span>
</span><span class='line'>  <span class="nl">slackRoom:</span> <span class="s2">&quot;&quot;</span><span class="o">,</span>
</span><span class='line'>  <span class="nl">slackBuildServerUrl:</span> <span class="s2">&quot;$JENKINS_URL&quot;</span><span class="o">,</span>
</span><span class='line'>  <span class="nl">slackSendAs:</span> <span class="s2">&quot;&quot;</span>
</span><span class='line'><span class="o">]</span>
</span><span class='line'><span class="kt">def</span> <span class="n">req</span> <span class="o">=</span> <span class="o">[</span>
</span><span class='line'>  <span class="nl">getParameter:</span> <span class="o">{</span> <span class="n">name</span> <span class="o">-&gt;</span> <span class="n">params</span><span class="o">[</span><span class="n">name</span><span class="o">]</span> <span class="o">}</span>
</span><span class='line'><span class="o">]</span> <span class="k">as</span> <span class="n">org</span><span class="o">.</span><span class="na">kohsuke</span><span class="o">.</span><span class="na">stapler</span><span class="o">.</span><span class="na">StaplerRequest</span>
</span><span class='line'><span class="n">slack</span><span class="o">.</span><span class="na">configure</span><span class="o">(</span><span class="n">req</span><span class="o">,</span> <span class="kc">null</span><span class="o">)</span>
</span><span class='line'><span class="n">slack</span><span class="o">.</span><span class="na">save</span><span class="o">()</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Global email settings</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">jenkins.model.*</span>
</span><span class='line'><span class="kt">def</span> <span class="n">instance</span> <span class="o">=</span> <span class="n">Jenkins</span><span class="o">.</span><span class="na">getInstance</span><span class="o">()</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// set email</span>
</span><span class='line'><span class="kt">def</span> <span class="n">location_config</span> <span class="o">=</span> <span class="n">JenkinsLocationConfiguration</span><span class="o">.</span><span class="na">get</span><span class="o">()</span>
</span><span class='line'><span class="n">location_config</span><span class="o">.</span><span class="na">setAdminAddress</span><span class="o">(</span><span class="s2">&quot;jenkins@skynet.net&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[`src` Folder in Jenkins Shared Library]]></title>
    <link href="http://tdongsi.github.io/blog/2017/12/26/class-in-jenkins-shared-library/"/>
    <updated>2017-12-26T11:18:09-08:00</updated>
    <id>http://tdongsi.github.io/blog/2017/12/26/class-in-jenkins-shared-library</id>
    <content type="html"><![CDATA[<p>This post reviews best practices when we implement Groovy classes and/or static Groovy methods, in <code>src</code> folder as opposed to <code>vars</code> folder, for Jenkins Shared Library.</p>

<!--more-->


<h3>Examples of shared libraries in <code>src</code> folder</h3>

<p>All Groovy files in Jenkins shared library for pipelines have to follow this directory structure:</p>

<figure class='code'><figcaption><span>Directory structure of a Shared Library repository</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>(root)
</span><span class='line'>+- src                     # Groovy source files
</span><span class='line'>|   +- org
</span><span class='line'>|       +- foo
</span><span class='line'>|           +- Bar.groovy  # for org.foo.Bar class
</span><span class='line'>+- vars
</span><span class='line'>|   +- foo.groovy          # for global 'foo' variable
</span><span class='line'>|   +- foo.txt             # help for 'foo' variable
</span><span class='line'>+- resources               # resource files (external libraries only)
</span><span class='line'>|   +- org
</span><span class='line'>|       +- foo
</span><span class='line'>|           +- bar.json    # static helper data for org.foo.Bar</span></code></pre></td></tr></table></div></figure>


<p><code>src</code> folder is intended to set up with <code>groovy</code> files in the standard directory structure, such as &ldquo;src/org/foo/bar.groovy&rdquo;.
It will be added to the class path when the Jenkins pipelines are executed.</p>

<p>Any custom function in a Jenkins shared library has to eventually use basic Pipeline steps such as <code>sh</code> or <code>git</code>, made available through various Jenkins plugins.
However, Groovy classes in shared Jenkins library cannot simply call those basic steps directly.
There are a few approaches on how to access those Pipeline steps indirectly.</p>

<h3>Method 1: Methods in implicit class</h3>

<p>Groovy scripts in <code>src</code> folder can implement methods that invoke Pipeline steps, like this:</p>

<figure class='code'><figcaption><span>Example 1</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="c1">// src/org/demo/buildUtils.groovy</span>
</span><span class='line'><span class="kn">package</span> <span class="n">org</span><span class="o">.</span><span class="na">demo</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="nf">checkOutFrom</span><span class="o">(</span><span class="n">repo</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">git</span> <span class="nl">url:</span> <span class="s2">&quot;git@github.com:jenkinsci/${repo}&quot;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The method is stored implicitly in library and can then be invoked from a Scripted Pipeline like this:</p>

<figure class='code'><figcaption><span>Example 1 (continued)</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">myUtils</span> <span class="o">=</span> <span class="k">new</span> <span class="n">org</span><span class="o">.</span><span class="na">demo</span><span class="o">.</span><span class="na">buildUtils</span><span class="o">()</span>
</span><span class='line'><span class="n">myUtils</span><span class="o">.</span><span class="na">checkOutFrom</span><span class="o">(</span><span class="n">repo</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>However, the requirement of this approach is that those methods defined in <code>buildUtils.groovy</code> cannot be enclosed in any class.
The &ldquo;implicit class&rdquo; mentioned in this approach refers to the fact that any Groovy script, such as <code>buildUtils.groovy</code>, has an implicit class (e.g., <code>org.demo.buildUtils</code>) that contains all the defined functions in it.
This approach has limitations; for example, it prevents the declaration of a superclass.</p>

<h3>Method 2: Explicit objects</h3>

<p>In the following example, we create an enclosing class that would facilitate things like defining a superclass.
In that case, to access standard DSL steps such as <code>sh</code> or <code>git</code>, we can explicitly pass special global variables <code>env</code> and <code>steps</code> into a constructor or a method of the class.
Global object <code>env</code> contains all current environment variables while <code>steps</code> contains all standard pipeline steps.
Note that the class must also implement Serializable interface to support saving the state if the pipeline is stopped or resumed.</p>

<figure class='code'><figcaption><span>Example 2</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">package</span> <span class="n">org</span><span class="o">.</span><span class="na">demo</span>
</span><span class='line'><span class="kd">class</span> <span class="nc">Utilities</span> <span class="kd">implements</span> <span class="n">Serializable</span> <span class="o">{</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">env</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">steps</span>
</span><span class='line'>  <span class="nf">Utilities</span><span class="o">(</span><span class="n">env</span><span class="o">,</span> <span class="n">steps</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="k">this</span><span class="o">.</span><span class="na">env</span> <span class="o">=</span> <span class="n">env</span>
</span><span class='line'>    <span class="k">this</span><span class="o">.</span><span class="na">steps</span> <span class="o">=</span> <span class="n">steps</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="kt">def</span> <span class="nf">mvn</span><span class="o">(</span><span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">steps</span><span class="o">.</span><span class="na">sh</span> <span class="s2">&quot;${steps.tool &#39;Maven&#39;}/bin/mvn -o ${args}&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Example 2 (continued)</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="nd">@Library</span><span class="o">(</span><span class="s1">&#39;utils&#39;</span><span class="o">)</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">org.foo.Utilities</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">utils</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Utilities</span><span class="o">(</span><span class="n">env</span><span class="o">,</span> <span class="n">steps</span><span class="o">)</span>
</span><span class='line'><span class="n">node</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">utils</span><span class="o">.</span><span class="na">mvn</span> <span class="s1">&#39;clean package&#39;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Method 3: Static methods in explicit class</h3>

<p>In the final example, we can also use static method and pass in the <code>script</code> object, which already has access to everything, including environment variables <code>script.env</code> and Pipeline steps such as <code>script.sh</code>.</p>

<figure class='code'><figcaption><span>Example 3</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">package</span> <span class="n">org</span><span class="o">.</span><span class="na">demo</span>
</span><span class='line'><span class="kd">class</span> <span class="nc">Utilities</span> <span class="o">{</span>
</span><span class='line'>  <span class="kd">static</span> <span class="kt">def</span> <span class="nf">mvn</span><span class="o">(</span><span class="n">script</span><span class="o">,</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">script</span><span class="o">.</span><span class="na">sh</span> <span class="s2">&quot;${script.tool &#39;Maven&#39;}/bin/mvn -s ${script.env.HOME}/jenkins.xml -o ${args}&quot;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The above example shows the script being passed in to one static method, invoked from a Scripted Pipeline as follows (note <code>import static</code>):</p>

<figure class='code'><figcaption><span>Example 3 (continued)</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="nd">@Library</span><span class="o">(</span><span class="s1">&#39;utils&#39;</span><span class="o">)</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">demo</span><span class="o">.</span><span class="na">Utilities</span><span class="o">.*</span>
</span><span class='line'><span class="n">node</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">mvn</span> <span class="k">this</span><span class="o">,</span> <span class="s1">&#39;clean package&#39;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Recommended practices</h3>

<p>All three approaches shown in three examples above are valid in Scripted Jenkinsfile.
However, per <a href="https://youtu.be/M8U9RyL756U?list=PLvBBnHmZuNQLqgKDFmGnUClw68qsQ9Hq5&amp;t=2310">recommended</a> by <a href="https://www.slideshare.net/BrentLaster/2017-jenkins-world/36">CloudBees Inc.</a>, <code>src</code> folder is best for utility classes that contains a bunch of static Groovy methods.
It is easier to use global variables in the <code>vars</code> directory instead of classes in the <code>src</code> directory, especially when you need to support <strong>declarative</strong> pipelines in your team.
The reason is that in declarative pipelines, the custom functions in Jenkins shared libraries must be callable in declarative syntax, e.g., &ldquo;myCustomFunction var1, var2&rdquo; format.
As you can see in the examples above, only in Method 3 (Static methods in explicit class), where custom functions are defined as static methods, the invocation of custom function is compatible with declarative pipeline syntax.</p>

<p>When using <code>src</code> area&rsquo;s Groovy codes with <code>library</code> step, you should use a temporary variable to reduce its verbosity, as follows:</p>

<figure class='code'><figcaption><span>Reduce verbosity</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">mvn</span> <span class="o">=</span> <span class="n">library</span><span class="o">(</span><span class="s1">&#39;utils&#39;</span><span class="o">).</span><span class="na">org</span><span class="o">.</span><span class="na">demo</span><span class="o">.</span><span class="na">Utilities</span><span class="o">.</span><span class="na">mvn</span>
</span><span class='line'><span class="n">mvn</span> <span class="k">this</span><span class="o">,</span> <span class="s1">&#39;clean package&#39;</span>
</span><span class='line'><span class="c1">// or </span>
</span><span class='line'><span class="c1">// mvn self, &#39;clean package&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Reference</h3>

<ul>
<li><a href="https://youtu.be/M8U9RyL756U?list=PLvBBnHmZuNQLqgKDFmGnUClw68qsQ9Hq5&amp;t=1576">Great Talk at Jenkins World 2017</a></li>
<li><a href="https://jenkins.io/doc/book/pipeline/shared-libraries/">Accessing Steps section in Jenkins Doc</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[TCP Ping]]></title>
    <link href="http://tdongsi.github.io/blog/2017/10/09/tcp-ping/"/>
    <updated>2017-10-09T14:36:56-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/10/09/tcp-ping</id>
    <content type="html"><![CDATA[<p>This post explains a work-around for pinging hosts in network where ICMP requests are blocked.</p>

<!--more-->


<p>There are times that we can <code>ssh</code> to our servers but simply can&rsquo;t <code>ping</code> those servers.</p>

<figure class='code'><figcaption><span>ssh works but ping fails</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>tdongsi$ ping 10.252.158.71
</span><span class='line'>PING 10.252.158.71 (10.252.158.71): 56 data bytes
</span><span class='line'>Request timeout for icmp_seq 0
</span><span class='line'>Request timeout for icmp_seq 1
</span><span class='line'>Request timeout for icmp_seq 2
</span><span class='line'>Request timeout for icmp_seq 3
</span><span class='line'>^C
</span><span class='line'>--- 10.252.158.71 ping statistics ---
</span><span class='line'>5 packets transmitted, 0 packets received, 100.0% packet loss
</span><span class='line'>
</span><span class='line'>tdongsi$ ssh centos@10.252.158.71
</span><span class='line'>Last login: Mon Feb  6 23:35:41 2017 from 10.3.55.249
</span><span class='line'>[centos@kube-1 ~]$ exit
</span><span class='line'>logout
</span><span class='line'>Connection to 10.252.158.71 closed.</span></code></pre></td></tr></table></div></figure>


<p>One possible explanation of seemingly perplexing situation like above is that <a href="https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol">ICMP requests</a> (i.e., ping) are blocked.
It is not unheard of that an ISP or a network administrator blocks ICMP requests.
To work around that limitation, you can use a &ldquo;TCP ping&rdquo; on a port, using a tool like <code>nmap</code>.
The following examples check if a host can be reached via port 80:</p>

<figure class='code'><figcaption><span>"TCP ping" with nmap: success and failure.</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>tdongsi$ nmap -sn -PS80 10.252.158.71
</span><span class='line'>
</span><span class='line'>Starting Nmap 7.40 ( https://nmap.org ) at 2017-03-03 17:17 PST
</span><span class='line'>Nmap scan report for master-1 (10.252.158.71)
</span><span class='line'>Host is up (0.051s latency).
</span><span class='line'>Nmap done: 1 IP address (1 host up) scanned in 0.06 seconds
</span><span class='line'>
</span><span class='line'>tdongsi$ nmap -sn -PS80 master-2
</span><span class='line'>
</span><span class='line'>Starting Nmap 7.40 ( https://nmap.org ) at 2017-10-09 14:35 PDT
</span><span class='line'>Note: Host seems down. If it is really up, but blocking our ping probes, try -Pn
</span><span class='line'>Nmap done: 1 IP address (0 hosts up) scanned in 2.02 seconds</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Jacoco in Maven Projects]]></title>
    <link href="http://tdongsi.github.io/blog/2017/09/23/jacoco-in-maven-project/"/>
    <updated>2017-09-23T21:39:13-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/09/23/jacoco-in-maven-project</id>
    <content type="html"><![CDATA[<p>This blog post goes over some recipes for adding code coverage report to Maven-based projects with Jacoco.</p>

<!--more-->


<h3>Standard usage</h3>

<p>Based on <a href="http://www.eclemma.org/jacoco/trunk/doc/maven.html">offical instruction</a> and <a href="https://stackoverflow.com/questions/36199422/maven-unit-test-code-coverage">this</a>, you need to add the following code snippet in to your Maven <code>pom.xml</code>.</p>

<figure class='code'><figcaption><span>Jacoco usage (typical Maven project)</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;project&gt;</span>
</span><span class='line'>...
</span><span class='line'>
</span><span class='line'>    <span class="nt">&lt;dependencies&gt;</span>
</span><span class='line'>        ...
</span><span class='line'>    <span class="nt">&lt;/dependencies&gt;</span>
</span><span class='line'>
</span><span class='line'>    <span class="nt">&lt;build&gt;</span>
</span><span class='line'>        <span class="nt">&lt;plugins&gt;</span>
</span><span class='line'>            ...
</span><span class='line'>            <span class="c">&lt;!-- Code Coverage report generation --&gt;</span>
</span><span class='line'>            <span class="nt">&lt;plugin&gt;</span>
</span><span class='line'>                <span class="nt">&lt;groupId&gt;</span>org.jacoco<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>                <span class="nt">&lt;artifactId&gt;</span>jacoco-maven-plugin<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>                <span class="nt">&lt;version&gt;</span>0.7.9<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>                <span class="nt">&lt;executions&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;execution&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;goals&gt;</span>
</span><span class='line'>                            <span class="nt">&lt;goal&gt;</span>prepare-agent<span class="nt">&lt;/goal&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;/goals&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;/execution&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;execution&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;id&gt;</span>generate-code-coverage-report<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;phase&gt;</span>test<span class="nt">&lt;/phase&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;goals&gt;</span>
</span><span class='line'>                            <span class="nt">&lt;goal&gt;</span>report<span class="nt">&lt;/goal&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;/goals&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;/execution&gt;</span>
</span><span class='line'>                <span class="nt">&lt;/executions&gt;</span>
</span><span class='line'>            <span class="nt">&lt;/plugin&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/plugins&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/build&gt;</span>
</span><span class='line'><span class="nt">&lt;/project&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>At least, you need &ldquo;prepare-agent&rdquo; before test phase for Jacoco instrumentation and &ldquo;report&rdquo; after test phase for generating the report.
You could subject the project to code coverage and generate the same report without making any changes to the pom file.
To do this, run the following command:</p>

<figure class='code'><figcaption><span>Jacoco from Maven command-line</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>mvn jacoco:prepare-agent test jacoco:report</span></code></pre></td></tr></table></div></figure>


<p>You may get the following error:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[ERROR] No plugin found for prefix 'jacoco' in the current project ...</span></code></pre></td></tr></table></div></figure>


<p>There are two options to fix that error.
The easiest way is to specify the <code>groupId</code> and <code>artifactId</code> parameters of the plugin explicitly.
You can also add <code>version</code> to ensure the stability of your build pipeline.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>mvn clean org.jacoco:jacoco-maven-plugin:0.7.9:prepare-agent install org.jacoco:jacoco-maven-plugin:0.7.9:report</span></code></pre></td></tr></table></div></figure>


<p>The more long-term solution is to add the following in to your Maven &ldquo;settings.xml&rdquo;.</p>

<figure class='code'><figcaption><span>Maven settings</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;pluginGroups&gt;</span>
</span><span class='line'>    <span class="nt">&lt;pluginGroup&gt;</span>org.jacoco<span class="nt">&lt;/pluginGroup&gt;</span>
</span><span class='line'><span class="nt">&lt;/pluginGroups&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Tests with Mock</h3>

<p>If mocking is involved in unit tests, you need to use “instrument” and “restore-instrumented” steps.</p>

<p>Reference:</p>

<ul>
<li><a href="https://github.com/powermock/powermock/wiki/Code-coverage-with-JaCoCo">PowerMock instruction</a></li>
<li><a href="https://github.com/powermock/powermock-examples-maven/blob/master/jacoco-offline/pom.xml">PowerMock example pom.xml</a></li>
</ul>


<h3>Multi-module Maven projects</h3>

<p>Officially, multi-module Maven projects are supported differently by Jacoco as documented <a href="https://github.com/jacoco/jacoco/wiki/MavenMultiModule">here</a>.
Instrumentation will be similar but the challenge of multi-module Maven projects lies in how to collect and report code coverage of all modules correctly.
Jacoco Maven standard goals, as shown in sections above, work on single modules only: Tests are executed within the module and contributed coverage only to code within the same module.
Coverage reports were created for each module separately.</p>

<p>In the past, there are some ad-hoc solutions such as <a href="https://dzone.com/articles/jacoco-maven-multi-module">this</a> (for Jacoco 0.5.x) to work around that limit.
However, those patterns are also error-prone and hard to customize, especially when Jacoco is used with Surefire plugin.
Fortunately, Jacoco recently introduced a new Maven goal &ldquo;report-aggregate&rdquo; in its release 0.7.7 which will aggregate code coverage data across Maven modules.
Its usage is also present in the same <a href="https://github.com/jacoco/jacoco/wiki/MavenMultiModule">link</a> (quoted below) but it is too succint and not very helpful for new users.</p>

<blockquote><p>Create a dedicated module in your project for generation of the report. This module should depend on all or some other modules in the project.</p></blockquote>


<p>Let' say you have a multi-module Maven project with this structure:</p>

<figure class='code'><figcaption><span>Multi-module Maven project</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>root pom
</span><span class='line'>  |- module a
</span><span class='line'>  |- module b
</span><span class='line'>  |- module c</span></code></pre></td></tr></table></div></figure>


<p>To use Jacoco &ldquo;report-aggregate&rdquo; goal for these modules, you first need to add a dedicated &ldquo;coverage&rdquo; module.
This &ldquo;coverage&rdquo; module should be added into the root POM.
The multi-module Maven project should now look like this:</p>

<figure class='code'><figcaption><span>Multi-module Maven project with aggregate coverage module</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>root pom
</span><span class='line'>  |- module a
</span><span class='line'>  |- module b
</span><span class='line'>  |- module c
</span><span class='line'>  |- module "coverage"</span></code></pre></td></tr></table></div></figure>


<p>The POMs for each module does not need to change at all.
The POM for the &ldquo;coverage&rdquo; module will look like this:</p>

<figure class='code'><figcaption><span>Maven pom.xml for coverage module</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
</span><span class='line'><span class="nt">&lt;project</span> <span class="na">xmlns=</span><span class="s">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="na">xmlns:xsi=</span><span class="s">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span class="na">xsi:schemaLocation=</span><span class="s">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>    <span class="nt">&lt;modelVersion&gt;</span>4.0.0<span class="nt">&lt;/modelVersion&gt;</span>
</span><span class='line'>
</span><span class='line'>    <span class="nt">&lt;parent&gt;</span>
</span><span class='line'>        <span class="nt">&lt;groupId&gt;</span>com.company<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>        <span class="nt">&lt;artifactId&gt;</span>company-pom<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>        <span class="nt">&lt;version&gt;</span>3.0<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/parent&gt;</span>
</span><span class='line'>
</span><span class='line'>    <span class="nt">&lt;artifactId&gt;</span>report<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>    <span class="nt">&lt;name&gt;</span>Jacoco Report<span class="nt">&lt;/name&gt;</span>
</span><span class='line'>
</span><span class='line'>    <span class="nt">&lt;dependencies&gt;</span>
</span><span class='line'>        <span class="nt">&lt;dependency&gt;</span>
</span><span class='line'>            <span class="nt">&lt;groupId&gt;</span>my.example<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>            <span class="nt">&lt;artifactId&gt;</span>module-a<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>            <span class="nt">&lt;version&gt;</span>210.0.00-SNAPSHOT<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/dependency&gt;</span>
</span><span class='line'>        <span class="nt">&lt;dependency&gt;</span>
</span><span class='line'>            <span class="nt">&lt;groupId&gt;</span>my.example<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>            <span class="nt">&lt;artifactId&gt;</span>module-b<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>            <span class="nt">&lt;version&gt;</span>210.0.00-SNAPSHOT<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/dependency&gt;</span>
</span><span class='line'>        <span class="nt">&lt;dependency&gt;</span>
</span><span class='line'>            <span class="nt">&lt;groupId&gt;</span>my.example<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>            <span class="nt">&lt;artifactId&gt;</span>module-c<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>            <span class="nt">&lt;version&gt;</span>210.0.00-SNAPSHOT<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/dependency&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/dependencies&gt;</span>
</span><span class='line'>    <span class="nt">&lt;build&gt;</span>
</span><span class='line'>        <span class="nt">&lt;plugins&gt;</span>
</span><span class='line'>            <span class="nt">&lt;plugin&gt;</span>
</span><span class='line'>                <span class="nt">&lt;groupId&gt;</span>org.jacoco<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>                <span class="nt">&lt;artifactId&gt;</span>jacoco-maven-plugin<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>                <span class="nt">&lt;version&gt;</span>0.7.9<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>                <span class="nt">&lt;configuration&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;excludes&gt;</span>
</span><span class='line'>                        <span class="c">&lt;!-- Example of excluding classes </span>
</span><span class='line'><span class="c">                        &lt;exclude&gt;**/com/company/config/AutoConfiguration.class&lt;/exclude&gt;</span>
</span><span class='line'><span class="c">                        --&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;/excludes&gt;</span>
</span><span class='line'>                <span class="nt">&lt;/configuration&gt;</span>
</span><span class='line'>                <span class="nt">&lt;executions&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;execution&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;id&gt;</span>report-aggregate<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;phase&gt;</span>verify<span class="nt">&lt;/phase&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;goals&gt;</span>
</span><span class='line'>                            <span class="nt">&lt;goal&gt;</span>report-aggregate<span class="nt">&lt;/goal&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;/goals&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;/execution&gt;</span>
</span><span class='line'>                <span class="nt">&lt;/executions&gt;</span>
</span><span class='line'>            <span class="nt">&lt;/plugin&gt;</span>
</span><span class='line'>
</span><span class='line'>            <span class="c">&lt;!-- This coverage module should never be deployed --&gt;</span>
</span><span class='line'>            <span class="nt">&lt;plugin&gt;</span>
</span><span class='line'>                <span class="nt">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>                <span class="nt">&lt;artifactId&gt;</span>maven-deploy-plugin<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>                <span class="nt">&lt;configuration&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;skip&gt;</span>true<span class="nt">&lt;/skip&gt;</span>
</span><span class='line'>                <span class="nt">&lt;/configuration&gt;</span>
</span><span class='line'>            <span class="nt">&lt;/plugin&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/plugins&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/build&gt;</span>
</span><span class='line'><span class="nt">&lt;/project&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note that we still require &ldquo;prepare-agent&rdquo; step to run before the first test suite.
Depending on what plugins are being used and how the modules are organized within the project, we might have different setup for that particular step.
One option is to run from the command-line:</p>

<figure class='code'><figcaption><span>Maven pom.xml for coverage module</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'>tdongsi$ ls
</span><span class='line'>Dockerfile              README.md               module1       pom.xml
</span><span class='line'>Jenkinsfile             coverage                module2       scripts
</span><span class='line'>
</span><span class='line'>tdongsi$ mvn -B clean org.jacoco:jacoco-maven-plugin:0.7.9:prepare-agent install
</span></code></pre></td></tr></table></div></figure>


<p>Links:</p>

<ul>
<li><a href="https://stackoverflow.com/questions/13031219/how-to-configure-multi-module-maven-sonar-jacoco-to-give-merged-coverage-rep/37871210#37871210">Example of report-aggregate</a></li>
<li><a href="https://github.com/jacoco/jacoco/tree/master/jacoco-maven-plugin.test/it/it-report-aggregate">Example Maven project</a></li>
</ul>


<h4>Customizations</h4>

<p>In theory, a global threshold can be defined in <code>coverage/pom.xml</code> to enforce code coverage standard across teams.
However, in practice, different teams are at different stages of module/service maturity and blindly having a global threshold will hamper teams working on newer services/modules.
In addition, it does not make sense to enforce code coverage on some Maven modules such as those generated in GRPC.</p>

<p>In Jacoco, you can set different coverage limits for individual modules instead of a global threshold for all modules.
In the following example, you can specify a coverage threshold for module A by modifying module A&rsquo;s pom.xml file:</p>

<figure class='code'><figcaption><span>Module A's pom.xml</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'>...
</span><span class='line'>
</span><span class='line'><span class="nt">&lt;/plugins&gt;</span>
</span><span class='line'>...
</span><span class='line'>    <span class="nt">&lt;plugin&gt;</span>
</span><span class='line'>        <span class="nt">&lt;groupId&gt;</span>com.atlassian.maven.plugins<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>        <span class="nt">&lt;artifactId&gt;</span>maven-clover2-plugin<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/plugin&gt;</span>
</span><span class='line'>
</span><span class='line'>    <span class="nt">&lt;plugin&gt;</span>
</span><span class='line'>        <span class="nt">&lt;groupId&gt;</span>org.jacoco<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>        <span class="nt">&lt;artifactId&gt;</span>jacoco-maven-plugin<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>        <span class="nt">&lt;version&gt;</span>0.7.9<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>
</span><span class='line'>        <span class="nt">&lt;executions&gt;</span>
</span><span class='line'>            <span class="nt">&lt;execution&gt;</span>
</span><span class='line'>                <span class="nt">&lt;id&gt;</span>check<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>                <span class="nt">&lt;goals&gt;</span>
</span><span class='line'>                <span class="nt">&lt;goal&gt;</span>check<span class="nt">&lt;/goal&gt;</span>
</span><span class='line'>                <span class="nt">&lt;/goals&gt;</span>
</span><span class='line'>                <span class="nt">&lt;configuration&gt;</span>
</span><span class='line'>                    <span class="c">&lt;!-- NOTE: Set haltOnFailureto true when code coverage is enforced --&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;haltOnFailure&gt;</span>false<span class="nt">&lt;/haltOnFailure&gt;</span>
</span><span class='line'>
</span><span class='line'>                    <span class="nt">&lt;rules&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;rule</span> <span class="nt">&gt;</span>
</span><span class='line'>                            <span class="nt">&lt;element&gt;</span>CLASS<span class="nt">&lt;/element&gt;</span>
</span><span class='line'>                            <span class="nt">&lt;limits&gt;</span>
</span><span class='line'>                                <span class="nt">&lt;limit</span> <span class="nt">&gt;</span>
</span><span class='line'>                                    <span class="nt">&lt;counter&gt;</span>LINE<span class="nt">&lt;/counter&gt;</span>
</span><span class='line'>                                    <span class="nt">&lt;value&gt;</span>COVEREDRATIO<span class="nt">&lt;/value&gt;</span>
</span><span class='line'>                                    <span class="nt">&lt;minimum&gt;</span>0.80<span class="nt">&lt;/minimum&gt;</span>
</span><span class='line'>                                <span class="nt">&lt;/limit&gt;</span>
</span><span class='line'>                                <span class="nt">&lt;limit</span> <span class="nt">&gt;</span>
</span><span class='line'>                                    <span class="nt">&lt;counter&gt;</span>BRANCH<span class="nt">&lt;/counter&gt;</span>
</span><span class='line'>                                    <span class="nt">&lt;value&gt;</span>COVEREDRATIO<span class="nt">&lt;/value&gt;</span>
</span><span class='line'>                                    <span class="nt">&lt;minimum&gt;</span>0.80<span class="nt">&lt;/minimum&gt;</span>
</span><span class='line'>                                <span class="nt">&lt;/limit&gt;</span>
</span><span class='line'>                            <span class="nt">&lt;/limits&gt;</span>
</span><span class='line'>                            <span class="nt">&lt;excludes&gt;</span>
</span><span class='line'>                                <span class="c">&lt;!-- </span>
</span><span class='line'><span class="c">                                &lt;exclude&gt;com.test.ExampleExclusion&lt;/exclude&gt;</span>
</span><span class='line'><span class="c">                                --&gt;</span>
</span><span class='line'>                            <span class="nt">&lt;/excludes&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;/rule&gt;</span>
</span><span class='line'>                    <span class="nt">&lt;/rules&gt;</span>
</span><span class='line'>                <span class="nt">&lt;/configuration&gt;</span>
</span><span class='line'>            <span class="nt">&lt;/execution&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/executions&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/plugin&gt;</span>
</span><span class='line'><span class="nt">&lt;/plugins&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>As you can see, you can also specify files being excluded from coverage calculation.</p>

<h3>References</h3>

<ul>
<li><a href="http://www.jacoco.org/jacoco/trunk/doc/maven.html">Jacoco Maven plugin</a>: there are example POMs.</li>
</ul>


<!--
* [Cross-module reporting](https://stackoverflow.com/questions/41885772/jacoco-simple-integration-test-solution/41901853#41901853)
-->

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Keep Gruntfile Clean With Load-grunt-config]]></title>
    <link href="http://tdongsi.github.io/blog/2017/09/15/keep-gruntfile-clean-with-load-grunt-config/"/>
    <updated>2017-09-15T23:29:17-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/09/15/keep-gruntfile-clean-with-load-grunt-config</id>
    <content type="html"><![CDATA[<p>In this post, we look into how to keep our Gruntfile clean and tidy.
By keeping our Gruntfile clean and tidy, it is easier for us to refine and improve the Grunt build process with its numerous plugins.</p>

<!--more-->


<h3>Starting point</h3>

<p>Let&rsquo;s say you start a new Node project.</p>

<figure class='code'><figcaption><span>Starting a Node project</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class=''><span class='line'># Init by creating package.json file
</span><span class='line'>npm init
</span><span class='line'># Answer questions to create package.json file
</span><span class='line'>
</span><span class='line'># Adding grunt
</span><span class='line'>npm install grunt --save-dev
</span><span class='line'>npm install grunt-jslint --save-dev
</span><span class='line'>npm install load-grunt-tasks --save-dev
</span><span class='line'>
</span><span class='line'># Initalizing Gruntfile
</span><span class='line'>npm install grunt-init -g
</span><span class='line'>grunt-init gruntfile
</span><span class='line'># grunt-init node</span></code></pre></td></tr></table></div></figure>


<p>At the end of these steps, you have a basic <code>package.json</code> and <code>Gruntfile</code>.
The basic Gruntfile would appear like this:</p>

<figure class='code'><figcaption><span>Basic Gruntfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="cm">/*global module:false*/</span>
</span><span class='line'><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">grunt</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Project configuration.</span>
</span><span class='line'>  <span class="nx">grunt</span><span class="p">.</span><span class="nx">initConfig</span><span class="p">({</span>
</span><span class='line'>    <span class="c1">// Metadata.</span>
</span><span class='line'>    <span class="nx">pkg</span><span class="o">:</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">file</span><span class="p">.</span><span class="nx">readJSON</span><span class="p">(</span><span class="s1">&#39;package.json&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="nx">banner</span><span class="o">:</span> <span class="s1">&#39;/*! &lt;%= pkg.title || pkg.name %&gt; - v&lt;%= pkg.version %&gt; - &#39;</span> <span class="o">+</span>
</span><span class='line'>      <span class="s1">&#39;&lt;%= grunt.template.today(&quot;yyyy-mm-dd&quot;) %&gt;\n&#39;</span> <span class="o">+</span>
</span><span class='line'>      <span class="s1">&#39;&lt;%= pkg.homepage ? &quot;* &quot; + pkg.homepage + &quot;\\n&quot; : &quot;&quot; %&gt;&#39;</span> <span class="o">+</span>
</span><span class='line'>      <span class="s1">&#39;* Copyright (c) &lt;%= grunt.template.today(&quot;yyyy&quot;) %&gt; &lt;%= pkg.author.name %&gt;;&#39;</span> <span class="o">+</span>
</span><span class='line'>      <span class="s1">&#39; Licensed &lt;%= _.pluck(pkg.licenses, &quot;type&quot;).join(&quot;, &quot;) %&gt; */\n&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="c1">// Task configuration.</span>
</span><span class='line'>    <span class="nx">concat</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">banner</span><span class="o">:</span> <span class="s1">&#39;&lt;%= banner %&gt;&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">stripBanners</span><span class="o">:</span> <span class="kc">true</span>
</span><span class='line'>      <span class="p">},</span>
</span><span class='line'>      <span class="nx">dist</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;lib/&lt;%= pkg.name %&gt;.js&#39;</span><span class="p">],</span>
</span><span class='line'>        <span class="nx">dest</span><span class="o">:</span> <span class="s1">&#39;dist/&lt;%= pkg.name %&gt;.js&#39;</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="nx">uglify</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">banner</span><span class="o">:</span> <span class="s1">&#39;&lt;%= banner %&gt;&#39;</span>
</span><span class='line'>      <span class="p">},</span>
</span><span class='line'>      <span class="nx">dist</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">src</span><span class="o">:</span> <span class="s1">&#39;&lt;%= concat.dist.dest %&gt;&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">dest</span><span class="o">:</span> <span class="s1">&#39;dist/&lt;%= pkg.name %&gt;.min.js&#39;</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="nx">jshint</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">curly</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">eqeqeq</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">immed</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">latedef</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">newcap</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">noarg</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">sub</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">undef</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">unused</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">boss</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">eqnull</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">browser</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">globals</span><span class="o">:</span> <span class="p">{}</span>
</span><span class='line'>      <span class="p">},</span>
</span><span class='line'>      <span class="nx">gruntfile</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">src</span><span class="o">:</span> <span class="s1">&#39;Gruntfile.js&#39;</span>
</span><span class='line'>      <span class="p">},</span>
</span><span class='line'>      <span class="nx">lib_test</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;lib/**/*.js&#39;</span><span class="p">,</span> <span class="s1">&#39;test/**/*.js&#39;</span><span class="p">]</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="nx">qunit</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">files</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;test/**/*.html&#39;</span><span class="p">]</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="nx">watch</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">gruntfile</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">files</span><span class="o">:</span> <span class="s1">&#39;&lt;%= jshint.gruntfile.src %&gt;&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">tasks</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;jshint:gruntfile&#39;</span><span class="p">]</span>
</span><span class='line'>      <span class="p">},</span>
</span><span class='line'>      <span class="nx">lib_test</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">files</span><span class="o">:</span> <span class="s1">&#39;&lt;%= jshint.lib_test.src %&gt;&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">tasks</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;jshint:lib_test&#39;</span><span class="p">,</span> <span class="s1">&#39;qunit&#39;</span><span class="p">]</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">});</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// These plugins provide necessary tasks.</span>
</span><span class='line'>  <span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">&#39;grunt-contrib-concat&#39;</span><span class="p">);</span>
</span><span class='line'>  <span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">&#39;grunt-contrib-uglify&#39;</span><span class="p">);</span>
</span><span class='line'>  <span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">&#39;grunt-contrib-qunit&#39;</span><span class="p">);</span>
</span><span class='line'>  <span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">&#39;grunt-contrib-jshint&#39;</span><span class="p">);</span>
</span><span class='line'>  <span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">&#39;grunt-contrib-watch&#39;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Default task.</span>
</span><span class='line'>  <span class="nx">grunt</span><span class="p">.</span><span class="nx">registerTask</span><span class="p">(</span><span class="s1">&#39;default&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;jshint&#39;</span><span class="p">,</span> <span class="s1">&#39;qunit&#39;</span><span class="p">,</span> <span class="s1">&#39;concat&#39;</span><span class="p">,</span> <span class="s1">&#39;uglify&#39;</span><span class="p">]);</span>
</span><span class='line'>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>


<h3><code>load-grunt-tasks</code> plugin</h3>

<p>In the original basic Gruntfile, we have to manually load our Grunt plugins, as</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">&#39;grunt-contrib-concat&#39;</span><span class="p">);</span>
</span><span class='line'><span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">&#39;grunt-contrib-uglify&#39;</span><span class="p">);</span>
</span><span class='line'><span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">&#39;grunt-contrib-imagemin&#39;</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>If you now uninstall the plugin via <code>npm</code> and update your <code>package.json</code>, but forget to update your <code>Gruntfile</code>, your build will break.
With <code>load-grunt-tasks</code> plugin, you can collapse that down to the following one-liner:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">require</span><span class="p">(</span><span class="s1">&#39;load-grunt-tasks&#39;</span><span class="p">)(</span><span class="nx">grunt</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>After requiring the plugin, it will analyze your package.json file, determine which of the dependencies are Grunt plugins and load them all automatically.</p>

<h3><code>load-grunt-config</code> plugin</h3>

<p><code>load-grunt-tasks</code> shrunk your Gruntfile in code and complexity a little, but task configurations still remain in the Gruntfile (defined in <code>grunt.initConfig</code>).
As you configure a large application, it will still become a very large file.</p>

<p>This is when <code>load-grunt-config</code> comes into play.
<code>load-grunt-config</code> lets you break up your Gruntfile config by task.
With <code>load-grunt-config</code>, your <code>Gruntfile</code> may look like this:</p>

<figure class='code'><figcaption><span>Gruntfile with load-grunt-config</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">grunt</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">);</span>
</span><span class='line'>  <span class="kr">const</span> <span class="nx">appOptions</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">data</span><span class="o">:</span> <span class="p">{},</span>
</span><span class='line'>      <span class="nx">configPath</span><span class="o">:</span> <span class="p">[</span>
</span><span class='line'>          <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">cwd</span><span class="p">(),</span> <span class="s1">&#39;/grunt/tasks&#39;</span><span class="p">)</span>
</span><span class='line'>      <span class="p">]</span>
</span><span class='line'>  <span class="p">};</span>
</span><span class='line'>
</span><span class='line'>  <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;time-grunt&#39;</span><span class="p">)(</span><span class="nx">grunt</span><span class="p">);</span>
</span><span class='line'>  <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;load-grunt-config&#39;</span><span class="p">)(</span><span class="nx">grunt</span><span class="p">,</span> <span class="nx">appOptions</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note that <code>load-grunt-config</code> also includes <code>load-grunt-tasks</code>&rsquo;s functionality.
The task configurations live in files in folder <code>./grunt/tasks</code>.
By default, <code>./grunt</code> folder is used but, in this example, using a custom path is demonstrated.
In other words, our directory structure should be like this:</p>

<figure class='code'><figcaption><span>Directory structure</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>- current_project/
</span><span class='line'>-- Gruntfile
</span><span class='line'>-- grunt/tasks/
</span><span class='line'>---- concat.js
</span><span class='line'>---- uglify.js
</span><span class='line'>---- imagemin.js</span></code></pre></td></tr></table></div></figure>


<p>The task configuration for each task is defined in respective file name.
For example, task <code>concat</code> is defined in &ldquo;grunt/tasks/concat.js&rdquo;:</p>

<figure class='code'><figcaption><span>grunt/tasks/concat.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>  <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">banner</span><span class="o">:</span> <span class="s1">&#39;&lt;%= banner %&gt;&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">stripBanners</span><span class="o">:</span> <span class="kc">true</span>
</span><span class='line'>  <span class="p">},</span>
</span><span class='line'>  <span class="nx">dist</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;lib/&lt;%= pkg.name %&gt;.js&#39;</span><span class="p">],</span>
</span><span class='line'>    <span class="nx">dest</span><span class="o">:</span> <span class="s1">&#39;dist/&lt;%= pkg.name %&gt;.js&#39;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>


<p>The list of registered task aliases such as <code>default</code> is defined in <code>aliases.js</code> file.</p>

<figure class='code'><figcaption><span>grunt/tasks/aliases.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">grunt</span><span class="p">,</span> <span class="nx">appOptions</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="kd">var</span> <span class="nx">buildList</span> <span class="o">=</span> <span class="p">[</span>
</span><span class='line'>        <span class="s1">&#39;jshint&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;qunit&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;concat&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;uglify&#39;</span>
</span><span class='line'>    <span class="p">];</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">return</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">default</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;build&#39;</span><span class="p">],</span>
</span><span class='line'>        <span class="nx">build</span><span class="o">:</span> <span class="nx">buildList</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">test</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;jslint&#39;</span><span class="p">]</span>
</span><span class='line'>    <span class="p">};</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>


<h3>References</h3>

<ul>
<li>Safari: Introducing Grunt: the JavaScript task runner</li>
<li>Common Grunt plugins:

<ul>
<li><a href="http://firstandthird.github.io/load-grunt-config/">load-grunt-config</a>: key plugin to keep Gruntfile organized.</li>
<li><a href="https://www.npmjs.com/package/grunt-contrib-concat">concat</a></li>
<li>Unit Testing: <a href="https://www.npmjs.com/package/grunt-contrib-qunit">qunit</a></li>
<li>Image optimization: imagemin</li>
<li>Deploying: deploy</li>
<li>Chaining: concurrent</li>
</ul>
</li>
<li><a href="https://gruntjs.com/project-scaffolding">Project scaffolding with <code>grunt-init</code></a>

<ul>
<li>grunt-init-commonjs - Create a commonjs module, including Nodeunit unit tests.</li>
<li>grunt-init-gruntfile - Create a basic Gruntfile.</li>
<li>grunt-init-gruntplugin - Create a Grunt plugin, including Nodeunit unit tests.</li>
<li>grunt-init-jquery - Create a jQuery plugin, including QUnit unit tests.</li>
<li><a href="https://github.com/gruntjs/grunt-init-node">grunt-init-node</a> - Create a Node.js module, including Nodeunit unit tests.</li>
</ul>
</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[DSL Implementation in Groovy]]></title>
    <link href="http://tdongsi.github.io/blog/2017/08/13/groovy-dsl/"/>
    <updated>2017-08-13T15:18:30-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/08/13/groovy-dsl</id>
    <content type="html"><![CDATA[<p>Domain-Specific Language is a mini language for a specific problem and/or in a narrow context.
For example, internally used automation tools usually define some small DSL for configuration and most users understand the context and what DSL offers.</p>

<p>This blog post offers my simplistic view of how an internal DSL is implemented in Groovy via closure delegation.
It shows the progression from standard Java-like implementation -> its fluent version -> final DSL form.
This might help undrestanding the inner workings of a DSL such as Jenkins&rsquo;s Pipeline steps.
There are probably more advanced methods/frameworks for creating DSL.
However, those are not in the scope of this post.</p>

<!--more-->


<h3>Example DSL</h3>

<p>We want to implement a simple DSL that is similar to <a href="https://jenkins.io/doc/pipeline/steps/">Pipeline steps in Jenkinsfile</a>.</p>

<figure class='code'><figcaption><span>DSL in Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>{
</span><span class='line'>    withEnv("PATH=/usr/bin")
</span><span class='line'>    echo("Starting pipeline")
</span><span class='line'>    sh("ls .")
</span><span class='line'>    error("Error here")
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>In this DSL example, users will write a sequence of steps using a small, pre-defined set of custom statements such as <code>echo</code> and <code>sh</code> above.
For each step in the DSL, the backend classes and objects will perform some execution in the background, using the relevant context specific to the domain (e.g., Jenkins domain).
For simplicity, <code>println</code> statements will be used in the following examples.</p>

<p>The advantage of DSL is that the <strong>developers</strong> can implement the backend in some fully-featured language such as Java but the <strong>users</strong> don&rsquo;t need to know such language to use it.
Such a separation is common in DevOps and automation frameworks where the users want the flexibility of configuring based on their needs but don&rsquo;t want to get exposed to the implementation details (which are usually ugly and compplicated).
Instead, the <strong>users</strong> only need to learn the DSL to use it while still have the flexibility to do what they want.
One example can be found in data science domain where data scientists are usually more comfortable developing in R or SQL but automated deployment frameworks or tools can be in another language such as Java.</p>

<h3>Version 1: Java-like standard implementation</h3>

<p>First, we show a standard implementation in Java to show how backend execution can be implemented.
In the advanced versions, the difference is only in its public interface to make it more user-friendly but the backend execution will be similar.</p>

<figure class='code'><figcaption><span>Standard Java implementation</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * Java code with standard implementation</span>
</span><span class='line'><span class="cm"> * Try to simulate some kind of DSL like Pipeline steps in Jenkins</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">class</span> <span class="nc">JavaDsl</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">void</span> <span class="nf">echo</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Echo: $message&quot;</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">void</span> <span class="nf">sh</span><span class="o">(</span><span class="n">String</span> <span class="n">script</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Shell: $script&quot;</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">void</span> <span class="nf">error</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Error here: $message&quot;</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// A more advanced DSL</span>
</span><span class='line'>    <span class="kt">void</span> <span class="nf">withEnv</span><span class="o">(</span><span class="n">String</span> <span class="n">var</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Using: $var&quot;</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">void</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Executing ...&quot;</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;1) Standard Java implementation&quot;</span><span class="o">;</span>
</span><span class='line'><span class="n">JavaDsl</span> <span class="n">javaDsl</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JavaDsl</span><span class="o">();</span>
</span><span class='line'><span class="n">javaDsl</span><span class="o">.</span><span class="na">withEnv</span><span class="o">(</span><span class="s2">&quot;PATH=/usr/bin&quot;</span><span class="o">);</span>
</span><span class='line'><span class="n">javaDsl</span><span class="o">.</span><span class="na">echo</span><span class="o">(</span><span class="s2">&quot;Starting pipeline&quot;</span><span class="o">);</span>
</span><span class='line'><span class="n">javaDsl</span><span class="o">.</span><span class="na">sh</span><span class="o">(</span><span class="s2">&quot;ls .&quot;</span><span class="o">);</span>
</span><span class='line'><span class="n">javaDsl</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s2">&quot;Error here&quot;</span><span class="o">);</span>
</span><span class='line'><span class="n">javaDsl</span><span class="o">.</span><span class="na">execute</span><span class="o">();</span>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;&quot;</span><span class="o">;</span>
</span></code></pre></td></tr></table></div></figure>


<p>The problem of this approach is that users have to write Java (or Groovy) code directly to use it.</p>

<h3>Version 2: Fluent interface with Builder pattern</h3>

<figure class='code'><figcaption><span>Fluent Java implementation</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * Java code with Builder pattern</span>
</span><span class='line'><span class="cm"> * Try to simulate some kind of DSL like Pipeline steps in Jenkins</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">class</span> <span class="nc">JavaBuilderDsl</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">JavaBuilderDsl</span> <span class="nf">echo</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Echo: $message&quot;</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">this</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">JavaBuilderDsl</span> <span class="nf">sh</span><span class="o">(</span><span class="n">String</span> <span class="n">script</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Shell: $script&quot;</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">this</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">JavaBuilderDsl</span> <span class="nf">error</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Error here: $message&quot;</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">this</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// A more advanced DSL</span>
</span><span class='line'>    <span class="n">JavaBuilderDsl</span> <span class="nf">withEnv</span><span class="o">(</span><span class="n">String</span> <span class="n">var</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Using: $var&quot;</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">this</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">void</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Executing ...&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;2) Fluent Java implementation (Builder)&quot;</span>
</span><span class='line'><span class="n">JavaBuilderDsl</span> <span class="n">builderDsl</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JavaBuilderDsl</span><span class="o">()</span>
</span><span class='line'><span class="n">builderDsl</span><span class="o">.</span><span class="na">withEnv</span><span class="o">(</span><span class="s2">&quot;PATH=/usr/bin&quot;</span><span class="o">)</span>
</span><span class='line'>        <span class="o">.</span><span class="na">echo</span><span class="o">(</span><span class="s2">&quot;Starting pipeline&quot;</span><span class="o">)</span>
</span><span class='line'>        <span class="o">.</span><span class="na">sh</span><span class="o">(</span><span class="s2">&quot;ls .&quot;</span><span class="o">)</span>
</span><span class='line'>        <span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s2">&quot;Error here&quot;</span><span class="o">)</span>
</span><span class='line'>        <span class="o">.</span><span class="na">execute</span><span class="o">()</span>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>In this version, <a href="https://en.wikipedia.org/wiki/Builder_pattern#Java">the Build design pattern</a> is used in the implementation.
As shown above, the code is much more fluent with the object name <code>builderDsl</code> not being repeated every single line.
As a result, the code is less verbose and much more user-friendly.</p>

<h3>Version 3: DSL with Groovy closure</h3>

<figure class='code'><figcaption><span>Standard Groovy implementation</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * Groovy code with standard implementation</span>
</span><span class='line'><span class="cm"> * Try to simulate some kind of DSL like Pipeline steps in Jenkins</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">class</span> <span class="nc">GroovyDsl</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="nf">echo</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Echo: $message&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="nf">sh</span><span class="o">(</span><span class="n">String</span> <span class="n">script</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Shell: $script&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="nf">error</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Error here: $message&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// A more advanced DSL</span>
</span><span class='line'>    <span class="kt">def</span> <span class="nf">withEnv</span><span class="o">(</span><span class="n">String</span> <span class="n">var</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Using: $var&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">static</span> <span class="kt">void</span> <span class="nf">execute</span><span class="o">(</span><span class="n">closure</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">GroovyDsl</span> <span class="n">body</span> <span class="o">=</span> <span class="k">new</span> <span class="n">GroovyDsl</span><span class="o">()</span>
</span><span class='line'>        <span class="n">closure</span><span class="o">(</span><span class="n">body</span><span class="o">)</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Executing ...&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;3) Standard Groovy implementation&quot;</span>
</span><span class='line'><span class="n">GroovyDsl</span><span class="o">.</span><span class="na">execute</span> <span class="o">{</span> <span class="n">dsl</span> <span class="o">-&gt;</span>
</span><span class='line'>    <span class="n">dsl</span><span class="o">.</span><span class="na">withEnv</span><span class="o">(</span><span class="s2">&quot;PATH=/usr/bin&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">dsl</span><span class="o">.</span><span class="na">echo</span><span class="o">(</span><span class="s2">&quot;Starting pipeline&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">dsl</span><span class="o">.</span><span class="na">sh</span><span class="o">(</span><span class="s2">&quot;ls .&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">dsl</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s2">&quot;Error here&quot;</span><span class="o">)</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>This first version of Groovy implementation is presented here to show connection with its Java counterparts.
As shown below, the input variable <code>dsl</code> in the closure can be abstracted away using delegate.</p>

<figure class='code'><figcaption><span>Transparent DSL with delegate</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kd">class</span> <span class="nc">GroovyDsl</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="nf">echo</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Echo: $message&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="nf">sh</span><span class="o">(</span><span class="n">String</span> <span class="n">script</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Shell: $script&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="nf">error</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Error here: $message&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// A more advanced DSL</span>
</span><span class='line'>    <span class="kt">def</span> <span class="nf">withEnv</span><span class="o">(</span><span class="n">String</span> <span class="n">var</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Using: $var&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">static</span> <span class="kt">void</span> <span class="nf">execute</span><span class="o">(</span><span class="n">Closure</span> <span class="n">closure</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">GroovyDsl</span> <span class="n">body</span> <span class="o">=</span> <span class="k">new</span> <span class="n">GroovyDsl</span><span class="o">()</span>
</span><span class='line'>        <span class="c1">// TRICKY: Modify the input var? Hmmm.</span>
</span><span class='line'>        <span class="n">closure</span><span class="o">.</span><span class="na">delegate</span> <span class="o">=</span> <span class="n">body</span>
</span><span class='line'>        <span class="n">closure</span><span class="o">()</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Executing ...&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">static</span> <span class="kt">void</span> <span class="nf">executeBest</span><span class="o">(</span><span class="n">Closure</span> <span class="n">closure</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">GroovyDsl</span> <span class="n">body</span> <span class="o">=</span> <span class="k">new</span> <span class="n">GroovyDsl</span><span class="o">()</span>
</span><span class='line'>        <span class="n">body</span><span class="o">.</span><span class="na">with</span><span class="o">(</span><span class="n">closure</span><span class="o">)</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;Executing ...&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;4) DSL-style Groovy implementation&quot;</span>
</span><span class='line'><span class="n">GroovyDsl</span><span class="o">.</span><span class="na">execute</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">withEnv</span><span class="o">(</span><span class="s2">&quot;PATH=/usr/bin&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">echo</span><span class="o">(</span><span class="s2">&quot;Starting pipeline&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">sh</span><span class="o">(</span><span class="s2">&quot;ls .&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">error</span><span class="o">(</span><span class="s2">&quot;Error here&quot;</span><span class="o">)</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;4b) DSL-style Groovy (better) implementation&quot;</span>
</span><span class='line'><span class="n">GroovyDsl</span><span class="o">.</span><span class="na">executeBest</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">withEnv</span><span class="o">(</span><span class="s2">&quot;PATH=/usr/bin&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">echo</span><span class="o">(</span><span class="s2">&quot;Starting pipeline&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">sh</span><span class="o">(</span><span class="s2">&quot;ls .&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">error</span><span class="o">(</span><span class="s2">&quot;Error here&quot;</span><span class="o">)</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">println</span> <span class="s2">&quot;&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>In this final version, only a very small boiler-plate code <code>GroovyDsl.executeBest</code> remains.
The following lines form a mini language (i.e., DSL) that can be exposed to users.
The users can start using the DSL without having to learn Groovy or Java.</p>

<p>Note that the <code>executeBest</code> is the equivalent but less straight-forward way to do the same thing with delegate.
Compared with <code>execute</code>, it has the benefit of NOT modifying the input reference <code>closure</code>.</p>

<h3>Reference</h3>

<ul>
<li><a href="http://groovy-lang.org/closures.html">Groovy closure</a></li>
<li><a href="https://jenkins.io/doc/pipeline/steps/">Jenkins pipeline steps</a></li>
<li><a href="https://dzone.com/articles/groovy-dsl-simple-example">Another example Groovy implementation</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Advanced Jenkinsfile Cookbook]]></title>
    <link href="http://tdongsi.github.io/blog/2017/07/19/jenkinsfile-cookbook/"/>
    <updated>2017-07-19T14:23:01-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/07/19/jenkinsfile-cookbook</id>
    <content type="html"><![CDATA[<p>This post details some of the more advanced Jenkins pipelines using Jenkinsfile.</p>

<!--more-->


<h3>Nexus authentication in Maven</h3>

<p>More detailed discussion is in <a href="http://tdongsi.github.io/blog/2017/06/17/groovy-in-jenkinsfile/">here</a>.</p>

<figure class='code'><figcaption><span>Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">myScript</span>
</span><span class='line'>
</span><span class='line'><span class="n">pipeline</span> <span class="o">{</span>
</span><span class='line'>   <span class="n">agent</span> <span class="o">{</span> <span class="n">node</span> <span class="o">{</span> <span class="n">label</span> <span class="s1">&#39;test-agent&#39;</span> <span class="o">}</span> <span class="o">}</span>
</span><span class='line'>   <span class="n">stages</span> <span class="o">{</span>
</span><span class='line'>       <span class="n">stage</span><span class="o">(</span><span class="s2">&quot;compile&quot;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>           <span class="n">steps</span> <span class="o">{</span>
</span><span class='line'>               <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>               <span class="nf">withCredentials</span><span class="o">([</span>
</span><span class='line'>                 <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">],</span>
</span><span class='line'>                 <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>               <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>                   <span class="n">script</span> <span class="o">{</span>
</span><span class='line'>                       <span class="n">myScript</span> <span class="o">=</span> <span class="n">load</span> <span class="s1">&#39;jenkins/xml.groovy&#39;</span>
</span><span class='line'>                       <span class="kt">def</span> <span class="n">xmlTemplate</span> <span class="o">=</span> <span class="n">readFile</span><span class="o">(</span> <span class="s1">&#39;jenkins/settings.xml&#39;</span> <span class="o">)</span>
</span><span class='line'>                       <span class="n">String</span> <span class="n">xmlFile</span> <span class="o">=</span> <span class="n">myScript</span><span class="o">.</span><span class="na">transformXml</span><span class="o">(</span><span class="n">xmlTemplate</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusUsername</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusPassword</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>                       <span class="n">String</span> <span class="n">myPath</span> <span class="o">=</span> <span class="s1">&#39;temp.xml&#39;</span>
</span><span class='line'>                       <span class="n">writeFile</span> <span class="nl">file:</span> <span class="n">myPath</span><span class="o">,</span> <span class="nl">text:</span> <span class="n">xmlFile</span>
</span><span class='line'>
</span><span class='line'>                       <span class="n">sh</span> <span class="s2">&quot;mvn -B clean compile -s ${myPath}&quot;</span>
</span><span class='line'>
</span><span class='line'>                       <span class="n">sh</span> <span class="s2">&quot;rm ${myPath}&quot;</span>
</span><span class='line'>                   <span class="o">}</span>
</span><span class='line'>               <span class="o">}</span>
</span><span class='line'>           <span class="o">}</span>
</span><span class='line'>           <span class="n">post</span> <span class="o">{</span>
</span><span class='line'>           <span class="n">failure</span> <span class="o">{</span>
</span><span class='line'>               <span class="n">echo</span> <span class="s2">&quot;Sending email for compile failed (TBD)&quot;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>           <span class="o">}</span>
</span><span class='line'>       <span class="o">}</span>
</span><span class='line'>   <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>xml.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.StreamingMarkupBuilder</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.XmlUtil</span>
</span><span class='line'>
</span><span class='line'><span class="nd">@NonCPS</span>
</span><span class='line'><span class="kt">def</span> <span class="nf">transformXml</span><span class="o">(</span><span class="n">String</span> <span class="n">xmlContent</span><span class="o">,</span> <span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">xml</span> <span class="o">=</span> <span class="k">new</span> <span class="n">XmlSlurper</span><span class="o">(</span><span class="kc">false</span><span class="o">,</span> <span class="kc">false</span><span class="o">).</span><span class="na">parseText</span><span class="o">(</span><span class="n">xmlContent</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">echo</span> <span class="s1">&#39;Start tranforming XML&#39;</span>
</span><span class='line'>  <span class="n">xml</span><span class="o">.</span><span class="na">servers</span><span class="o">.</span><span class="na">server</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">node</span> <span class="o">-&gt;</span>
</span><span class='line'>    <span class="n">node</span><span class="o">.</span><span class="na">username</span> <span class="o">=</span> <span class="n">username</span>
</span><span class='line'>    <span class="n">node</span><span class="o">.</span><span class="na">password</span> <span class="o">=</span> <span class="n">password</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="kt">def</span> <span class="n">outWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringWriter</span><span class="o">()</span>
</span><span class='line'>  <span class="n">XmlUtil</span><span class="o">.</span><span class="na">serialize</span><span class="o">(</span> <span class="n">xml</span><span class="o">,</span> <span class="n">outWriter</span> <span class="o">)</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">outWriter</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">return</span> <span class="k">this</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Running test suite in parallel</h3>

<p>It is possible to take a Maven/JUnit-based test suite that takes too long to run on a single node and parallelize the test execution across multiple nodes instead.
The Parallel Test Executor Plugin is exactly for that purpose.</p>

<figure class='code'><figcaption><span>Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">node</span><span class="o">(</span><span class="s1">&#39;remote&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">git</span> <span class="s1">&#39;https://github.com/jenkinsci/parallel-test-executor-plugin-sample.git&#39;</span>
</span><span class='line'>  <span class="n">stash</span> <span class="nl">name:</span> <span class="s1">&#39;sources&#39;</span><span class="o">,</span> <span class="nl">includes:</span> <span class="s1">&#39;pom.xml,src/&#39;</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="kt">def</span> <span class="n">splits</span> <span class="o">=</span> <span class="n">splitTests</span> <span class="n">count</span><span class="o">(</span><span class="mi">2</span><span class="o">)</span>
</span><span class='line'><span class="kt">def</span> <span class="n">branches</span> <span class="o">=</span> <span class="o">[:]</span>
</span><span class='line'><span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">splits</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">index</span> <span class="o">=</span> <span class="n">i</span> <span class="c1">// fresh variable per iteration; i will be mutated</span>
</span><span class='line'>  <span class="n">branches</span><span class="o">[</span><span class="s2">&quot;split${i}&quot;</span><span class="o">]</span> <span class="o">=</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">node</span><span class="o">(</span><span class="s1">&#39;remote&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">deleteDir</span><span class="o">()</span>
</span><span class='line'>      <span class="n">unstash</span> <span class="s1">&#39;sources&#39;</span>
</span><span class='line'>      <span class="kt">def</span> <span class="n">exclusions</span> <span class="o">=</span> <span class="n">splits</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">index</span><span class="o">);</span>
</span><span class='line'>      <span class="n">writeFile</span> <span class="nl">file:</span> <span class="s1">&#39;exclusions.txt&#39;</span><span class="o">,</span> <span class="nl">text:</span> <span class="n">exclusions</span><span class="o">.</span><span class="na">join</span><span class="o">(</span><span class="s2">&quot;\n&quot;</span><span class="o">)</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s2">&quot;${tool &#39;M3&#39;}/bin/mvn -B -Dmaven.test.failure.ignore test&quot;</span>
</span><span class='line'>      <span class="n">junit</span> <span class="s1">&#39;target/surefire-reports/*.xml&#39;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">parallel</span> <span class="n">branches</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note that, this is <strong>different</strong> from modiyfing test harness (e.g., JUnit, TestNG) to parallelize the test execution on a single node.
It could be time-consuming and risk destabalizing the tests while the chance of success is usually small.</p>

<p>More details can be found in the following links:</p>

<ul>
<li><a href="https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md">TUTORIAL</a></li>
<li><a href="https://jenkins.io/blog/2016/06/16/parallel-test-executor-plugin/">More in Blog post</a></li>
<li><a href="https://wiki.jenkins-ci.org/display/JENKINS/Parallel+Test+Executor+Plugin">Plugin page</a>: <code>splitTests</code> defined by this plugin.</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Basic Jenkinsfile Cookbook]]></title>
    <link href="http://tdongsi.github.io/blog/2017/07/18/basic-jenkinsfile-cookbook/"/>
    <updated>2017-07-18T11:20:20-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/07/18/basic-jenkinsfile-cookbook</id>
    <content type="html"><![CDATA[<p>This post shows how to customize standard Pipeline &ldquo;steps&rdquo; in Jenkinsfile besides their common usage.</p>

<!--more-->


<p>List of basic Jenkinsfile steps in this post:</p>

<ul>
<li><code>checkout</code>/<code>git</code></li>
<li><code>emailext</code></li>
<li><code>findFiles</code></li>
<li><code>input</code></li>
<li><code>junit</code></li>
<li><code>parameters</code>/<code>properties</code></li>
<li><code>podTemplates</code></li>
<li><code>sendSlack</code></li>
<li><code>stash</code>/<code>unstash</code></li>
<li><code>withCredentials</code></li>
</ul>


<h3><code>checkout</code>/<code>git</code> step</h3>

<p><code>scm</code> is the global variable for the current commit AND branch AND repository of Jenkinsfile.
<code>checkout scm</code> means checking out all other files with same version as the Jenkinsfile associated with running pipeline.
To check out another repository, you need to specify the paremeters to <code>checkout</code> step.</p>

<figure class='code'><figcaption><span>Checkout from another Git repo</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">checkout</span><span class="o">([</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;GitSCM&#39;</span><span class="o">,</span> <span class="nl">branches:</span> <span class="o">[[</span><span class="nl">name:</span> <span class="s1">&#39;*/master&#39;</span><span class="o">]],</span>
</span><span class='line'>     <span class="nl">userRemoteConfigs:</span> <span class="o">[[</span><span class="nl">url:</span> <span class="s1">&#39;http://git-server/user/repository.git&#39;</span><span class="o">]]])</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// From README file.</span>
</span><span class='line'><span class="n">checkout</span> <span class="nl">scm:</span> <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;MercurialSCM&#39;</span><span class="o">,</span> <span class="nl">source:</span> <span class="s1">&#39;ssh://hg@bitbucket.org/user/repo&#39;</span><span class="o">,</span> <span class="nl">clean:</span> <span class="kc">true</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;1234-5678-abcd&#39;</span><span class="o">],</span> <span class="nl">poll:</span> <span class="kc">false</span>
</span><span class='line'><span class="c1">// If scm is the only parameter, you can omit its name, but Groovy syntax then requires parentheses around the value:</span>
</span><span class='line'><span class="n">checkout</span><span class="o">([</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;MercurialSCM&#39;</span><span class="o">,</span> <span class="nl">source:</span> <span class="s1">&#39;ssh://hg@bitbucket.org/user/repo&#39;</span><span class="o">])</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Short hand form for Git</span>
</span><span class='line'><span class="n">git</span> <span class="nl">branch:</span> <span class="s1">&#39;develop&#39;</span><span class="o">,</span> <span class="nl">url:</span> <span class="s1">&#39;https://github.com/WtfJoke/Any.git&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Reference:</p>

<ul>
<li><a href="https://jenkins.io/doc/pipeline/steps/git/#git-git"><code>git</code> step</a></li>
<li><a href="https://stackoverflow.com/questions/14843696/checkout-multiple-git-repos-into-same-jenkins-workspace"><code>git</code> example</a></li>
<li><a href="https://jenkins.io/doc/pipeline/steps/workflow-scm-step/#checkout-general-scm"><code>checkout</code> step</a></li>
<li><a href="https://github.com/jenkinsci/workflow-scm-step-plugin/blob/master/README.md"><code>checkout</code> README</a></li>
</ul>


<h3><code>emailext</code> step</h3>

<p>To send email as HTML page, set content type to HTML and use content as <code>${FILE,path="email.html"}</code>.
In Jenkinsfile, the code should look like this:</p>

<figure class='code'><figcaption><span>Send HTML report as email</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">emailext</span><span class="o">(</span>
</span><span class='line'>    <span class="nl">subject:</span> <span class="s1">&#39;Deploy Notice&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">to:</span> <span class="n">EMAIL_AUDIENCE</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">body:</span> <span class="s1">&#39;${FILE,path=&quot;deploy_email.html&quot;}&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">presendScript:</span> <span class="s1">&#39;$DEFAULT_PRESEND_SCRIPT&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">replyTo:</span> <span class="s1">&#39;devops@my.company.com&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">mimeType:</span> <span class="s1">&#39;text/html&#39;</span>   <span class="c1">// email as HTML</span>
</span><span class='line'><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note that it&rsquo;s single-quoted strings, not double-quoted, being used for <code>body</code> and <code>presendScript</code> parameters in the example code above.</p>

<p>Reference:</p>

<ul>
<li><a href="https://support.cloudbees.com/hc/en-us/articles/226237768-How-to-embed-html-report-in-email-body-using-Email-ext-plugin-">How to embed HTML report?</a></li>
</ul>


<h3><code>findFiles</code> step</h3>

<p>Doing in Bash:</p>

<figure class='code'><figcaption><span>Doing in Bash</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">sh</span> <span class="s1">&#39;&#39;&#39;</span>
</span><span class='line'><span class="s1">for file in target/surefire-reports/*.txt;</span>
</span><span class='line'><span class="s1">do</span>
</span><span class='line'><span class="s1">    echo $file &gt;&gt; testresult</span>
</span><span class='line'><span class="s1">done</span>
</span><span class='line'><span class="s1">cat testresult</span>
</span><span class='line'><span class="s1">&#39;&#39;&#39;</span>
</span><span class='line'><span class="kt">def</span> <span class="n">result</span> <span class="o">=</span> <span class="n">readFile</span> <span class="s2">&quot;testresult&quot;</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Doing in Groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">files</span> <span class="o">=</span> <span class="n">findFiles</span><span class="o">(</span><span class="nl">glob:</span> <span class="s1">&#39;target/surefire-reports/*.txt&#39;</span><span class="o">)</span>
</span><span class='line'><span class="k">for</span> <span class="n">file</span> <span class="k">in</span> <span class="nl">files:</span>
</span><span class='line'>  <span class="n">echo</span> <span class="s2">&quot;&quot;&quot;</span>
</span><span class='line'><span class="s2">  ${files[0].name} ${files[0].path} ${files[0].directory} </span>
</span><span class='line'><span class="s2">  ${files[0].length} ${files[0].lastModified}</span>
</span><span class='line'><span class="s2">  &quot;&quot;&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Reference:</p>

<ul>
<li><a href="https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/"><code>findFiles</code> step</a></li>
<li>Related: <code>readFile</code>, <code>writeFile</code>.</li>
</ul>


<h3><code>input</code> step</h3>

<p>Simple <code>input</code> step can be used to ask for approval to proceed.
For asking input from a list of multiple choices, you can use the advanced version of input.</p>

<figure class='code'><figcaption><span>Input from list of choices</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>   <span class="n">sh</span> <span class="s2">&quot;source scripts/findCL.sh &gt; choiceLists.txt&quot;</span>
</span><span class='line'>   <span class="kt">def</span> <span class="n">choiceOptions</span> <span class="o">=</span> <span class="n">readFile</span> <span class="s2">&quot;${env.WORKSPACE}/choiceLists.txt&quot;</span>
</span><span class='line'>   <span class="kt">def</span> <span class="n">choice</span> <span class="o">=</span> <span class="n">input</span><span class="o">(</span>
</span><span class='line'>   <span class="nl">id:</span> <span class="s1">&#39;CHOICE_LIST&#39;</span><span class="o">,</span> <span class="nl">message:</span><span class="s1">&#39;Choose a CL&#39;</span> <span class="o">,</span> <span class="nl">parameters:</span> <span class="o">[</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;ChoiceParameterDefinition&#39;</span><span class="o">,</span> <span class="nl">name:</span><span class="s1">&#39;CHOICE_LIST_SELECTED&#39;</span><span class="o">,</span> <span class="nl">description:</span><span class="s1">&#39;Select one&#39;</span><span class="o">,</span> <span class="nl">choices:</span><span class="n">choiceOptions</span><span class="o">]</span>
</span><span class='line'>  <span class="o">])</span>
</span></code></pre></td></tr></table></div></figure>


<p>Reference:</p>

<ul>
<li><a href="https://jenkins.io/doc/pipeline/steps/pipeline-input-step/"><code>input</code> step</a></li>
</ul>


<h3><code>junit</code> step</h3>

<p>JUnit tests + PMD, FindBugs, CheckStyle.
In Blue Ocean interface, these will be displayed in a separate tab.</p>

<figure class='code'><figcaption><span>Related steps</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">stage</span><span class="o">(</span><span class="s1">&#39;JUnit-Reports&#39;</span><span class="o">){</span>
</span><span class='line'>    <span class="n">junit</span> <span class="nl">allowEmptyResults:</span> <span class="kc">true</span><span class="o">,</span> <span class="nl">testResults:</span> <span class="s1">&#39;**/build/test-results/*.xml&#39;</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">stage</span><span class="o">(</span><span class="s1">&#39;FindBugs-Reports&#39;</span><span class="o">){</span>
</span><span class='line'>    <span class="n">step</span><span class="o">([</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;FindBugsPublisher&#39;</span><span class="o">,</span> <span class="nl">canComputeNew:</span> <span class="kc">false</span><span class="o">,</span> <span class="nl">defaultEncoding:</span> <span class="s1">&#39;&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">excludePattern:</span> <span class="s1">&#39;&#39;</span><span class="o">,</span> <span class="nl">healthy:</span> <span class="s1">&#39;&#39;</span><span class="o">,</span> <span class="nl">includePattern:</span> <span class="s1">&#39;&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">pattern:</span> <span class="s1">&#39;**/build/reports/findbugs/*.xml&#39;</span><span class="o">,</span> <span class="nl">unHealthy:</span> <span class="s1">&#39;&#39;</span><span class="o">])</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">stage</span><span class="o">(</span><span class="s1">&#39;PMD-Reports&#39;</span><span class="o">){</span>
</span><span class='line'>    <span class="n">step</span><span class="o">([</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;PmdPublisher&#39;</span><span class="o">,</span> <span class="nl">canComputeNew:</span> <span class="kc">false</span><span class="o">,</span> <span class="nl">defaultEncoding:</span> <span class="s1">&#39;&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">healthy:</span> <span class="s1">&#39;&#39;</span><span class="o">,</span> <span class="nl">pattern:</span> <span class="s1">&#39;**/build/reports/pmd/*.xml&#39;</span><span class="o">,</span> <span class="nl">unHealthy:</span> <span class="s1">&#39;&#39;</span><span class="o">])</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">stage</span><span class="o">(</span><span class="s1">&#39;CheckStyle-Reports&#39;</span><span class="o">){</span>
</span><span class='line'>    <span class="n">step</span><span class="o">([</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;CheckStylePublisher&#39;</span><span class="o">,</span> <span class="nl">canComputeNew:</span> <span class="kc">false</span><span class="o">,</span> <span class="nl">defaultEncoding:</span> <span class="s1">&#39;&#39;</span><span class="o">,</span>
</span><span class='line'>    <span class="nl">healthy:</span> <span class="s1">&#39;&#39;</span><span class="o">,</span> <span class="nl">pattern:</span> <span class="s1">&#39;**/build/reports/checkstyle/*.xml&#39;</span><span class="o">,</span> <span class="nl">unHealthy:</span> <span class="s1">&#39;&#39;</span><span class="o">])</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3><code>parameters</code>/<code>properties</code> step</h3>

<p><code>parameters</code> step adds certain job parameters for the overall pipeline job.</p>

<figure class='code'><figcaption><span>parameters step in Declarative pipeline</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">pipeline</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">options</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">buildDiscarder</span><span class="o">(</span><span class="n">logRotator</span><span class="o">(</span><span class="nl">numToKeepStr:</span> <span class="s1">&#39;30&#39;</span><span class="o">,</span> <span class="nl">artifactNumToKeepStr:</span> <span class="s1">&#39;30&#39;</span><span class="o">))</span>
</span><span class='line'>        <span class="n">disableConcurrentBuilds</span><span class="o">()</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="n">agent</span> <span class="o">{</span> <span class="n">node</span> <span class="o">{</span> <span class="n">label</span> <span class="s1">&#39;aqueduct-agent&#39;</span> <span class="o">}</span> <span class="o">}</span>
</span><span class='line'>    <span class="n">parameters</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">choice</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;ClusterName&#39;</span><span class="o">,</span> <span class="nl">choices:</span> <span class="s1">&#39;func\ninteg\nperf&#39;</span><span class="o">,</span> <span class="nl">description:</span> <span class="s1">&#39;Name of the cluster to test.&#39;</span><span class="o">)</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="n">stages</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">stage</span><span class="o">(</span><span class="s2">&quot;Build&quot;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">steps</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">echo</span> <span class="s2">&quot;Hello&quot;</span>
</span><span class='line'>                <span class="o">...</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>        <span class="o">}</span> <span class="c1">//  end of stage</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="n">post</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">always</span> <span class="o">{</span>
</span><span class='line'>            <span class="o">...</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>In Scripted pipeline, its equivalent counterpart is <code>properties</code> step, as shown below.</p>

<figure class='code'><figcaption><span>parameters step for Scripted pipeline</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">properties</span><span class="o">(</span>
</span><span class='line'>    <span class="o">[</span>
</span><span class='line'>        <span class="o">[</span>
</span><span class='line'>            <span class="n">$class</span>  <span class="o">:</span> <span class="s1">&#39;jenkins.model.BuildDiscarderProperty&#39;</span><span class="o">,</span>
</span><span class='line'>            <span class="nl">strategy:</span> <span class="o">[</span>
</span><span class='line'>                <span class="n">$class</span>      <span class="o">:</span> <span class="s1">&#39;LogRotator&#39;</span><span class="o">,</span>
</span><span class='line'>                <span class="nl">numToKeepStr:</span> <span class="s1">&#39;20&#39;</span>
</span><span class='line'>            <span class="o">]</span>
</span><span class='line'>        <span class="o">],</span>
</span><span class='line'>        <span class="n">pipelineTriggers</span><span class="o">(</span>
</span><span class='line'>            <span class="o">[</span>
</span><span class='line'>                <span class="o">[</span>
</span><span class='line'>                    <span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;hudson.triggers.TimerTrigger&#39;</span><span class="o">,</span>
</span><span class='line'>                    <span class="n">spec</span>  <span class="o">:</span> <span class="s2">&quot;H 8 * * *&quot;</span>
</span><span class='line'>                <span class="o">]</span>
</span><span class='line'>            <span class="o">]</span>
</span><span class='line'>        <span class="o">)</span>
</span><span class='line'>    <span class="o">]</span>
</span><span class='line'><span class="o">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">node</span><span class="o">(</span><span class="s1">&#39;agent&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Checkout&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="o">...</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>In the Jenkins UI, this will be converted to configurations when you click on &ldquo;View Configuration&rdquo; for that job, as shown in screenshot below.
Note that the configurations in this page is read-only when using Jenkinsfile.
Any modifications made to the page will be ignored, leaving configurations set in Jenkinsfile final (&ldquo;Infrastructure as Code&rdquo;).</p>

<p><img class="center" src="http://tdongsi.github.io/images/jenkins/properties.png" width="600" height="400" title="View Configuration" alt="Screenshot of View Configuration page"></p>

<p>Reference:</p>

<ul>
<li><a href="https://stackoverflow.com/questions/39542485/how-to-write-pipeline-to-discard-old-builds">buildDiscarder</a></li>
<li><a href="https://thepracticalsysadmin.com/limit-jenkins-multibranch-pipeline-builds/">disableConcurrentBuilds</a></li>
</ul>


<h3><code>podTemplate</code> step</h3>

<p>This step is used to specify a new pod template for running jobs on Kubernetes cluster.</p>

<figure class='code'><figcaption><span>Kubernetes plugin</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">podTemplate</span><span class="o">(</span><span class="nl">label:</span><span class="s1">&#39;base-agent&#39;</span><span class="o">,</span> <span class="nl">containers:</span> <span class="o">[</span>
</span><span class='line'>    <span class="n">containerTemplate</span><span class="o">(</span><span class="nl">name:</span> <span class="s1">&#39;maven&#39;</span><span class="o">,</span>
</span><span class='line'>        <span class="nl">image:</span> <span class="s1">&#39;docker.my/tdongsi/jenkins-agent:13&#39;</span><span class="o">,</span>
</span><span class='line'>        <span class="nl">workingDir:</span> <span class="s1">&#39;/home/jenkins&#39;</span><span class="o">,</span>
</span><span class='line'>        <span class="nl">volumes:</span> <span class="o">[</span><span class="n">hostPathVolume</span><span class="o">(</span><span class="nl">mountPath:</span> <span class="s1">&#39;/srv/jenkins&#39;</span><span class="o">,</span> <span class="nl">hostPath:</span> <span class="s1">&#39;/usr/local/npm&#39;</span><span class="o">),</span>
</span><span class='line'>        <span class="n">secretVolume</span><span class="o">(</span><span class="nl">mountPath:</span> <span class="s1">&#39;/etc/mount2&#39;</span><span class="o">,</span> <span class="nl">secretName:</span> <span class="s1">&#39;my-secret&#39;</span><span class="o">)],</span>
</span><span class='line'>        <span class="nl">imagePullSecrets:</span> <span class="s1">&#39;sfregistry&#39;</span><span class="o">)</span>
</span><span class='line'><span class="o">])</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">node</span><span class="o">(</span><span class="s1">&#39;base-agent&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Checkout&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;main&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">sh</span> <span class="s1">&#39;java -version&#39;</span>
</span><span class='line'>            <span class="n">sh</span> <span class="s1">&#39;mvn -version&#39;</span>
</span><span class='line'>            <span class="n">sh</span> <span class="s1">&#39;python -V&#39;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">input</span> <span class="s1">&#39;Finished with K8S pod?&#39;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Reference:</p>

<ul>
<li><a href="https://wiki.jenkins.io/display/JENKINS/Kubernetes+Plugin">Kubernetes plugin</a></li>
<li><a href="https://www.infoq.com/articles/scaling-docker-with-kubernetes">Tutorial</a></li>
<li><a href="https://github.com/jenkinsci/kubernetes-plugin">Github repo</a></li>
<li><a href="https://jenkins.io/doc/pipeline/steps/kubernetes/">Pipeline steps</a></li>
</ul>


<h3><code>sendSlack</code> step</h3>

<p>Standard Jenkinsfile for testing Slack</p>

<figure class='code'><figcaption><span>Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">node</span><span class="o">(</span><span class="s1">&#39;test-agent&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Checkout&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Main&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">withCredentials</span><span class="o">([</span><span class="n">string</span><span class="o">(</span><span class="nl">credentialsId:</span> <span class="s1">&#39;matrixsfdc-slack&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;TOKEN&#39;</span><span class="o">)])</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">slackSend</span> <span class="o">(</span> <span class="nl">teamDomain:</span> <span class="s1">&#39;matrixsfdc&#39;</span><span class="o">,</span> <span class="nl">channel:</span> <span class="s1">&#39;#jenkins-pcloud&#39;</span><span class="o">,</span> <span class="nl">token:</span> <span class="n">env</span><span class="o">.</span><span class="na">TOKEN</span><span class="o">,</span>
</span><span class='line'>                   <span class="nl">baseUrl:</span> <span class="s1">&#39;https://matrixsfdc.slack.com/services/hooks/jenkins-ci/&#39;</span><span class="o">,</span>
</span><span class='line'>                   <span class="nl">color:</span> <span class="s1">&#39;#FFFF00&#39;</span><span class="o">,</span>
</span><span class='line'>                   <span class="nl">message:</span> <span class="s2">&quot;STARTED: Job &#39;${env.JOB_NAME} [${env.BUILD_NUMBER}]&#39; (${env.BUILD_URL})&quot;</span>
</span><span class='line'>                   <span class="o">)</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">input</span> <span class="s1">&#39;Finished with K8S pod?&#39;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3><code>stash</code>/<code>unstash</code> steps</h3>

<p><code>stash</code> step can be used to save a set of files, to be <code>unstash</code>ed later in the same build, generally for using in another workspace.
<code>unstash</code> will restore the files into the same relative locations as when they are <code>stash</code>ed.
If you want to change the base directory of the stashed files, you should wrap the <code>stash</code> steps in <code>dir</code> step.</p>

<p>We should use <code>stash</code>/<code>unstash</code> to avoid the common anti-pattern of copying files into some special, globally visible directory such as Jenkins home or one of its subdirectories.
Using such anti-pattern will make it hard to support many jobs for many users since, eventually, there will be some name clash and, subsequently, some convoluted naming of those files to avoid such name clashes.</p>

<p>Note that <code>stash</code> and <code>unstash</code> steps are designed for use with small files.
If the size is above 5 MB, we should consider an alternative such as Nexus/Artifactory for jar files, blob stores for images.</p>

<p>Example usage of <code>stash</code> and <code>unstash</code>:</p>

<figure class='code'><figcaption><span>stash/unstash example</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Stash&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">dir</span> <span class="o">(</span><span class="s1">&#39;test&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">sh</span> <span class="s1">&#39;&#39;&#39;</span>
</span><span class='line'><span class="s1">                cd release_notes</span>
</span><span class='line'><span class="s1">                touch deployment_ids.json</span>
</span><span class='line'><span class="s1">                touch deployment_summary.json</span>
</span><span class='line'><span class="s1">                ls -al</span>
</span><span class='line'><span class="s1">            &#39;&#39;&#39;</span>
</span><span class='line'>
</span><span class='line'>            <span class="n">stash</span> <span class="nl">name:</span> <span class="s1">&#39;deployment_ids&#39;</span><span class="o">,</span> <span class="nl">includes:</span> <span class="s1">&#39;release_notes/deployment_ids.json&#39;</span>
</span><span class='line'>            <span class="n">stash</span> <span class="nl">name:</span> <span class="s1">&#39;deployment_summary&#39;</span><span class="o">,</span> <span class="nl">includes:</span> <span class="s1">&#39;release_notes/deployment_summary.json&#39;</span>
</span><span class='line'>
</span><span class='line'>        <span class="o">}</span> <span class="c1">// end dir</span>
</span><span class='line'>    <span class="o">}</span> <span class="c1">// end stage &#39;Stash&#39;</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Check unstash&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">dir</span><span class="o">(</span><span class="s1">&#39;check&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">unstash</span> <span class="s1">&#39;deployment_ids&#39;</span>
</span><span class='line'>
</span><span class='line'>            <span class="n">sh</span> <span class="s1">&#39;tree -L 2&#39;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">dir</span><span class="o">(</span><span class="s1">&#39;check2&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">unstash</span> <span class="s1">&#39;deployment_summary&#39;</span>
</span><span class='line'>
</span><span class='line'>            <span class="n">sh</span> <span class="s1">&#39;tree -L 2&#39;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Clean up&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">sh</span> <span class="s1">&#39;tree -L 2&#39;</span>
</span><span class='line'>        <span class="n">deleteDir</span><span class="o">()</span>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Example output:</p>

<figure class='code'><figcaption><span>Console output of the above stash/unstash example</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[Pipeline] sh
</span><span class='line'>04:14:04 + cd release_notes
</span><span class='line'>04:14:04 + touch deployment_ids.json
</span><span class='line'>04:14:04 + touch deployment_summary.json
</span><span class='line'>04:14:04 + ls -al
</span><span class='line'>04:14:04 total 128
</span><span class='line'>04:14:04 drwxr-xr-x  5 jenkins cdrom  4096 Aug 18 04:14 .
</span><span class='line'>04:14:04 drwxr-xr-x 26 jenkins cdrom  4096 Aug 18 04:14 ..
</span><span class='line'>04:14:04 -rw-r--r--  1 jenkins cdrom     0 Aug 18 04:14 deployment_ids.json
</span><span class='line'>04:14:04 -rw-r--r--  1 jenkins cdrom     0 Aug 18 04:14 deployment_summary.json
</span><span class='line'>[Pipeline] stash
</span><span class='line'>04:14:04 Stashed 1 file(s)
</span><span class='line'>[Pipeline] stash
</span><span class='line'>04:14:04 Stashed 1 file(s)
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] // dir
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] // stage
</span><span class='line'>[Pipeline] stage
</span><span class='line'>[Pipeline] { (Check unstash)
</span><span class='line'>[Pipeline] dir
</span><span class='line'>04:14:04 Running in /var/lib/jenkins/workspace/feature_test-23NL25SRLW3W6WXNVW2NXBADUXIYBTCBZAO5YRKVQPVT3NUSEOTQ/check
</span><span class='line'>[Pipeline] {
</span><span class='line'>[Pipeline] unstash
</span><span class='line'>[Pipeline] sh
</span><span class='line'>04:14:04 [check] Running shell script
</span><span class='line'>04:14:05 + tree -L 2
</span><span class='line'>04:14:05 .
</span><span class='line'>04:14:05 └── release_notes
</span><span class='line'>04:14:05     └── deployment_ids.json
</span><span class='line'>04:14:05 
</span><span class='line'>04:14:05 1 directory, 1 file
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] // dir
</span><span class='line'>[Pipeline] dir
</span><span class='line'>04:14:05 Running in /var/lib/jenkins/workspace/feature_test-23NL25SRLW3W6WXNVW2NXBADUXIYBTCBZAO5YRKVQPVT3NUSEOTQ/check2
</span><span class='line'>[Pipeline] {
</span><span class='line'>[Pipeline] unstash
</span><span class='line'>[Pipeline] sh
</span><span class='line'>04:14:05 [check2] Running shell script
</span><span class='line'>04:14:05 + tree -L 2
</span><span class='line'>04:14:05 .
</span><span class='line'>04:14:05 └── release_notes
</span><span class='line'>04:14:05     └── deployment_summary.json
</span><span class='line'>04:14:05 
</span><span class='line'>04:14:05 1 directory, 1 file
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] // dir
</span><span class='line'>[Pipeline] }
</span><span class='line'>[Pipeline] // stage
</span><span class='line'>[Pipeline] stage
</span><span class='line'>[Pipeline] { (Clean up)
</span><span class='line'>[Pipeline] sh
</span><span class='line'>04:14:05 [feature_test-23NL25SRLW3W6WXNVW2NXBADUXIYBTCBZAO5YRKVQPVT3NUSEOTQ] Running shell script
</span><span class='line'>04:14:05 + tree -L 2
</span><span class='line'>04:14:05 .
</span><span class='line'>04:14:05 ├── check
</span><span class='line'>04:14:05 │   └── release_notes
</span><span class='line'>04:14:05 ├── check2
</span><span class='line'>04:14:05 │   └── release_notes
</span><span class='line'>04:14:05 ├── check2@tmp
</span><span class='line'>04:14:05 ├── check@tmp
</span><span class='line'>04:14:05 ├── Jenkinsfile
</span><span class='line'>...</span></code></pre></td></tr></table></div></figure>


<h3><code>withCredentials</code> step</h3>

<p>There are different variations of <code>withCredentials</code> step.
The most common ones are:</p>

<figure class='code'><figcaption><span>Binding secret to username and password separately</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">node</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">withCredentials</span><span class="o">([</span><span class="n">usernamePassword</span><span class="o">(</span><span class="nl">credentialsId:</span> <span class="s1">&#39;amazon&#39;</span><span class="o">,</span> <span class="nl">usernameVariable:</span> <span class="s1">&#39;USERNAME&#39;</span><span class="o">,</span> <span class="nl">passwordVariable:</span> <span class="s1">&#39;PASSWORD&#39;</span><span class="o">)])</span> <span class="o">{</span>
</span><span class='line'>        <span class="c1">// available as an env variable, but will be masked if you try to print it out any which way</span>
</span><span class='line'>        <span class="n">sh</span> <span class="s1">&#39;echo $PASSWORD&#39;</span>
</span><span class='line'>        <span class="c1">// also available as a Groovy variable—note double quotes for string interpolation</span>
</span><span class='line'>        <span class="n">echo</span> <span class="s2">&quot;$USERNAME&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Binding secret to $username:$password</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">node</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">withCredentials</span><span class="o">([</span><span class="n">usernameColonPassword</span><span class="o">(</span><span class="nl">credentialsId:</span> <span class="s1">&#39;mylogin&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;USERPASS&#39;</span><span class="o">)])</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">sh</span> <span class="s1">&#39;&#39;&#39;</span>
</span><span class='line'><span class="s1">      set +x</span>
</span><span class='line'><span class="s1">      curl -u $USERPASS https://private.server/ &gt; output</span>
</span><span class='line'><span class="s1">    &#39;&#39;&#39;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Binding secret string to a variable</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">node</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">withCredentials</span><span class="o">([</span><span class="n">string</span><span class="o">(</span><span class="nl">credentialsId:</span> <span class="s1">&#39;secretString&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;MY_STRING&#39;</span><span class="o">)])</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">sh</span> <span class="s1">&#39;&#39;&#39;</span>
</span><span class='line'><span class="s1">      echo $MY_STRING</span>
</span><span class='line'><span class="s1">    &#39;&#39;&#39;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>For secret file, the file will be passed into some secret location and that secret location will be bound to some variable.
If you want the secret files in specific locations, the workaround is to create symlinks to those secret files.</p>

<figure class='code'><figcaption><span>Binding secret file</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>    <span class="n">withCredentials</span><span class="o">(</span> <span class="o">[</span><span class="n">file</span><span class="o">(</span><span class="nl">credentialsId:</span> <span class="s1">&#39;host-cert&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;HOST_CERT&#39;</span><span class="o">),</span>
</span><span class='line'>                    <span class="n">file</span><span class="o">(</span><span class="nl">credentialsId:</span> <span class="s1">&#39;host-key&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;HOST_KEY&#39;</span><span class="o">),</span>
</span><span class='line'>                    <span class="n">file</span><span class="o">(</span><span class="nl">credentialsId:</span> <span class="s1">&#39;cert-ca&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;CERT_CA&#39;</span><span class="o">)</span>
</span><span class='line'>                    <span class="o">])</span>
</span><span class='line'>    <span class="o">{</span>
</span><span class='line'>        <span class="n">sh</span> <span class="s2">&quot;&quot;&quot;</span>
</span><span class='line'><span class="s2">            mkdir download</span>
</span><span class='line'><span class="s2">            ln -s ${env.HOST_CERT} download/hostcert.crt</span>
</span><span class='line'><span class="s2">            ln -s ${env.HOST_KEY} download/hostcert.key</span>
</span><span class='line'><span class="s2">            ln -s ${env.CERT_CA} download/ca.crt</span>
</span><span class='line'><span class="s2">        &quot;&quot;&quot;</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// The Python script read those files download/*.* by default</span>
</span><span class='line'>        <span class="n">sh</span> <span class="s2">&quot;python python/main.py&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>For &ldquo;private key with passphrase&rdquo; Credential type, <code>sshagent</code> is only usage that I know (credential ID is <code>jenkins_ssh_key</code> in this example):</p>

<figure class='code'><figcaption><span>Binding private key with passphrase</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">node</span><span class="o">(</span><span class="s1">&#39;my-agent&#39;</span><span class="o">){</span>
</span><span class='line'>  <span class="n">stage</span> <span class="s1">&#39;Checkout&#39;</span>
</span><span class='line'>  <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>
</span><span class='line'>  <span class="nf">if</span> <span class="o">(</span><span class="n">env</span><span class="o">.</span><span class="na">BRANCH_NAME</span> <span class="o">==</span> <span class="s1">&#39;master&#39;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">stage</span> <span class="s1">&#39;Commit&#39;</span>
</span><span class='line'>    <span class="n">println</span> <span class="s2">&quot;Pushing Jenkins Shared Library&quot;</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">sshagent</span><span class="o">([</span><span class="s1">&#39;jenkins_ssh_key&#39;</span><span class="o">])</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s2">&quot;&quot;&quot;</span>
</span><span class='line'><span class="s2">        git branch master</span>
</span><span class='line'><span class="s2">        git checkout master</span>
</span><span class='line'><span class="s2">        ssh-keyscan -H -p 12222 \${JENKINS_ADDR} &gt;&gt; ~/.ssh/known_hosts</span>
</span><span class='line'><span class="s2">        git remote add jenkins ssh://tdongsi@\${JENKINS_ADDR}:12222/workflowLibs.git</span>
</span><span class='line'><span class="s2">        git push --force jenkins master</span>
</span><span class='line'><span class="s2">      &quot;&quot;&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Reference:</p>

<ul>
<li><a href="https://wiki.jenkins.io/display/JENKINS/Credentials+Binding+Plugin?focusedCommentId=80184884">Credentials Binding plugin</a></li>
</ul>


<h3>References</h3>

<ul>
<li><a href="https://jenkins.io/doc/pipeline/steps/">Basic Jenkinsfile steps</a></li>
<li><a href="https://github.com/jenkinsci/workflow-basic-steps-plugin/tree/master/src/main/java/org/jenkinsci/plugins/workflow/steps">Source code of Basic steps</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Troubleshooting Groovy Code in Jenkinsfile]]></title>
    <link href="http://tdongsi.github.io/blog/2017/06/16/troubleshooting-groovy-scripts-in-jenkinsfile/"/>
    <updated>2017-06-16T23:52:44-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/06/16/troubleshooting-groovy-scripts-in-jenkinsfile</id>
    <content type="html"><![CDATA[<p>In this post, we look into some troubleshooting tips when <a href="http://tdongsi.github.io/blog/2017/04/18/groovy-code-in-jenkins-pipeline/">using independent Groovy scripts in Jenkins pipeline</a> and how to work around those.</p>

<!--more-->


<h3>Cannot load a Groovy script in Declarative Pipeline</h3>

<p><strong>Problem</strong>: Loading Groovy methods from a file with <code>load</code> step does not work inside Declarative Pipeline step, as reported in <a href="https://issues.jenkins-ci.org/browse/JENKINS-43455">this issue</a>.</p>

<p><strong>Workaround</strong>: There are a few work-arounds. The most straight-forward one is to use <a href="https://jenkins.io/doc/book/pipeline/syntax/#declarative-pipeline"><code>script</code> step</a>.</p>

<figure class='code'><figcaption><span>Loading Groovy script</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">steps</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>    <span class="nf">withCredentials</span><span class="o">([</span>
</span><span class='line'>        <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUserName&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusUserName&#39;</span><span class="o">],</span>
</span><span class='line'>        <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>    <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">script</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">myScript</span> <span class="o">=</span> <span class="n">load</span> <span class="s1">&#39;jenkins/xml.groovy&#39;</span>
</span><span class='line'>            <span class="n">String</span> <span class="n">myFile</span> <span class="o">=</span> <span class="n">myScript</span><span class="o">.</span><span class="na">transformXml</span><span class="o">(</span><span class="n">settingsFile</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusUserName</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusPassword</span><span class="o">)</span>
</span><span class='line'>            <span class="n">sh</span> <span class="s2">&quot;mvn -B -s ${myFile} clean compile&quot;</span>
</span><span class='line'>
</span><span class='line'>            <span class="n">sh</span> <span class="s2">&quot;rm ${myFile}&quot;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p> You can also define Groovy methods from inside the Jenkinsfile.</p>

<figure class='code'><figcaption><span>Example Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.StreamingMarkupBuilder</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.XmlUtil</span>
</span><span class='line'>
</span><span class='line'><span class="nd">@NonCPS</span>
</span><span class='line'><span class="kt">def</span> <span class="nf">xmlTransform</span><span class="o">(</span><span class="n">txt</span><span class="o">,</span> <span class="n">username</span><span class="o">,</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="n">xmlRoot</span> <span class="o">=</span> <span class="k">new</span> <span class="n">XmlSlurper</span><span class="o">(</span><span class="kc">false</span><span class="o">,</span> <span class="kc">false</span><span class="o">).</span><span class="na">parseText</span><span class="o">(</span><span class="n">txt</span><span class="o">)</span>
</span><span class='line'>    <span class="n">echo</span> <span class="s1">&#39;Start tranforming XML&#39;</span>
</span><span class='line'>    <span class="n">xmlRoot</span><span class="o">.</span><span class="na">servers</span><span class="o">.</span><span class="na">server</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">node</span> <span class="o">-&gt;</span>
</span><span class='line'>       <span class="n">node</span><span class="o">.</span><span class="na">username</span> <span class="o">=</span> <span class="n">username</span>
</span><span class='line'>       <span class="n">node</span><span class="o">.</span><span class="na">password</span> <span class="o">=</span> <span class="n">password</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// TRICKY: FileWriter does NOT work</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">outWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringWriter</span><span class="o">()</span>
</span><span class='line'>    <span class="n">XmlUtil</span><span class="o">.</span><span class="na">serialize</span><span class="o">(</span> <span class="n">xmlRoot</span><span class="o">,</span> <span class="n">outWriter</span> <span class="o">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">outWriter</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">...</span>
</span><span class='line'>
</span><span class='line'>   <span class="n">steps</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>        <span class="nf">withCredentials</span><span class="o">([</span>
</span><span class='line'>            <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUserName&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusUserName&#39;</span><span class="o">],</span>
</span><span class='line'>            <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>        <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">script</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">myScript</span> <span class="o">=</span> <span class="n">load</span> <span class="s1">&#39;jenkins/xml.groovy&#39;</span>
</span><span class='line'>                <span class="n">String</span> <span class="n">myFile</span> <span class="o">=</span> <span class="n">xmlTransform</span><span class="o">(</span><span class="n">settingsFile</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusUserName</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusPassword</span><span class="o">)</span>
</span><span class='line'>                <span class="n">sh</span> <span class="s2">&quot;mvn -B -s ${myFile} clean compile&quot;</span>
</span><span class='line'>
</span><span class='line'>                <span class="n">sh</span> <span class="s2">&quot;rm ${myFile}&quot;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>For Declarative Pipeline, to reuse the code from a Groovy script, you must use Shared Libraries.
Shared Libraries are not specific to Declarative; they were released some time ago and were seen in Scripted Pipeline.
<a href="http://tdongsi.github.io/blog/2017/03/17/jenkins-pipeline-shared-libraries/">This blog post</a> discusses an older mechanism for Shared Library.
For the newer mechanism of importing library, please check out <a href="https://jenkins.io/blog/2017/02/15/declarative-notifications/">this blog post</a>.
Due to Declarative Pipeline’s lack of support for defining methods, Shared Libraries definitely take on a vital role for code-reuse in Jenkinsfile.</p>

<h3><code>File</code> reading and writing not supported</h3>

<p>Java/Grooy reading and writing using &ldquo;java.io.File&rdquo; class is not directly supported.</p>

<figure class='code'><figcaption><span>Using File class does NOT work</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">myFile</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="s1">&#39;/home/data/myfile.xml&#39;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>In fact, using that class in Jenkinsfile must go through &ldquo;In-Process Script Approval&rdquo; with this warning.</p>

<blockquote><p>new java.io.File java.lang.String Approving this signature may introduce a security vulnerability! You are advised to deny it.</p></blockquote>


<p>Even then, &ldquo;java.io.File&rdquo; will refer to <strong>files on the master</strong> (where Jenkins is running), not the current workspace on Jenkins slave (or slave container).
As a result, it will report the following error even though the file is present in filesystem (<a href="https://stackoverflow.com/questions/41739468/groovy-reports-that-a-file-doesnt-exists-when-it-really-is-present-in-the-syste">relevant Stackoverflow</a>) on slave:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>java.io.FileNotFoundException: /home/data/myfile.xml (No such file or directory)
</span><span class='line'>  at java.io.FileInputStream.open0(Native Method)</span></code></pre></td></tr></table></div></figure>


<p>That also means related class such as FileWriter will NOT work as expected.
It reports no error during execution but you will find no file since those files are created on Jenkins master.</p>

<p><strong>Workaround</strong>:</p>

<ul>
<li>For file reading, use <a href="https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/#readfile-read-file-from-workspace"><code>readFile</code> step</a>.</li>
<li>For file writing, use <code>writeFile</code> step. However, Pipeline steps (such as <code>writeFile</code>) are NOT allowed in <code>@NonCPS</code> methods. For more complex file writing, you might want to export the file content as String and use the following code snippet:</li>
</ul>


<figure class='code'><figcaption><span>Shell command</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">String</span> <span class="n">xmlFile</span> <span class="o">=</span> <span class="o">...</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// TRICKY: FileWriter does NOT work in xmlTransform</span>
</span><span class='line'><span class="kt">def</span> <span class="n">mCommand</span> <span class="o">=</span> <span class="s2">&quot;cat &gt;${settingsFile} &lt;&lt;EOF&quot;</span>
</span><span class='line'><span class="n">mCommand</span> <span class="o">+=</span> <span class="s2">&quot;\n${xmlFile}\nEOF&quot;</span>
</span><span class='line'><span class="n">sh</span> <span class="n">mCommand</span>
</span></code></pre></td></tr></table></div></figure>


<p>In the code snippet above, we construct a <a href="https://en.wikipedia.org/wiki/Here_document#Unix_shells"><em>here document</em>-formatted</a> command for writing multi-line string in <code>mCommand</code> before passing to <code>sh</code> step for executing.</p>

<figure class='code'><figcaption><span>heredoc example to explain mCommand</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ cat &gt;output.txt &lt;&lt;EOF
</span><span class='line'>SELECT foo, bar FROM db
</span><span class='line'>WHERE foo='baz'
</span><span class='line'>More line from xmlFile
</span><span class='line'>EOF
</span><span class='line'>
</span><span class='line'>$ cat output.txt
</span><span class='line'>SELECT foo, bar FROM db
</span><span class='line'>WHERE foo='baz'
</span><span class='line'>More line from xmlFile</span></code></pre></td></tr></table></div></figure>


<h3>Serialization errors</h3>

<p>You often encounter this type of errors when using non-serialiable classes from Groovy/Java libraries.</p>

<figure class='code'><figcaption><span>Error in Jenkins log</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>java.io.NotSerializableException: org.codehaus.groovy.control.ErrorCollector
</span><span class='line'>  at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:860)</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Related error in Jenkins log</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>java.lang.UnsupportedOperationException: Calling public static java.lang.Iterable 
</span><span class='line'>org.codehaus.groovy.runtime.DefaultGroovyMethods.each(java.lang.Iterable,groovy.lang.Closure) on a
</span><span class='line'>CPS-transformed closure is not yet supported (JENKINS-26481); 
</span><span class='line'>encapsulate in a @NonCPS method, or use Java-style loops
</span><span class='line'>  at org.jenkinsci.plugins.workflow.cps.GroovyClassLoaderWhitelist.checkJenkins26481
</span><span class='line'>    (GroovyClassLoaderWhitelist.java:90)</span></code></pre></td></tr></table></div></figure>


<p>There is also some known <a href="https://issues.jenkins-ci.org/browse/JENKINS-35140">issue about JsonSlurper</a>.
These problems come from the fact that variables in Jenkins pipelines must be serializable.
Since pipeline must survive a Jenkins restart, the state of the running program is periodically saved to disk for possible resume later.
Any &ldquo;live&rdquo; objects such as a network connection is not serializble.</p>

<p><strong>Workaround</strong>:
Explicitly discard non-serializable objects or use <a href="https://support.cloudbees.com/hc/en-us/articles/230612967-Pipeline-The-pipeline-even-if-successful-ends-with-java-io-NotSerializableException">@NonCPS</a> methods.</p>

<p>Quoted from <a href="https://github.com/jenkinsci/workflow-cps-plugin/blob/master/README.md">here</a>: <code>@NonCPS</code> methods may safely use non-<code>Serializable</code> objects as local variables, though they should NOT accept nonserializable parameters or return or store nonserializable values.
You may NOT call regular (CPS-transformed) methods, or Pipeline steps, from a <code>@NonCPS</code> method, so they are best used for performing some calculations before passing a summary back to the main script.</p>

<h3>References</h3>

<ul>
<li><a href="https://jenkins.io/doc/book/pipeline/syntax/#declarative-pipeline">Declarative syntax</a></li>
<li><a href="https://jenkins.io/blog/2017/02/15/declarative-notifications/">Shared Libraries for Declarative Pipeline</a></li>
<li>Here document

<ul>
<li><a href="https://en.wikipedia.org/wiki/Here_document#Unix_shells">Theory</a></li>
<li><a href="https://stackoverflow.com/questions/2500436/how-does-cat-eof-work-in-bash">Common usage from Stackoverflow</a></li>
<li><a href="http://www.guguncube.com/2140/unix-set-a-multi-line-text-to-a-string-variable-or-file-in-bash">Heredoc with and without variable expansion</a></li>
</ul>
</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Groovy in Jenkinsfile]]></title>
    <link href="http://tdongsi.github.io/blog/2017/06/07/groovy-in-jenkinsfile/"/>
    <updated>2017-06-07T12:08:15-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/06/07/groovy-in-jenkinsfile</id>
    <content type="html"><![CDATA[<p>Groovy is supported in Jenkinsfile for quick scripting.
However, lots of features in the Groovy language is not supported and simple works in Groovy can be really tricky in Jenkinsfile.</p>

<!--more-->


<h3>Different ways to process XML file</h3>

<p>In summary, if it is possible, use another script language (e.g., Python) for <strong>file manipulation</strong> in Jenkinsfile.
It is time consuming to navigate all tricky stuffs of Groovy implementaiton in Jenkins:</p>

<ul>
<li>In-process Script Approval: you have to approve every single class and method <em>one by one</em>.</li>
<li>Some features of Groovy is not supported and it takes time to figure out what is not supported and how to work around. When in doubt, use <code>@NonCPS</code>.</li>
</ul>


<h4>Groovy method in Jenkinsfile</h4>

<figure class='code'><figcaption><span>Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.StreamingMarkupBuilder</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.XmlUtil</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="n">settingsFile</span> <span class="o">=</span> <span class="s1">&#39;temp.xml&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="nd">@NonCPS</span>
</span><span class='line'><span class="kt">def</span> <span class="nf">xmlTransform</span><span class="o">(</span><span class="n">txt</span><span class="o">,</span> <span class="n">username</span><span class="o">,</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="n">xmlRoot</span> <span class="o">=</span> <span class="k">new</span> <span class="n">XmlSlurper</span><span class="o">(</span><span class="kc">false</span><span class="o">,</span> <span class="kc">false</span><span class="o">).</span><span class="na">parseText</span><span class="o">(</span><span class="n">txt</span><span class="o">)</span>
</span><span class='line'>    <span class="n">echo</span> <span class="s1">&#39;Start tranforming XML&#39;</span>
</span><span class='line'>    <span class="n">xmlRoot</span><span class="o">.</span><span class="na">servers</span><span class="o">.</span><span class="na">server</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">node</span> <span class="o">-&gt;</span>
</span><span class='line'>       <span class="n">node</span><span class="o">.</span><span class="na">username</span> <span class="o">=</span> <span class="n">username</span>
</span><span class='line'>       <span class="n">node</span><span class="o">.</span><span class="na">password</span> <span class="o">=</span> <span class="n">password</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// TRICKY: FileWriter does NOT work</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">outWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringWriter</span><span class="o">()</span>
</span><span class='line'>    <span class="n">XmlUtil</span><span class="o">.</span><span class="na">serialize</span><span class="o">(</span> <span class="n">xmlRoot</span><span class="o">,</span> <span class="n">outWriter</span> <span class="o">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">outWriter</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">pipeline</span> <span class="o">{</span>
</span><span class='line'>   <span class="n">agent</span> <span class="o">{</span> <span class="n">node</span> <span class="o">{</span> <span class="n">label</span> <span class="s1">&#39;test-agent&#39;</span> <span class="o">}</span> <span class="o">}</span>
</span><span class='line'>   <span class="n">stages</span> <span class="o">{</span>
</span><span class='line'>       <span class="n">stage</span><span class="o">(</span><span class="s2">&quot;compile&quot;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>           <span class="n">steps</span> <span class="o">{</span>
</span><span class='line'>               <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>               <span class="nf">withCredentials</span><span class="o">([</span>
</span><span class='line'>                 <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">],</span>
</span><span class='line'>                 <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>               <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>                   <span class="n">script</span> <span class="o">{</span>
</span><span class='line'>                       <span class="kt">def</span> <span class="n">xmlTemplate</span> <span class="o">=</span> <span class="n">readFile</span><span class="o">(</span> <span class="s1">&#39;jenkins/settings.xml&#39;</span> <span class="o">)</span>
</span><span class='line'>                       <span class="kt">def</span> <span class="n">xmlFile</span> <span class="o">=</span> <span class="n">xmlTransform</span><span class="o">(</span><span class="n">xmlTemplate</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusUsername</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusPassword</span><span class="o">)</span>
</span><span class='line'>                       <span class="n">writeFile</span> <span class="nl">file:</span> <span class="n">settingsFile</span><span class="o">,</span> <span class="nl">text:</span> <span class="n">xmlFile</span>
</span><span class='line'>
</span><span class='line'>                       <span class="n">sh</span> <span class="s2">&quot;mvn -B -s ${settingsFile} clean compile&quot;</span>
</span><span class='line'>                   <span class="o">}</span>
</span><span class='line'>               <span class="o">}</span>
</span><span class='line'>           <span class="o">}</span>
</span><span class='line'>           <span class="n">post</span> <span class="o">{</span>
</span><span class='line'>           <span class="n">failure</span> <span class="o">{</span>
</span><span class='line'>               <span class="n">echo</span> <span class="s2">&quot;Sending email for compile failed (TBD)&quot;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>           <span class="o">}</span>
</span><span class='line'>       <span class="o">}</span>
</span><span class='line'>   <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Some notes:</p>

<ul>
<li><code>import</code> statements must be at the top, right after the shebang and before anything else.</li>
<li>The Groovy methods must be annotated with <code>@NonCPS</code> or Jenkins will report the error &ldquo;java.io.NotSerializableException&rdquo;.</li>
<li>The Groovy methods can not be defined inside a <code>step</code> block. It must be defined at the top.</li>
<li><code>@NonCPS</code> is required since the Groovy method uses several non-serializble objects.</li>
</ul>


<h4>Groovy method in separate script</h4>

<figure class='code'><figcaption><span>Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="n">myScript</span>
</span><span class='line'>
</span><span class='line'><span class="n">pipeline</span> <span class="o">{</span>
</span><span class='line'>   <span class="n">agent</span> <span class="o">{</span> <span class="n">node</span> <span class="o">{</span> <span class="n">label</span> <span class="s1">&#39;test-agent&#39;</span> <span class="o">}</span> <span class="o">}</span>
</span><span class='line'>   <span class="n">stages</span> <span class="o">{</span>
</span><span class='line'>       <span class="n">stage</span><span class="o">(</span><span class="s2">&quot;compile&quot;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>           <span class="n">steps</span> <span class="o">{</span>
</span><span class='line'>               <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>               <span class="nf">withCredentials</span><span class="o">([</span>
</span><span class='line'>                 <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">],</span>
</span><span class='line'>                 <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>               <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>                   <span class="n">script</span> <span class="o">{</span>
</span><span class='line'>                       <span class="n">myScript</span> <span class="o">=</span> <span class="n">load</span> <span class="s1">&#39;jenkins/xml.groovy&#39;</span>
</span><span class='line'>                       <span class="kt">def</span> <span class="n">xmlTemplate</span> <span class="o">=</span> <span class="n">readFile</span><span class="o">(</span> <span class="s1">&#39;jenkins/settings.xml&#39;</span> <span class="o">)</span>
</span><span class='line'>                       <span class="n">String</span> <span class="n">xmlFile</span> <span class="o">=</span> <span class="n">myScript</span><span class="o">.</span><span class="na">transformXml</span><span class="o">(</span><span class="n">xmlTemplate</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusUsername</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusPassword</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>                       <span class="n">String</span> <span class="n">myPath</span> <span class="o">=</span> <span class="s1">&#39;temp.xml&#39;</span>
</span><span class='line'>                       <span class="kt">def</span> <span class="n">mCommand</span> <span class="o">=</span> <span class="s2">&quot;cat &gt;${myPath} &lt;&lt;EOF&quot;</span>
</span><span class='line'>                       <span class="n">mCommand</span> <span class="o">+=</span> <span class="s2">&quot;\n${xmlFile}\nEOF&quot;</span>
</span><span class='line'>                       <span class="n">sh</span> <span class="n">mCommand</span>
</span><span class='line'>
</span><span class='line'>                       <span class="n">sh</span> <span class="s2">&quot;mvn -B clean compile -s ${myPath}&quot;</span>
</span><span class='line'>
</span><span class='line'>                       <span class="n">sh</span> <span class="s2">&quot;rm ${myPath}&quot;</span>
</span><span class='line'>                   <span class="o">}</span>
</span><span class='line'>               <span class="o">}</span>
</span><span class='line'>           <span class="o">}</span>
</span><span class='line'>           <span class="n">post</span> <span class="o">{</span>
</span><span class='line'>           <span class="n">failure</span> <span class="o">{</span>
</span><span class='line'>               <span class="n">echo</span> <span class="s2">&quot;Sending email for compile failed (TBD)&quot;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>           <span class="o">}</span>
</span><span class='line'>       <span class="o">}</span>
</span><span class='line'>   <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>xml.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.StreamingMarkupBuilder</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.XmlUtil</span>
</span><span class='line'>
</span><span class='line'><span class="nd">@NonCPS</span>
</span><span class='line'><span class="kt">def</span> <span class="nf">transformXml</span><span class="o">(</span><span class="n">String</span> <span class="n">xmlContent</span><span class="o">,</span> <span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">xml</span> <span class="o">=</span> <span class="k">new</span> <span class="n">XmlSlurper</span><span class="o">(</span><span class="kc">false</span><span class="o">,</span> <span class="kc">false</span><span class="o">).</span><span class="na">parseText</span><span class="o">(</span><span class="n">xmlContent</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">echo</span> <span class="s1">&#39;Start tranforming XML&#39;</span>
</span><span class='line'>  <span class="n">xml</span><span class="o">.</span><span class="na">servers</span><span class="o">.</span><span class="na">server</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">node</span> <span class="o">-&gt;</span>
</span><span class='line'>    <span class="n">node</span><span class="o">.</span><span class="na">username</span> <span class="o">=</span> <span class="n">username</span>
</span><span class='line'>    <span class="n">node</span><span class="o">.</span><span class="na">password</span> <span class="o">=</span> <span class="n">password</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="kt">def</span> <span class="n">outWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringWriter</span><span class="o">()</span>
</span><span class='line'>  <span class="n">XmlUtil</span><span class="o">.</span><span class="na">serialize</span><span class="o">(</span> <span class="n">xml</span><span class="o">,</span> <span class="n">outWriter</span> <span class="o">)</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">outWriter</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">return</span> <span class="k">this</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Groovy method in shared library</h4>

<p>The above Nexus authentication code is likely repeated across multiple Maven builds.
Therefore, it is worth converting it into a DSL into a Shared Library in Jenkins.
The DSL takes two parameters:</p>

<ul>
<li>templateFile: settings.xml template with Nexus credentials info redacted.</li>
<li>command: Maven command with settings file NOT specified (i.e., NO &ldquo;-s&rdquo; option in the command).</li>
</ul>


<p>The example usage is as follows:</p>

<figure class='code'><figcaption><span>Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">pipeline</span> <span class="o">{</span>
</span><span class='line'>   <span class="n">agent</span> <span class="o">{</span> <span class="n">node</span> <span class="o">{</span> <span class="n">label</span> <span class="s1">&#39;test-agent&#39;</span> <span class="o">}</span> <span class="o">}</span>
</span><span class='line'>   <span class="n">stages</span> <span class="o">{</span>
</span><span class='line'>       <span class="n">stage</span><span class="o">(</span><span class="s2">&quot;compile&quot;</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>           <span class="n">steps</span> <span class="o">{</span>
</span><span class='line'>               <span class="n">checkout</span> <span class="n">scm</span>
</span><span class='line'>               <span class="n">script</span> <span class="o">{</span>
</span><span class='line'>                    <span class="n">withNexusMaven</span> <span class="o">{</span>
</span><span class='line'>                        <span class="n">templateFile</span> <span class="o">=</span> <span class="s1">&#39;jenkins/settings.xml&#39;</span>
</span><span class='line'>                        <span class="n">command</span> <span class="o">=</span> <span class="s2">&quot;mvn -B clean compile&quot;</span>
</span><span class='line'>                    <span class="o">}</span>
</span><span class='line'>               <span class="o">}</span>
</span><span class='line'>           <span class="o">}</span>
</span><span class='line'>           <span class="n">post</span> <span class="o">{</span>
</span><span class='line'>           <span class="n">failure</span> <span class="o">{</span>
</span><span class='line'>               <span class="n">echo</span> <span class="s2">&quot;Sending email for compile failed (TBD)&quot;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>           <span class="o">}</span>
</span><span class='line'>       <span class="o">}</span>
</span><span class='line'>   <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The Jenksinfile is much cleaner since most of implementation details have been moved inside the DSL:</p>

<figure class='code'><figcaption><span>withNexusMaven.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="cp">#!groovy</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">groovy.xml.XmlUtil</span>
</span><span class='line'>
</span><span class='line'><span class="nd">@NonCPS</span>
</span><span class='line'><span class="kt">def</span> <span class="nf">transformXml</span><span class="o">(</span><span class="n">String</span> <span class="n">xmlContent</span><span class="o">,</span> <span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="kt">def</span> <span class="n">xml</span> <span class="o">=</span> <span class="k">new</span> <span class="n">XmlSlurper</span><span class="o">(</span><span class="kc">false</span><span class="o">,</span> <span class="kc">false</span><span class="o">).</span><span class="na">parseText</span><span class="o">(</span><span class="n">xmlContent</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">echo</span> <span class="s1">&#39;Start tranforming XML&#39;</span>
</span><span class='line'>  <span class="n">xml</span><span class="o">.</span><span class="na">servers</span><span class="o">.</span><span class="na">server</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">node</span> <span class="o">-&gt;</span>
</span><span class='line'>    <span class="n">node</span><span class="o">.</span><span class="na">username</span> <span class="o">=</span> <span class="n">username</span>
</span><span class='line'>    <span class="n">node</span><span class="o">.</span><span class="na">password</span> <span class="o">=</span> <span class="n">password</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="kt">def</span> <span class="n">outWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringWriter</span><span class="o">()</span>
</span><span class='line'>  <span class="n">XmlUtil</span><span class="o">.</span><span class="na">serialize</span><span class="o">(</span> <span class="n">xml</span><span class="o">,</span> <span class="n">outWriter</span> <span class="o">)</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">outWriter</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="kt">def</span> <span class="nf">call</span><span class="o">(</span><span class="n">Closure</span> <span class="n">body</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="n">config</span> <span class="o">=</span> <span class="o">[:]</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">body</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">body</span><span class="o">.</span><span class="na">resolveStrategy</span> <span class="o">=</span> <span class="n">Closure</span><span class="o">.</span><span class="na">DELEGATE_FIRST</span>
</span><span class='line'>        <span class="n">body</span><span class="o">.</span><span class="na">delegate</span> <span class="o">=</span> <span class="n">config</span>
</span><span class='line'>        <span class="n">body</span><span class="o">()</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">def</span> <span class="n">templateFile</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">templateFile</span> <span class="o">?:</span> <span class="s1">&#39;/home/data/settings.xml&#39;</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">command</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">command</span> <span class="o">?:</span> <span class="s2">&quot;mvn -B clean compile&quot;</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">withCredentials</span><span class="o">([</span>
</span><span class='line'>        <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">],</span>
</span><span class='line'>        <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>    <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>        <span class="kt">def</span> <span class="n">xmlTemplate</span> <span class="o">=</span> <span class="n">readFile</span> <span class="n">templateFile</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">xmlFile</span> <span class="o">=</span> <span class="n">transformXml</span><span class="o">(</span><span class="n">xmlTemplate</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusUsername</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusPassword</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">String</span> <span class="n">tempFile</span> <span class="o">=</span> <span class="s1">&#39;temp.xml&#39;</span>
</span><span class='line'>        <span class="n">writeFile</span> <span class="nl">file:</span> <span class="n">tempFile</span><span class="o">,</span> <span class="nl">text:</span> <span class="n">xmlFile</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">sh</span> <span class="s2">&quot;${command} -s ${tempFile}&quot;</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// Clean up</span>
</span><span class='line'>        <span class="n">sh</span> <span class="s2">&quot;rm ${tempFile}&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Sending Emails From Docker Containers]]></title>
    <link href="http://tdongsi.github.io/blog/2017/05/25/sending-emails-from-docker-containers/"/>
    <updated>2017-05-25T13:42:45-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/05/25/sending-emails-from-docker-containers</id>
    <content type="html"><![CDATA[<p>In this post, we looks into how to set up notification emails at the end of CI pipelines in a containerized Jenkins system.
First, we look into conventional Jenkins system (directly hosted) that has direct communication to the SMTP server.
After that, we will look into adjustments required for a containerized Jenkins system to run in the same environment.</p>

<!--more-->


<h3>Sending emails in standard Jenkins setup</h3>

<p>We first look at a typical Jenkins setup, where the Jenkins instance is installed directly on a host machine (VM or bare-metal) and has direct communication to the SMTP server.
For corporate network, you may have to use an SMTP relay server instead.
For those cases, you can configure SMTP communication by <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-ubuntu-14-04">setting up Postfix</a>.
In CentOS, it could be a simple &ldquo;sudo yum install -y mailx&rdquo;.</p>

<p>After installing, update <em>/etc/postfix/main.cf</em> with correct relay information: myhostname, myorigin, mydestination, relayhost, alias_maps, alias_database.
An example <em>/etc/postfix/main.cf</em> is shown below:</p>

<figure class='code'><figcaption><span>/etc/postfix/main.cf example</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class=''><span class='line'># See /usr/share/postfix/main.cf.bak for a commented, more complete version
</span><span class='line'>
</span><span class='line'>myhostname = dev-worker-1.example.com
</span><span class='line'>smtpd_banner = $myhostname ESMTP $mail_name
</span><span class='line'>biff = no
</span><span class='line'>
</span><span class='line'># appending .domain is the MUA's job.
</span><span class='line'>append_dot_mydomain = no
</span><span class='line'>
</span><span class='line'># Uncomment the next line to generate "delayed mail" warnings
</span><span class='line'>#delay_warning_time = 4h
</span><span class='line'>
</span><span class='line'>readme_directory = no
</span><span class='line'>
</span><span class='line'># TLS parameters
</span><span class='line'>smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
</span><span class='line'>smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
</span><span class='line'>smtpd_use_tls=yes
</span><span class='line'>
</span><span class='line'># See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
</span><span class='line'># information on enabling SSL in the smtp client.
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>alias_maps = hash:/etc/aliases
</span><span class='line'>alias_database = hash:/etc/aliases
</span><span class='line'>myorigin = dev-worker-1.example.com
</span><span class='line'>mydestination = dev-worker-1.example.com, localhost.example.com, localhost
</span><span class='line'>relayhost = smtprelay-prd.example.com
</span><span class='line'>mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
</span><span class='line'>mailbox_size_limit = 0
</span><span class='line'>recipient_delimiter = +
</span><span class='line'>inet_interfaces = localhost
</span><span class='line'>inet_protocols = all</span></code></pre></td></tr></table></div></figure>


<p>We can test the setup by sending a test email with the following command:</p>

<figure class='code'><figcaption><span>Send a test email</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[tdongsi@dev-worker-1 ~]# echo "Test localhost" | mailx -s Test tdongsi@example.com
</span><span class='line'>send-mail: warning: inet_protocols: disabling IPv6 name/address support: Address family not supported by protocol
</span><span class='line'>postdrop: warning: inet_protocols: disabling IPv6 name/address support: Address family not supported by protocol</span></code></pre></td></tr></table></div></figure>


<p>After the <code>postfix</code> service is up, Jenkins can be configured to send email with <a href="https://wiki.jenkins-ci.org/display/JENKINS/Mailer">Mailer plugin</a>.
Mail server can be configured in <strong>Manage Jenkins</strong> page, <strong>E-mail Notification</strong> section.
Please visit <a href="http://www.nailedtothex.org/roller/kyle/entry/articles-jenkins-email">Kohei Nozaki&rsquo;s blog post</a> for more detailed instructions and screenshots.
We can also test the configuration by sending test e-mail in the same <strong>E-mail Notification</strong> section.</p>

<h3>Sending email from container</h3>

<p>Many Jenkins-based CI systems have been containerized and deployed on Kubernetes cluster (in conjunction with <a href="https://wiki.jenkins-ci.org/display/JENKINS/Kubernetes+Plugin">Kubernetes plugin</a>).
For email notifications in such CI systems, one option is to reuse <code>postfix</code> service, which is usually configured and ready on the Kubernetes nodes, and expose it to the Docker containers.</p>

<p>There are two changes need to be made on Postfix to expose it to Docker containers on one host.</p>

<ol>
<li>Exposing Postfix to the docker network, that is, Postfix must be configured to bind to localhost as well as the docker network.</li>
<li>Accepting all incoming connections which come from any Docker containers.</li>
</ol>


<p>Docker bridge (<code>docker0</code>) acts a a bridge between your ethernet port and docker containers so that data can go back and forth.
We achieve the first requirement by adding the IP of <code>docker0</code> to <code>inet_iterfaces</code>.</p>

<figure class='code'><figcaption><span>ifconfig example output</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[centos@dev-worker-1 ~]$ ifconfig
</span><span class='line'>docker0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1472
</span><span class='line'>        inet 172.22.91.1  netmask 255.255.255.0  broadcast 0.0.0.0
</span><span class='line'>        ether 02:42:88:5f:24:28  txqueuelen 0  (Ethernet)
</span><span class='line'>        RX packets 8624183  bytes 18891507332 (17.5 GiB)
</span><span class='line'>        RX errors 0  dropped 0  overruns 0  frame 0
</span><span class='line'>        TX packets 15891332  bytes 16911210191 (15.7 GiB)
</span><span class='line'>        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
</span><span class='line'>
</span><span class='line'>flannel0: flags=4305&lt;UP,POINTOPOINT,RUNNING,NOARP,MULTICAST&gt;  mtu 1472
</span><span class='line'>        inet 172.22.91.0  netmask 255.255.0.0  destination 172.22.91.0
</span><span class='line'>        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 500  (UNSPEC)
</span><span class='line'>        RX packets 10508237  bytes 7051646109 (6.5 GiB)
</span><span class='line'>        RX errors 0  dropped 0  overruns 0  frame 0
</span><span class='line'>        TX packets 15511583  bytes 18744591891 (17.4 GiB)
</span><span class='line'>        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0</span></code></pre></td></tr></table></div></figure>


<p>For the second requirement, the whole docker network as well as localhost should be added to <code>mynetworks</code>.
In our kubernetes setup, the docker network should be <code>flannel0</code> and its subnet&rsquo;s CIDR notation is added to the <code>mynetworks</code> line:</p>

<figure class='code'><figcaption><span>Modified "/etc/postfix/main.cf"</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class=''><span class='line'># See /usr/share/postfix/main.cf.bak for a commented, more complete version
</span><span class='line'>
</span><span class='line'>myhostname = dev-worker-1.example.com
</span><span class='line'>smtpd_banner = $myhostname ESMTP $mail_name
</span><span class='line'>biff = no
</span><span class='line'>
</span><span class='line'># appending .domain is the MUA's job.
</span><span class='line'>append_dot_mydomain = no
</span><span class='line'>
</span><span class='line'># Uncomment the next line to generate "delayed mail" warnings
</span><span class='line'>#delay_warning_time = 4h
</span><span class='line'>
</span><span class='line'>readme_directory = no
</span><span class='line'>
</span><span class='line'># TLS parameters
</span><span class='line'>smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
</span><span class='line'>smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
</span><span class='line'>smtpd_use_tls=yes
</span><span class='line'>
</span><span class='line'># See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
</span><span class='line'># information on enabling SSL in the smtp client.
</span><span class='line'>
</span><span class='line'>alias_maps = hash:/etc/aliases
</span><span class='line'>alias_database = hash:/etc/aliases
</span><span class='line'>myorigin = dev-worker-1.example.com
</span><span class='line'>mydestination = dev-worker-1.example.com, localhost.example.com, localhost
</span><span class='line'>relayhost = smtprelay-prd.example.com
</span><span class='line'>mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 172.22.0.0/16
</span><span class='line'>mailbox_size_limit = 0
</span><span class='line'>recipient_delimiter = +
</span><span class='line'>inet_interfaces = localhost, 172.22.91.1
</span><span class='line'>inet_protocols = all</span></code></pre></td></tr></table></div></figure>


<p>Note the differences in <code>inet_interfaces</code> and <code>mynetworks</code> from the last section.
One can simply enter the Docker container/Kubernetes pod to verify such setup.
Note that application <code>mailx</code> maybe not available in a container since we tend to keep the containers light-weight.
Instead, prepare a <code>sendmail.txt</code> file (based on <a href="http://docs.blowb.org/setup-host/postfix.html">this</a>) with the following SMTP commands and use <code>nc</code> to send out the email as shown below.</p>

<figure class='code'><figcaption><span>Send test email from container</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>mymac:k8s tdongsi$ kubectl --kubeconfig kubeconfig --namespace jenkins exec -it jenkins-8hgsn -- bash -il
</span><span class='line'>
</span><span class='line'>jenkins@jenkins-8hgsn:~/test$ cat sendmail.txt
</span><span class='line'>HELO x
</span><span class='line'>MAIL FROM: test@example.com
</span><span class='line'>RCPT TO: tdongsi@example.com
</span><span class='line'>DATA
</span><span class='line'>From: test@example.com
</span><span class='line'>To: $YOUR_EMAIL
</span><span class='line'>Subject: This is a test
</span><span class='line'>
</span><span class='line'>The test is successful
</span><span class='line'>
</span><span class='line'>.
</span><span class='line'>quit
</span><span class='line'>
</span><span class='line'>jenkins@jenkins-8hgsn:~/test$ nc 172.22.91.1 25 &lt;sendmail.txt
</span><span class='line'>220 dev-worker-1.eng.sfdc.net ESMTP Postfix
</span><span class='line'>250 dev-worker-1.eng.sfdc.net
</span><span class='line'>250 2.1.0 Ok
</span><span class='line'>250 2.1.5 Ok
</span><span class='line'>354 End data with &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;
</span><span class='line'>250 2.0.0 Ok: queued as 1EF9E60C34
</span><span class='line'>221 2.0.0 Bye</span></code></pre></td></tr></table></div></figure>


<p></p>

<p>For containerized Jenkins system, mail server can also be configured in same <strong>Manage Jenkins</strong> page, <strong>E-mail Notification</strong> section.
The only difference is the IP/hostname provided to <strong>SMTP server</strong> option.
Instead of providing the known SMTP server&rsquo;s IP and host, one should use the IP of <code>docker0</code>, as explained above.
In the case of many nodes in Kubernetes cluster with different <code>docker0</code> IP, the Docker container of Jenkins master should reside only on one host and <code>docker0</code>&rsquo;s IP on that host should be used.</p>

<h3>References</h3>

<ul>
<li><a href="http://www.nailedtothex.org/roller/kyle/entry/articles-jenkins-email">Standard email setup in Jenkins</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-ubuntu-14-04">Setup Postfix</a></li>
<li><a href="http://docs.blowb.org/setup-host/postfix.html">Configure Postfix for Docker Containers</a></li>
<li><a href="http://satishgandham.com/2016/12/sending-email-from-docker-through-postfix-installed-on-the-host/">More on Postfix for Docker Containers</a></li>
</ul>


<figure class='code'><figcaption><span>postfix version used in this post</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[tdongsi@dev-worker-1 ~]$ postconf -v | grep mail_version
</span><span class='line'>mail_version = 2.10.1</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Maven and Gradle Builds in Jenkinsfile]]></title>
    <link href="http://tdongsi.github.io/blog/2017/05/20/gradle-settings-in-jenkinsfile/"/>
    <updated>2017-05-20T23:27:08-07:00</updated>
    <id>http://tdongsi.github.io/blog/2017/05/20/gradle-settings-in-jenkinsfile</id>
    <content type="html"><![CDATA[<p>In this post, we will look into how to securely authenticate with Nexus for Maven and Gradle builds in Jenkins pipelines.
Nexus username and password should NOT be stored in plain text on Jenkins slaves or Docker images.
Instead, those credentials should be passed into Jenkins pipeline using <code>withCredentials</code> step.</p>

<!--more-->


<h3>Maven</h3>

<p>Maven builds in corporates usually use private repositories on Nexus, instead of public ones in Maven Central Repository.
To do that, we usually configure Maven to check Nexus instead of the default, built-in connection to Maven Central.
These configurations is stored in <em>~/.m2/settings.xml</em> file.</p>

<p>For authentication with Nexus and for deployment, we must <a href="https://books.sonatype.com/nexus-book/reference/_adding_credentials_to_your_maven_settings.html">provide credentials accordingly</a>.
We usually add the credentials into our Maven Settings in <em>settings.xml</em> file.</p>

<figure class='code'><figcaption><span>Example Credentials in settings.xml</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;settings&gt;</span>
</span><span class='line'>  <span class="nt">&lt;servers&gt;</span>
</span><span class='line'>    <span class="nt">&lt;server&gt;</span>
</span><span class='line'>      <span class="nt">&lt;id&gt;</span>nexus<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>      <span class="nt">&lt;username&gt;</span>deployment<span class="nt">&lt;/username&gt;</span>
</span><span class='line'>      <span class="nt">&lt;password&gt;</span>deployment123<span class="nt">&lt;/password&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/server&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/servers&gt;</span>
</span><span class='line'><span class="nt">&lt;/settings&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>However, for automated build and deployment in Jenkins pipelines, it is not safe to store credentials in plain text files.
Instead, one should store Nexus credentials as <a href="https://wiki.jenkins-ci.org/display/JENKINS/Credentials+Plugin">secrets in Jenkins</a> and pass them into Jenkinsfile using their IDs (<code>credentialsId</code>).
See <a href="https://support.cloudbees.com/hc/en-us/articles/203802500-Injecting-Secrets-into-Jenkins-Build-Jobs">this article</a> for the full picture of related plugins used for storing and passing secrets in Jenkins.</p>

<figure class='code'><figcaption><span>Nexus authentication for Maven in Jenkinsfile.</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>  <span class="n">withCredentials</span><span class="o">([</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">],</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>  <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">withEnv</span><span class="o">([</span>
</span><span class='line'>      <span class="s1">&#39;nexusPublic=https://nexus.example.com/nexus/content/groups/public/&#39;</span>
</span><span class='line'>    <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>      <span class="kt">def</span> <span class="n">xmlTemplate</span> <span class="o">=</span> <span class="n">readFile</span> <span class="n">templateFile</span>
</span><span class='line'>      <span class="n">String</span> <span class="n">xmlFile</span> <span class="o">=</span> <span class="n">transformXml</span><span class="o">(</span><span class="n">xmlTemplate</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusUsername</span><span class="o">,</span> <span class="n">env</span><span class="o">.</span><span class="na">nexusPassword</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>      <span class="n">String</span> <span class="n">tempFile</span> <span class="o">=</span> <span class="s1">&#39;temp.xml&#39;</span>
</span><span class='line'>      <span class="n">writeFile</span> <span class="nl">file:</span> <span class="n">tempFile</span><span class="o">,</span> <span class="nl">text:</span> <span class="n">xmlFile</span>
</span><span class='line'>
</span><span class='line'>      <span class="n">sh</span> <span class="s2">&quot;mvn -B clean build -s ${tempFile}&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>  <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The <a href="https://jenkins.io/doc/pipeline/steps/credentials-binding/">step <code>withCredentials</code></a> will not only provide a secure way of injecting secrets (e.g., Nexus credentials) into Jenkins pipeline, but also scrub away such sensitive information if we happen to print them out in log files.
<code>transformXml</code> is my Groovy function that generates the <code>settings.xml</code> from the redacted Maven settings.xml template (no credentials) and the provided Nexus credentials.</p>

<h4>Maven 3.0</h4>

<p>Since <strong>Maven 3.0</strong>, the above problem is made much easier since environment variables can be referred inside <code>settings.xml</code> file by using special expression <code>${env.VAR_NAME}</code>, based on <a href="https://maven.apache.org/settings.html">this doc</a>.
Nexus authentication for Maven 3.0 in Jenkins pipeline can be done as follows:</p>

<figure class='code'><figcaption><span>settings.xml in Maven 3.0</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;settings&gt;</span>
</span><span class='line'>  <span class="nt">&lt;servers&gt;</span>
</span><span class='line'>    <span class="nt">&lt;server&gt;</span>
</span><span class='line'>      <span class="nt">&lt;id&gt;</span>nexus<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>      <span class="nt">&lt;username&gt;</span>${env.MVN_SETTINGS_nexusUsername}<span class="nt">&lt;/username&gt;</span>
</span><span class='line'>      <span class="nt">&lt;password&gt;</span>${env.MVN_SETTINGS_nexusPassword}<span class="nt">&lt;/password&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/server&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/servers&gt;</span>
</span><span class='line'><span class="nt">&lt;/settings&gt;</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Passing Nexus credentials for Maven 3.0 in Jenkinsfile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>  <span class="n">withCredentials</span><span class="o">([</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;MVN_SETTINGS_nexusUsername&#39;</span><span class="o">],</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;MVN_SETTINGS_nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>  <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">withEnv</span><span class="o">([</span>
</span><span class='line'>      <span class="s1">&#39;nexusPublic=https://nexus.example.com/nexus/content/groups/public/&#39;</span>
</span><span class='line'>    <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s1">&#39;mvn -s settings.xml clean build&#39;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>  <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>However, note that it is still tricky even in Maven 3.0 since this is not always applicable, as noted in <a href="https://maven.apache.org/settings.html">the same doc</a>.</p>

<blockquote><p>Note that properties defined in profiles within the settings.xml cannot be used for interpolation.</p></blockquote>


<h3>Gradle</h3>

<p>In Gradle, Nexus authentication can be specified in both <code>build.gradle</code> and <code>gradle.properties</code> file, where <code>build.gradle</code> should be checked into VCS (e.g., git) while <code>gradle.properties</code> contains sensitive credentials information.</p>

<figure class='code'><figcaption><span>Example build.gradle</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">repositories</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">maven</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">credentials</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">username</span> <span class="n">nexusUsername</span>
</span><span class='line'>            <span class="n">password</span> <span class="n">nexusPassword</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>        <span class="n">url</span> <span class="o">{</span> <span class="n">nexusPublic</span> <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Example gradle.properties</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='properties'><span class='line'><span class="na">nexusUsername</span><span class="o">=</span><span class="s">myUsername</span>
</span><span class='line'><span class="na">nexusPassword</span><span class="o">=</span><span class="s">password123</span>
</span><span class='line'><span class="na">nexusPublic</span><span class="o">=</span><span class="s">https://nexus.example.com/nexus/content/groups/public/</span>
</span></code></pre></td></tr></table></div></figure>


<p>The default location of the <code>gradle.properties</code> file is <code>~/.gradle</code>.
This is due to the environment variable <code>GRADLE_USER_HOME</code> usually set to <code>~/.gradle</code>.
For custom location of <code>gradle.properties</code> (i.e., other than <code>~/.gradle</code>), ensure that the variable <code>GRADLE_USER_HOME</code> is set accordingly.</p>

<p>However, similar to Maven, for Jenkins pipeline automation, it is not safe to store credentials in plain text file <code>gradle.properties</code>, no matter how &ldquo;hidden&rdquo; its location is.
For that purpose, you should use the following Groovy code:</p>

<figure class='code'><figcaption><span>Nexus authentication for Gradle in Jenkinsfile.</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'>  <span class="n">withCredentials</span><span class="o">([</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusUsername&#39;</span><span class="o">],</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>  <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">withEnv</span><span class="o">([</span>
</span><span class='line'>      <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusPublic=https://nexus.example.com/nexus/content/groups/public/&#39;</span><span class="o">,</span>
</span><span class='line'>      <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusReleases=https://nexus.example.com/nexus/content/repositories/releases&#39;</span><span class="o">,</span>
</span><span class='line'>      <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusSnapshots=https://nexus.example.com/nexus/content/repositories/snapshots&#39;</span>
</span><span class='line'>    <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">sh</span> <span class="s1">&#39;./gradlew jenkinsBuild&#39;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>  <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note that, in Gradle, the solution is much simpler because Gradle respects properies set through environment variales.
Based on <a href="https://docs.gradle.org/current/userguide/build_environment.html">its doc</a>, if the environment variable name looks like <strong><em>ORG_GRADLE_PROJECT_prop=somevalue</em></strong>, then Gradle will set a <code>prop</code> property on your project object, with the value of <code>somevalue</code>.
Therefore, in <code>withCredentials</code> step, we specifically bind the secrets <code>nexusUsername</code> and <code>nexusPassword</code> to the environment variables <em>ORG_GRADLE_PROJECT_nexusUsername</em> and <em>ORG_GRADLE_PROJECT_nexusPassword</em> and not some arbitrary variable names.
These environment variables should match the ones used in <code>builde.gradle</code> and, in the following Closure, we simply call the standard Gradle wrapper command <code>./gradlew &lt;target&gt;</code>.
Compared with Maven solution in the last section, there is no intermediate step to generate <code>settings.xml</code> based on the provided secrets.</p>

<h3>More Tips</h3>

<p>If Maven/Gradle build is used in multiple repositories across organization, it is recommended to move the above Groovy code into shared Jenkins library, as shown in <a href="http://tdongsi.github.io/blog/2017/03/17/jenkins-pipeline-shared-libraries/">last post</a>.
For example, the Gradle builds can be simplified by defining <code>useNexus</code> step (see <a href="https://jenkins.io/doc/book/pipeline/shared-libraries/#defining-steps">here</a>) and adding it into the <a href="http://tdongsi.github.io/blog/2017/03/17/jenkins-pipeline-shared-libraries/">shared library <em>workflow-lib</em></a>.</p>

<figure class='code'><figcaption><span>vars/useNexus.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="nf">call</span><span class="o">(</span><span class="n">Closure</span> <span class="n">body</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">withCredentials</span><span class="o">([</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusUsername&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusUsername&#39;</span><span class="o">],</span>
</span><span class='line'>    <span class="o">[</span><span class="n">$class</span><span class="o">:</span> <span class="s1">&#39;StringBinding&#39;</span><span class="o">,</span> <span class="nl">credentialsId:</span> <span class="s1">&#39;nexusPassword&#39;</span><span class="o">,</span> <span class="nl">variable:</span> <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusPassword&#39;</span><span class="o">]</span>
</span><span class='line'>  <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">withEnv</span><span class="o">([</span>
</span><span class='line'>      <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusPublic=https://nexus.example.com/nexus/content/groups/public/&#39;</span><span class="o">,</span>
</span><span class='line'>      <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusReleases=https://nexus.example.com/nexus/content/repositories/releases&#39;</span><span class="o">,</span>
</span><span class='line'>      <span class="s1">&#39;ORG_GRADLE_PROJECT_nexusSnapshots=https://nexus.example.com/nexus/content/repositories/snapshots&#39;</span>
</span><span class='line'>    <span class="o">])</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">body</span><span class="o">()</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>After that, all the Gradle builds with Nexus authentication in Jenkinsfile will now be reduced to simply this:</p>

<figure class='code'><figcaption><span>Simplified Nexus authentication for Gradle in Jenkinsfile.</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">useNexus</span> <span class="o">{</span>
</span><span class='line'>  <span class="n">sh</span> <span class="s1">&#39;./gradlew jenkinsBuild&#39;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>As shown above, it will reduce lots of redundant codes for Gradle builds, repeated again and again in Jenkinsfiles across multiple repositories in an organizaiton.</p>

<h3>References</h3>

<ul>
<li><a href="https://support.cloudbees.com/hc/en-us/articles/203802500-Injecting-Secrets-into-Jenkins-Build-Jobs">Secrets in Jenkins build jobs</a></li>
<li><a href="https://docs.gradle.org/current/userguide/build_environment.html">Gradle build environment</a></li>
<li><a href="https://stackoverflow.com/questions/12749225/where-to-put-gradle-configuration-i-e-credentials-that-should-not-be-committe">Stackoverflow dicussion</a>: for older versions of Gradle.</li>
</ul>

]]></content>
  </entry>
  
</feed>
