    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: Piping Software for Less</title>
        <link>https://members.accu.org/index.php/articles/2832</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>




<div class="xar-mod-head"><span class="xar-mod-title">Design of applications and programs + CVu Journal Vol 32, #4 - September 2020</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c67/">Design</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c77/">CVu</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c414/">324</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/c67-414/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/articles/c67+414/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;Piping Software for Less</h1>
<p><strong>Author:</strong>&nbsp;Bob Schmidt</p>
<p>
<strong>Date:</strong> 06 September 2020 18:23:37 +01:00 or Sun, 06 September 2020 18:23:37 +01:00</p>
<p><strong>Summary:</strong>&nbsp;Paul Grenyer continues his mission to build a DevOps pipeline on a budget.</p>
<p><strong>Body:</strong>&nbsp;<p>Developing software is hard and all good developers are lazy. This is one of the reasons we have tools which automate practices like continuous integration, static analysis and measuring test coverage. The practices help us to measure quality and find problems with code early. When you measure something you can make it better. Automation makes it easy to perform the practices and means that lazy developers are likely to perform them more often, especially if theyâ€™re automatically performed every time the developer checks code in.</p>

<p>This is old news. These practices have been around for more than twenty years. They have become industry standards and not using them is, quite rightly, frowned upon. What is relatively new is the introduction of cloud based services such as BitBucket Pipelines, CircleCI and SonarCloud, which allow you to set up these practices in minutes â€“ however, this flexibility and efficiency comes with a cost.</p>

<table class="sidebartable">
	<tr>
		<td class="title">Definitions</td>
	</tr>
	<tr>
		<td>
			<table class="journaltable">
				<tr>
					<td>
						<p><strong>Continuous Integration</strong></p>
						<p>Continuous Integration (CI) is a development practice where developers integrate code into a shared repository frequently, preferably several times a day. Each integration can then be verified by an automated build and automated tests. While automated testing is not strictly part of CI it is typically implied. <a href="#1">[1]</a></p>
						
						<p><strong>Static Analysis</strong></p>
						<p>Static (code) analysis is a method of debugging by examining source code before a program is run. Itâ€™s done by analyzing a set of code against a set (or multiple sets) of coding rules. <a href="#2">[2]</a></p>
						
						<p><strong>Measuring Code Coverage</strong></p>
						<p>Code coverage is a metric that can help you understand how much of your source is tested. Itâ€™s a very useful metric that can help you assess the quality of your test suite. <a href="#3">[3]</a></p>
					</td>
				</tr>
			</table>
		</td>
	</tr>
</table>

<h2>Why</h2>

<p>While BitBucket Pipelines, CircleCI and SonarCloud have free tiers, there are limits.</p>

<p>With BitBucket Pipelines, you only get 50 build minutes a month on the free tier. The next step up is $15/month and then you get 2500 build minutes.</p>

<p>On the free CircleCI tier, you get 2500 free credits per week <a href="#4">[4]</a> but you can only use public repositories, which means anyone and everyone can see your code. The use of private repositories starts at $15 per month.</p>

<p>With SonarCloud, you can analyse as many lines of code as you like, but again you have to have your code in a public repository or pay $10 per month for the first 100,000 lines of code.</p>

<p>If you want continuous integration <em>and</em> a static analysis repository that includes test coverage <em>and</em> you need to keep your source code private, youâ€™re looking at a minimum of $15 per month for these cloud based solutions and thatâ€™s if you can manage with only 50 build minutes per month. If you canâ€™t, itâ€™s more likely to be $30 per monthâ€¦ thatâ€™s $360 per year.</p>

<p>Thatâ€™s not a lot of money for a large software company or even for a well funded startup or SME, although as the number of users goes up so does that price. For a personal project, itâ€™s a lot of money.</p>

<p>Cost isnâ€™t the only drawback, with these approaches you can lose some flexibility as well.</p>

<p>The alternative is to build your own development pipelines.</p>

<p>I bet youâ€™re thinking that setting up these tools from scratch is a royal pain and will take hours, when the cloud solutions can be set up in minutes. Not to mention running and managing your own pipeline on your personal machineâ€¦ and donâ€™t they suck resources when theyâ€™re running in the background all the time? And shouldnâ€™t they be set up on isolated machines? What if I told you that you could set all of this up in about an hour and turn it all on and off as necessary with a single command? And that if you wanted to, you could run it all on a DigitalOcean Droplet for around $20 per month. Interested? Read on.</p>

<h2>What</h2>

<p>When you know how, setting up a continuous integration server such as Jenkins and a static analysis repository such as SonarQube in a Docker container is relatively straightforward. As is starting and stopping them altogether using Docker Compose. As I said, the key is knowing how, and what I explain in the rest of this article is the product of around twenty development hours, a lot of which was banging my head against a number of individual issues which turned out to have really simple solutions.</p>

<h3>Docker</h3>

<p>Docker is a way of encapsulating software in a container. Anything from an entire operating system such as Ubuntu to a simple tool such as the scanner for SonarQube. The configuration of the container is detailed in a Dockerfile and Docker uses Dockerfiles to build, start and stop containers. Jenkins and SonarQube each have publicly available Docker images, which weâ€™ll use with a few relatively minor modifications to build a development pipeline.</p>

<h3>Docker Compose</h3>

<p>Docker Compose is a tool which orchestrates Docker containers. Via a simple YML file, it is possible to start and stop multiple Docker containers with a single command. This means that â€“ once configured â€“ we can start and stop the entire development pipeline so that it is only running when we need it or, via a tool such as Terraform, construct and provision a DigitalOcean droplet (or AWS service, etc.) with a few simple commands, and tear it down again just as easily so that it only incurs cost when weâ€™re actually developing. Terraform and DigitalOcean are beyond the scope of this article, but I plan to cover them in the near future.</p>

<p>See the Docker and Docker Compose websites for instructions on how to install them for your operating system.</p>

<table class="sidebartable">
	<tr>
		<td class="title">What is Terraform?</td>
	</tr>
	<tr>
		<td>
			<table class="journaltable">
				<tr>
					<td>
						<p>Terraform <a href="#5">[5]</a> is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions.</p>
						
						<p>Configuration files describe to Terraform the components needed to run a single application or your entire datacentre. Terraform generates an execution plan describing what it will do to reach the desired state, and then executes it to build the described infrastructure. As the configuration changes, Terraform is able to determine what changed and create incremental execution plans which can be applied.</p>
						
						<p>The infrastructure Terraform can manage includes low-level components such as compute instances, storage, and networking, as well as high-level components such as DNS entries, SaaS features, etc.</p>
					</td>
				</tr>
			</table>
		</td>
	</tr>
</table>

<h2>How</h2>

<p>In order to focus on the development pipeline configuration, Iâ€™ll describe how to create an extremely simple Dotnet Core class library with a very basic test and describe in more detail how to configure and run Jenkins and SonarQube Docker containers and setup simple projects in both to demonstrate the pipeline. Iâ€™ll also describe how to orchestrate the containers with Docker Compose.</p>

<p>Iâ€™m using Dotnet Core because thatâ€™s what Iâ€™m working with on a daily basis. The development pipeline can also be used with Java, Node, TypeScript or any other of the supported languages. Dotnet Core is also free to install and use on Windows, Linux and Mac which means that anyone can follow along.</p>

<h3>A simple Dotnet Core class library project</h3>

<p>Iâ€™ve chosen to use a class library project as an example for two reasons. It means that I can easily use a separate project for the tests, which allows me to describe the development pipeline more iteratively. It also means that I can use it as the groundwork for a future article which introduces the NuGet server Liget to the development pipeline.</p>

<p>Open a command prompt and start off by creating an empty directory and moving into it.</p>

<pre class="programlisting">
  mkdir messagelib
  cd messagelib</pre>

<p>Then open the directory in your favourite IDE, I like VSCode for this sort of project. Add a Dotnet Core appropriate <span class="filename">.gitignore</span> file and then create a solution and a class library project and add it to the solution:</p>

<pre class="programlisting">
  dotnet new sln
  dotnet new classLib --name Messagelib
  dotnet sln add Messagelib/Messagelib.csproj</pre>

<p>Delete <span class="filename">MessageLib/class1.cs</span> and create a new class file and class called <code>Message</code>:</p>

<pre class="programlisting">
  using System;
  namespace Messagelib
  {
    public class Message
    {
      public string Deliver()
      {
        return &quot;Hello, World!&quot;;
      }
    }
  }</pre>

<p>Make sure it builds with:</p>

<pre class="programlisting">
  dotnet build</pre>

<p>Commit the solution to a public git repository, or you can use the existing one in my bitbucket account here: <a href="https://bitbucket.org/findmytea/messagelib">https://bitbucket.org/findmytea/messagelib</a>.</p>

<p>A public repository keeps this example simple and, although I wonâ€™t cover it here, itâ€™s quite straightforward to add a key to a BitBucket or GitHub private repository and to Jenkins so that it can access them.</p>

<p>Remember that one of the main driving forces for setting up the development pipeline is to allow the use of private repositories without having to incur unnecessary cost.</p>

<h3>Run Jenkins in Docker with Docker Compose</h3>

<p>Why use Jenkins, I hear you ask. Well, for me the answers are simple: familiarity and the availability of an existing tested and officially supported Docker image. I have been using Jenkins for as long as I can remember.</p>

<p>The official image is here: <a href="https://hub.docker.com/r/jenkins/jenkins">https://hub.docker.com/r/jenkins/jenkins</a></p>

<p>After getting Jenkins up and running in the container weâ€™ll look at creating a â€˜Pipelineâ€™ with the Docker Pipeline plugin. Jenkins supports lots of different â€˜Itemsâ€™, which used to be called â€˜Jobsâ€™, but Docker can be used to encapsulate build and test environments as well. In fact this is what BitBucket Pipelines and CircleCi also do.</p>

<p>To run Jenkins Pipeline, we need a Jenkins installation with Docker installed. The easiest way to do this is to use the existing Jenkins Docker image from Docker Hub. Open a new command prompt and create a new directory for the development pipeline configuration and a sub directory called Jenkins with the Dockerfile in Listing 1 in it.</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting">
FROM jenkins/jenkins:lts

USER root
RUN apt-get update
RUN apt-get -y install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN add-apt-repository \
   &quot;deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable&quot;

RUN apt-get update
RUN apt-get install -y docker-ce docker-ce-cli containerd.io
RUN service docker start

# drop back to the regular jenkins user - good practice
USER jenkins
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 1</td>
	</tr>
</table>

<p>You can see that our <span class="filename">Dockerfile</span> imports the existing Jenkins Docker image and then installs Docker for Linux. The Jenkins image, like most Docker images, is based on a Linux base image.</p>

<p>To get Docker Compose to build and run the image, we need a simple <span class="filename">docker-compose.yml</span> file in the root of the development pipeline directory with the details of the Jenkins service (see Listing 2).</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting"><span class="dimmed">
version: '3'
services:
  jenkins:
    container_name: jenkins</span>
    build: ./jenkins/<span class="dimmed">
    ports:
      - &quot;8080:8080&quot;
      - &quot;5000:5000&quot;
    volumes:</span>
      - ~/.jenkins:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 2</td>
	</tr>
</table>

<p>Note the <code>build</code> parameter which references a sub directory where the Jenkins Dockerfile should be located. Also note the volumes. We want the builds to persist even if the container does not, so create a <span class="filename">.jenkins </span>directory in your home directory:</p>

<pre class="programlisting">
  mkdir ~/.jenkins</pre>

<p>Specifying it as a volume in <span class="filename">docker-compose.yml</span> tells the Docker image to write anything which Jenkins writes to <span class="filename">/var/jenkins_home</span> in the container to <span class="filename">~/.jenkins</span> on the host â€“ your local machine. If the development pipeline is running on a DigitalOcean droplet, spaces can be used to persist the volumes even after the droplet is torn down.</p>

<p>As well as running Jenkins in a Docker container weâ€™ll also be doing our build and running our tests in a Docker container. Docker doesnâ€™t generally like being run in a Docker container itself, so by specifying:</p>

<p><span class="filename">  /var/run/docker.sock</span></p>

<p> as a volume, the Jenkins container and the test container can be run on the same Docker instance.</p>

<p>To run Jenkins, simply bring it up with Docker compose:</p>

<pre class="programlisting">
  docker-compose up</pre>

<p>(To stop it again just use Ctrl+C.)</p>

<p>Make sure the first time that you note the default password. It will appear in the log like this:</p>

<pre class="programlisting">
  Jenkins initial setup is required. An admin user  has been created and a password generated.
  Please use the following password to proceed to  installation:
  &lt;password&gt;
  This may also be found at: /var/jenkins_home/  secrets/initialAdminPasswor</pre>

<p>To configure Jenkins for the first time open a browser and navigate to:</p>

<pre class="programlisting">
  http://localhost:8080</pre>

<p>Then:</p>

<ol>
	<li>Paste in the default password and click continue.</li>
	<li>Install the recommended plugins. This will take a few minutes. There is another plugin we need too which can be installed afterwards.</li>
	<li>Create the first admin user and click <em>Save &amp; Continue</em>.</li>
	<li>Confirm the Jenkins url and click <em>Save &amp; Finish</em>.</li>
	<li>Click <em>Start Jenkins</em> to start Jenkins.</li>
</ol>

<p>You now have Jenkins up and running locally in a Docker container!</p>

<p>To use Docker pipelines in Jenkins, we need to install the plugin. To do this:</p>

<ol>
	<li>Select <em><strong>Manage Jenkins</strong></em> from the left hand menu, followed by <em><strong>Manage Plugins</strong></em>.</li>
	<li>Select the <em><strong>Available</strong></em> tab, search for <em><strong>Docker Pipeline</strong></em> and select it.</li>
	<li>Click <em><strong>Download now and install after restart</strong></em>.</li>
	<li>On the next page, put a tick in the <em><strong>restart after download</strong></em> check box and wait for the installation and for Jenkins to restart. Then log in again.</li>
</ol>

<p>Next we need to create the Docker Pipeline for the Messagelib solution.</p>

<ol>
	<li>Select <em><strong>New Item</strong></em> from the left hand menu, enter <code>Messagelib</code> as the name, select <em><strong>Pipeline</strong></em> and click <em><strong>ok</strong></em>.</li>
	<li>Scroll to the <em><strong>Pipeline</strong></em> section and select <em><strong>Pipeline script from SCM</strong></em> from the <em><strong>Definition</strong></em> dropdown. This is because weâ€™re going to define our pipeline in a file in the Messagelib solution.</li>
	<li>From the <em><strong>SCM</strong></em> dropdown, select <em><strong>Git</strong></em> and enter the repository URL of the Messagelib solution. (See Figure 1.)</li>
	<li>Then click <em><strong>Save</strong></em>.</li>
</ol>

<table class="sidebartable">
	<tr>
		<td><img src="/content/images/journals/cvu32-4/Grenyer/Grenyer-01.png" /></td>
	</tr>
	<tr>
		<td class="title">Figure 1</td>
	</tr>
</table>

<p>Jenkins is now configured to run the Messagelib pipeline, but we need to tell it what to do by adding a text file called <span class="filename">Jenkinsfile</span> to the root of the Messagelib solution. (See Listing 3.)</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting">
/* groovylint-disable CompileStatic, GStringExpressionWithinString, LineLength */
pipeline
{
  agent
  {
    docker {image 
    'pjgrenyer/dotnet-build-sonarscanner:latest'}
  }
  stages
  {
    stage('Build &amp; Test')
    {
      steps
      {
        sh 'dotnet clean'
        sh 'dotnet restore'
        sh 'dotnet build'
      }
    }
  }
}</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 3</td>
	</tr>
</table>

<p>This very simple Groovy script tells the Jenkins pipeline to get the latest â€˜dotnet-build-sonarscannerâ€™ Docker image and then use it to clean, restore and build the dotnet project. â€˜dotnet-build-sonarscannerâ€™ is a Docker image I built and pushed to Docker Hub using the <span class="filename">Dockerfile</span> in Listing 4.</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting">
FROM mcr.microsoft.com/dotnet/core/sdk:latest AS build-env
WORKDIR /

RUN apt update
RUN apt install -y default-jre

ARG dotnet_cli_home_arg=/tmp
ENV DOTNET_CLI_HOME=$dotnet_cli_home_arg
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
ENV PATH=&quot;${DOTNET_CLI_HOME}/.dotnet/tools:${PATH}&quot;
ENV HOME=${DOTNET_CLI_HOME}

RUN dotnet tool install --global dotnet-sonarscanner
RUN chmod 777 -R ${dotnet_cli_home_arg}</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 4</td>
	</tr>
</table>

<p>This creates and configures a development environment for Dotnet Core and Sonar Scanner, which requires Java. (There is a way to use the Dockerfile directly, rather than getting it from Docker Hub.<a href="#6">[6]</a>)</p>

<p>Once the Jenkins file is added to the project and committed, set the build off by clicking <em><strong>Build now</strong></em> from the left hand menu of the MessageLib item. The first run will take a little while as the Docker image is pulled (or built). Future runs wonâ€™t have to do that and will be quicker. You should find that once the image is downloaded, the project is built quickly and Jenkins shows success.</p>

<h3>Run SonarQube in Docker with Docker Compose</h3>

<p>When I first started using Jenkins, there were plugins for lots of different static analysis tools which would generate reports as part of the build. Then SonarQube came along as both a central repository for static analysis results and as a tool for grouping multiple static analysis tools together. <a href="#7">[7]</a></p>

<p>SonarQube ships with a H2 in-memory database for evaluation purposes, but this isnâ€™t ideal for long term use. Fortunately itâ€™s very easy to run PostgreSQL in a Docker container and use that instead.</p>

<ol>
	<li>Create a <span class="filename">postgresql</span> directory at the root of your development pipeline project:
		<p><code>mkdir postgresql</code></p>
	</li>
	
	<li>Move into the directory and create a new file called <span class="filename">init-sonar.sql</span> with following content:
		<pre class="programlisting">
CREATE DATABASE sonar;
CREATE USER sonar WITH PASSWORD 'sonar';
GRANT ALL PRIVILEGES ON DATABASE sonar to sonar;</pre>
		<p>This file is used by the PostgreSQL Docker container to create an empty Sonar database. It is created the first time the container starts. You may wish to use a more secure password, but as weâ€™ll see shortly, the database will only be accessible to other containers on the same Docker host.</p>
	</li>
	
	<li>Create a <span class="filename">Dockerfile</span> in the <span class="filename">postgresql</span> directory:
		<pre class="programlisting">
FROM postgres:latest
COPY init-sonar.sql /docker-entrypoint-initdb.d/init-sonar.sql</pre>
		<p>The Dockerfile uses the official postgres image <a href="#8">[8]</a> and copies in the <span class="filename">init-sonar.sql</span> file.</p>
	</li>
	
	<li>Add the following to the <span class="filename">docker-compose.yml</span> file:
		<pre class="programlisting">
db:
build: ./postgresql/
environment:
  POSTGRES_PASSWORD: 3PdrAz6xWe5yErnQ
volumes:
  - ~/.postgresql/:/var/lib/postgresql/dat</pre>
		<p>To be able to build the postgresql image, you need to specify a root user password and configure a volume to write the data to your local machine. Note that there is no port (<code>-p</code>) argument. This is because, as weâ€™ll see, Docker containers running on the same host and started by Docker Compose are all added to the same network. So while the postgresql container is not accessible from the host or any remote machine, it is accessible to other Docker containers.</p>
	</li>
	
	<li>Create a directory on your local machine for the data:
<pre class="programlisting">
mkdir ~/.postgresql</pre>
	</li>
</ol>

<p>The easiest way to add SonarQube is directly into the <span class="filename">docker-compose.yml</span> file and configure it there as the Docker image does not need to be modified.</p>

<ol>
	<li>Add the information in Listing 5 to the <span class="filename">docker-compose.yml</span> file.
		<p>The SonarQube Docker image is used, the port, the database credentials and the volumes are specified. Note that the host section of the connection string is <code>db</code>, the name of the <span class="filename">docker-compose.yml</span> section which starts the postgresql database. This is how Docker images created on the same host by Docker Compose refer to each other.</p>
	</li>
	
	<li>Create directories on your local machine for the data and logs:
<pre class="programlisting">
mkdir ~/.sonar
mkdir ~/.sonar/data
mkdir ~/.sonar/logs</pre>
	</li>
	
	<li>SonarQube uses an embedded Elasticsearch, so if youâ€™re using Linux you need to configure your local machine with the Elasticsearch production mode requirements and File Descriptors configuration. To do this execute the following at the command line:
<pre class="programlisting">
sysctl -w vm.max_map_count=262144
sysctl -w fs.file-max=65536
ulimit -n 65536
ulimit -u 4096</pre>
		<p>If youâ€™re running on Windows, you donâ€™t need to do this.</p>
	</li>
</ol>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting">
sonar:
  container_name: sonar
  image: sonarqube:latest
  ports:
    - &quot;9000:9000&quot;
  environment:
    - SONARQUBE_JDBC_USERNAME=sonar
    - SONARQUBE_JDBC_PASSWORD=sonar
    - SONARQUBE_JDBC_URL=jdbc:postgresql:
      //db:5432/sonar
  volumes:
    - ~/.sonar/data:/opt/sonarqube/data
    - ~/.sonar/logs:/opt/sonarqube/logs
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 5</td>
	</tr>
</table>

<p>SonarQube is now ready to run, so run Docker Compose:</p>
<pre class="programlisting">
  docker-compose up</pre>

<p>The first time it runs, SonarQube will configure itself and create the database it needs. To see SonarQube running, open a browser and navigate to <code>http://localhost:9000</code>.</p>

<h3>Static analysis with SonarQube</h3>

<p>Now that SonarQube is up and running, we can create a project to hold the analysis results from Messagelib.</p>

<p>The default SonarQube username and password are both â€˜adminâ€™. This can be easily changed. Letâ€™s create a project so we can analyse Messagelib:</p>

<ol>
	<li>Login to SonarQube (user: admin, password: admin).</li>
	<li>Click <em><strong>Create new project</strong></em></li>
	<li>Enter <code>messagelib</code> as the Project Key and Display Name â€“ this is how SonarQube knows which project it is being asked to analyse.</li>
	<li>Click <em><strong>setup</strong></em>.</li>
	<li>Then ignore the rest of the process as we donâ€™t need it.</li>
</ol>

<p>Next we need to return to the Messsagelib solution and update the <span class="filename">Jenkinsfile</span> so that it scans the project build output (see Listing 6). You can see the new sections of the file highlighted in darker text.</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting"><span class="dimmed">
pipeline
{
  agent
  {
    docker
    {
      image 
      'pjgrenyer/dotnet-build-sonarscanner:latest'</span>
      args '--network host'<span class="dimmed">
    }
  }
  stages
  {</span>
    stage('Sonar setup')
    {
      steps
      {
        sh 'dotnet sonarscanner begin k:messagelib'
      }
    }<span class="dimmed">
    stage('Build &amp; Test')
    {
      steps
      {
        sh 'dotnet clean'
        sh 'dotnet restore'
        sh 'dotnet build'
        sh 'dotnet test'
      }
    }</span>
    stage('Sonar End')
    {
      steps
      {
        sh 'dotnet sonarscanner end'
      }
    }<span class="dimmed">
  }
}</span>
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 6</td>
	</tr>
</table>

<p>The Docker container which is used to run the build and tests isnâ€™t created by Docker Compose so we have to manually configure it to be on the host network so that it can talk to the SonarQube container by passing an extra argument:</p>

<pre class="programlisting">
  args '--network host'</pre>

<p>Before the build starts the SonarQube scanner needs to be started and configured with the project key for the SonarQube project:</p>

<pre class="programlisting">
  stage('Sonar setup')
  {
    steps
    {
      sh 'dotnet sonarscanner begin /k:messagelib'
    }
  }</pre>

<p>Recall that the SonarQube scanner is part of the â€˜dotnet-build-sonarscannerâ€™ image and therefore available to all builds which use it in a Docker pipeline. After the build, the SonarQube scanner needs to be stopped and told to send the results to SonarQube:</p>

<pre class="programlisting">
  stage('Sonar End')
  {
    steps
    {
      sh 'dotnet sonarscanner end'
    }
  }</pre>

<p>Check the Jenkinsfile changes into your repository and kick off the Jenkins item again and once itâ€™s finished you should see something like Figure 2 in SonarQube after you login.</p>

<table class="sidebartable">
	<tr>
		<td><img src="/content/images/journals/cvu32-4/Grenyer/Grenyer-02.png" /></td>
	</tr>
	<tr>
		<td class="title">Figure 2</td>
	</tr>
</table>

<p>Then if you drill down (click on the project name) you get more detail (see Figure 3).</p>

<table class="sidebartable">
	<tr>
		<td><img src="/content/images/journals/cvu32-4/Grenyer/Grenyer-03.png" /></td>
	</tr>
	<tr>
		<td class="title">Figure 3</td>
	</tr>
</table>

<p>If you then click on the number of â€˜code smellsâ€™ and then on one of the issues youâ€™ll get the details for the individual issues and their position in the code (see Figure 4).</p>

<table class="sidebartable">
	<tr>
		<td><img src="/content/images/journals/cvu32-4/Grenyer/Grenyer-04.png" /></td>
	</tr>
	<tr>
		<td class="title">Figure 4</td>
	</tr>
</table>

<p>Iâ€™ll leave fixing the issues as an exercise for the reader.</p>

<h3>Measuring test coverage with SonarQube</h3>

<p>As well as static analysis, SonarQube also collates test code coverage. While SonarQube supports a handful of different code coverage tools, the important thing is to get the format of the coverage output file to match one of the supported tools.</p>

<p>SonarQube is showing 0% code coverage. While this is an accurate figure of the coverage, itâ€™s not actually a reflection of any measuring. In order to measure test coverage, we need to write and run some tests and tell SonarQube scanner where to find the output and what format it is in. Letâ€™s start with a test.</p>

<p>Go to your Messagelib solution top level directory, create an XUnit test project, add it to the solution and add the Messagelib class library project as a dependency:</p>

<pre class="programlisting">
  dotnet new xunit --name MessagelibTest
  dotnet sln add MessagelibTest/  MessagelibTest.csproj
  dotnet add MessagelibTest/MessagelibTest.csproj  reference Messagelib/Messagelib.csproj</pre>

<p>Dotnet Core XUnit projects come with OpenCover <a href="#9">[9]</a> by default, but to generate a coverage report an extra package is needed. Change to the MessagelibTest directory and add the package:</p>

<pre class="programlisting">
  dotnet add package coverlet.msbuild --version 2.9.0</pre>

<p>Then switch back to the root directory of the solution.</p>

<p>Delete <span class="filename">Messagelib/UnitTest.cs</span> and create a new class file and class called <span class="filename">MessageTest</span> (see Listing 7).</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting">
using Messagelib;
using Xunit;

namespace MessagelibTest
{
  public class MessageTest
  {
    [Fact(DisplayName = &quot;Check that the correct delivery message is returned.&quot;)]
    public void DeliverMessage()
    {
      Assert.Equal(&quot;Hello, World!&quot;, 
        new Message().Deliver());
    }
  }
}
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 7</td>
	</tr>
</table>

<p>Check that the solution builds and the test passes by running:</p>

<pre class="programlisting">
  dotnet test</pre>

<p>at the command line. To generate the necessary output files and to tell SonarQube scanner where to find them we need to update the <span class="filename">Jenkinsfile</span> again (see Listing 8).</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting"><span class="dimmed">
stage('Sonar setup')
{
  steps
  {
    sh 'dotnet sonarscanner begin</span> /k:messagelib 
    /d:sonar.cs.opencover.reportsPaths=
    MessagelibTest/coverage.opencover.xml 
    /d:sonar.coverage.exclusions=&quot;**Test*.cs&quot;'<span class="dimmed">
  }
}</span>
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 8</td>
	</tr>
</table>

<p>Adding the highlighted arguments tells SonarQube scanner to look for a file called <span class="filename">coverage.opencover.xml</span> in the <span class="filename">MessagelibTest</span> directory and not to measure the test coverage on the test classes themselves (see Listing 9).</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting">
stage('Build &amp; Test')
{
  steps
  {
    sh 'dotnet clean'
    sh 'dotnet restore'
    sh 'dotnet build'
    sh 'dotnet test /p:CollectCoverage=true 
       /p:CoverletOutputFormat=opencover'
  }
}
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 9</td>
	</tr>
</table>

<p>Adding the highlighted arguments tells dotnet to generate test coverage and output the results in OpenCoverâ€™ format when it runs the tests.</p>

<p>Check the solution in, run the Jenkins item again and see the coverage reach 100% (Figure 5).</p>

<table class="sidebartable">
	<tr>
		<td><img src="/content/images/journals/cvu32-4/Grenyer/Grenyer-05.png" /></td>
	</tr>
	<tr>
		<td class="title">Figure 5</td>
	</tr>
</table>

<h2>Finally</h2>

<p>This brings us to the end. Iâ€™ve shown you:</p>

<ol>
	<li>How to build, configure and run a development pipeline with Docker, Docker Compose, Jenkins, SonarQube and Postgres.</li>
	
	<li>How to set up a Docker pipeline on Jenkins for continuous integration.</li>
	
	<li>How to perform static analysis and measure code coverage on a simple Dotnet Core project.</li>
</ol>

<p>In many cases this will be all small project developers need. However, thereâ€™s plenty more which can be done, including:</p>

<ol>
	<li>Building and running the development pipeline on DigitalOcean.</li>
	<li>Adding a NuGet server to publish private packages to.</li>
</ol>

<p>I hope this article has persuaded you that you can save money on your existing cloud based development pipelines and whetted your appetite for what can be achieved with Docker and Docker Compose.</p>

<p>Until next time... </p>

<h2>Acknowledgements</h2>

<p>Thank you to Jez Higgins, Neil Carroll, Philip Watson, Alex Scotton and Lauren Gwynn for inspiration, ideas and reviews!</p>

<h2>References</h2>

<p class="bibliomixed"><a id="1"></a>[1]	Continuous Integration: <a href="https://codeship.com/continuous-integration-essentials">https://codeship.com/continuous-integration-essentials</a></p>

<p class="bibliomixed"><a id="2"></a>[2]	Static Analysis: <a href="https://www.perforce.com/blog/sca/what-static-analysis">https://www.perforce.com/blog/sca/what-static-analysis</a></p>

<p class="bibliomixed"><a id="3"></a>[3]	Code Coverage: <a href="https://www.atlassian.com/continuous-delivery/software-testing/code-coverage">https://www.atlassian.com/continuous-delivery/software-testing/code-coverage</a></p>

<p class="bibliomixed"><a id="4"></a>[4]	CircleCI Credits: Credits are used to pay for your teamâ€™s usage based on machine type and size, and premium features like Docker layer caching. See <a href="https://circleci.com/pricing/">https://circleci.com/pricing/</a></p>

<p class="bibliomixed"><a id="5"></a>[5]	Terraform: <a href="https://www.terraform.io/intro/">https://www.terraform.io/intro/</a></p>

<p class="bibliomixed"><a id="6"></a>[6]	Using Dockerfile directly: <a href="https://www.jenkins.io/doc/book/pipeline/docker/">https://www.jenkins.io/doc/book/pipeline/docker/</a></p>

<p class="bibliomixed"><a id="7"></a>[7]	SonarQube official image: <a href="https://hub.docker.com/_/sonarqube/">https://hub.docker.com/_/sonarqube/</a></p>

<p class="bibliomixed"><a id="8"></a>[8]	Postgres official image: <a href="https://hub.docker.com/_/postgres">https://hub.docker.com/_/postgres</a></p>

<p class="bibliomixed"><a id="9"></a>[9]	OpenCover is a code coverage tool for .NET 2 and above with support for 32 and 64 processes and covering both branch and sequence points: <a href="https://github.com/OpenCover/opencover">https://github.com/OpenCover/opencoverâ€˜</a></p>

<p class="bio"><span class="author"><b>Paul Grenyer</b></span> Paul Grenyer is a husband, father, software consultant, author, testing and agile evangelist.</p>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
