<?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: Yousuf Kalim</title>
    <description>The latest articles on DEV Community by Yousuf Kalim (@yousufkalim).</description>
    <link>https://dev.to/yousufkalim</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%2F761812%2F5982ba81-4398-45e0-b0d6-ae3d7bfe0ac3.JPG</url>
      <title>DEV Community: Yousuf Kalim</title>
      <link>https://dev.to/yousufkalim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yousufkalim"/>
    <language>en</language>
    <item>
      <title>Use GitHub Actions to Automate Your CI/CD for React and Node.js Applications on Ubuntu</title>
      <dc:creator>Yousuf Kalim</dc:creator>
      <pubDate>Thu, 18 May 2023 09:42:19 +0000</pubDate>
      <link>https://dev.to/yousufkalim/use-github-actions-to-automate-your-cicd-for-react-and-nodejs-applications-on-ubuntu-5555</link>
      <guid>https://dev.to/yousufkalim/use-github-actions-to-automate-your-cicd-for-react-and-nodejs-applications-on-ubuntu-5555</guid>
      <description>&lt;h2&gt;
  
  
  Introduction:
&lt;/h2&gt;

&lt;p&gt;Continuous Integration and Continuous Deployment (CI/CD) have become crucial aspects of modern software development. By automating the build, testing, and deployment processes, developers can save time, reduce errors, and deliver high-quality applications. GitHub Actions, a powerful feature provided by GitHub, allows developers to automate these processes easily. In this article, we will explore how to set up GitHub Actions for CI/CD of React and Node.js applications on an Ubuntu environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;p&gt;Before diving into setting up GitHub Actions, ensure that you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub account with a repository for your React or Node.js application.&lt;/li&gt;
&lt;li&gt;A basic understanding of Git and GitHub workflows.&lt;/li&gt;
&lt;li&gt;An Ubuntu-based machine or a virtual machine to run the CI/CD pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up the Environment:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Start by creating a new directory on your Ubuntu machine to store the necessary files for the CI/CD pipeline.&lt;/li&gt;
&lt;li&gt;Install Node.js and npm (Node Package Manager) if they are not already installed. You can do this by running the following command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nodejs npm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring GitHub Actions:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your GitHub repository and click on the "Actions" tab.&lt;/li&gt;
&lt;li&gt;Click on the "Set up a workflow yourself" button to create a new workflow file.&lt;/li&gt;
&lt;li&gt;Choose a name for the file, such as ci-cd.yml, and update the contents as follows:
&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="c1"&gt;# This workflow is a Continuous Integration/Continuous Deployment (CI/CD) pipeline for deploying a Node.js backend application&lt;/span&gt;
&lt;span class="c1"&gt;# to a remote ubuntu server.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Here are some comments to make it easier for everyone to use:&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# 1. The workflow uses environment variables to set the application name, component name, and release path.&lt;/span&gt;
&lt;span class="c1"&gt;#    Ensure that these are set correctly according to your application's configuration.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# 2. The workflow runs on a push to the main branch and can be manually triggered with inputs to specify the target environment.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# 3. It's important to note that this workflow uses several secrets that need to be set up&lt;/span&gt;
&lt;span class="c1"&gt;#    in order for it to work properly. You will need to create the following secrets in your repository settings:&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#    3.1. REMOTE_SERVER: the IP address or domain name of the remote server you are deploying to.&lt;/span&gt;
&lt;span class="c1"&gt;#    3.2. REMOTE_USER: the username you use to SSH into the remote server (root user is recommended).&lt;/span&gt;
&lt;span class="c1"&gt;#    3.3. REMOTE_KEY: the SSH private key you use to authenticate with the remote server.&lt;/span&gt;
&lt;span class="c1"&gt;#    3.4. ENV_FILE: the contents of your application's .env file. This should be a single-line string with each&lt;/span&gt;
&lt;span class="c1"&gt;#         environment variable separated by spaces.&lt;/span&gt;
&lt;span class="c1"&gt;#    3.5. NGINX_CONFIG: the contents of your NGINX configuration file. This should be a multi-line string with each line&lt;/span&gt;
&lt;span class="c1"&gt;#         separated by a newline character.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Overall, this workflow provides a good starting point for deploying a Node.js backend application to a remote server using GitHub Actions.&lt;/span&gt;
&lt;span class="c1"&gt;# However, it should be customized according to your application's specific requirements.&lt;/span&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CI/CD'&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;APP_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;node'&lt;/span&gt;
  &lt;span class="na"&gt;COMPONENT_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;backend'&lt;/span&gt;
  &lt;span class="na"&gt;RELEASE_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/root/node/backend&lt;/span&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;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&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;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&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;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# The first step checks out the code from the repository.&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;Checkout code&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="c1"&gt;# The second step sets up Node.js with version 16.&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;Setup Node.js&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/setup-node@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;

      &lt;span class="c1"&gt;# The third step transfers the code to the remote server using the Secure Copy (SCP) protocol.&lt;/span&gt;
      &lt;span class="c1"&gt;# The source directory is set to .,!./uploads, which excludes the uploads directory from being transferred.&lt;/span&gt;
      &lt;span class="c1"&gt;# Ensure that this is set correctly according to your application's file structure.&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;Transfer files to server&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;appleboy/scp-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SERVER }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;rm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.,!./uploads'&lt;/span&gt;
          &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.RELEASE_PATH }}/build&lt;/span&gt;

      &lt;span class="c1"&gt;# The fourth step creates shared folders for the uploads directory and the node_modules directory and creates symbolic links&lt;/span&gt;
      &lt;span class="c1"&gt;# to these folders in the current release directory. This helps to ensure that the uploads and node_modules directories&lt;/span&gt;
      &lt;span class="c1"&gt;# persist across releases and are not overwritten.&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;Create shared folders&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;appleboy/ssh-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SERVER }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;cd ${{ env.RELEASE_PATH }}&lt;/span&gt;
            &lt;span class="s"&gt;mkdir -p shared/uploads&lt;/span&gt;
            &lt;span class="s"&gt;mkdir -p shared/node_modules&lt;/span&gt;
            &lt;span class="s"&gt;ln -sfn ${{ env.RELEASE_PATH }}/shared/uploads ${{ env.RELEASE_PATH }}/build/uploads&lt;/span&gt;
            &lt;span class="s"&gt;ln -sfn ${{ env.RELEASE_PATH }}/shared/node_modules ${{ env.RELEASE_PATH }}/build/node_modules&lt;/span&gt;

      &lt;span class="c1"&gt;# The fifth step copies configuration files to the remote server, including the .env file and an NGINX configuration file.&lt;/span&gt;
      &lt;span class="c1"&gt;# The NGINX configuration file is used to proxy requests to the Node.js backend application.&lt;/span&gt;
      &lt;span class="c1"&gt;# Ensure that this is set correctly according to your application and server's configuration.&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;Copy config files&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;appleboy/ssh-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SERVER }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo '${{ secrets.ENV_FILE }}' &amp;gt; ${{ env.RELEASE_PATH }}/build/.env&lt;/span&gt;
            &lt;span class="s"&gt;echo '${{ vars.NGINX_CONFIG }}' &amp;gt; ${{ env.RELEASE_PATH }}/build/nginx.conf&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl restart nginx&lt;/span&gt;

      &lt;span class="c1"&gt;# The sixth step installs dependencies, builds the application.&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;Install dependencies&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;appleboy/ssh-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SERVER }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;cd ${{ env.RELEASE_PATH }}/build&lt;/span&gt;
            &lt;span class="s"&gt;yarn install&lt;/span&gt;
            &lt;span class="s"&gt;yarn run build&lt;/span&gt;

      &lt;span class="c1"&gt;# The seventh step starts it with PM2.&lt;/span&gt;
      &lt;span class="c1"&gt;# PM2 is a process manager for Node.js applications that helps to ensure that the application runs continuously&lt;/span&gt;
      &lt;span class="c1"&gt;# and can be easily managed. Ensure that this is set correctly according to your application's dependencies and build process.&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;Start PM2&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;appleboy/ssh-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_SERVER }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.REMOTE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;cd ${{ env.RELEASE_PATH }}/build&lt;/span&gt;
            &lt;span class="s"&gt;pm2 delete -s ${{ env.APP_NAME }} || :&lt;/span&gt;
            &lt;span class="s"&gt;pm2 start dist/index.js --name "${{ env.APP_NAME }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;In the above configuration, we set up the sample workflow for Node.js/Express to trigger on pushes to the &lt;code&gt;main&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;build&lt;/code&gt; job checks out the repository, sets up Node.js version 16, transfer files to the server, installs dependencies, creates the build and restart PM2 service.&lt;/li&gt;
&lt;li&gt;You can add/modify deployment commands in the steps to deploy your application to a hosting provider like Heroku, Netlify, or AWS.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Running the CI/CD Pipeline:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Commit and push the &lt;code&gt;ci-cd.yml&lt;/code&gt; file to your GitHub repository.&lt;/li&gt;
&lt;li&gt;GitHub Actions will automatically detect the new workflow and start the CI/CD pipeline.&lt;/li&gt;
&lt;li&gt;You can monitor the progress of the pipeline in the "Actions" tab of your repository.&lt;/li&gt;
&lt;li&gt;If any errors occur during the build or test stages, GitHub Actions will provide detailed logs to help you identify and resolve issues.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;GitHub Actions offers a convenient way to automate your CI/CD pipeline for React and Node.js applications. By leveraging the power of workflows, you can save time and effort while maintaining the quality of your codebase. In this article, we explored how to set up a basic CI/CD workflow using GitHub Actions on an Ubuntu environment. Feel free to customize the workflow to suit your specific needs and take advantage of the rich ecosystem of GitHub Actions to enhance your development process further.&lt;/p&gt;

&lt;p&gt;Happy automating!&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>react</category>
      <category>node</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
