Master Git and Github Workflow for Devops: Base of Everything

Vishal SharmaVishal Sharma
10 min read

Hello, Amazing People! Welcome to the second blog in our DevOps for Developers series. Today, we’re diving deep into Git and GitHub, the backbone of version control and collaboration in DevOps. Whether you’re a beginner or leveling up your DevOps game, this post will equip you with the knowledge to master Git and GitHub like a pro. We’ll cover everything from commits to CI/CD integration, with practical tips to streamline your workflow. Let’s get started!

Git and Github Basics

  • What is Git? : Git is a distributed version control system that tracks changes your code, allowing multiple developers to collaborate seamlessly.

  • What is Github? : GitHub is a platform for hosting Git repositories, enabling collaboration, code reviews, and automation.

  • Commits: A commit is a snapshot of your changes, like saving a checkpoint in a game. Use git commit -m "Your message" to save your work.

  • Branches: Branches let you work on features or fixes in isolation. Create one with git branch feature-name and switch using git checkout feature-name.

  • Git Workflow Setup: A typical workflow involves cloning a repo (git clone), making changes, committing, and pushing to GitHub (git push).

  • Resolving Merge Conflicts: When changes clash, Git flags conflicts. You resolve them by editing the conflicting files, then running git add. and git commit.

Pull Requests: Collaboration in Action

Alright, Amazing People, now that you’ve got the basics down, let’s talk about pull requests (PRs)—the secret sauce of team collaboration in DevOps. A PR is how you propose merging your branch’s changes into the main codebase, usually the main or develop branch. It’s like saying, “Hey team, check out my work and let’s make it official!”

Here’s how to create a PR:

  1. Push your branch to GitHub: git push origin feature-name.

  2. Head to GitHub, click “New Pull Request,” and select your branch to compare with the target (e.g., main).

  3. Write a clear description of your changes (e.g., “Added a shiny new login page!”).

  4. Your team reviews the code, leaves feedback, and approves. Once it’s good to go, merge the PR and celebrate!

Why PRs Rock in DevOps:

  • They ensure code quality through peer reviews, catching bugs before they sneak into production.

  • They keep a record of changes and discussions, so you always know who did what and why.

  • They trigger automated tests and deployments (more on that in a bit!).

Link your PR to issues (e.g., “Closes #123”) to keep your project organized and your team in the loop.

Branching Strategies for DevOps

In DevOps, how you manage branches can make or break your workflow. A good branching strategy keeps your project organized and your team sane. Here are two popular approaches:

  • Git Flow: Think of this as a structured playbook. You have a main branch for production-ready code, a develop branch for integrating new features, and separate branches for features (e.g., feature/new-button) or bug fixes. It’s perfect for projects with planned releases, like a big app update.

  • Trunk-Based Development: This is the fast lane. Everyone works off the main branch, using short-lived feature branches that get merged quickly. It’s ideal for teams pushing updates daily with CI/CD.

If you’re on a small team or love rapid deployments, go trunk-based for simplicity. For larger projects with formal release cycles, Git Flow keeps things tidy.

Github Actions : Your First Step in CI/CD

Now for the exciting part—GitHub Actions! This is where Git meets DevOps automation. GitHub Actions automatically run workflows when specific events happen in your repository, like pushing code or creating a PR. It's like having a tireless assistant that tests, builds, and deploys your code 24/7.

For practical explanation I gonna make a basic node.js project and going to integrate CI/CD pipeline into so let’s get started

Our pipeline will be super simple:

  1. Push code → GitHub Actions kicks in

  2. Test & Build → Make sure everything works

  3. Deploy → Put it live automatically using Render

Let's build this step by step using a simple Node.js web application as our example.

  1. Create a basic node.js app with express installed in it.

     {
       "name": "demo-app",
       "version": "1.0.0",
       "scripts": {
         "start": "node server.js",
         "test": "node test.js"
       },
       "dependencies": {
          "express": "^5.1.0"
       }
    
     //server.js
     const express = require('express');
     const app = express();
     const PORT = process.env.PORT || 3000;
    
     app.get('/', (req, res) => {
       res.json({ 
         message: 'Hello CI/CD World!', 
         version: '1.0.0'
       });
     });
    
     app.get('/health', (req, res) => {
       res.json({ status: 'OK' });
     });
    
     app.listen(PORT, () => {
       console.log(`Server running on port ${PORT}`);
     });
    
     module.exports = app;
    
     //test.js (In real world testing we could use jest or mocha)
     const http = require('http');
     const app = require('./server.js');
    
     // Start server for testing
     const server = app.listen(3001, () => {
       console.log('Test server started on port 3001');
     });
    
     // Simple test function
     function runTests() {
       console.log('🧪 Running tests...');
    
       let testsCompleted = 0;
       const totalTests = 2;
    
       // Test 1: Health check
       const healthReq = http.get('http://localhost:3001/health', (res) => {
         if (res.statusCode === 200) {
           console.log('✅ Health check passed');
         } else {
           console.log('❌ Health check failed');
           cleanup(1);
           return;
         }
    
         testsCompleted++;
         if (testsCompleted === totalTests) {
           console.log('🎉 All tests passed!');
           cleanup(0);
         }
       });
    
       healthReq.on('error', (err) => {
         console.log('❌ Health check request failed:', err.message);
         cleanup(1);
       });
    
       // Test 2: Main endpoint
       const mainReq = http.get('http://localhost:3001/', (res) => {
         if (res.statusCode === 200) {
           console.log('✅ Main endpoint test passed');
         } else {
           console.log('❌ Main endpoint test failed');
           cleanup(1);
           return;
         }
    
         testsCompleted++;
         if (testsCompleted === totalTests) {
           console.log('🎉 All tests passed!');
           cleanup(0);
         }
       });
    
       mainReq.on('error', (err) => {
         console.log('❌ Main endpoint request failed:', err.message);
         cleanup(1);
       });
     }
    
     // Cleanup function to properly close server and exit
     function cleanup(exitCode) {
       server.close(() => {
         console.log('Test server closed');
         process.exit(exitCode);
       });
    
       // Force exit after 5 seconds if server doesn't close gracefully
       setTimeout(() => {
         console.log('Force closing...');
         process.exit(exitCode);
       }, 5000);
     }
    
     // Handle process termination
     process.on('SIGINT', () => cleanup(1));
     process.on('SIGTERM', () => cleanup(1));
    
     // Add timeout for the entire test suite
     const testTimeout = setTimeout(() => {
       console.log('❌ Tests timed out');
       cleanup(1);
     }, 30000); // 30 second timeout
    
     // Run tests after server starts
     setTimeout(() => {
       runTests();
     }, 1000);
    
  2. Create a simple CI/CD pipeline

Now create .github/workflows/cicd.yml:

Ok so before moving further I just want to clear some jargons for you and here are they:

  • What is CI/CD ? : CI/CD stands for Continuous Integration and Continuous Deployment.

    • CI (Continuous Integration) means automatically testing and building your code every time you push changes to a shared repo (like GitHub).

    • CD (Continuous Deployment) means automatically deploying your code to a server (like production or staging) after it passes all tests.

Together, CI/CD helps developers ship code faster, safer, and more often.

name: Simple CI/CD Pipeline

# When to run this workflow
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

# What to do
jobs:
  # Job 1: Test our code
  test:
    name: Test Application
    runs-on: ubuntu-latest

    steps:
    - name: Get our code
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'

    - name: Install dependencies
      run: npm install

    - name: Run tests
      run: npm test

  # Job 2: Deploy (only when pushing to main)
  deploy:
    name: Deploy Application
    runs-on: ubuntu-latest
    needs: test
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'


    steps:
    - name: Get our code
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'

    - name: Install dependencies
      run: npm install
    #find the deploy hook key from Render Dashboard of your application
    - name: Trigger Render Deploy
      run: |
        curl -X POST ${{ secrets.RENDER_HOOK }}

Now let’s understand what that mean and how you can write your own YAML configuration as per your need.

🔖 Workflow Name

name: Simple CI/CD Pipeline

This is the name that will show up in GitHub's Actions tab. You can name it anything like Node.js CI/CD, Auto Deploy, etc.


🕒 Triggering Events

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

This tells GitHub:

  • Run this workflow every time someone:

    • pushes code to the main branch,

    • creates or updates a pull request targeting main.


🔧 Jobs

Jobs are sets of steps that GitHub Actions runs — either in parallel or in sequence.


✅ Job 1: test — Run Tests

jobs:
  test:
    name: Test Application
    runs-on: ubuntu-latest
  • This job is named "Test Application".

  • It runs on a virtual machine with Ubuntu (Azure VM).

🪜 Steps in the test job:

- name: Get our code
  uses: actions/checkout@v4
  • Clones your GitHub repo into the runner.
- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '18'
  • Installs Node.js version 18 on the machine.
- name: Install dependencies
  run: npm install
  • Installs your app dependencies from package.json.
- name: Run tests
  run: npm test
  • Runs your tests using npm test.

🚀 Job 2: deploy — Deploy to Render

deploy:
  name: Deploy Application
  runs-on: ubuntu-latest
  needs: test
  if: github.ref == 'refs/heads/main'
  • needs: test: This means the deploy job will only run if the test job passes.

  • if: github.ref == 'refs/heads/main': This condition ensures deployment only happens on the main branch — not on PRs or other branches.

🪜 Steps in the deploy job:

Same setup as before — checkout, install Node.js, install dependencies.

Then this:

- name: Trigger Render Deploy
      run: |
        curl -X POST ${{ secrets.RENDER_HOOK }}
  • This sends a POST request to Render using curl.

  • The srv-xxxxxx part identifies your app on Render.

  • The DEPLOY_HOOK_KEY is your secret key to allow the deploy (like a password).

💡 Tip: Instead of hardcoding the key, store it safely:

And then define RENDER_HOOK in GitHub Settings → Secrets.

  1. Push the Code to Github and setup Render for First Time

We are using Render here because it’s simple and beginner-friendly. It takes care of heavy lifting like managing virtual machines, handling SSH access, and setting up infrastructure.

However, in real-world production environments, teams typically use more robust and customizable setups — involving tools like Docker, Kubernetes, AWS, and Terraform — for greater flexibility, control, and scalability in the CI/CD pipeline.

git init
git remote add origin <your-repo-url>
git add .
git commit -m "initial commit"
git push -u origin main

After this, go to the Render Dashboard, deploy your code using the free plan, and generate a Deploy Hook.

This hook provides a secret URL (called the HOOK_SECRET) which you can use in your CI/CD pipeline to automatically trigger deployments whenever you push changes to your main branch.

Copy the Deploy Hook and look the key and put it in the secret section in settings of github.

https://api.render.com/deploy/srv-d17ss583e5dus73doie1g?key=6sssssfCf2yokg (dummy)

And configure a new environment and add secret that could be used in your workflow and in our case is RENDER_HOOK. (Project→Setting→Secrets and Variables→

Now as we are all ready to test the thing so let’s try to commit something to our main branch here.

In real-world projects, there are usually multiple contributors. When someone opens a pull request (PR), basic CI checks—like linting, running tests, and build verification—are automatically triggered.

If everything passes, a maintainer or reviewer reviews the code. Once approved, they merge the PR into the main branch.

This is when CD (Continuous Deployment) comes into play: the pipeline picks up the change and automatically deploys it to production or staging environments.

After the change is pushed to main or merge to main branch the CI/CD pipeline starts and test the changes and after testing it gets deployed to render.

Now also check the Render Dashboard and you will notice the change.

This is your first small step into the world of DevOps — where we automated the CI/CD process using GitHub Actions and deployed code effortlessly with Render. You’ve now seen how tests, linting, and secure deploy hooks fit together in a basic pipeline. While this was a simple setup for learning, it mirrors real-world practices at a smaller scale. As we continue this "DevOps for Developers" series, we’ll dive deeper into topics like Docker, Kubernetes, Infrastructure as Code with Terraform, monitoring tools like Prometheus and Grafana, and advanced CI/CD workflows with AWS and GitHub Actions.

As a small challenge, try integrating Slack notifications into your workflow — send alerts when tests pass or fail, or when deployments begin. Push your changes and raise a PR! It’s a great way to reinforce what you’ve learned. Until then, happy coding — and stay curious as you build towards automating real-world production systems! 🚀🛠️

1
Subscribe to my newsletter

Read articles from Vishal Sharma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vishal Sharma
Vishal Sharma

I am a developer from Surat who loves to write about DSA,Web Development, and Computer Science stuff.