<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nirajan Mahara</title>
    <description>The latest articles on DEV Community by Nirajan Mahara (@nirajan_mahara).</description>
    <link>https://dev.to/nirajan_mahara</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2451430%2F9752c3b6-a88c-4316-bc00-142cde5a9ec8.png</url>
      <title>DEV Community: Nirajan Mahara</title>
      <link>https://dev.to/nirajan_mahara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nirajan_mahara"/>
    <language>en</language>
    <item>
      <title>Building and Testing the Gemini API with CI/CD Pipeline</title>
      <dc:creator>Nirajan Mahara</dc:creator>
      <pubDate>Tue, 26 Nov 2024 02:48:16 +0000</pubDate>
      <link>https://dev.to/nirajan_mahara/building-and-testing-the-gemini-api-with-cicd-pipeline-1e7p</link>
      <guid>https://dev.to/nirajan_mahara/building-and-testing-the-gemini-api-with-cicd-pipeline-1e7p</guid>
      <description>&lt;h3&gt;
  
  
  Building and Testing the Gemini API with CI/CD Pipeline
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;In today’s fast-paced world of software development, efficiency, reliability, and scalability are key to delivering high-quality applications. One way to achieve this is by automating the software development lifecycle using Continuous Integration and Continuous Deployment (CI/CD). This technical blog explores the process of building, testing, and deploying a Gemini API using Flask and Docker, while also integrating a CI/CD pipeline using GitHub Actions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Introduction
&lt;/h4&gt;

&lt;p&gt;The Gemini API serves as a backend interface for a chatbot application that generates responses to specific prompts. This API is designed using Flask and interacts with the Google Gemini AI model, configured with specific instructions to handle particular domains of queries. By implementing a CI/CD pipeline, we can automate the build, testing, and deployment processes, ensuring a smoother and more reliable workflow for future updates to the application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In This blog I will walk through the setup and functionality of the Gemini API, how we tested it, and how we integrated it with GitHub Actions to automate the deployment process. Additionally, we'll explore how CI/CD enhances the efficiency of software delivery and helps catch potential issues early.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Setting Up the Gemini API
&lt;/h4&gt;

&lt;p&gt;The core of the project is the &lt;strong&gt;Gemini API&lt;/strong&gt;, a Flask-based web application that processes user input and generates responses using Google’s &lt;strong&gt;Gemini API&lt;/strong&gt;. The API interacts with the Gemini AI model, which is specifically configured to provide responses related to DevOps, Flask, Docker, and GitHub CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Here’s an overview of the functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User Input (Prompt Handling)&lt;/strong&gt;: When the user sends a request to the &lt;code&gt;/chat&lt;/code&gt; endpoint with a prompt (e.g., "How do I set up Docker for Flask?"), the API retrieves the prompt and sends it to the Gemini AI model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Configuration&lt;/strong&gt;: The model is configured with a system instruction that restricts the responses to a specific domain of knowledge (DevOps-related topics, in this case). This ensures that the chatbot only provides relevant answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generating Content&lt;/strong&gt;: The &lt;code&gt;generate_content()&lt;/code&gt; function sends the prompt to the Gemini AI model and returns the response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the relevant code snippet for generating content in &lt;code&gt;gemini_api.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;google.generativeai&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;genai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;instructions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are a DevOps Engineer, you must provide details about how to build, test, and deploy a flask application
in GitHub. You can answer questions about flask in Python, GitHub CI/CD pipeline and docker container.
Any question outside of this topic must be answered with a response as &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;I can only answer questions about flask,
GitHub CI/CD pipeline, and Docker container.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
Format your answer using markdown formatting and try to provide reasoning and details about your answer.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GenerativeModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-1.5-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z2dem87sxazsbn674a4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z2dem87sxazsbn674a4.png" alt="code snippet for generating content in  raw `gemini_api.py` endraw " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Testing the Gemini API
&lt;/h4&gt;

&lt;p&gt;Testing is a critical aspect of ensuring that the Gemini API works correctly and reliably. For this project, we employed both unit tests and integration tests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit Testing&lt;/strong&gt;: We wrote unit tests to verify that the &lt;code&gt;generate_content()&lt;/code&gt; function handles different types of prompts correctly. For example, we tested the API's response to valid prompts (e.g., "How do I deploy a Flask app with GitHub Actions?") and invalid prompts (e.g., "Tell me about quantum mechanics.").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Testing&lt;/strong&gt;: In addition to unit tests, we also wrote integration tests to ensure that the Flask routes behave as expected. For example, we tested the &lt;code&gt;/chat&lt;/code&gt; route to ensure it responds correctly to POST requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an example of the test for a valid prompt in &lt;code&gt;tests/test_gemini_api.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;gemini_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;generate_content&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestGeminiAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Load the environment variables in the setUp function
&lt;/span&gt;    &lt;span class="c1"&gt;# Since it's already defined in gemini_api.py, this function is not doing anything for us here
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;../.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_valid_prompt_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Test for a valid prompt within the topic
&lt;/span&gt;        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How do I deploy a Flask app with GitHub Actions?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# print(response)
&lt;/span&gt;        &lt;span class="c1"&gt;# check if "Flask" is part of the response
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Flask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# check if "GitHub" is part of the response
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_invalid_prompt_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Test for an invalid prompt within the topic
&lt;/span&gt;        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tell me about quantum mechanics.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# check if generic response is part of the response
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I can only answer questions about flask, GitHub CI/CD pipeline, and Docker container&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokp83aflfdhkzrf3ckzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokp83aflfdhkzrf3ckzh.png" alt="Testing the Gemini API" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0zecftmt8gwf3iz1z2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0zecftmt8gwf3iz1z2r.png" alt="API Request Using Postman with text valid prompt" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfrczpu5rfqo2wwvssgn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfrczpu5rfqo2wwvssgn.png" alt="API Request Using Postman with text invalid prompt" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also implemented tests for invalid prompts to ensure that the API returns the correct message when the user asks about topics outside the allowed scope.&lt;/p&gt;
&lt;h4&gt;
  
  
  Setting Up CI/CD with GitHub Actions
&lt;/h4&gt;

&lt;p&gt;CI/CD helps automate repetitive tasks such as building Docker images, running tests, and deploying the application. In this section, we will walk through the configuration of our CI/CD pipeline using &lt;strong&gt;GitHub Actions&lt;/strong&gt;.&lt;/p&gt;
&lt;h5&gt;
  
  
  Building the Docker Image
&lt;/h5&gt;

&lt;p&gt;We first create Docker images to containerize the application. A &lt;strong&gt;Dockerfile&lt;/strong&gt; defines the steps to build the application image. The &lt;strong&gt;Dockerfile&lt;/strong&gt; installs dependencies, copies the code into the container, and starts the Flask application:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use the official Python 3.11 slim image as the base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory in the container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /&lt;/span&gt;

&lt;span class="c"&gt;# Copy the contents of the current directory into the container&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Install the dependencies specified in the requirements.txt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Expose port 5001 (since we're running our app on this port)&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5001&lt;/span&gt;

&lt;span class="c"&gt;# Set the default command to run when the container starts&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "main.py"]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This Dockerfile will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build the image using Python 3.11 slim.&lt;/li&gt;
&lt;li&gt;Set the working directory to / and copy all the project files into the container.&lt;/li&gt;
&lt;li&gt;Install the dependencies listed in &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Expose port 5001 to make the Flask app accessible from outside the container.&lt;/li&gt;
&lt;li&gt;Set the default command to run the Flask app using &lt;code&gt;python main.py&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdo6rzcdciq7x4t4ztex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdo6rzcdciq7x4t4ztex.png" alt="Docker build" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;DockerfileTest&lt;/strong&gt; is used for testing purposes. It installs dependencies and runs unit tests:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use the official Python 3.11 slim image as the base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory inside the container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /&lt;/span&gt;

&lt;span class="c"&gt;# Copy the entire current directory into the container&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Install the dependencies specified in requirements.txt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Set the default command to run when the container starts: run unit tests&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "-m", "unittest", "discover", "-s", "tests"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This DockerfileTest will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build the image using Python 3.11 slim.&lt;/li&gt;
&lt;li&gt;Set the working directory to /.&lt;/li&gt;
&lt;li&gt;Copy all files from our project into the container.&lt;/li&gt;
&lt;li&gt;Install the dependencies listed in &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;By default, run unit tests from the &lt;code&gt;tests&lt;/code&gt; directory using the &lt;code&gt;unittest&lt;/code&gt; &lt;code&gt;discover&lt;/code&gt; command when the container starts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3od1dn78i1kcm29iui44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3od1dn78i1kcm29iui44.png" alt="dockerfiletest build" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuring GitHub Actions for Continuous Integration and Deployment
&lt;/h3&gt;

&lt;p&gt;GitHub Actions is a powerful tool for automating workflows in our repository. By configuring actions, we can automate tasks like testing, building, and deploying our code each time we push a change to our repository. Here, we will discuss how to configure GitHub Actions in the context of building, testing, and deploying Docker images using a simple CI/CD pipeline.&lt;/p&gt;
&lt;h4&gt;
  
  
  Overview of the Pipeline
&lt;/h4&gt;

&lt;p&gt;In this case, we define a workflow in &lt;code&gt;.github/workflows/pipeline.yml&lt;/code&gt;. This pipeline automates the process of building Docker images, running tests on them, and deploying them to a development environment. The pipeline consists of three main jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build Job:&lt;/strong&gt; This job is responsible for building the Docker images. It builds both the main application image and the test image, which will be used in subsequent jobs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test Job:&lt;/strong&gt; This job is responsible for running unit tests on the test Docker image. The test image runs tests against the application to ensure the code is functioning as expected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy Job:&lt;/strong&gt; After the tests are successfully run, the deploy job will deploy the main application Docker image to a development environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Structure of the GitHub Actions Workflow
&lt;/h3&gt;

&lt;p&gt;Let's break down the pipeline configuration:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_TAG_MAIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;geminiapi&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_TAG_TEST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;geminiapitest&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.0.0&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build_job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Docker Image&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gemini-win-runner&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Clone and checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Main Image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build -t ${{ env.IMAGE_TAG_MAIN }}:${{ env.IMAGE_VERSION }} -f Dockerfile .&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Test Image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;mv .dockerignore .dockerignore.temp&lt;/span&gt;
          &lt;span class="s"&gt;docker build -t ${{ env.IMAGE_TAG_TEST }}:${{ env.IMAGE_VERSION }} -f DockerfileTest .&lt;/span&gt;
          &lt;span class="s"&gt;mv dockerignore.temp .dockerignore&lt;/span&gt;

  &lt;span class="na"&gt;test_job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test Docker Image&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gemini-win-runner&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Unit testing&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker run --rm \&lt;/span&gt;
            &lt;span class="s"&gt;-e GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }} \&lt;/span&gt;
            &lt;span class="s"&gt;${{ env.IMAGE_TAG_TEST }}:${{ env.IMAGE_VERSION }}&lt;/span&gt;

  &lt;span class="na"&gt;deploy_job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Docker Image&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gemini-win-runner&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Remove Running Containers&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker rm -f ${{ env.IMAGE_TAG_MAIN }} || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Main Image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker run -d -p 5001:5001 -e GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }} --name ${{ env.IMAGE_TAG_MAIN }} ${{ env.IMAGE_TAG_MAIN }}:${{ env.IMAGE_VERSION }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Breakdown of the Pipeline Configuration
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. &lt;strong&gt;Triggering the Pipeline&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The pipeline is triggered on a &lt;strong&gt;push&lt;/strong&gt; event to the &lt;code&gt;main&lt;/code&gt; branch. This means that every time a commit is pushed to the &lt;code&gt;main&lt;/code&gt; branch, this workflow will automatically start.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;Environment Variables&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;We define some environment variables that are used throughout the workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IMAGE_TAG_MAIN&lt;/code&gt;: Tag for the main Docker image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IMAGE_TAG_TEST&lt;/code&gt;: Tag for the test Docker image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IMAGE_VERSION&lt;/code&gt;: Version of the Docker images (in this case, &lt;code&gt;v1.0.0&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_TAG_MAIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;geminiapi&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_TAG_TEST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;geminiapitest&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;Build Job&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Build Job&lt;/strong&gt; is responsible for building both the main and test Docker images. This job is executed on a GitHub-hosted runner using the &lt;code&gt;gemini-win-runner&lt;/code&gt; environment. It consists of the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Checkout Code:&lt;/strong&gt; The first step uses the &lt;code&gt;actions/checkout@v2&lt;/code&gt; action to clone the repository to the runner.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Clone and checkout&lt;/span&gt;
     &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build Main Image:&lt;/strong&gt; The second step builds the main Docker image using the &lt;code&gt;Dockerfile&lt;/code&gt;. The image is tagged using the &lt;code&gt;IMAGE_TAG_MAIN&lt;/code&gt; and &lt;code&gt;IMAGE_VERSION&lt;/code&gt; environment variables.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Main Image&lt;/span&gt;
     &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build -t ${{ env.IMAGE_TAG_MAIN }}:${{ env.IMAGE_VERSION }} -f Dockerfile .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build Test Image:&lt;/strong&gt; In the third step, the &lt;code&gt;.dockerignore&lt;/code&gt; file is temporarily renamed to ensure that the &lt;code&gt;docker build&lt;/code&gt; command uses the correct configuration for the test image. The test image is built using the &lt;code&gt;DockerfileTest&lt;/code&gt;, and then the &lt;code&gt;.dockerignore&lt;/code&gt; file is restored to its original state.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Test Image&lt;/span&gt;
     &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;mv .dockerignore .dockerignore.temp&lt;/span&gt;
       &lt;span class="s"&gt;docker build -t ${{ env.IMAGE_TAG_TEST }}:${{ env.IMAGE_VERSION }} -f DockerfileTest .&lt;/span&gt;
       &lt;span class="s"&gt;mv dockerignore.temp .dockerignore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  4. &lt;strong&gt;Test Job&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Test Job&lt;/strong&gt; runs unit tests on the test Docker image. The &lt;code&gt;docker run&lt;/code&gt; command executes the test image, passing the &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; from GitHub secrets as an environment variable. This job is also executed in the &lt;code&gt;gemini-win-runner&lt;/code&gt; environment.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;test_job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test Docker Image&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gemini-win-runner&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Unit testing&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;docker run --rm \&lt;/span&gt;
          &lt;span class="s"&gt;-e GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }} \&lt;/span&gt;
          &lt;span class="s"&gt;${{ env.IMAGE_TAG_TEST }}:${{ env.IMAGE_VERSION }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  5. &lt;strong&gt;Deploy Job&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Deploy Job&lt;/strong&gt; deploys the main Docker image to a development environment. It performs the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Remove Running Containers:&lt;/strong&gt; This step ensures that any previously running container with the same name (&lt;code&gt;geminiapi&lt;/code&gt;) is removed before starting the new container.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Remove Running Containers&lt;/span&gt;
     &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker rm -f ${{ env.IMAGE_TAG_MAIN }} || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deploy Main Image:&lt;/strong&gt; The main Docker image is then deployed, running the container in detached mode (&lt;code&gt;-d&lt;/code&gt;) and mapping port &lt;code&gt;5001&lt;/code&gt; on the host to &lt;code&gt;5001&lt;/code&gt; in the container. The &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; is passed as an environment variable for the container.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Main Image&lt;/span&gt;
     &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker run -d -p 5001:5001 -e GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }} --name ${{ env.IMAGE_TAG_MAIN }} ${{ env.IMAGE_TAG_MAIN }}:${{ env.IMAGE_VERSION }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This configuration defines a simple CI/CD pipeline in GitHub Actions that automates the process of building, testing, and deploying Docker images. The key jobs in the pipeline are the build, test, and deploy jobs. Each job ensures that the Docker images are properly built, tested, and deployed to the correct environment.&lt;/p&gt;

&lt;p&gt;With this configuration in place, we can ensure that every change pushed to the &lt;code&gt;main&lt;/code&gt; branch will trigger an automated process to build, test, and deploy our application, providing an efficient and streamlined workflow for continuous integration and deployment.&lt;/p&gt;
&lt;h4&gt;
  
  
  Benefits of Automation
&lt;/h4&gt;

&lt;p&gt;Implementing CI/CD automation streamlines the software development lifecycle by eliminating manual, repetitive tasks, reducing the risk of human error, and ensuring consistency across the build, test, and deployment stages. Every code change is automatically validated through predefined workflows, enhancing reliability and catching issues early in the pipeline. This results in faster feedback loops, enabling developers to identify and address problems quickly. Automation also accelerates the release process, allowing teams to deploy updates or fixes more frequently and efficiently. Ultimately, it fosters a more agile, scalable, and error-resilient development environment.&lt;/p&gt;
&lt;h4&gt;
  
  
  Challenges and Solutions
&lt;/h4&gt;

&lt;p&gt;Managing sensitive data, like the &lt;strong&gt;GEMINI_API_KEY&lt;/strong&gt;, posed a challenge as we needed to ensure its security while still making it accessible to the CI/CD pipeline. To address this, we utilized GitHub’s &lt;strong&gt;Secrets&lt;/strong&gt; feature, which securely stores sensitive information and allows us to reference it in workflows without exposing it in the codebase. This ensured that the API key remained protected while still being available for deployment and testing.&lt;/p&gt;

&lt;p&gt;Another challenge was maintaining consistency across test environments. To solve this, we leveraged Docker containers, ensuring that both the application and tests ran in an isolated, consistent environment. This approach eliminates discrepancies between local development setups and the CI/CD pipeline, guaranteeing that the tests produce reliable, reproducible results regardless of where they are executed.&lt;/p&gt;
&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;Building the Gemini API and setting up a CI/CD pipeline has been an invaluable learning experience, enabling me to gain practical knowledge in developing and deploying a Flask application. I was able to effectively integrate testing and automation, streamlining the build and deployment process using Docker and GitHub Actions. This not only improved efficiency but also reduced the risk of errors during the deployment phase.&lt;/p&gt;

&lt;p&gt;Looking ahead, there are numerous opportunities to enhance the project. I could introduce more comprehensive test cases to ensure better coverage, enhance the chatbot’s capabilities for more dynamic and accurate responses, and integrate advanced features such as user authentication and sophisticated natural language processing techniques.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In conclusion, CI/CD has proven to be an essential component of modern software development. It empowers teams to deliver high-quality features more quickly, with fewer bugs, and with increased reliability. This project has laid a strong foundation for further exploration into DevOps practices, containerization, and automated deployment, and has equipped me with the skills to continue refining and scaling software delivery processes in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabzfl6oc6fygzvkw2zxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabzfl6oc6fygzvkw2zxq.png" alt="Creately's CI/CD Pipeline Example" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://creately.com" rel="noopener noreferrer"&gt;Check out Creately's customizable CI/CD pipeline templates here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More Articles&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@nirajanmahara/mastering-ci-cd-for-the-gemini-api-building-testing-and-automating-deployment-a65ba9381f9c" class="ltag__link__link" rel="noopener noreferrer"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fda%3Atrue%2Fresize%3Afill%3A88%3A88%2F0%2AETh05O-rjI24byCa" alt="Nirajanmahara"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@nirajanmahara/mastering-ci-cd-for-the-gemini-api-building-testing-and-automating-deployment-a65ba9381f9c" class="ltag__link__link" rel="noopener noreferrer"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Mastering CI/CD for the Gemini API: Building, Testing, and Automating Deployment | by Nirajanmahara | Nov, 2024 | Medium&lt;/h2&gt;
      &lt;h3&gt;Nirajanmahara ・ &lt;time&gt;Nov 26, 2024&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fmedium-f709f79cf29704f9f4c2a83f950b2964e95007a3e311b77f686915c71574fef2.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
cover image courtesy: &lt;a href="https://shalb.com/blog/what-is-devops-and-where-is-it-applied/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://shalb.com/blog/what-is-devops-and-where-is-it-applied/" rel="noopener noreferrer"&gt;https://shalb.com/blog/what-is-devops-and-where-is-it-applied/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>api</category>
      <category>flask</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
