Setting Up a CI/CD Pipeline for PHP Applications with Jenkins

Continuous integration and continuous deployment (CI/CD) are essential practices that help automate the development process, ensuring fast and reliable software delivery. Jenkins, a widely used open-source automation server, is an excellent tool for implementing CI/CD pipelines. This guide will walk you (and me, as I’m trying to learn as well) through setting up a CI/CD pipeline for PHP applications using Jenkins.

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Installing Jenkins
  4. Configuring Jenkins for PHP
  5. Creating a Jenkins Pipeline
  6. Automating Tests
  7. Building the PHP Application
  8. Deploying the Application
  9. Best Practices

Introduction

Continuous Integration (CI) and Continuous Deployment (CD) aim to reduce the manual effort involved in the software development process by automating the steps of building, testing, and deploying code changes. Jenkins, as a robust and extendable platform, allows developers to create pipelines that integrate these steps seamlessly. In this guide, we will focus on setting up a CI/CD pipeline for PHP applications using Jenkins, from installation to deployment.

Prerequisites

Before we begin, ensure you have the following:

  • A server or local machine with a Unix-based OS.
  • Java (JDK 11 or later) installed on your server.
  • PHP and Composer installed.
  • Git for version control.
  • Access to a web server or cloud environment for deployment (Apache or Nginx).
  • Basic understanding of PHP and Jenkins.

Installing Jenkins

First, you need to install Jenkins on your machine. Follow these steps to set up Jenkins:

Step 1: Install Java

Jenkins requires Java to run. Install OpenJDK using the following command:

sudo apt update
sudo apt install openjdk-11-jdk -y

Verify the installation:

java -version

Step 2: Add Jenkins Repository

Add the Jenkins Debian package repository to your system:

curl -fsSL https://pkg.jenkins.io/debian/jenkins.io.key | sudo tee \
 /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
 https://pkg.jenkins.io/debian binary/ | sudo tee \
 /etc/apt/sources.list.d/jenkins.list > /dev/null

Step 3: Install Jenkins

Update the package list and install Jenkins:

sudo apt update
sudo apt install jenkins -y

Step 4: Start and Enable Jenkins

Start Jenkins and enable it to start at boot:

sudo systemctl start jenkins
sudo systemctl enable jenkins

Step 5: Access Jenkins

Open a web browser and navigate to http://your-server-ip:8080. You will see the Jenkins setup screen.

Unlock Jenkins using the password from:

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Follow the instructions to install suggested plugins and create an admin user.

Configuring Jenkins for PHP

Once Jenkins is installed, configure it to work with PHP applications:

Step 1: Install PHP and Composer

If not already installed, install PHP and Composer:

sudo apt install php php-cli php-mbstring unzip
sudo apt install composer

Step 2: Install Required Jenkins Plugins

Navigate to Manage Jenkins > Manage Plugins. Install the following plugins:

  • Git: For version control.
  • PHP: For PHP code analysis and testing.
  • Pipeline: For creating pipelines as code.
  • SSH Agent: For deployment over SSH.

Step 3: Configure Global Tools

Go to Manage Jenkins > Global Tool Configuration and set up the following:

  • Git: Specify the path to the Git executable if not automatically detected.
  • PHP: Add the PHP installation path (usually /usr/bin/php).
  • Composer: Add the Composer installation path (usually /usr/bin/composer).

Creating a Jenkins Pipeline

Let’s create a Jenkins pipeline to automate the build and deployment process for a PHP application.

Step 1: Create a New Pipeline

  1. Go to New Item in Jenkins.
  2. Enter a name for your project, select Pipeline, and click OK.

Step 2: Define the Pipeline Script

Use Jenkins Pipeline syntax to define your pipeline in the Pipeline section of the project configuration. Below is a sample Jenkinsfile for a PHP application:

pipeline {
agent any

    environment {
        // Define any environment variables you need
        PHP_VERSION = '7.4'
    }

    stages {
        stage('Clone Repository') {
            steps {
                git branch: 'main', url: 'https://github.com/your-repo/php-app.git'
            }
        }

        stage('Install Dependencies') {
            steps {
                sh 'composer install'
            }
        }

        stage('Run Tests') {
            steps {
                sh 'vendor/bin/phpunit --coverage-text'
            }
        }

        stage('Static Analysis') {
            steps {
                sh 'vendor/bin/phpstan analyse src'
                sh 'vendor/bin/phpcs --standard=PSR12 src'
            }
        }

        stage('Build') {
            steps {
                echo 'Building the application...'
                // Add build steps here
            }
        }

        stage('Deploy') {
            steps {
                echo 'Deploying the application...'
                // Add deployment steps here
            }
        }
    }

    post {
        always {
            echo 'Cleaning up...'
            // Clean workspace
            cleanWs()
        }
        success {
            echo 'Deployment succeeded!'
        }
        failure {
            echo 'Deployment failed!'
        }
    }

}

Explanation

  • Agent: Specifies where the pipeline will run. Use any to run on any available agent.
  • Environment: Defines environment variables used across stages.
  • Stages: Represents steps in the pipeline (e.g., Clone, Test, Build, Deploy).
  • Post: Specifies actions to run after the pipeline (e.g., cleanup, notifications).

Automating Tests

Automating tests is a critical part of CI/CD. We’ll use PHPUnit for unit testing and integrate it into our Jenkins pipeline.

Step 1: Install PHPUnit

Add PHPUnit to your project using Composer:

composer require --dev phpunit/phpunit

Step 2: Write Test Cases

Create a tests directory in your project root and add your test cases. A simple example test case might look like this:


<?php

use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    public function testAddition()
    {
        $this->assertEquals(2, 1 + 1);
    }
}

Step 3: Configure PHPUnit

Create a phpunit.xml configuration file in your project root:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php" colors="true">
    <testsuites>
        <testsuite name="Application Test Suite">
            <directory>tests</directory>
        </testsuite>
    </testsuites>
</phpunit>

Step 4: Run Tests in Jenkins

In the pipeline script, the Run Tests stage executes PHPUnit:

stage('Run Tests') {
    steps {
        sh 'vendor/bin/phpunit --coverage-text'
    }
}

Building the PHP Application

The build stage in the pipeline compiles or packages the application for deployment. In PHP applications, this might involve:

  • Generating autoload files with Composer.
  • Packaging application assets.
  • Building Docker images if using containers.

Example Build Steps

Update the Build stage in the Jenkinsfile:

stage('Build') {
    steps {
        sh 'composer dump-autoload -o'
        // Add more build steps if necessary
    }
}

Deploying the Application

Deployment can be done using SSH, FTP, or a cloud provider’s API. For simplicity, we’ll use SSH to deploy to a remote server.

Step 1: Configure SSH Credentials

  1. Go to Manage Jenkins > Manage Credentials > (global) > Add Credentials.
  2. Add SSH credentials with your server’s details.

Step 2: Add Deployment Steps

In the Jenkinsfile, update the Deploy stage to use SSH for deployment:

stage('Deploy') {
    steps {
    script {
        def remote = [:]
        remote.name = 'production'
        remote.host = 'your-server-ip'
        remote.user = 'deploy-user'
        remote.identityFile = '/path/to/private/key'
        remote.allowAnyHosts = true

        sshCommand remote: remote, command: '''
            cd /path/to/application
            git pull origin main
            composer install --no-dev
            php artisan migrate --force
            php artisan cache:clear
            php artisan config:cache
            php artisan route:cache
        '''
        }
    }

}

Explanation

  • SSH Command: Executes remote commands over SSH to deploy the application.
  • Identity File: Path to the private SSH key for authentication.
  • Git Pull: Updates the codebase on the server.
  • Composer Install: Installs production dependencies.
  • Artisan Commands: Executes Laravel-specific commands (adjust for non-Laravel applications).

Best Practices

Here are some best practices for setting up a CI/CD pipeline with Jenkins for PHP applications:

  1. Use Branch-Based Pipelines: Create separate pipelines for different branches (e.g., develop, main) to handle development and production deployments.

  2. Implement Code Quality Checks: Use tools like PHPStan and PHP CodeSniffer to enforce coding standards and static analysis.

  3. Automate Security Checks: Integrate tools like Dependabot or Composer audit for checking dependencies’ vulnerabilities.

  4. Use Docker for Consistency: Dockerize your application and use Docker agents in Jenkins to ensure a consistent environment.

  5. Monitor and Log Pipelines: Implement logging and monitoring for your pipelines to catch and debug issues promptly.

  6. Secure Jenkins: Regularly update Jenkins and plugins, use SSL for secure connections, and implement role-based access control.

Setting up a CI/CD pipeline for PHP applications using Jenkins can significantly improve your development workflow by automating testing, building, and deployment processes.