GitHub actions: How to improve your CI/CD workflow
Tags: github, devops
Published: 2020-04-24 18:31:53

[GitHub Actions](https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions) is one of the most powerful tools that you can use if you are storing our code on this service repository. The idea is simple, you can design a complex workflow using automated tasks (called actions) in order to be activated and run when a commit is done in your repository. 

The evolution of GitHub from a simple distributed git repository to something bigger is notorious. Tasks include cloud computing, so you can build images, push to different repositories, orchestrate cloud providers or whatever you need to do without any computing resources on your side, so any server or local machine is required. Even better, you can choose which kind of operating system will be used to run actions: Ubuntu Linux, Windows or Mac OS X.

GitHub has different official actions, but they also launched a Marketplace where you can find several actions created by the community, so the integration with your own workflow is quicker and easier than writing the whole code from the scratch. During the next steps will use both of them: official and community ones.

#How to start with Github Actions: Building Docker images, send them to Docker Hub and deploy them on AWS ECS.#

Imagine this scenario, you are developing your app using Docker containers, storing your code on GitHub, pushing your image on Docker Hub and deploying the app on AWS ECS to  be run on your cloud platform.

*Initial tip: By default, GitHub is going to notify you all workflow errors by email. This could be something useful in production environments but I really recommended to disable them if you are doing some tests.  To do it, go to your GitHub account settings -> Notifications and uncheck the Email box*.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lae04a2rtr7zda5v7scl.png)

First of all, upload a Dockerfile to your repository if you do not have one. Then, go to your GitHub repository and click on Actions. Depending on the content of your repository you will receive a different suggestion, build a Docker image, build a Go project or just the most popular continuous integration workflows. Skip the recommendations and go to **Set up a workflow yourself**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nu7fmvst58pioxpzslb6.png)

A new file will be created using the path your_repository/.github/workflows named by default main.yml but you can rename it as you prefer. Our first step will be publishing the image to Docker Hub so you can set `dockerhub.yml`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1q7nuwnpp8ug2sfwfvse.png)

The code is running a hello world sample, so you can remove all steps. As you can see, the right side window shows the marketplace and the documentation tabs. This will be there always you are editing an action file.

Go to the Search field box and type Docker and select **Publish Docker by elgohr**. This action is going to build our Dockerfile and push the built image to Docker Hub. You just need to copy the content, paste into your file and fill out the variables with your own values, to get something like that:

```
name: Build and Publish to Docker Hub
    on: [push]
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@master
        - name: Publish Docker
          uses: elgohr/Publish-Docker-Github-Action@2.11
          with:
            # The name of the image you would like to push
            name: cloudblog/githubactions:v7
            # The login username for the registry
            username: ${{ secrets.DOCKERHUB_USER }}
            # The login password for the registry
            password: ${{ secrets.DOCKERHUB_PASS }}
            # Use registry for pushing to a custom registry
            #registry: # optional
            # Use snapshot to push an additional image
            #snapshot: # optional
            # Use dockerfile when you would like to explicitly build a Dockerfile
            #dockerfile: Dockerfile
            # Use workdir when you would like to change the directory for building
            #workdir: # optional
            # Use buildargs when you want to pass a list of environment variables as build-args
            #buildargs: # optional
            # Use cache when you have big images, that you would only like to build partially
            #cache: # optional
            # Use tag_names when you want to push tags/release by their git name
            #tag_names: $(date +%s)
```

This action is going to get our Dockerfile, build the image cloudblog/githubactions:v7 and push it to Docker Hub. 

*Security tip: You should avoid writing your username and password on any file, even on private repositories, using secrets instead. You just need to store them on Settings → Secrets and reference them using the syntax:* 

`${{ secrets.MYSERVICE_PASS }}*`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/awmi1w6b2k08mxcb1x74.png)

**Working with AWS ECR & ECS**

Our second step will be to deploy the image to AWS ECR. We can add more steps on the same file or we can create a new file, depending on our preferences. As both processes are independent I prefer to create a different file to get two workflows, even the trigger will be the same (a commit), so both will be running in parallel. 

Go to **Actions** again and click **New Workflow** at your left. This time we are going to use an official action, so search for **Deploy to Amazon ECS** and click on **Set up this workflow**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ltc5ogqhlp80bbi3cw9x.png)

Yo just need to replace your own values related to your AWS environment following the instructions of the text. Then you need to copy the task definition file in json format to your Github repository. To get the required json code just open your task definition on AWS ECS and click on JSON tab:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/b0z6ps4cjbytir3n692y.png)

The final action file should be something similar to this one:

```
 on:
      push:
        branches:
          - master

    name: Deploy to Amazon ECS

    jobs:
      deploy:
        name: Deploy
        runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v1

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-west-1

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: Build, tag, and push image to Amazon ECR
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: githubactions
        IMAGE_TAG: latest
      run: |
        # Build a docker container and
        # push it to ECR so that it can
        # be deployed to ECS.
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"

    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: mytaskdef.json
        container-name: cloudblog
        image: ${{ steps.build-image.outputs.image }}

    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: ${{ steps.task-def.outputs.task-definition }}
        service: myapp
        cluster: cloudblog
        wait-for-service-stability: true
```

You can see several  actions are being called in the same workflow definition file. Most of the steps are references to another action like login or deploy the task definition but you can also run commands like the building images using the Docker cli.

Just save the file with the default name and commit the changes. Go to **Actions** and you will be able to see the output. If everything was fine you should get something like this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/q69iwqexguc1ie8mcs62.png)

If you get some errors, you can go to the details for every step and check the output:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/oekguei0gjp1x4vpa54j.png)

When everything is running fine, you should see your image on Docker Hub and AWS repositories and the app running in your AWS ECS environment.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lgcb1c46c57enr2i807e.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5yy6jvlfom4li824bnek.png)

To going further just edit your current action file and look for new actions (or create your own one). You can add more steps to your workflow like update a Lambda function, copy a file to a S3 bucket to generate a trigger on AWS or deploy to multiple cloud providers at the same time using Azure and GCP related actions.