🚧 Under construction — VoTechWorks is being built. A free tech program — complimentary, as our commitment to get America working.
voTechWorksGrade 13 · a vocational technology learning center, by urbanicity
newSchoolWorks.com › VoTechWorks › Technology & Design › Web & Software Development › Deployment: Servers, CI/CD, and Cloud Publishing › Lesson 9
TEC-WEB46-07 · Lesson 9 · 18 clock hours · lab

Building a CI/CD Pipeline with GitHub Actions

Objective. Students create a GitHub Actions workflow that automatically runs linting and unit tests on push and deploys a passing build to their VPS via SSH on merge to the main branch.

⚠ Pending professional review

This lesson, its worked examples and its practice questions has not yet been verified by an industry-certified professional or by a certification or approval body. It is a study draft and may contain errors. Do not use it as instructions for work on a job site or on live equipment. Always follow your instructor, your supervising professional, and current official references and regulations.

VoTechWorks program approval and accreditation are still in progress. Credentials are issued only by the certifying bodies named in each program, after their own exams.

What Is a CI/CD Pipeline?

Continuous Integration (CI) is the practice of automatically building and testing code every time a developer pushes a change. Continuous Delivery or Deployment (CD) extends that by automatically shipping code that passes all checks to a staging or production environment. Together, a CI/CD pipeline shortens the feedback loop between writing code and knowing whether it works — and whether it is live.

GitHub Actions is a CI/CD platform built directly into GitHub. You define workflows in YAML files stored in your repository under `.github/workflows/`. Each workflow describes when to run (triggers), what environment to use (runners), and what commands to execute (steps and jobs).

Workflow File Structure

A GitHub Actions workflow file has four key sections:

A minimal Python CI workflow looks like this:

```yaml

name: CI

on: [push]

jobs:

test:

runs-on: ubuntu-latest

steps:

with:

python-version: '3.11'

```

The `uses` keyword pulls in a pre-built action from the GitHub Marketplace. `actions/checkout` fetches your repository code onto the runner. `actions/setup-python` installs a specific Python version.

Linting and Automated Tests

Linting checks code style and common errors before tests even run. For Python projects, flake8 is the standard linter. For JavaScript projects, eslint serves the same role. Running a linter first catches formatting problems quickly and cheaply.

Unit tests verify that individual functions behave correctly. In Python the most common frameworks are pytest and the built-in `unittest` module. Both integrate with GitHub Actions the same way — run the test command as a step.

If any step exits with a non-zero status code, GitHub Actions marks that job as failed and stops running subsequent steps. This is the enforcement mechanism: a broken test prevents deployment.

Adding a Deployment Job

The deployment job should only run when the CI job passes and when the trigger is a merge to the `main` branch. You control this with the `needs` keyword (to wait for the test job) and an `if` condition.

To deploy to your VPS over SSH, the community action `appleboy/ssh-action` is widely used. It opens an SSH session to your server and runs any shell commands you provide — typically `git pull` and a command to restart your server process (such as `systemctl restart myapp` or restarting a Gunicorn or Node process).

Because SSH requires a private key, you must never paste that key directly into a workflow file. Instead, store it as a GitHub Actions secret (`Settings → Secrets and variables → Actions → New repository secret`). Reference it in the workflow as `${{ secrets.VPS_KEY }}`. Secrets are masked in logs and are never exposed to pull requests from forks.

```yaml

deploy:

needs: test

runs-on: ubuntu-latest

if: github.ref == 'refs/heads/main' && github.event_name == 'push'

steps:

uses: appleboy/ssh-action@v1.0.3

with:

host: ${{ secrets.VPS_HOST }}

username: ${{ secrets.VPS_USER }}

key: ${{ secrets.VPS_KEY }}

script: |

cd /var/www/myapp

git pull origin main

systemctl restart myapp

```

Security Considerations

Keep these points in mind whenever credentials or remote access are involved:

Verifying the Pipeline

To confirm your pipeline enforces quality gates, you must deliberately break it. Introduce a syntax error or make a test assertion fail, push to a feature branch, and confirm that the Actions run shows a red failure on the test job and that the deploy job never starts. Then fix the code, push again, and verify the green run shows deployment completing. Capture screenshots of both runs in your lab log — this is the evidence that your pipeline actually blocks bad code.

Checking the Actions tab in your GitHub repository shows the full run history with each job's status, step-by-step logs, and timing. This is the primary tool you and an instructor use to verify pipeline behavior.

Key terms9

Continuous Integration (CI)The practice of automatically building and testing every code change as soon as it is pushed to a shared repository, so problems are caught immediately.
Continuous Deployment (CD)The automated process of releasing code that passes all CI checks to a production or staging environment without manual intervention.
GitHub Actions workflowA YAML file stored in `.github/workflows/` that defines triggers, jobs, and steps GitHub will execute automatically on specified events.
RunnerThe virtual machine or container GitHub provides to execute a workflow job; `ubuntu-latest` is the most common choice.
GitHub Actions secretAn encrypted variable stored in repository or organization settings and referenced in workflows; its value is masked in logs and never exposed to fork pull requests.
flake8A Python linting tool that checks source code for style violations and common errors according to PEP 8 and other rules.
appleboy/ssh-actionA community GitHub Action that opens an SSH connection to a remote server and runs shell commands, commonly used to trigger deployments on a VPS.
Quality gateA checkpoint in a pipeline that blocks subsequent steps (such as deployment) if defined criteria — like passing tests or linting — are not met.
Deploy keyAn SSH key pair created specifically to grant a CI/CD system limited access to a server or repository, separate from any personal key.
In class: Fork or clone a provided sample application repository, add a GitHub Actions workflow file that runs a linter (flake8 for Python or eslint for JS) and at least one automated test on every push to any branch. · Add a deployment job to the workflow that uses an SSH action (such as appleboy/ssh-action) with the VPS private key stored as a GitHub Actions secret, pulling the latest code and restarting the server process on successful merge to main. · Intentionally break a test in the code, push to a branch, and confirm the pipeline fails and does not deploy; fix the code, push again, and confirm the pipeline passes and deploys. Document both runs in the lab log with screenshots.
Materials: Student GitHub account, student VPS, sample application repository, GitHub Actions secrets configuration, GitHub Actions documentation, lab log
Check: Instructor reviews GitHub Actions run history: a failed run (broken test) must show no deployment step completing, and a passing run must show successful deployment. Lab log with screenshots required. Both required to pass.

Practice check8

Answer each question, then check it. Nothing is recorded; this is your own practice.

CompTIA ITF+ — IT concepts and terminology
1. In a GitHub Actions workflow file, which key specifies what event causes the workflow to run?
CompTIA Linux+ — automation and scripting
2. You want the deployment job in your workflow to run only after the test job has completed successfully. Which workflow keyword enforces this dependency?
CompTIA Linux+ — security
3. A developer stores a VPS private SSH key directly in the workflow YAML file and commits it to the public repository. What is the primary security risk?
CompTIA ITF+ — security concepts
4. Which GitHub Actions feature masks a stored value so it never appears in plain text in workflow logs?
CompTIA Linux+ — automation and scripting
5. You push a commit that intentionally breaks a unit test to a feature branch. What should you observe in the GitHub Actions run history?
PCEP Python — Python basics and best practices
6. Which Python tool is commonly used as a linter in CI pipelines to enforce PEP 8 style and catch common coding errors?
CompTIA Linux+ — automation and scripting
7. In the `appleboy/ssh-action`, where should you place the remote commands that pull new code and restart the server?
CompTIA ITF+ — security concepts
8. To protect against supply-chain attacks where a third-party GitHub Action is compromised, which versioning practice is recommended?
Record this lesson on my transcript. Sign in with your key and enrollment email to record lessons (enroll free if you have no key). This page stays open to read and practise.