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:
Each command serves a different purpose.
git initinitializes a Git repository.git statusshows the current working-tree status.git addstages changes.git commitsaves a snapshot of staged changes.git logdisplays commit history.git diffshows 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:
This creates and switches to a new branch.
After making changes, I can stage and commit them:
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:
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:
The directory structure looks like this:
my-project/
├── .github/
│ └── workflows/
│ └── ci.yml
├── index.html
└── README.mdMy 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
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.htmlLet's understand the important parts.
`name`
name: My First CIThis defines the workflow name shown on GitHub Actions.
`on`
on:
push:
branches:
- mainThis defines the event that triggers the workflow.
In this example, the workflow runs when code is pushed to the main branch.
`jobs`
jobs:
check:A workflow contains one or more jobs.
Each job consists of steps that run in a particular environment.
`runs-on`
runs-on: ubuntu-latestThis tells GitHub Actions to execute the job on a GitHub-hosted Ubuntu runner.
`steps`
steps:
- name: Checkout repository
uses: actions/checkout@v6The 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`
- name: Check for index.html
run: test -f index.htmlThis 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:
- name: Explore runner
run: |
echo "Current directory:"
pwd
echo "Repository files:"
ls -la
echo "Repository name:"
echo "$GITHUB_REPOSITORY"The commands helped me understand:
pwddisplays the current working directory.ls -lalists files and directories.GITHUB_REPOSITORYprovides the repository name inowner/repositoryformat.
For my git-revice repository, the runner's working directory was similar to:
/home/runner/work/git-revice/git-reviceThis 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:
0means 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:
test -f index.htmlcan 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:
- 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
fiThe 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:
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 buildThis 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:
- name: Run tests
run: npm testThe 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:
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
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
on:
push:
branches:
- mainThis runs the workflow when code is pushed to main.
Pull Request
on:
pull_request:
branches:
- mainThis runs the workflow for qualifying Pull Request events targeting main.
Manual workflow execution
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:
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:
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:
And add the correct remote:
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.