Learn how to get 70% more accurate AI Agent output
Download the free prompt guide
  • AI Agent EnvironmentsBetaIsolated environments to run agent fleets
  • Development EnvironmentsModern, Realistic, Self-service on K8s
  • Preview EnvironmentsShareable environments with every pull request
  • Test EnvironmentsRun tests directly in your development environments
  • Platform FeaturesOkteto Experience features and benefits
  • Build Development ExperiencesDevelopment experiences for your entire team

Automating the deployment of your development environment using Okteto Actions and GitHub Actions.

Nwani Victory works remotely as a software engineer, building scalable and sustainable software. Outside working hours, he doubles as a technical writer, creating technical articles focused on modern web technologies and public cloud providers.

Introduction

GitHub Actions provides developers with the functionality of building and automating the execution of their software development workflows directly in a project’s repository.

In this tutorial you'll learn how you can automate the workflow of an existing Flask API whose continuous integration process is managed using GitHub Actions. You'll learn about the GitHub actions created by Okteto and how you can use them to work with Okteto with a GitHub Action workflow.

Why automate your workflow?

The Okteto CLI does a fantastic job at simplifying the process of working with your Okteto resources. However, over time, executing commands to build and deploy your docker image to Okteto after major changes could become repetitive and tiring.

According to Google’s SRE practices, a way to eliminate the toil time resulting from such operations is to automate the execution of such commands through the use of a script, or an external automation service.

For this article, we'll be using GitHub Actions as an automation tool to build a docker image from merged code changes and deploy the docker image to Okteto using the available Okteto Actions.

Prerequisites

In order to follow along with this tutorial, it's expected that you satisfy the following requirements;

Step 1: Clone a sample Python Flask app

To get started, fork the sample application from its repository here. After forking the repository, execute the command below from your terminal to clone your forked copy of the repository to your host machine;

Replace the GITHUB_USERNAME in the URL below with your GitHub username to match the remote origin of the forked repository.

$ git clone https://github.com/{{GITHUB_USERNAME}}/okteto-flask-app

In the flaskr.py file, there are three API routes defined for performing a create, retrieve, and delete operation against a connected Couch database.

The cloned project also contains a Dockerfile and docker-compose.yml file that contains all defined steps and services needed to create a multi-stage build for this application.

To run this application, execute the docker-compose command below to build and run the application container that consists of a database and app service.

$ docker-compose up --build

To test the application above, execute the command below from a new terminal window to make a POST request to the /api/customer api route within the flask API using cURL which inserts a new document into the customer collection within the running Couch database through it’s RESTful Apiserver.

$ curl -X POST -d '{"name":"Victory Nwani","occupation":"Software Engineer"}'  -H 'Content-Type: application/json' http://localhost:5050/api/customer

To view the data inserted from the POST request above, execute the command below from your terminal to make a GET request to the /api/customer api route within the flask API using cURL which retrieves all documents in the customer collection within the couch database.

$ curl http://localhost:5050/api/customer

You can also work with the running couch database through the Fauxton web interface that comes by default with couchdb image at http://localhost:5984/_utils.

As defined in the environment field within the database service in the docker-compose.yml file, the couchdb username is couchdb-admin, while the password is couchdb-password. Feel free to change them to your own preferred secured values.

Step 2: Deploy Flask application To Okteto

Now that you have the cloned application working locally on your computer, you'll deploy the cloned version to Okteto to serve as a deployment for your development environment. The folder already contains a Dockerfile and a docker-compose.yml file that defines the services and steps needed to build the image for this application.

To begin the deployment from your terminal using the Okteto CLI, execute the command below to log in and create a session between your Okteto account and your local terminal;

$ okteto context

Next, build a docker image of the entire application using the docker-compose.yml file and deploy it to your Okteto namespace:

$ okteto deploy --build

Going through the resources listed in your Okteto dashboard, you would find the deployed application, and the two services specified in the docker-compose.yml file.

Step 3: Write unit tests for the Flask application

One important step within any continuous integration pipeline is to Test new commits made to the code source before a new release is pushed to the continuous deployment pipeline to avoid a regression.

To get started, execute the command below from your terminal to create a new git branch where you would create unit tests for the flask API.

$ git checkout -b feat/ci-pipeline

From your code editor, create a tests directory with a test_flask.py file within the okteto-flask-app project. This file would be used to test the three API endpoints within the flask application. Executing the commands below will create the test file, or you can alternatively use the editor interface to create the file.

$ mkdir tests && cd tests
$ touch test_flaskr.py

All HTTP requests that were to be made to the Couch Apiserver from the API routes were intercepted and mocked in the test suites using the Httpretty package.

Add the test suite in the code block below to test the default route handler that returns a response with some information about the REST API.

import os
from requests import get, post

STAGING_API_ENDPOINT = os.environ.get("STAGING_COUCHDB_URL")

def test_handle_default_route():
 request = get(STAGING_API_ENDPOINT)
 response = request.json()
 assert (response['status'] == "OK")
 assert 'description' in response

The test suite above asserts that a JSON response containing an “OK” status, and description field will be returned each time a request is made to the default route. Looking through the code block, you might have taken note of the STAGING_API_ENDPOINT variable whose value will be gotten from the STAGING_COUCHDB_URL environment variable. The STAGING_COUCHDB_URL environment variable will be set during the continuous flow, and will point to a staging version of the API within an Okteto namespace. You will learn more about this when setting up the GitHub actions flow.

Next, add the code block below into the test_flaskr.py file to test the route POST request handler within the api/customer endpoint by making a POST HTTP verb to insert a new document into the customer collection.

def test_handle_items_post():
    request = post('/api/customer', json={
           'name': 'John Mike',
           'occupation': 'Software Eng'
        })

    responseData = request.json()
    assert responseData['status'] == "USER CREATED"

The test case above asserts that a status field with a USER CREATED value was returned from the POST request to insert a test document into the customer collection. After the test case above is executed successfully, you can rightly expect that a sample document will exist within the customer collection. The next test case will use this sample document to assert that the GET handler is within the api/customer endpoint.

Add the code block below into the test_flaskr.py file to test the route handling all requests made to the api/customer route with a GET HTTP verb to fetch all documents within the customer collection.

import json

def test_handle_items_fetch():

    request = get('{}/api/customer'.format(STAGING_API_ENDPOINT))
    data = request.json()
    assert (data['status'] == 'OK')
    assert 'customers' in data
    assert '_id' in data['customers'][0]

When the test case above is executed, a GET request will be made to the GET handler within api/customer route. The test case will further run assertions against the JSON response to ensure that an OK status field was returned, alongside a list of customers.

At this point we have three test suites to test the three corresponding endpoints exposed within this API. In the next step you'll create a GitHub Actions workflow that runs them automatically.

Step 4: Automate deployments with a GitHub Actions workflow

With tests in place, you can create a GitHub Actions workflow that spins up an Okteto preview environment for every pull request and runs your tests against it. Okteto publishes a set of GitHub Actions for this; the two you'll use here are okteto/context, to authenticate against your Okteto instance, and okteto/deploy-preview, to deploy the preview environment.

Configure the required secrets

The workflow authenticates to your Okteto instance using two repository secrets:

  • OKTETO_TOKEN: an Okteto personal access token. See Personal Access Tokens for how to create one.
  • OKTETO_CONTEXT: the URL of your Okteto instance (for example, https://okteto.example.com).

Add both to your forked repository by following GitHub's encrypted secrets guide. GitHub provides GITHUB_TOKEN automatically.

Add the workflow

Create a .github/workflows/ci.yml file in your repository with the following content:

name: Okteto Flask REST API CI

on:
  pull_request:
    branches:
      - master

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set context to Okteto
        uses: okteto/context@latest
        with:
          url: ${{ secrets.OKTETO_CONTEXT }}
          token: ${{ secrets.OKTETO_TOKEN }}

      - name: Deploy preview environment
        uses: okteto/deploy-preview@latest
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          name: pr-${{ github.event.number }}
          timeout: 15m

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install python dependencies
        run: pip install -r requirements.txt

      - name: Run API tests against the preview environment
        run: pytest
        env:
          STAGING_COUCHDB_URL: https://api-pr-${{ github.event.number }}-your-namespace.okteto.example.com

Each time a pull request is opened against your default branch, this workflow authenticates to your Okteto instance, deploys an isolated preview environment for that pull request, and runs the test suite against it. The STAGING_COUCHDB_URL value points pytest at the endpoint Okteto generates for the preview, so your tests exercise a production-like deployment instead of a local mock.

To keep your instance tidy, Okteto can tear the preview down automatically when the pull request is closed, using the okteto/destroy-preview action in a companion workflow. The Preview Environments with GitHub Actions guide walks through the complete, maintained setup, including the cleanup workflow and the exact secret configuration.

Trigger the workflow

Commit and push your changes to a branch in your fork, then open a pull request against the default branch:

$ git add .
$ git commit -m "ci: add tests and Okteto preview workflow"
$ git push -u origin feat/ci-pipeline

When the pull request is opened, GitHub Actions runs the workflow: it deploys the preview environment and runs your tests against it. You can follow the run from the Actions tab of your repository, and Okteto adds the preview environment's endpoints to the pull request so you can review the running application before merging.

Conclusion

In this article you cloned a sample Python application, built and deployed a docker image of the application to Okteto, and automated testing against an Okteto preview environment for every pull request using a GitHub Actions workflow.

For the complete, maintained reference, including the preview cleanup workflow, see Preview Environments with GitHub Actions in the Okteto docs.

The sample application used in this tutorial is available on GitHub in this repository, for you to clone and use.

Nwani VictoryView all posts
The image features the text "Collaboration: Challenges Building Modern Apps" in bold white and blue letters. The background is dark with a subtle dotted pattern on the right side. To the right of the text is an illustration of three people working together to build or design a large interface, symbolizing teamwork in the development of modern applications. The overall theme emphasizes the importance and challenges of collaboration in the process of building contemporary software applications.The image features the text "Collaboration: Challenges Building Modern Apps" in bold white and blue letters. The background is dark with a subtle dotted pattern on the right side. To the right of the text is an illustration of three people working together to build or design a large interface, symbolizing teamwork in the development of modern applications. The overall theme emphasizes the importance and challenges of collaboration in the process of building contemporary software applications.

How Developers Can Seamlessly Collaborate When Building Microservice Apps

Building microservices based applications is inherently challenging. Given the multitude of components involved, it is unrealistic to expect any individual...

Avatar of Arsh SharmaAvatar of Arsh SharmaArsh Sharma
The image features the text "Slash Your K8s Costs" in bold green, white, and blue letters. The background is dark with subtle, wavy patterns. On the left side of the text is the Kubernetes logo, a white ship's wheel inside a blue hexagon. The design focuses on the concept of reducing costs associated with Kubernetes (K8s), a popular platform for automating deployment, scaling, and operations of application containers. The overall theme is about optimizing financial efficiency in Kubernetes operations.The image features the text "Slash Your K8s Costs" in bold green, white, and blue letters. The background is dark with subtle, wavy patterns. On the left side of the text is the Kubernetes logo, a white ship's wheel inside a blue hexagon. The design focuses on the concept of reducing costs associated with Kubernetes (K8s), a popular platform for automating deployment, scaling, and operations of application containers. The overall theme is about optimizing financial efficiency in Kubernetes operations.

Slash Your Kubernetes Costs During Development

Using Kubernetes can be quite costly, to be frank. Regardless of the cloud provider you choose for your cluster, there are expenses associated with the...

Avatar of Arsh SharmaAvatar of Arsh SharmaArsh Sharma