![](/images/7/8/b/a/b/78babb9c4f2c70aacd118641e86dc45b62d58347-featured.webp)
Streamline Development with CI/CD on GitHub
Continuous Integration and Continuous Deployment or CI/CD have become a key tool for software development. CI/CD makes it easy to deliver code frequently, accurately and with the best quality possible since it removes the requirement of manual testing and deployment.
What is GitHub Actions?
GitHub Actions is a powerful feature integrated into GitHub, designed to help us automate our software development pipelines. It allows us to build, test, and deploy our code directly from GitHub. The magic of GitHub Actions relies in its flexibility and ease of use. With just a few YAML configuration files, we can build custom workflows that run whenever specific events occur in our repository, such as a push to a branch or pull request creation.
Imagine not having to worry about manually run tests or deploy applications. GitHub Actions will do it all for us. It supports a wide range of programming languages and platforms, making it suitable for any project. And obviously, it seamlessly integrates with the GitHub ecosystem, so we can use it alongside other tools we already love.
What is a Workflow?
In the world of GitHub Actions, a workflow is a customizable set of automated processes that can handle anything from running tests to deploying your latest masterpiece We may also know them as pipelines
. These workflows are defined using YAML files, which we simply check into our repository.
Setting Up a Workflow
Setting up GitHub Actions is simple, we just need to create a YAML file that defines our workflow and place it in the .github/workflows
directory of our repository. Let's add a workflow into the project of the previous post Static Analysis of PHP Code with PHPStan.The public repository is hexagonal-architecture-example-in-php.
name: Test
on:
push:
branches: [ 'master' ]
pull_request:
types: [ 'opened', 'synchronize', 'reopened' ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: [ '8.2', '8.3' ]
steps:
- name: Checkout source code
uses: actions/checkout@v2
with:
fetch-depth: 0
In the example above we have defined a new workflow, let's break down the code and explain what is happening:
- The workflow name is
Test
. - This work flow will be executed for two events:
- When
push
code into the branchmaster
. - When
pull_request
isopened
,synchronize
andreopened
.
- When
- We have define one job named
test
. - The
test
job runs on aubuntu-latest
operative system. - As our code is PHP we define that the
test
job needs to be executed for two different PHP versions8.2
and8.3
. - Finally, we use the
steps
section to define each action we need to execute within our workflow or pipeline.
What to Include in the Workflow
At least, our workflows should include the following steps:
- Runtime Configuration: Set up the necessary runtime environment for the project.
- Code Checkout: Retrieve the latest code from the repository.
- Install Dependencies: Install all required dependencies for the project.
- Test Execution: Run all necessary tests to ensure code functionality.
- Code Style Check: Verify that the code adheres to the project's style guidelines.
- Static Code Analysis: Analyze the code to identify potential issues and enforce coding standards.
Let's add some of these steps to our workflow:
name: Test
on:
push:
branches: [ 'master' ]
pull_request:
types: [ 'opened', 'synchronize', 'reopened' ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: [ '8.2', '8.3' ]
steps:
- name: Checkout Source Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: xdebug
tools: composer:v2
- name: Cache Dependencies
uses: actions/cache@v4
with:
path: ~/.composer/cache
key: php-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: php-${{ matrix.php-version }}-composer-
- name: Validate Composer Files
run: composer validate
- name: Install Dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
run: composer install --prefer-dist --no-progress --no-suggest
- name: Prepare App Installation
run: |
mkdir -p data
composer db:initialize
- name: Execute Static Code Analysis
run: composer workflow:analyze
- name: Execute Unit, Integration and Acceptance Tests
run: composer workflow:test
Again, let's break down the code and explain what we just did:
- Code Checkout: Use the Checkout Action.
- Set Up PHP Version: Use the Setup PHP Action.
- Cache Dependencies: Use the Cache Action. For more information, see Caching dependencies to speed up workflows.
- Validate Composer Files: Run the
composer validate
command. - Install Dependencies: First, check if cached dependencies are available. If not, run the usual
composer install
command. - Prepare Project Installations: Run commands to prepare and install the application, such as deploying the database schema if required.
- Execute Static Code Analysis.
- Execute Tests.
Running a Workflow
Now that everything is set up, all we have to do is commit and push our code to a new branch. After that, we'll create a new pull request (PR). GitHub Actions will kick in automatically since we've configured our workflow to run when a pull request is opened. On the PR page, we'll see the status of the workflows we've defined.
In this case the workflow has been successful, let's review an example of a failed workflow. For example, we can remove some PHPDoc to force PHPStan to fail static code analysis.
We can get more intel about the error by clicking in the "Details" link of each workflow check, let's review the 8.2
execution.
Here, we can check out the details about the errors. Pretty cool, huh? But guess what? There's another way to get even more detailed insights into what's gone wrong. We've set up PHPStan with --error-format=github
, so this will give us the following output:
> vendor/bin/phpstan analyse --error-format=github --no-progress
Note: Using configuration file hexagonal-architecture-example/phpstan.neon.
::error file=src/UserManagement/Application/Contract/UserRepository.php,line=13,col=0::Method OtherCode\UserManagement\Application\Contract\UserRepository::all() return type has no value type specified in iterable type array.
::error file=src/UserManagement/Infrastructure/Persistence/DoctrineUserRepository.php,line=28,col=0::Method OtherCode\UserManagement\Infrastructure\Persistence\DoctrineUserRepository::all() return type has no value type specified in iterable type array.
::error file=src/UserManagement/Infrastructure/Persistence/JsonFileUserRepository.php,line=65,col=0::Method OtherCode\UserManagement\Infrastructure\Persistence\JsonFileUserRepository::all() return type has no value type specified in iterable type array.
Script vendor/bin/phpstan analyse --error-format=github --no-progress handling the workflow:analyze event returned with error code 1
This is a special output that GitHub knows how to handle, letting us see the errors directly in the "file changes" tab of the PR.
Now, this is pretty amazing! With this setup, we can easily understand what is happening and fix it properly. This integration dramatically improves the developer experience.
Conclusion
In conclusion, integrating CI/CD with GitHub Actions really improve our development workflow. By automating tasks like code analysis and testing, we ensure quicker and more reliable software delivery. GitHub Actions gives us this awesome platform to define our development pipelines in a easy way, which means our code gets better and our team works more efficiently.