The essentials about CI/CD and GitOps enabled by a fancy Jenkinsfile

How setting up a Jenkins pipeline looks like

There are a lot of developers working in a daily basis pushing code and deploying it to different environments. Sometimes they don't even notice the details, but how this code gets deployed? how a pipeline looks like to deploy into different environments? are we running enough code quality checks? what are the steps taken to build and deploy it?

To get a broader vision about how it can be done (and it's just one way of doing it), let's clarify a little bit more about what Jenkins, CI/CD and GitOps are and how a Jenkins pipeline looks like.

Jenkins, CI/CD and GitOps

As you probably know, Jenkins is a very popular and complete tool for managing your builds, deployments and scripts related to you CI/CD process.

CI/CD stands for continuous integration and continuous delivery that aim to deliver frequent software to end customers. Think about your whole application lifecycle where you implement new features, run different levels of testing and quality code stuff (such as lints, static code analysis, etc) and then integrate this new code into different environments without breaking anything.

Now GitOps comes tied together to the DevOps world, that brings the mindset of keeping on your Git/VCS all details about the actual implementation of your processes (like your pipelines and automation scripts), with all versions history and the most updated implementation. Going beyond, it also enables collaboration, compliance and an automated way to provision your infrastructure based on the versioned scripts (just like we do for applications).

Since we want to keep track of all the changes and most updated version of our application pipeline, Jenkins enables that by allowing you to use a Jenkinsfile to define each step taken from build to deploy.

A simple and effective example of how a Jenkinsfile looks like

Putting in simple words, by using a Jenkinsfile you will have flexibility to write all logic needed with declarative/scripted pipelines and mostly keep a single source of truth about how your deployments and CI/CD looks like with all history of changes of it.

As you can see below, there's no magic about how this pipeline is going to work. It implicitly uses Jenkins pre-configured items like tools, AWS environment variables, a default maven installation (mvn command) and that's it. Other than that, you can see each Stage and all it's Steps taken to run from end-to-end. There are no hidden details.

pipeline {
    agent any
    options {
        timeout(time: 10, unit: 'MINUTES') // you can set a global pipeline timeout
    }
    tools {
        jdk "jdk8" // you can use pre-configured Jenkins tools
    }
    environment {

        // using pre-configured Jenkins environment variables
        awsKey = "${env.AWS_ACCESS_KEY_ID}"
        awsSecretKey = "${env.AWS_SECRET_ACCESS_KEY}"
        awsRegion = "${env.AWS_DEFAULT_REGION}"

        isDevEnvironment = true
        shouldCreateTag = false
    }

    stages {
        stage('Initialize') {
            steps {
                script {
                    isBranchValid = ['master', 'feature/', 'hotfix/', 'bugfix/'].any { standard_branch -> env.BRANCH_NAME.startsWith(standard_branch) }
                    if (!is_branch_valid) {
                        error("You couldn't have a branch named like that!")
                    }
                }
            }
        }
        stage('Build') {
            options {
                timeout(time: 5, unit: 'MINUTES') // you can set a stage specific timeout
            }
            steps {
                sh 'mvn clean package -DskipTests -U'
            }
        }
        stage("Unit Tests") {
            steps {
                sh "mvn test"
            }
        }
        stage('Create tag') {
            when {
                expression { return env.shouldCreateTag }
            }
            steps {
                nextTag = createTag()
                echo "Created tag as [${nextTag}]!"
            }
        }
        stage("Deploy DEV") {
            when {
                expression { return env.isDevEnvironment }
            }
            steps {
                echo "Deploying to DEV..."
                deploy()
            }
        }

    }
    post {
        always {
            cleanWs() // post.always will run at the end
        }
    }
}

Boolean deploy() {
    // do some logic to deploy..
}

String createTag() {
    // logic to create a new Git tag..
}

After that, you will link your new Jenkins job to read the Jenkinsfile in your git repository, and then it will be turned in your actual pipeline:

image.png

Conclusions

There are plenty talks, sample code and walkthroughs over the internet about how you can setup your Jenkins pipelines. It comes down to first understanding what are the problems you are trying to solve and go for it.

Jenkins pipeline documentation is a great place to go deeper over details like pipeline syntax, declarative vs scripted pipelines, best practices and much more.