Working with Bitbucket Pipelines
Tags: node, webdev, beginners, devops
Published: 2021-10-17 12:25:28



##Hi Guys Good Day!

It's been a long time since I posted here there were a lot of changes in my life for over almost 2 years now, mostly because of the pandemic. But anyways, let's learn about Pipelines in Bitbucket.

Before that let's understand few concepts that you may have heard but dont understand.

###CI - Continuous Integration
is a software development practice where developers regularly merge their code changes into a central repository.

###CD - Continuous Delivery or Continuous Deployment

Continuous Delivery - is a software development practice where code changes are automatically prepared for a release to production

Continuous Deployment - every change that passes all stages of your production environment.

Basically, the difference between _Continuous Delivery_ and _Continuous Deployment_ is that the former releases our project in a non-production environment like testing or staging but also can be released in the production environment with a manual approval in the pipeline while the latter releases our project in the production environment automatically without a manual approval.

These two combined makes __CI/CD__ (CD can be interchangeable between _Continuous Delivery_ and _Continuous Deployment_) __CI/CD__ automate steps in your software delivery process, such as testing or building our application when someone pushes in the repository and also automates the release process in the specific environments after the test or build steps depending the configuration in your pipeline.

That's where __Bitbucket Pipelines__ comes into play.

A __Pipeline__ in Bitbucket helps as achieve building a __CI/CD__ in our application. All we need is a configuration file _bitbucket-pipelines.yml_. The Free Plan gives us 50 _build minutes_ which is enough for us. We will be deploying our project in __AWS ElasticBeanstalk__.


Before making the _bitbucket-pipelines.yml_ config file. We will install the packages that we will need in this demo. We will be using [Node.js](https://nodejs.org/en/) in our project.

Run this command in your command line. We will be initializing the node project and install the _express_ framework to build our api.
```cmd
 npm init -y && npm i express
```

_app.js_
```js
const express = require('express')
const app = express()

app.use(express.json())

app.get('/', (req, res) => {
  return res.send({ message: 'Hello World' })
})

app.all('*', (req, res) => {
  return res.status(404).send({ message: 'Not Found' })
})

module.exports = app
```

_server.js_
```js
const app = require('./app')
const port = process.env.PORT || 3000

app.listen(port, () => {
  console.log(`Server listening at port: ${port}`)
})
```

We also need to make some sample tests for our api. Install these packages to use for our testing.

```cmd
 npm i -D jest supertest
```

Make a directory for our testing.
```cmd
 mkdir test
```
Inside the _test_ folder make this file.

_app.test.js_
```js
const app = require("../app")
const request = require("supertest")

describe('request server', () => {
  it('should return with a status of 200 for the root path', (done) => {
    request(app)
      .get('/')
      .expect(200)
      .end(done)
  })

  it('should return with a status of 200 and the correct response', (done) => {
    request(app)
      .get('/')
      .expect(200)
      .expect((res) => {
        expect(res.body.message).toBe('Hello World')
      })
      .end(done)
  })

  it('should return with a status of 404 for an invalid path', (done) => {
    request(app)
      .get('/ddd')
      .expect(404)
      .end(done)
  })
})
```

_package.json_
```json
{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "jest",
    "start": "node server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "jest": "^27.2.5",
    "supertest": "^6.1.6"
  }
}
```


_bitbucket-pipelines.yml_
```yml
image: atlassian/default-image:2

pipelines:
  default:
    - step:
        name: "Install"
        image: node:12.13.0
        caches:
          - node
        script:
          - npm install
    - parallel:
        - step:
            name: "Test"
            image: node:12.13.0
            caches:
              - node
            script:
              - npm test
        - step:
            name: "Build zip"
            script:
              - apt-get update && apt-get install -y zip
              - zip -r application.zip . -x "node_modules/**"
            artifacts:
              - application.zip
    - step:
        name: "Deployment to Production"
        deployment: production
        script:
          - pipe: atlassian/aws-elasticbeanstalk-deploy:1.0.2
            variables:
              AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
              AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
              AWS_DEFAULT_REGION: $AWS_REGION
              APPLICATION_NAME: $APPLICATION_NAME
              ENVIRONMENT_NAME: $ENVIRONMENT_NAME
              ZIP_FILE: "application.zip"

```
Ok, I will explain our pipeline config. If you want to know more about _yaml_ files [here's](https://www.cloudbees.com/blog/yaml-tutorial-everything-you-need-get-started) a link that will help you to get started.




```yml
image: atlassian/default-image:2
```
This field specifies the __docker image__ that we will be running our build environment. You can see the list of valid values [here](https://support.atlassian.com/bitbucket-cloud/docs/use-docker-images-as-build-environments/).



```yml
pipelines:
  default:
```
This _pipelines_ field speaks for itself. The _default_ pipeline field run on every change on the repository or push. We can also use the _branches_ pipeline field to configure our pipeline to run in specific branch changes but in our case we will be using the [_default_](https://support.atlassian.com/bitbucket-cloud/docs/configure-bitbucket-pipelinesyml/#default). 




```yml
 - step:
        name: "Install"
        image: node:12.13.0
        caches:
          - node
        script:
          - npm install
```
This specifies a _build step_ in our pipeline. The _name_ field specifies the Name of the step. The _image_ field specifies a different docker image that we can use in this step. I am specifying a new image because this _atlassian/default-image:2_ has an older version of node installed. The _caches_ field specifies the list of dependencies that we need to cache every build so we can save time for future builds, it will only download the dependencies when the pipeline first runs and it will cached it after a successful build. The _script_ field specifies the list of scripts we need to run in this step. 

__Note:__ Steps are executed in the order that they appear in the config file.

```yml
  - parallel:
      - step:
          name: "Test"
          image: node:12.13.0
          caches:
            - node
          script:
              - npm test
      - step:
          name: "Build zip"
          script:
            - apt-get update && apt-get install -y zip
            - zip -r application.zip . -x "node_modules/**"
          artifacts:
            - application.zip
```
The _parallel_ field is really useful if you want to run a couple or a lot of steps at the same time. This will save you a lot of time and of course makes your build faster if the steps you run here don't rely on other steps. As you can see above we are running the __Test__ step and __Build zip__ that will make zip file that we can use to our last step. The _artifacts_ field specifies the output file or files of the step which in the __Build zip__ is the _application.zip_.


```yml
- step:
    name: "Deployment to Production"
    deployment: production
    script:
      - pipe: atlassian/aws-elasticbeanstalk-deploy:1.0.2
        variables:
          AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
          AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
          AWS_DEFAULT_REGION: $AWS_REGION
          APPLICATION_NAME: $APPLICATION_NAME
          ENVIRONMENT_NAME: $ENVIRONMENT_NAME
          ZIP_FILE: "application.zip"

```
Ok, we are in our last step. The _deployment_ field indicates the environment of this deployment, the only valid values are _production_, _staging_ and _test_. In our script, you can see that we have a _pipe_ field, we need the _pipe_ field to integrate to __ElasticBeanstalk__. Pipes are an amazing feature to work with third party services. If you see this syntax $VARIABLE this is __Repository Variables__, we can add dynamic configuration using __Repository Variables__, you can see this in __Repository Setting__ > __Pipelines__ > __Repository variables__, but first you need enable Pipelines which we will be talking a little bit later.

After this, you need to make repository in Bitbucket, you can name it whatever you want or make. Here's a gif on how to make a repo in BitBucket.
![repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fnjf6yhplvmdx8836ygb.gif)

Also we need to enable the pipeline. Here's a gif on how to enable the pipeline in Bitbucket.
![enable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zpv8kwrrqaqqakps9yq2.gif)

Adding Repository Variables.
![Repo Variables](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nht8qrotzofifs0hz4ni.gif)

And also we need to make a Application in ElasticBeanstalk.  Here's a gif on how to make a Application in ElasticBeanstalk.
![application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06ig6yo1ud23nm8fbxcx.gif)

And lastly, bear with me. We need to make a __AWS S3 Bucket__ to store our zip files. The name of the bucket must be in this format 
__(APPLICATION_NAME)-elasticbeanstalk-deployment__. The <APPLICATION_NAME> refers to the ElasticBeanstalk Application that we created earlier. The name of your bucket must be globally unique, this is a S3 constraint that we must follow, so you must application name must be really different because it's a part of the name our bucket.
![S3 bucket](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5inrhazc0utc0k4d1fk7.gif)

You need to initialize git in your project and also add the remote repository in Bitbucket as the origin.
```cmd
  git init 
  git remote add origin <your-repo-link>
  git add .
  git commit -m "Initial commit"
  git pull origin master 
  git push origin master
```

This is my finished pipeline. Sorry I can't post another gif file because of the maximum frames. 
![Pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vzt5af1bquydchuvkkp6.png)
By the way, if you notice the __#2__ this is the second time that my pipeline ran, the first time I encountered the S3 bucket PutObject error, basically the bucket didn't exist because it had a different name, the bucket that existed in my S3 had the name __demo-api-312-elasticbeanstalk-deployment__, it should have the name __demo-api-321-elasticbeanstalk-deployment__.
![Error pipeline bucket issue](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n9ljnmks782jhaxhcwsf.png)
So let's access our ElasticBeanstalk Environment.
![Eb environment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4dzanl4ore2yi4io2phs.gif)

Yey, it works. Even though we learned a lot, this still is basically simple, you might change the pipeline configuration base on your application needs. But anyways, One step at a time guys.

## Thank you guys for reading this post.
# Have a Nice Day :smiley:!.