Skip to content

Instantly share code, notes, and snippets.

@techyugadi
Last active May 3, 2020 01:51
Show Gist options
  • Save techyugadi/f72fed5860824401a05f5ddb0144b08b to your computer and use it in GitHub Desktop.
Save techyugadi/f72fed5860824401a05f5ddb0144b08b to your computer and use it in GitHub Desktop.

Continuous Integration Pipeline with Jenkins and Gitlab

This is the third part of the three-part gist for setting up a basic CI environment on a single machine (e.g., a laptop), for dev / test / proof-of-concept, etc.
This CI environment consists of:

  1. Gitlab (Community Edition) as a source-code repository
  2. Jenkins for build and continuous integration Gitlab cannot be used without a mail server (Postfix). So, we also set up Postfix.

Gist Breakup

This part of the three-part gist outlines the steps to create a pipeline in Jenkins wherein the steps of a Continuous Integration process are properly configured. Here we explain:

  • Reconfiguring Gitlab after installation
  • Creating users and project repos in Gitlab
  • Accessing Gitlab through SSH
  • Installing Jenkins and accessing the Jenkins dashboard

For reference, please follow the three links below:

Platform / Environment

Here we describe the setup steps on Ubuntu 19.10 platform (Ubuntu Desktop), allocated 5GB RAM and 4 CPUs.(We actually set this up on a Ubuntu Virtual Machine on VirtualBox, running on Windows.)
A bash-like shell will be needed to run commands for the set-up.

The setup on other Linux flavors will be very similar.

Note: In our setup, the machine name (hostname) was archlab and the OS user running the commands was techie, included in the sudoers list. You will find these two string tokens in the rest of the gist. You should be able to replace these with suitable values for your environment.

Software Version

In this set-up, the following versions have been used:
Gitlab: Community Edition 12.9.3
Jenkins: 2.222.1

Overall Goal

We want to try out Continuous Integration feature in Jenkins. For this purpose we want to:

  • Create a Java project repo in Gitlab CE
  • Create a webhook in Gitlab so that a notification is sent to Jenkins whenever any code changes are pushed into this repo
  • Create a CI pipeline in Jenkins that builds the project whenever any changes are pushed into this repo, using Gradle

Initial Project setup in Gitlab

Create an OS user named devusr on Ubuntru: sudo adduser devusr.
We will create a Java project in the home directory of devusr and build it using Gradle, just to be sure there are no compilation errors. Later, this project will be used for our CI experiment.

Install Gradle

Note that JDK is already installed on the local machine, since Jenkins depends on JDK.

Now download Gradle and make it available for all users on this machine.
The latest version of Gradle (v 6.3) can be downloaded from this link.
Extract it into the /opt folder, and create a symlink for convenience: sudo ln -s /opt/gradle-6.3 /opt/gradle.
Set appropriate ownership, e.g., sudo chown -R <your_OS_user>:<your_OS_group> /opt/gradle.

To enable all OS users to invoke gradle, create a file /etc/profile.d/gradle.sh with the following content:

export GRADLE_HOME=/opt/gradle
export PATH=$PATH:${GRADLE_HOME}/bin

Assign executable permissions to this file: sudo chmod +x /etc/profile.d/gradle.sh

Create a Project in Gitlab

Login as Administrator into Gitlab Dashboard (root user with administrator password) and create a new user named devusr.

From the local mail inbox (~/.mail/new) retrieve the URL for devusr to access Gitlab dashboard for the first time. Access this URL, and set a password for the Gitlab user named devusr.

Now, as devusr, access Gitlab dashboard, and create a new (empty) project named demoproj.

Back to a Ubuntu terminal, login as OS user named devusr: su - devusr
Enable SSH access for devusr to Gitlab repos: run ssh-keygen -t rsa -C "devusr@<your_machine_name>" and do not supply any passphrase.
Copy the SSH public key (from file ~/.ssh/id_rsa.pub) into the 'Add_keys page on Gitlab dashboard.

Back in the Ubuntu terminal, run the following commands:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa
ssh-add -l

Update the Project in Gitlab

Now as devusr we want to initialize and build a project in our home directory, by the same name (demoproj). Then we will push the files in the project into Gitlab.

Create a Gradle project

In the home directory (/home/devusr), create a sub-directory demoproj and navigate to the sub-directory: mkdir ~/demoproj & cd ~/demoproj

Now initialize the gradle project: gradle init. Choose the following options:

Select type of project to generate: <application>
Select implementation language: <java>
Select build script DSL: <groovy>
Select test framework: <JUnit4>
Project name (default: demoproj):
Source package (default: demoproj)

Create two Java files: src/main/java/demoproj/App.java and src/test/java/demoproj/AppTest.java.

  • Source Code:
// App.java
package demoproj;

public class App {
    public String getGreeting() {
        return "Hello world.";
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
    }
}

  • Source Code:
// AppTest.java
package demoproj;

import org.junit.Test;
import static org.junit.Assert.*;

public class AppTest {
    @Test public void testAppHasAGreeting() {
        App classUnderTest = new App();
        assertNotNull("app should have a greeting", classUnderTest.getGreeting());
    }
}

Now, a directory structure like the following is created:

demoproj/
    build.gradle
    gradle
        wrapper
            gradle-wrapper.jar
            gradle-wrapper.properties
    gradlew
    gradlew.bat
    settings.gradle
    src
        main
            java
                demoproj
                    App.java
            resources
        test
            java
                demoproj
                    AppTest.java
            resources

Actually there are some more hidden files, particularly, a .gitignore file suitable for a gradle project like this one. Run cat demoproj/.gitignore, and the output will be:

# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build

Ensure that the project builds without any compilation errors or test failures: run ./gradlew build.

Push the project to Gitlab Repo

Set up the environment for the Git client:

git config --global user.email "devusr@<your_machine_name>"
git config --global user.name "devusr"

First login into Gitlab over SSH: ssh -T git@<your_machine_name>.

Now we have a project named demoproj in the home directory of devusr, and an empty project repo by the same name in Gitlab. So we should not clone from the Gitlab repo, but add the contents of project into Gitlab, instead, as follows: \

git init
git remote add origin git@archlab:devusr/demoproj.git
git add .
git commit -m "first commit"
git push -u origin master

On this occasion, the git push will not trigger a build in Jenkins. But we will now set up the CI pipeline, so that any subsequent updates to the source code does trigger a build.

Set up a CI Pipeline

This is a multi-step process that involves configuration in both Gitlab and Jenkins.

Create a Build User

In response to a build trigger, Jenkins needs to clone a project repo from Gitlab. Therefore, Jenkins needs to access Gitlab using suitable credentials. A separate Gitlab user may be created solely for this purpose.

Login to Gitlab dashboard as Administrator (user root), and create a new Gitlab user named buildusr.
As usual, retrieve the URL for buildusr to access Gitlab dashboard for the first time, from your Postfix mailbox, and then set a Gitlab password for this user.

Build User Token

Jenkins will access Gitlab (to retrieve files from project repository) as buildusr (the Gitlab user created above). But it will access Gitlab through APIs, using a token. So, a token for buildusr has to be created in Gitlab first.

While logged into Gitlab as buildusr, click on the Profile icon on the top horizontal menu (extreme right), and then, from the left-hand panel select 'Access Tokens'.
Give your access token a name (say bldtoken), and select all the scopes. Then click on 'Create Personal Access Token'.
Copy and save the generated token, since it will have to be supplied into Jenkins UI.

Configure Jenkins Pipeline

Before creating a CI pipeline, we will plug in the access token for buildusr and create a _Jenkins_project.

Apply Gitlab Access Token in Jenkins UI

Log in to Jenkins Dashboard. From the left-hand menu, select 'Manage Jenkins' and then 'Configure System'.
Scroll down to the Gitlab section and apply the following settings:
Enable Authentication for /project endpoint: Uncheck
Connection name: gitcon (you can supply any name)
Gitlab Host URL: https://<your_machine_name>
Credentials: Click on 'Add' and then 'Jenkins'. From the pop-up:
Domain: Global Credentials (unrestricted)
Kind: Username with password
Scope: Global (Jenkins ..)
Username: buildusr
Password: Gitlab password for buildusr
ID and description can be left blank.
Click on the 'Add' button.
Click on the 'Advanced' button and check 'Ignore SSL certificate Errors'.
Click on 'Add' button at the end to save the credentials (Gitlab access token).

Create a Jenkins Project

Now we will create a Jenkins project within which a CI pipeline can be configured.

From the Jenkins dashboard, on the left hand top corner, click on the 'Jenkins' link and then 'New Item'.
Let Item name (i.e., project name) be demo, and select 'Freestyle Project'. Then click 'OK' button.

A Jenkins project named demo is created.

Install Required Plugins in Jenkins

For our application, we will install the Gradle plugin for Jenkins.

From Jenkins dashboard, click on Jenkins on the top left, and then on 'Manage Jenkins'. Then, click on 'Manage Plugins'.
From the list of available plugins select and install the Gradle plugin.

Create a CI pipeline

From the Jenkins dashboard, click on 'Jenkins' (top left corner), and from the list of projects shown, select demo. Then click on 'Configure' on the left side, in order to set up a pipeline.
The pipeline consists of several elements. Follow the settings applied for each part of the pipeline, below:

  • General: In this section, set the 'Gitlab connection' to gitcon, i.e., the connection created in Jenkins, using the Access Token of buildusr.

  • Source Code Management: Select the 'Git' radio-button.
    For 'Repository URL' specify : https://devusr@archlab/devusr/demoproj.git. This is the correct format for accessing the repo from Jenkins.
    For 'Credentials' click on 'Add' and then on 'Jenkins'. In the pop-up, specify the following:
    Domain: Global Credentials (unrestricted)
    Kind: Username with password
    Scope: Global (Jenkins ..)
    Username: buildusr
    Password: Gitlab password for buildusr
    ID and description can be left blank.
    Click on the 'Add' button.
    Click on the 'Advanced' button and check 'Ignore SSL certificate Errors'.
    Click on 'Add' button at the end to save the credentials (Gitlab access token).
    For 'Branches to build' specify */master

  • Build Triggers: Check 'Build when a change is pushed to GitLab'.
    Important: Note the GitLab webhook URL: http://<your_machine_name>:3030/project/demo This is required for creating webhook in Gitlab.
    For 'Enabled Gitlab triggers' check only 'Push Events'.
    Click on 'Advanced', and ensure that the following settings are configured:
    Check 'Enable [ci-skip]'
    Check 'Ignore WIP Merge Requests'
    Check 'Set build description to build cause (eg. Merge request or Git Push )'
    Allowed Branches: Select radio button 'Allow all branches to trigger this job'

  • Build: Select the radio button: 'Use Gradle Wrapper'.
    For 'Tasks' select 'Build' from the drop-down.

  • Post-build Actions: Enable the option: 'Delete workspace when build is done'.

Click on the 'Save' button to save the pipeline configuration.

Configure Gitlab Webhook

Back to the Gitlab UI, log in as the user (devusr) who created the Gitlab project named demoproj.
From the top horizontal menu select 'Projects' and then 'Your Projects'. from the list of projects, select devusr/demoproj.
Now from the left-hand panel, you can select 'Settings' and then 'Webhooks'. Apply the following settings:
URL: http://<your_machine_name>:3030/project/demo (i.e, the URL mentioned while configuring the pipeline in Jenkins - see above.) Trigger: Check 'Push events' only
Uncheck 'Enable SSL Verification'.

Now, whenever there is a git push into demoproj in Gitlab, a build should be triggered in the demo CI pipeline in Jenkins.

Test CI Functionality

Back to a Ubuntu terminal, as OS user devusr, make a small change in the demoproj source code:
In the file src/main/java/demoproj/App.java, change the greeting from: 'Hello world' to 'Hello new world'. Push the changes:

git add .
git commit -m "code changes"
git push -u origin master

From _Jenkins Dashboard_, click on demo on the list of projects. You will find a recently completed build with console output like the following:

Started by GitLab push by devusr
Running as SYSTEM
Building in workspace /var/lib/jenkins/workspace/demo
using credential fd5cf3f7-cbc9-4a6b-9764-fd48819e1fa6
Cloning the remote Git repository
Cloning repository https://devusr@archlab/devusr/demoproj.git
 > git init /var/lib/jenkins/workspace/demo # timeout=10
Fetching upstream changes from https://devusr@archlab/devusr/demoproj.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials https access
 > git fetch --tags --force --progress -- https://devusr@archlab/devusr/demoproj.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://devusr@archlab/devusr/demoproj.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://devusr@archlab/devusr/demoproj.git # timeout=10
Fetching upstream changes from https://devusr@archlab/devusr/demoproj.git
using GIT_ASKPASS to set credentials https access
 > git fetch --tags --force --progress -- https://devusr@archlab/devusr/demoproj.git +refs/heads/*:refs/remotes/origin/* # timeout=10
skipping resolution of commit remotes/origin/master, since it originates from another repository
 > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
 > git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10
Checking out Revision 9d7dc009ecc3cfe3a8e64e3a5b15c59431662276 (refs/remotes/origin/master)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 9d7dc009ecc3cfe3a8e64e3a5b15c59431662276 # timeout=10
Commit message: "code updates2"
 > git rev-list --no-walk 5b376c00b11feb3fac5cb2ab8fd076d302bbc1a2 # timeout=10
[Gradle] - Launching build.
[demo] $ /var/lib/jenkins/workspace/demo/gradlew build
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :jar
> Task :startScripts
> Task :distTar
> Task :distZip
> Task :assemble
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
> Task :check
> Task :build

BUILD SUCCESSFUL in 20s
7 actionable tasks: 7 executed
Build step 'Invoke Gradle script' changed build result to SUCCESS
[WS-CLEANUP] Deleting project workspace...
[WS-CLEANUP] Deferred wipeout is used...
[WS-CLEANUP] done
Finished: SUCCESS

Firewall Settings

For completeness, we specify the appropriate firewall settings: only HTTPS port 443 (nginx reverse-proxying Gitlab CE) and port HTTP 3030 (the port we set for Jenkins) should be accessible externally, apart from SSH access.

sudo ufw allow http
sudo ufw allow https
sudo ufw allow SSH
sudo ufw allow 3030

References

Gitlab CE documentation
Jenkins documentation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment