Skip to main content
GitHub Actions
CHAPTER 06

Building CI Pipelines

Updated: May 15, 2026
25 min read

# CHAPTER 6

Building CI Pipelines

1. Introduction

A workflow that just echoes "Hello World" is a great learning tool, but it provides no business value. The true purpose of Continuous Integration (CI) is to validate that the code a developer wrote actually functions properly before it is allowed to merge. To do this, the pipeline must mimic the developer's computer: it needs to install the correct programming language (like PHP or Node.js), download third-party dependencies, and compile the code. In this chapter, we will construct the first half of a professional DevOps pipeline: the Build Phase.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Define the "Build Phase" of a CI/CD pipeline.
  • Use the actions/setup-* marketplace actions to configure programming languages.
  • Execute package managers (like composer or npm) within a workflow.
  • Understand how to structure a pipeline to "Fail Fast" on syntax errors.
  • Construct a functional CI pipeline for a web application.

3. Beginner-Friendly Explanation

Imagine building a Lego car.
  • Checkout: The robot opens the Lego box and dumps the pieces on the table. (Chapter 5).
  • The Build Phase: The robot realizes it's missing the wheels. It calls the Lego factory and orders the exact wheels needed. (Downloading dependencies). Once the wheels arrive, the robot snaps all the pieces together according to the instruction manual. (Compiling the code). If a piece doesn't fit, the robot stops immediately and says "Build Failed!"

4. Setting Up the Environment (actions/setup-*)

While the ubuntu-latest runner comes with many tools pre-installed, relying on default versions is dangerous. If your app requires Node.js v18, but the runner defaults to Node.js v20, your code might break.

Best Practice: Always explicitly declare the exact version of the language your app requires. GitHub provides official actions for this:

  • actions/setup-node@v4
  • shivammathur/setup-php@v2 (The industry standard for PHP in GitHub Actions)
  • actions/setup-python@v5

5. Installing Dependencies

Modern web apps rarely consist of just the code you wrote. They rely on thousands of files written by other people (Libraries/Packages).
  • PHP uses composer.
  • JavaScript uses npm.
You do not commit these thousands of files to GitHub. Instead, your CI pipeline must download them dynamically on the runner.

6. Mini Project: Build CI Pipeline for a PHP Application

Let's build a professional Continuous Integration pipeline that checks out code, sets up a specific PHP version, downloads vendor packages, and performs a rapid syntax check (Linting).

Step-by-Step Walkthrough:

  1. 1. Create .github/workflows/ci-pipeline.yml.
  1. 2. Paste the following declarative code:

yaml
12345678910111213141516171819202122232425262728
name: PHP CI Pipeline
on:
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # Step 1: Open the Lego Box
      - name: Checkout Code
        uses: actions/checkout@v4

      # Step 2: Set up the specific tools we need
      - name: Setup PHP 8.2
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, xml, ctype, iconv # Common PHP extensions
          coverage: none

      # Step 3: Order the missing pieces
      - name: Install Dependencies
        run: composer install --prefer-dist --no-progress

      # Step 4: Snap the pieces together (Syntax Check)
      - name: Lint PHP files
        run: find . -name "*.php" -print0 | xargs -0 -n1 php -l

7. Real-World Scenarios

A team was relying on the default PHP version installed on ubuntu-latest for their CI pipeline. Their application was written in PHP 7.4. One weekend, GitHub updated their runner images, bumping the default PHP version to 8.1. On Monday morning, every single Pull Request the team opened failed instantly because PHP 8.1 introduced breaking changes that their code couldn't handle. The DevOps engineer fixed the outage by explicitly adding the shivammathur/setup-php step and locking the version to 7.4. You must explicitly control your build environment!

8. Best Practices

  • Fail Fast (Linting First): In the mini-project, Step 4 is a "Linter" (a syntax checker). It takes 2 seconds to run. Always run your fastest checks first. If a developer forgot a semicolon, the pipeline fails instantly, saving 10 minutes of cloud compute time that would have been wasted running heavy, slow integration tests.

9. Security Recommendations

  • Supply Chain Security: When you run npm install or composer install in your pipeline, you are downloading code from the internet and executing it on your server. If a malicious actor hacks an open-source library you depend on, your pipeline could be compromised. Always use lock files (e.g., composer.lock or package-lock.json) and run security audit commands (like npm audit) inside your pipeline.

10. Troubleshooting Tips

  • Composer/NPM Errors: If your dependency installation step fails with a network timeout, it's often a temporary issue with the package registry (like Packagist or npmjs.com), not your code. Try re-running the failed job in the GitHub UI before changing your workflow YAML.

11. Exercises

  1. 1. Why is it dangerous to omit the setup-* step and rely on the default software installed on the ubuntu-latest runner?
  1. 2. What is the purpose of the "Linting" step in a Continuous Integration pipeline?

12. FAQs

Q: Can I run a database like MySQL during the build phase? A: Yes! GitHub Actions provides a feature called services. You can spin up a temporary MySQL or Redis container right alongside your job to test database connections. We will explore this in the next chapter.

13. Interview Questions

  • Q: Explain the concept of "Dependency Hell" and how explicit environment setup steps (like actions/setup-node) mitigate this risk in a CI/CD pipeline.
  • Q: A developer commits a change where they accidentally missed a closing bracket } in a core application file. Walk through how the CI pipeline designed in this chapter detects and handles this error.

14. Summary

In Chapter 6, we evolved our workflows from simple scripts into functional Continuous Integration pipelines. We learned the critical necessity of explicitly defining our execution environment using setup actions, preventing unexpected breakages caused by underlying runner updates. We integrated package managers to hydrate our application with external dependencies, and we instituted a "Fail Fast" methodology by implementing rapid syntax linting as our first line of defense against buggy code.

15. Next Chapter Recommendation

Syntax checking is great, but it doesn't prove the math works. If 2 + 2 equals 5 in your code, the syntax linter won't care. We need real tests. Proceed to Chapter 7: Automated Testing with GitHub Actions.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·