Build a CI/CD Pipeline with GitHub Actions
Automate testing and deployment of a portfolio site with GitHub Actions.
Introduction
30 Second Summary
Imagine if every time you saved your work, someone automatically checked it for mistakes and published it for you. That's exactly what professional development teams do with automated pipelines.
In this project, you will build a personal portfolio website with a fully automated pipeline that tests your code and deploys it live to the internet every time you push a change.
What You'll Build
A portfolio website powered by a CI/CD pipeline built with GitHub Actions that automatically validates your HTML structure and deploys your site to GitHub Pages on every push.
By the end of this project, you'll have:
- A public GitHub repository containing your portfolio website with HTML and CSS.
- A CI workflow that automatically validates your code structure on every push.
- A deployment workflow that publishes your site live to GitHub Pages whenever checks pass.
- Secret Mission: Add pull request checks and branch protection rules so changes must pass validation before merging.
Are there any prerequisites?
You just need a GitHub account, VS Code, and Git installed on your machine. This project walks you through everything else from scratch.
Set Up Your Repository
Every CI/CD pipeline starts with a home for your code. Automated workflows need a place to watch for changes, run checks, and trigger deployments.
In this step, you'll create a GitHub repository, clone it to your computer, and configure it so GitHub knows to use a workflow file for deployments instead of serving files directly from a branch.
In this step, get ready to:
- Create a public repository on GitHub.
- Clone it to your local machine and open it in VS Code.
- Configure GitHub Pages to deploy from a workflow.
Create your repository on GitHub
A repository is the folder where GitHub stores your project files, tracks every change, and runs your automated workflows against them.
- Open your browser and go to github.com.
- In the upper-right corner of any page, click the + icon, then click New repository.
- In the Repository name field, type my-portfolio.
- Under visibility, select Public.
- Check the box labelled Add a README file.
- Click Create repository.
What is a repository?
A Git repository tracks every version of every file in your project. GitHub hosts your repository online so that automated tools (like the pipeline you'll build) can access it whenever you push new code.
Clone the repository to your computer
Cloning downloads a full copy of the repository to your machine so you can edit files locally and push changes back to GitHub.
- On your new repository page, click the green Code button.
- Copy the HTTPS URL shown in the dropdown. It looks like https://github.com/your-username/my-portfolio.git.
Now open VS Code so you can use its built-in terminal.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Visual Studio Code and press Enter to open it.
- Open the integrated terminal by pressing Ctrl+` (the backtick key, usually above Tab).
- Move to your Desktop by running this command:
cd ~/Desktop
- Clone your repository by running this command:
git clone [[REPO_URL="https://github.com/your-username/my-portfolio.git"]]
- Move into the cloned folder by running:
cd my-portfolio
What does git clone do?
The git clone command downloads the entire repository from GitHub to your computer. It creates a folder with the same name as the repository and sets up the connection back to GitHub so you can push and pull changes.
Now open this folder in VS Code so you can work inside it.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the my-portfolio folder, then click Open (or Select Folder on Windows).
You should see the my-portfolio folder name in the VS Code title bar and a README.md file in the Explorer sidebar.
Don't see README.md in the sidebar?
Make sure you opened the my-portfolio folder itself, not the Desktop. The Explorer sidebar should show README.md at the top level.
If you accidentally cloned into the wrong location, delete the folder and re-run git clone from your Desktop.
Help me find my cloned repository.
Configure GitHub Pages to use a workflow
By default, GitHub Pages serves files directly from a branch. You need to change this so it waits for a GitHub Actions workflow to handle deployment instead.
- Go back to your repository on GitHub in your browser.
- Click the Settings tab near the top of the repository page.
- In the left sidebar under Code and automation, click Pages.
- Under Build and deployment, find the Source dropdown.
- Select GitHub Actions from the dropdown.
Why GitHub Actions instead of a branch?
When the source is set to a branch, GitHub publishes files directly without running any checks first. Setting it to GitHub Actions means your code only goes live after your workflow validates it. This is the foundation of a CI/CD pipeline.
GitHub may suggest workflow templates after you change the source. You can ignore these suggestions for now because you will write your own workflow from scratch in a later step.
Confirm the Source dropdown now shows GitHub Actions. No save button is needed. The setting applies immediately.
Don't see the Pages option?
Make sure your repository is set to Public. GitHub Pages requires a public repository on the free plan.
If you don't see the Settings tab, click the overflow menu (three dots) next to the tabs and look for Settings there.
Help me find the GitHub Pages settings.
Verify your setup
- In VS Code, open the terminal again with Ctrl+` if it's not already visible.
- Confirm you are inside the correct folder by running:
git status
You should see On branch main and a message indicating a clean working tree. This confirms your repository is cloned correctly and you are on the main branch.
Seeing 'not a git repository'?
This means your terminal is not inside the my-portfolio folder. Run cd ~/Desktop/my-portfolio and try git status again.
Help me fix my git status error.
Your repository is ready and configured for automated deployments. Next up, you'll build the portfolio website that your pipeline will validate and deploy.
Build Your Portfolio Website
Your repository is cloned and ready to go. Now it is time to build the actual website that your pipeline will test and deploy.
In this step, you will create a simple portfolio page using HTML and CSS. Then you will push it to GitHub so your future pipeline has something to validate and deploy.
In this step, get ready to:
- Create an HTML portfolio page with proper structure.
- Style the page with a CSS file.
- Push both files to your GitHub repository.
Create the HTML file
Every website starts with an index.html file. This is the file browsers load by default when someone visits your site.
- In VS Code, look at the Explorer panel on the left sidebar.
- Hover over your my-portfolio folder name at the top of the Explorer. You will see a row of small icons appear.
- Click the New File icon (it looks like a page with a small plus sign).
- Type index.html and press Enter.
- Paste the following code into the new file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Portfolio</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Hi, I'm a DevOps Learner</h1>
<p>Building automated pipelines, one commit at a time.</p>
</header>
<section id="about">
<h2>About Me</h2>
<p>I'm learning DevOps by building real projects. This site is automatically tested and deployed using GitHub Actions every time I push code.</p>
</section>
<section id="skills">
<h2>What I'm Learning</h2>
<ul>
<li>CI/CD Pipelines</li>
<li>GitHub Actions</li>
<li>Infrastructure as Code</li>
<li>Containers</li>
</ul>
</section>
<footer>
<p>Deployed automatically with GitHub Actions</p>
</footer>
</body>
</html>
What does this code do?
This is a complete HTML5 document with all the structural elements a browser expects.
- The <!DOCTYPE html> declaration tells the browser to use modern HTML5 standards.
- The <head> section contains metadata, a page title, and a link to the stylesheet you will create next.
- The <body> has a header, two content sections (about and skills), and a footer.
- Save index.html by pressing Ctrl+S (Windows) or Cmd+S (macOS).
- Open the file in your browser by right-clicking index.html in the Explorer panel and selecting Reveal in File Explorer (Windows) or Reveal in Finder (macOS). Then double-click the file to open it.
- You should see your portfolio text displayed in your browser. It will look plain and unstyled for now.
Don't see your text in the browser?
- Make sure you saved the file. Unsaved files in VS Code show a dot on the tab name.
- Check that the file is named exactly index.html with no extra spaces or capital letters.
Help me figure out why my HTML file isn't showing in the browser.
✔️ Awesome, I've got everything!
Great. Double check you have saved your file before moving on.
ⓧ I'd like to double check the full code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Portfolio</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Hi, I'm a DevOps Learner</h1>
<p>Building automated pipelines, one commit at a time.</p>
</header>
<section id="about">
<h2>About Me</h2>
<p>I'm learning DevOps by building real projects. This site is automatically tested and deployed using GitHub Actions every time I push code.</p>
</section>
<section id="skills">
<h2>What I'm Learning</h2>
<ul>
<li>CI/CD Pipelines</li>
<li>GitHub Actions</li>
<li>Infrastructure as Code</li>
<li>Containers</li>
</ul>
</section>
<footer>
<p>Deployed automatically with GitHub Actions</p>
</footer>
</body>
</html>
Style your portfolio
Your HTML has the structure, but it needs CSS to look presentable. You will create a stylesheet that adds layout, colour, and spacing.
- In the Explorer panel, hover over your my-portfolio folder name and click the New File icon again.
- Type style.css and press Enter.
- Paste the following base and header styles into the new file:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
header {
text-align: center;
padding: 3rem 0;
border-bottom: 2px solid #0366d6;
margin-bottom: 2rem;
}
header h1 {
color: #0366d6;
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
header p {
font-size: 1.2rem;
color: #586069;
}
What does this code do?
- The * selector resets all default browser spacing so every element starts from the same baseline.
- The body rule centres the page content at a max width of 800px with comfortable padding.
- The header styles centre the title text and add a blue bottom border to visually separate it from the rest of the page.
- Save style.css and refresh your browser tab that has index.html open.
- You should see your header centred with blue text and a blue border line underneath it.
Header still looks unstyled?
- Make sure the file is saved as style.css in the same folder as index.html. The <link> tag in your HTML references style.css by name.
- Try a hard refresh in your browser by pressing Ctrl+Shift+R (Windows) or Cmd+Shift+R (macOS). Cached styles can prevent updates from showing.
Help me figure out why my CSS isn't being applied to my HTML page.
- Now add the section, list, and footer styles below the existing code in style.css:
section {
margin-bottom: 2rem;
padding: 1.5rem 0;
}
section h2 {
color: #24292e;
margin-bottom: 1rem;
font-size: 1.5rem;
}
ul {
list-style: none;
padding: 0;
}
ul li {
padding: 0.5rem 0;
padding-left: 1.5rem;
position: relative;
}
ul li::before {
content: ">";
position: absolute;
left: 0;
color: #0366d6;
font-weight: bold;
}
footer {
text-align: center;
padding: 2rem 0;
border-top: 1px solid #e1e4e8;
margin-top: 2rem;
color: #586069;
font-size: 0.9rem;
}
What does this code do?
- The section and section h2 rules add spacing between content areas and style the subheadings.
- The list styles remove the default bullet points and replace them with blue > characters for a clean, developer-friendly look.
- The footer gets a subtle top border and muted text to visually close the page.
- Save style.css and refresh your browser.
- You should see styled list items with blue > markers and a footer with a light grey top border.
List items still showing default bullets?
- Make sure you pasted the new code below the existing header styles, not replacing them.
- Check for any missing closing braces } in the header section above. A missing brace can break all the styles that follow it.
Help me fix my CSS so the list styles are applied correctly.
✔️ Awesome, I've got everything!
Great. Double check you have saved style.css before moving on.
ⓧ I'd like to double check the full code
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
header {
text-align: center;
padding: 3rem 0;
border-bottom: 2px solid #0366d6;
margin-bottom: 2rem;
}
header h1 {
color: #0366d6;
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
header p {
font-size: 1.2rem;
color: #586069;
}
section {
margin-bottom: 2rem;
padding: 1.5rem 0;
}
section h2 {
color: #24292e;
margin-bottom: 1rem;
font-size: 1.5rem;
}
ul {
list-style: none;
padding: 0;
}
ul li {
padding: 0.5rem 0;
padding-left: 1.5rem;
position: relative;
}
ul li::before {
content: ">";
position: absolute;
left: 0;
color: #0366d6;
font-weight: bold;
}
footer {
text-align: center;
padding: 2rem 0;
border-top: 1px solid #e1e4e8;
margin-top: 2rem;
color: #586069;
font-size: 0.9rem;
}
Push your code to GitHub
Your website files are ready locally. Now you need to push them to GitHub so they exist in your remote repository.
- Open a terminal in VS Code by pressing Ctrl+` (Windows) or Cmd+` (macOS).
- Stage, commit, and push your files by running these commands:
git add .
git commit -m "Add portfolio website"
git push origin main
What do these commands do?
- git add . stages all new and changed files so Git knows to include them in the next commit.
- git commit -m "Add portfolio website" saves a snapshot of your staged files with a descriptive message.
- git push origin main sends your local commit to the remote repository on GitHub.
- Visit your repository on GitHub in your browser.
- You should see both index.html and style.css listed alongside the README.md file.
Push was rejected or files aren't showing?
- If you see a rejection error, try running git pull origin main first to sync any remote changes (like the README), then push again.
- Make sure your terminal is open inside the my-portfolio folder. Run pwd (macOS) or cd (Windows) to check your current directory.
Help me push my portfolio files to GitHub.
Your portfolio site is built and pushed to GitHub. Next, you will create a CI workflow that automatically validates this HTML structure every time you push new code.
Create Your CI Workflow
Your portfolio site is on GitHub and ready to go. Now it's time to add the automation that turns this into a real DevOps project.
Every time you push code, you want to automatically check that nothing is broken before it goes any further. This is the core idea behind Continuous Integration. In this step, you'll write a workflow file that tells GitHub Actions to validate your HTML structure on every push.
In this step, get ready to:
- Create the .github/workflows/ directory and ci.yml file.
- Write a CI workflow that validates your HTML structure.
- Push the workflow and verify it passes on GitHub.
Create the workflow file
GitHub Actions looks for workflow definitions inside a special .github/workflows/ directory in your repository. You need to create this directory structure and add your first workflow file.
- In VS Code, look at the Explorer sidebar on the left (the top icon that looks like two overlapping files).
- Hover over the MY-PORTFOLIO workspace heading and click the New File icon (a page with a small plus sign).
- Type .github/workflows/ci.yml as the file name and press Enter.
VS Code automatically creates the .github and workflows folders for you. The new ci.yml file opens in your editor, ready for you to write your workflow.
What is a workflow file?
A workflow file is a YAML configuration file that tells GitHub Actions what to do when certain events happen (like a push to your repository). Each workflow file defines one or more jobs that run on GitHub's servers.
Don't see a .github folder in the Explorer?
Make sure you typed the full path .github/workflows/ci.yml (starting with a dot). If you accidentally created the file in the wrong location, right-click it in the Explorer sidebar, delete it, and try again.
Help me create the .github/workflows/ci.yml file in VS Code
Write the CI validation workflow
Your CI workflow will run two kinds of checks. First it confirms that your required files exist. Then it scans your HTML for proper structural elements like DOCTYPE, head, and body tags.
- Paste the following code into your empty ci.yml file:
name: CI
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Validate HTML structure
run: |
echo "Checking required files..."
test -f index.html || (echo "FAIL: index.html not found" && exit 1)
test -f style.css || (echo "FAIL: style.css not found" && exit 1)
echo "Files present: PASS"
echo "Checking HTML structure..."
grep -q "<!DOCTYPE html>" index.html || (echo "FAIL: Missing DOCTYPE" && exit 1)
grep -q "<html" index.html || (echo "FAIL: Missing html tag" && exit 1)
grep -q "<head>" index.html || (echo "FAIL: Missing head tag" && exit 1)
grep -q "<body>" index.html || (echo "FAIL: Missing body tag" && exit 1)
grep -q "<title>" index.html || (echo "FAIL: Missing title tag" && exit 1)
echo "HTML structure: PASS"
echo "All checks passed!"
What does this workflow do?
Here's how each section works:
- name: CI gives the workflow a name that appears in GitHub's Actions tab.
- on: [push, pull_request] triggers this workflow every time you push code or open a pull request.
- runs-on: ubuntu-latest tells GitHub to run your checks on a fresh Linux server.
- actions/checkout@v6 copies your repository code onto that server so the validation step can access your files.
- Each test -f line checks if a file exists. Each grep -q line searches for a required HTML tag. If any check fails, the workflow stops with an error.
- Save ci.yml by pressing Ctrl+S (Windows) or Cmd+S (macOS).
✔️ Awesome, I've got everything!
Great. Double check you've saved ci.yml and it matches the code above exactly.
ⓧ I'd like to double check the full code
Here's the complete .github/workflows/ci.yml file for comparison:
name: CI
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Validate HTML structure
run: |
echo "Checking required files..."
test -f index.html || (echo "FAIL: index.html not found" && exit 1)
test -f style.css || (echo "FAIL: style.css not found" && exit 1)
echo "Files present: PASS"
echo "Checking HTML structure..."
grep -q "<!DOCTYPE html>" index.html || (echo "FAIL: Missing DOCTYPE" && exit 1)
grep -q "<html" index.html || (echo "FAIL: Missing html tag" && exit 1)
grep -q "<head>" index.html || (echo "FAIL: Missing head tag" && exit 1)
grep -q "<body>" index.html || (echo "FAIL: Missing body tag" && exit 1)
grep -q "<title>" index.html || (echo "FAIL: Missing title tag" && exit 1)
echo "HTML structure: PASS"
echo "All checks passed!"
Push and verify your workflow
Now you need to push the workflow file to GitHub. Once GitHub receives it, the CI workflow triggers automatically because you just pushed code.
- Open the integrated terminal in VS Code by pressing Ctrl+` (Windows) or Cmd+` (macOS).
- Stage, commit, and push your workflow by running these commands:
git add .
git commit -m "Add CI workflow"
git push origin main
What do these commands do?
- git add . stages all new and changed files (your .github/workflows/ci.yml file).
- git commit -m "Add CI workflow" saves a snapshot of your changes with a descriptive message.
- git push origin main uploads everything to GitHub, which automatically triggers your new workflow.
- Go to your my-portfolio repository on GitHub in your browser.
- Click the Actions tab near the top of the page.
- You should see a workflow run triggered by your "Add CI workflow" commit. Click on it to watch the progress.
- Wait for the workflow to complete. A green checkmark means all validation checks passed.
What should I see?
The Actions tab shows a list of workflow runs. Your most recent run appears at the top with the commit message "Add CI workflow". Once it finishes, a green checkmark appears next to it. Click the run to see the job details, where you can expand the "Validate HTML structure" step to see each check printing PASS.
Seeing a red X instead of a green checkmark?
Click the failed run, then click the validate job to see the logs. The error message tells you exactly which check failed (for example, "FAIL: Missing title tag"). Go back to your index.html in VS Code and confirm it has all required structural tags.
If you see a YAML syntax error, double-check that your indentation uses spaces (not tabs) and matches the code above exactly.
Help me debug my failing CI workflow
Your pipeline is catching errors automatically. Next up, you'll add automated deployment so that every time your code passes checks, it goes live on the internet.
Add Automated Deployment to GitHub Pages
Your CI workflow validates code on every push. That covers the "continuous integration" half of CI/CD.
Now you'll add the "continuous deployment" half. In this step, you'll write a second workflow that automatically publishes your portfolio to GitHub Pages every time you push to main. No manual uploads, no file transfers. Just push and your site goes live.
In this step, get ready to:
- Create a deployment workflow file that publishes your site to GitHub Pages.
- Push the workflow and verify your portfolio is live on the internet.
Create the deploy workflow file
Your CI workflow lives at .github/workflows/ci.yml. The deploy workflow will sit alongside it in the same folder.
- In VS Code, create a new file called deploy.yml inside the .github/workflows/ folder.
- Add the workflow header by pasting the following code into deploy.yml:
name: Deploy to GitHub Pages
on:
push:
branches: ["main"]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
What does this code do?
- name: Deploy to GitHub Pages gives this workflow a descriptive name that appears in the Actions tab.
- on: push: branches: ["main"] triggers the workflow only when code is pushed to the main branch. Unlike your CI workflow, this one ignores pull requests.
- workflow_dispatch lets you manually trigger a deployment from the Actions tab if needed.
- permissions grants the workflow the minimum access it needs: read your code, write to Pages, and authenticate the deployment.
You won't see a change yet. The next section adds the deployment job that completes this workflow.
Why a separate deploy workflow?
Your CI workflow runs on every push and every pull request. Deployment should only happen on pushes to main. Keeping them in separate files means each workflow has a single, clear responsibility.
Define the deployment job
- In deploy.yml, add the following code below the permissions block:
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
path: '.'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
What does this job do?
The deploy job uses four official GitHub Actions in sequence:
- actions/checkout@v6 pulls your repository code onto the runner.
- actions/configure-pages@v5 prepares the Pages environment.
- actions/upload-pages-artifact@v4 packages your entire repository (path: '.') as a deployment artifact.
- actions/deploy-pages@v4 publishes that artifact to your live GitHub Pages URL.
The environment block tells GitHub this job targets the github-pages deployment environment. GitHub creates this environment automatically if it does not already exist.
- Save deploy.yml.
Seeing red squiggles in VS Code?
YAML is indentation-sensitive. Make sure each nested level uses exactly 2 spaces (not tabs). The jobs key should start at column 0, deploy at 2 spaces, and steps at 4 spaces.
help me fix YAML indentation in my deploy.yml workflow file
✔️ Awesome, I've got everything!
Great. Double check you've saved deploy.yml before moving on.
ⓧ I'd like to double check the full code
Here is the complete deploy.yml file:
name: Deploy to GitHub Pages
on:
push:
branches: ["main"]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
path: '.'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Push and verify your live site
- Stage, commit, and push your new workflow by running these commands in your terminal:
git add .
git commit -m "Add deploy workflow"
git push origin main
What happens when you push?
Pushing to main triggers both your workflows. The CI workflow validates your HTML structure. The Deploy workflow packages your files and publishes them to GitHub Pages. You can watch both run in parallel from the Actions tab.
- Go to your repository on GitHub.
- Click the Actions tab.
- Wait for both the CI and Deploy to GitHub Pages workflows to show green checkmarks.
- Visit your live site at https://your-github-username.github.io/my-portfolio to see your portfolio on the internet.
How do I find my exact URL?
In the Actions tab, click the completed Deploy to GitHub Pages workflow run. You'll see a deployment link showing your live URL. You can also find it in your repository Settings > Pages section.
You should see your portfolio page with the blue heading and your skills list. It may take 1-2 minutes after the first deployment for the site to become accessible.
Site not loading?
- Check the Actions tab for a failed workflow run. Click on it to read the error log.
- Confirm you set the Pages source to GitHub Actions in Step 1. Go to Settings > Pages and check that the Source dropdown shows GitHub Actions.
- Make sure your repository is public. GitHub Pages on the Free plan requires public repositories.
help me debug why my GitHub Pages deployment is not working
Your portfolio is live on the internet with zero manual deployment steps. Next up, you'll test the full pipeline by making changes and watching the entire CI/CD cycle in action.
Test Your Pipeline End-to-End
Your pipeline is fully built. You have a CI workflow that validates your code and a Deploy workflow that publishes your site automatically.
But how do you know it actually catches errors? In this step, you will push a good change, then intentionally break something, and finally fix it. This proves your pipeline works the way professional CI/CD pipelines do: catch problems before they reach production.
In this step, get ready to:
- Push a code change and watch both workflows pass.
- Intentionally break your code and observe a failed pipeline.
- Fix the error and verify the pipeline recovers automatically.
Push a code change and watch it pass
To prove your pipeline works on every push, you will make a visible change to your portfolio and push it to GitHub. Both the CI and Deploy workflows should trigger automatically.
- In VS Code, open index.html.
- Find this line inside the <header> section:
<h1>Hi, I'm a DevOps Learner</h1>
- Change DevOps Learner to DevOps Engineer, so the line looks like this:
<h1>Hi, I'm a DevOps Engineer</h1>
- Save index.html.
- Push the change to GitHub by running these commands in your terminal:
git add .
git commit -m "Update heading"
git push origin main
- On GitHub, click the Actions tab in your repository.
You should see both the "CI" and "Deploy to GitHub Pages" workflows running. After a minute or so, both show a green checkmark.
- Refresh your GitHub Pages URL. You should see your heading now says "Hi, I'm a DevOps Engineer".
What just happened?
Your push triggered the CI workflow, which validated your HTML structure. Since all checks passed, the Deploy workflow also ran and published the updated site to GitHub Pages. This is the "happy path" of CI/CD in action.
Break the code and watch the pipeline fail
A pipeline that only passes is not very useful. The real value is catching errors before they reach your live site. Let's intentionally break something to see what a failed pipeline looks like.
- In index.html, find this line inside the <head> section:
<title>My Portfolio</title>
- Delete the entire line.
- Save index.html.
- Push the broken code to GitHub by running:
git add .
git commit -m "Remove title tag"
git push origin main
- On GitHub, click the Actions tab.
This time, the CI workflow shows a red X. The Deploy workflow did not run because the validation failed first.
- Click on the failed workflow run (the one with the red X).
- Click on the validate job in the left sidebar.
- Expand the Validate HTML structure step to read the error log.
You should see the message FAIL: Missing title tag in the log output. This is the exact check you wrote in your CI workflow catching the missing <title> tag.
Why did the deploy not run?
The CI and Deploy workflows are independent. However, the Deploy workflow only triggers on pushes to main after the push event completes. The CI failure does not block Deploy directly in this setup. GitHub Pages still deployed your broken code.
In the Secret Mission, you will add branch protection rules that prevent broken code from reaching main at all. For now, focus on reading the CI failure signal.
Fix the error and verify the full cycle
You have seen a passing pipeline and a failing pipeline. Now let's close the loop by fixing the error and confirming the pipeline recovers automatically.
- In index.html, find the <head> section. It currently looks like this:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
</head>
- Add the <title> line back between the viewport meta tag and the stylesheet link, so the <head> section looks like this:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Portfolio</title>
<link rel="stylesheet" href="style.css">
</head>
- Also find the <h1> line you changed earlier and restore it to the original:
<h1>Hi, I'm a DevOps Learner</h1>
- Save index.html.
- Push the fix to GitHub by running:
git add .
git commit -m "Restore title tag"
git push origin main
- On GitHub, click the Actions tab and watch the new workflow run.
Both the CI and Deploy workflows should pass with green checkmarks. Your fix was validated automatically and deployed to your live site.
- Refresh your GitHub Pages URL. You should see the original heading "Hi, I'm a DevOps Learner" and the page title is back to "My Portfolio".
What did you just prove?
You completed the full CI/CD cycle that professional teams use every day. Push code, automated checks run, and if they pass, the site deploys. If they fail, you get immediate feedback about what went wrong.
This loop (push, validate, deploy, verify) is the core of DevOps automation. Every change is tested before it reaches users.
Workflow not triggering?
Make sure your git push command completed without errors. If you see "Everything up-to-date", your commit was empty. Check that you saved the file and ran git add . before committing.
If the Actions tab is empty, confirm your workflow files are in the .github/workflows/ directory by checking your repository file list on GitHub.
Help me troubleshoot why my GitHub Actions workflow is not running after I push.
✔️ Awesome, I've got everything!
Great. Double check you have saved index.html and that your GitHub Pages site reflects the restored state.
ⓧ I'd like to double check the full code
Here is what your index.html should look like after restoring everything:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Portfolio</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Hi, I'm a DevOps Learner</h1>
<p>Building automated pipelines, one commit at a time.</p>
</header>
<section id="about">
<h2>About Me</h2>
<p>I'm learning DevOps by building real projects. This site is automatically tested and deployed using GitHub Actions every time I push code.</p>
</section>
<section id="skills">
<h2>What I'm Learning</h2>
<ul>
<li>CI/CD Pipelines</li>
<li>GitHub Actions</li>
<li>Infrastructure as Code</li>
<li>Containers</li>
</ul>
</section>
<footer>
<p>Deployed automatically with GitHub Actions</p>
</footer>
</body>
</html>
Secret mission
Add Pull Request Checks and Branch Protection
On a real DevOps team, code never goes straight to main. Every change goes through a pull request, automated checks run, and only passing code gets merged. In this secret mission, you'll set up that exact workflow with branch protection rules.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely on free GitHub services, so there are no ongoing costs. Decide whether to keep your resources running or delete them entirely.
Resources you used:
- GitHub repository my-portfolio (free, public repository).
- GitHub Pages site at https://your-username.github.io/my-portfolio (free for public repos).
- Local clone of my-portfolio on your Desktop.
Keep everything running
No action needed. Choose this if you want to keep your live portfolio site and CI/CD pipeline intact.
- Your GitHub Pages site stays live at no cost.
- Your CI and Deploy workflows continue to run on every push for free (public repo).
- Your branch protection rules stay active on main.
Pause - I'll come back to this later
Since all resources are free, there is nothing to pause. Your repository and GitHub Pages site cost nothing to keep running.
- Leave everything as-is and come back whenever you want to add more features.
Delete - I don't want to use this again
Remove all project resources and start fresh. Deleting the repository also removes the GitHub Pages site and all workflow history.
- On GitHub, navigate to your my-portfolio repository.
- Click Settings (under the repository name).
- On the General settings page, scroll down to the Danger Zone section.
- Click Delete this repository.
- Click I want to delete this repository, read the warnings, then click I have read and understand these effects.
- Type my-portfolio in the confirmation box and click Delete this repository.
Delete the local folder from your machine by running this command in your terminal:
macOS
rm -rf ~/Desktop/my-portfolio
Windows
rmdir /s /q "$HOME\Desktop\my-portfolio"
Nice Work!
Nice Work!
You just built a fully automated CI/CD pipeline that tests and deploys your code every time you push. Your portfolio site is live on the internet, powered by the same automation patterns that professional DevOps teams use every day.
You've learned how to:
- Write a CI workflow that automatically validates your HTML structure on every push and pull request.
- Write a deployment workflow that publishes your site to GitHub Pages automatically when checks pass.
- Test the full CI/CD loop end-to-end: push a change, watch it validate, see it fail, fix it, and confirm the fix deploys live.
- Secret Mission: Added branch protection rules with required status checks and a PR-based workflow so changes must pass CI before merging to main.
Ready to quiz yourself?