The essentials about CI/CD and GitOps enabled by a fancy Jenkinsfile
Published: 2022-06-14 19:29:09

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](https://www.jenkins.io/) for managing your builds, deployments and scripts related to you CI/CD process.

[CI/CD stands for *continuous integration* and *continuous delivery*](https://www.redhat.com/en/topics/devops/what-is-ci-cd) 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](https://about.gitlab.com/topics/gitops/), 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 description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ab3bgqtvt7bvey8wbn9.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](https://www.jenkins.io/doc/book/pipeline/) is a great place to go deeper over details like *pipeline syntax*, *declarative vs scripted pipelines*,  *best practices* and much more.

-----------------------------------------------------------

* [_This was first posted by me on Hashnode tech blogging platform_.](https://rodrigocprates.hashnode.dev/jenkins-pipeline-essentials)