When I started learning web development, my main focus was writing code, building websites, connecting databases, and making projects work.

But as I started working on my own projects, I realized that writing code was only one part of the development process.

I also needed to understand how to manage code changes, collaborate through GitHub, test my code automatically, and deploy updates to a production server.

That was when I started learning Git, GitHub, GitHub Actions, and CI/CD.

Instead of trying to understand everything at once, I decided to learn these concepts step by step and practice them using my own repositories.

In this article, I'll document what I learned, the workflows I created, and how I started connecting development with deployment.

1. Understanding Git and GitHub

Before learning CI/CD, I needed to understand the tools used to manage my code.

What is Git?

Git is a distributed version control system.

It helps developers track changes in their source code, maintain different versions, create branches, and restore earlier versions when necessary.

For example, imagine I am developing a portfolio website.

I make some changes to the navigation bar, but those changes accidentally break the layout.

With Git, I can inspect the changes and potentially restore the previous working version.

Some important Git commands I practiced:

bash
$git init
$git status
$git add .
$git commit -m "Initial commit"
$git log
$git diff

Each command serves a different purpose.

  • git init initializes a Git repository.
  • git status shows the current working-tree status.
  • git add stages changes.
  • git commit saves a snapshot of staged changes.
  • git log displays commit history.
  • git diff shows changes between versions or states.

What is GitHub?

GitHub is a platform for hosting Git repositories online.

It provides additional features such as:

  • Remote repositories
  • Pull Requests
  • Issues
  • Code reviews
  • GitHub Actions
  • Collaboration tools

Git and GitHub are not the same thing.

Git manages version control. GitHub provides an online platform for hosting and collaborating on Git repositories.

2. Understanding Branches and Pull Requests

One of the important concepts I explored was working with branches.

Instead of making every change directly on the main branch, developers can create a separate branch for a feature or fix.

For example:

bash
$git checkout -b feature/login-fix

This creates and switches to a new branch.

After making changes, I can stage and commit them:

bash
$git add .
$git commit -m "Fix login functionality"
$git push origin feature/login-fix

The branch can then be used to create a Pull Request on GitHub.

What is a Pull Request?

A Pull Request (PR) is a request to review and merge changes from one branch into another.

A typical workflow looks like this:

1. Create a feature branch.

2. Make code changes.

3. Commit the changes.

4. Push the branch to GitHub.

5. Open a Pull Request.

6. Review and test the changes.

7. Merge the Pull Request into main.

This workflow helps keep the main branch organized and makes code changes easier to review.

3. What is CI/CD?

CI/CD is a collection of practices that helps automate parts of the software development process.

CI/CD commonly refers to:

  • CI — Continuous Integration
  • CD — Continuous Delivery or Continuous Deployment

Continuous Integration (CI)

Continuous Integration is the practice of frequently integrating code changes into a shared repository and automatically running checks or tests.

For example, whenever I push code to GitHub, a workflow can automatically:

  • Check out the repository.
  • Run tests.
  • Validate the code.
  • Report whether the workflow succeeded or failed.

The purpose is to identify problems early.

Continuous Delivery

Continuous Delivery keeps the application in a deployable state.

The deployment process is automated, but production deployment may still require manual approval.

Continuous Deployment

Continuous Deployment automatically deploys changes to production after they pass the required checks.

For example:

GitHub-CI-CD
Step 1Developer pushes code
done
Step 2GitHub Actions starts CI
done
Step 3Automated checks run
done
Step 4Changes are merged
done
Step 5CD deploys to production
active
Step 6Production verification
pending

The important distinction is that Continuous Integration and Continuous Deployment solve different problems.

CI helps validate code changes. CD helps deliver those changes to an environment.

4. My First GitHub Actions Workflow

GitHub Actions allows developers to automate workflows directly inside their GitHub repositories.

A workflow is defined using a YAML file.

The workflow file is usually stored inside:

bash
$mkdir -p .github/workflows

The directory structure looks like this:

snippet.text
text
my-project/
├── .github/
│   └── workflows/
│       └── ci.yml
├── index.html
└── README.md

My first workflow was intentionally simple.

The goal was to understand how GitHub Actions works before introducing complex tests or deployment configurations.

A simple CI workflow

ci.yml
yaml
name: My First CI

on:
  push:
    branches:
      - main

jobs:
  check:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Check for index.html
        run: test -f index.html

Let's understand the important parts.

`name`

snippet.yaml
yaml
name: My First CI

This defines the workflow name shown on GitHub Actions.

`on`

snippet.yaml
yaml
on:
  push:
    branches:
      - main

This defines the event that triggers the workflow.

In this example, the workflow runs when code is pushed to the main branch.

`jobs`

snippet.yaml
yaml
jobs:
  check:

A workflow contains one or more jobs.

Each job consists of steps that run in a particular environment.

`runs-on`

snippet.yaml
yaml
runs-on: ubuntu-latest

This tells GitHub Actions to execute the job on a GitHub-hosted Ubuntu runner.

`steps`

snippet.yaml
yaml
steps:
  - name: Checkout repository
    uses: actions/checkout@v6

The checkout action downloads the repository's contents into the runner's working directory.

Without checkout, the runner would not automatically have my project files available.

`run`

snippet.yaml
yaml
- name: Check for index.html
  run: test -f index.html

This executes a shell command.

The command checks whether the file index.html exists.

If the file exists, the command succeeds. If it does not exist, the command fails.

This was my first practical example of an automated check.

5. Exploring the GitHub-Hosted Runner

After getting my first workflow running, I wanted to understand where the workflow actually executes.

GitHub Actions jobs run on runners.

A runner is the machine or execution environment responsible for running the workflow's jobs and steps.

GitHub provides hosted runners, and developers can also configure self-hosted runners.

Exploring the runner environment

I created steps to inspect the runner:

ci.yml
yaml
- name: Explore runner
  run: |
    echo "Current directory:"
    pwd

    echo "Repository files:"
    ls -la

    echo "Repository name:"
    echo "$GITHUB_REPOSITORY"

The commands helped me understand:

  • pwd displays the current working directory.
  • ls -la lists files and directories.
  • GITHUB_REPOSITORY provides the repository name in owner/repository format.

For my git-revice repository, the runner's working directory was similar to:

snippet.text
text
/home/runner/work/git-revice/git-revice

This helped me understand that GitHub Actions was not running directly on my local computer.

It was executing the workflow in a GitHub-hosted environment.

Understanding exit codes

Shell commands return an exit code.

Generally:

  • 0 means the command completed successfully.
  • A non-zero exit code indicates an error or unsuccessful result.

GitHub Actions uses the results of commands to determine whether steps and jobs succeed or fail.

This is why a simple command like:

snippet.bash
bash
test -f index.html

can be used as an automated CI check.

6. Understanding GitHub Actions Secrets

As I started exploring deployment workflows, I learned that workflows sometimes need sensitive information.

Examples include:

  • FTP passwords
  • API tokens
  • Database credentials
  • SSH keys

These values should not be hardcoded into workflow files.

GitHub Actions provides Secrets for storing sensitive configuration.

For example, a workflow can access a secret like this:

ci.yml
yaml
- name: Example secret usage
  env:
    MY_SECRET: ${{ secrets.SEC_NEW }}
  run: |
    if [ -n "$MY_SECRET" ]; then
      echo "Secret is configured."
    else
      echo "Secret is missing."
      exit 1
    fi

The example checks whether the secret is available without displaying its actual value.

Important security practices

  • Never commit real passwords or API tokens.
  • Never print secrets in workflow logs.
  • Use GitHub Secrets for sensitive values.
  • Give credentials only the permissions they need.
  • Rotate credentials if they are accidentally exposed.

A secret should remain secret even while debugging a workflow.

7. Building CI Workflows for Different Projects

After understanding the basics, I started experimenting with CI workflows for different projects.

HTML project

For my basic repository, I created a workflow that checked for the existence of index.html.

React project

I also explored a CI workflow for a React project.

The general workflow for a JavaScript application can include:

1. Checking out the repository.

2. Setting up Node.js.

3. Installing dependencies.

4. Running tests.

5. Running a build command.

An illustrative example:

ci.yml
yaml
name: React CI

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

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

      - name: Install dependencies
        run: npm ci

      - name: Build project
        run: npm run build

This is an illustrative workflow for a React application that has a compatible package-lock.json and a configured build script.

The exact steps depend on the project.

For example, if the project includes tests, I can add:

ci.yml
yaml
- name: Run tests
  run: npm test

The key lesson was that CI is not limited to one programming language.

The workflow can be adapted to the requirements of the project.

8. Learning Continuous Deployment with FTP

One of the most practical milestones in my learning journey was setting up deployment using GitHub Actions and FTP.

My portfolio is hosted at:

https://devzoop.in

I wanted to understand how code changes could move from GitHub to a production website without manually uploading every file.

The deployment workflow

The general process:

1. I make changes to my local project.

2. I commit the changes.

3. I push them to GitHub.

4. GitHub Actions starts the deployment workflow.

5. The workflow connects to the hosting server using FTP or FTPS.

6. The deployment action uploads the configured files.

7. I verify the changes on the production website.

Example FTP deployment workflow

cd.yml
yaml
name: Portfolio CD

on:
  push:
    branches:
      - main

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

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Deploy using FTP
        uses: SamKirkland/FTP-Deploy-Action@v4.4.0
        with:
          server: ${{ secrets.FTP_SERVER }}
          username: ${{ secrets.FTP_USERNAME }}
          password: ${{ secrets.FTP_PASSWORD }}
          port: ${{ secrets.FTP_PORT }}
          protocol: ftps
          local-dir: ./
          server-dir: /public_html/

This is an illustrative deployment configuration.

The actual values for server-dir, protocol, and the deployment credentials depend on the hosting environment.

Understanding the configuration

| Configuration | Purpose |

|---|---|

| server | FTP server hostname |

| username | FTP account username |

| password | FTP account password |

| port | FTP/FTPS server port |

| protocol | Connection protocol |

| local-dir | Directory containing local project files |

| server-dir | Destination directory on the hosting server |

The FTP deployment action handles the file-transfer process.

The workflow does not need to manually execute an FTP command for every file.

What I learned from this milestone

This was the point where CI/CD started feeling practical.

I could connect the development workflow with an actual production website.

However, automated deployment does not mean the website is automatically safe or bug-free.

A deployment workflow can upload broken code just as easily as working code if the required checks are not configured.

That is why CI checks and CD deployment should be designed together.

9. Understanding GitHub Actions Triggers

A workflow does not run randomly.

It runs when the configured event occurs.

Some common events include:

Push

ci.yml
yaml
on:
  push:
    branches:
      - main

This runs the workflow when code is pushed to main.

Pull Request

ci.yml
yaml
on:
  pull_request:
    branches:
      - main

This runs the workflow for qualifying Pull Request events targeting main.

Manual workflow execution

ci.yml
yaml
on:
  workflow_dispatch:

This allows a workflow to be started manually from GitHub Actions when configured appropriately.

Choosing the correct trigger is important.

For example, CI checks can run during Pull Requests, while a production deployment workflow can be configured to run only after changes are merged or pushed to the main branch.

10. A Practical CI/CD Workflow

Putting the concepts together, a development workflow can look like this:

Development-CI-CD
Step 1Create Feature Branch
done
Step 2Develop and Commit Changes
done
Step 3Push Branch to GitHub
done
Step 4Open Pull Request
done
Step 5GitHub Actions CI Checks
done
Step 6Review and Merge
active
Step 7Deploy to Production
pending
Step 8Verify Production Website
pending

This workflow separates development, validation, integration, and deployment.

For a team, it can also include code reviews, automated tests, staging deployments, and production approval.

The exact workflow depends on the project and its requirements.

11. Common Problems I Encountered While Learning Git and GitHub

Learning Git and GitHub was not only about understanding commands.

I also had to troubleshoot mistakes.

Error: `src refspec main does not match any`

This error can occur when the local branch does not exist under the specified name or when there is no commit to push.

A common solution for a newly initialized repository is:

bash
$git add .
$git commit -m "Initial commit"
$git branch -M main
$git push -u origin main

The exact fix depends on the repository's current state.

Error: No configured push destination

If Git does not have a remote repository configured, it cannot determine where to push.

I can inspect the configured remotes:

bash
$git remote -v

And add the correct remote:

bash
$git remote add origin https://github.com/USERNAME/REPOSITORY.git

The repository URL must be replaced with the actual remote URL.

GitHub Actions workflow failure

When a workflow fails, I should not immediately rewrite the entire YAML file.

Instead:

1. Open the failed workflow run.

2. Identify the failed job.

3. Open the failed step.

4. Read the error message.

5. Check the command, configuration, or environment.

6. Fix the issue.

7. Push the updated changes.

8. Verify the next workflow run.

Debugging the specific failure is often more effective than making random changes.

12. What I Learned

This learning journey helped me understand several important concepts.

Git and GitHub

I learned how to track changes, create commits, manage branches, push code, and work with remote repositories.

GitHub Actions

I learned how workflows are defined using YAML, how events trigger workflows, and how jobs and steps execute.

Runners

I explored GitHub-hosted runners and learned how to inspect their working directories and environment.

Secrets

I learned why sensitive configuration should be stored securely instead of hardcoded into source code.

CI

I started creating automated checks for my projects, including basic HTML checks and workflows for JavaScript applications.

CD

I practiced connecting GitHub Actions with FTP-based deployment to a production hosting environment.

Most importantly, I learned that CI/CD is not just about writing a YAML file.

It is about creating a repeatable process for moving code from development to production.

13. What I Want to Learn Next

There is still a lot I want to explore.

My next learning goals include:

  • Improving automated testing.
  • Understanding workflow permissions.
  • Learning better deployment strategies.
  • Exploring environment-specific configurations.
  • Understanding staging and production environments.
  • Learning Docker-based workflows.
  • Exploring deployment using SSH.
  • Improving monitoring and production verification.
  • Understanding how CI/CD works in larger applications.

I want to introduce these concepts gradually as my projects become more complex.

I do not want to add infrastructure simply because it is popular. I want to understand why it is needed and what problem it solves.

Conclusion

Learning GitHub CI/CD has changed the way I think about web development.

Earlier, my primary goal was to write code and make a website work.

Now, I am also thinking about how code is managed, tested, integrated, deployed, and maintained.

From creating my first GitHub Actions workflow in git-revice to exploring automated FTP deployment for my portfolio, each step has helped me understand the development process more practically.

I am still learning, and this is not the end of the journey.

My goal is to keep building real projects, understand the tools I use, and gradually improve the way I develop and deploy applications.

Build. Learn. Break. Fix. Deploy. Repeat.

---

This article documents my personal learning journey with Git, GitHub, GitHub Actions, and CI/CD. The examples are simplified for educational purposes and may need adjustments depending on the project and hosting environment.