A fully working example is available at https://github.com/robiningelbrecht/continuous-integration-example
Acronyms for
DevOps best practice where developers frequently merge code changes into a central repository where automated builds and tests run.
An automated release process where you can deploy your application any time by clicking a button.
A CI/CD platform that allows you to
https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions
A runner is a server that runs your workflows when they're triggered
An event is a specific activity in a repository that triggers a workflow run
https://docs.github.com/en/actions/reference/events-that-trigger-workflows
Contains a set of steps that execute on the same runner. Each step is either a
Steps are executed in order and are dependent on each other
name: learn-github-actions
on: [push] # Event
jobs:
name: install-dependencies
runs-on: ubuntu-latest # Runner
steps: # Actions
- name: Setup PHP 8.1 with Xdebug 3.x # Pre-defined action
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
coverage: xdebug
- name: Install dependencies # Shell script
run: composer install --prefer-dist
Small and easy testable app to order pizza
https://github.com/robiningelbrecht/continuous-integration-example/tree/master/src
$pizza = new ExtraCheese(new Pepperoni(new BasicPizza(Size::MEDIUM, Crust::THIN)));
$pizza->getPrice();
// [
// "amount": "1575",
// "currency": "EUR"
// ]
$pizza->getDescription();
// Medium pizza, Thin crust, Tomato sauce, Cheese, Extra cheese, Pepperoni
Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface. The resulting object will get a stacking behavior of all wrappers.
$pizza = PizzaBuilder::fromSizeAndCrust(Size::MEDIUM, Crust::THIN)
->withToppings(
Topping::EXTRA_CHEESE,
Topping::PEPPERONI
)
->build();
$pizza->getPrice();
// [
// "amount": "1575",
// "currency": "EUR"
// ]
$pizza->getDescription();
// Medium pizza, Thin crust, Tomato sauce, Cheese, Extra cheese, Pepperoni
Builder is a creational design pattern, which allows constructing complex objects step by step.
$pizza = PizzaFactory::pepperoni(Size::MEDIUM, Crust::THIN)
$pizza->getPrice();
// [
// "amount": "1575",
// "currency": "EUR"
// ]
$pizza->getDescription();
// Medium pizza, Thin crust, Tomato sauce, Cheese, Extra cheese, Pepperoni
(Abstract) Factory is a creational design pattern, which solves the problem of creating entire product families without specifying their concrete classes.
You can set the default branch to whatever you want, but usually "main" or "master" are used.
https://github.com/username/repository/settings/branches
Important to ensure code quality and have a solid CI
All other options should stay unchecked... for now π
Standardize the information for issues and pull requests in your repository
Contains two jobs that should ensure code quality
name: CI
on:
pull_request:
workflow_dispatch:
Install PHP and Xdebug
# https://github.com/marketplace/actions/setup-php-action
name: Setup PHP 8.1 with Xdebug 3.x
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
coverage: xdebug
Pull in the code and install dependencies
# https://github.com/marketplace/actions/checkout
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: composer install --prefer-dist
- name: Run test suite
run: vendor/bin/phpunit --testsuite unit --fail-on-incomplete --log-junit junit.xml --coverage-clover clover.xml
# https://github.com/marketplace/actions/publish-unit-test-results
- name: Publish test results
uses: EnricoMi/[email protected]
if: always()
with:
files: "junit.xml"
check_name: "Unit test results"
with codecov.io and generated clover.xml report
# https://github.com/marketplace/actions/codecov
- name: Send test coverage to codecov.io
uses: codecov/[email protected]
with:
files: clover.xml
fail_ci_if_error: true # optional (default = false)
verbose: true # optional (default = false)
Ensure minimum test coverage across the project.
- name: Check minimum required test coverage
run: |
CODE_COVERAGE=$(vendor/bin/coverage-checker clover.xml 90 --processor=clover-coverage)
echo ${CODE_COVERAGE}
if [[ ${CODE_COVERAGE} == *"test coverage, got"* ]] ; then
exit 1;
fi
Run your test suite against multiple PHP versions and/or operating systems
name: Test suite PHP ${{ matrix.php-versions }} on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: ['ubuntu-latest', 'ubuntu-18.04']
php-versions: [ '7.4', '8.0', '8.1' ]
steps:
- name: Setup PHP ${{ matrix.php-versions }} with Xdebug 3.x
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
coverage: xdebug
This should result in a workflow run for all possible combinations in the matrix
# https://github.com/marketplace/actions/setup-php-action
- name: Setup PHP 8.1
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
# https://github.com/marketplace/actions/checkout
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: composer install --prefer-dist
Run static code analyser
- name: Run PHPStan
run: vendor/bin/phpstan analyse
Check coding standards
- name: Run PHPcs fixer dry-run
run: vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --config=.php-cs-fixer.dist.php
Tighten branch protection rules by adding extra required status checks
Both jobs in CI workflow need to succeed before the PR can be merged.
Contains two jobs
name: Build & deploy
on:
workflow_dispatch:
Only run when initialized with proper branches
if: github.ref_name == 'master' || github.ref_name == 'development'
name: Create build ${{ github.run_number }} for ${{ github.ref_name }}
runs-on: ubuntu-latest
# https://github.com/marketplace/actions/checkout
- name: Checkout code
uses: actions/checkout@v2
# https://github.com/marketplace/actions/setup-php-action
- name: Setup PHP 8.1
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
- name: Install dependencies
run: composer install --prefer-dist --no-dev
# https://github.com/marketplace/actions/upload-a-build-artifact
- name: Create artifact
uses: actions/upload-artifact@v3
with:
name: release-${{ github.run_number }}
path: |
src/**
vendor/**
Can be downloaded and verified from the workflow summary page.
Deploy the build we created in the previous step, but before we can do this, we first need to configure environments.
https://github.com/username/repository/settings/environments
needs: build
environment:
name: ${{ github.ref_name }}
url: https://${{ github.ref_name }}.env
${{ github.ref_name }}
contains branch or tag.
Make sure only one deploy (per environment) at a time can be run.
concurrency: ${{ github.ref_name }}
# https://github.com/marketplace/actions/download-a-build-artifact
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: release-${{ github.run_number }}
# https://github.com/marketplace/actions/rsync-deployments-action
- name: Rsync build to server
uses: burnett01/[email protected]
with:
switches: -avzr --delete
path: .
remote_path: /var/www/release-${{ github.run_number }}/
remote_host: ${{ secrets.SSH_HOST }}
remote_user: ${{ secrets.SSH_USERNAME }}
remote_key: ${{ secrets.SSH_KEY }}
# https://github.com/marketplace/actions/ssh-remote-commands
- name: Run remote SSH commands
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script: |
RELEASE_DIRECTORY=/var/www/release-${{ github.run_number }}
CURRENT_DIRECTORY=/var/www/app
# Manage symlinks.
rm -r "${CURRENT_DIRECTORY}"
ls -s "${RELEASE_DIRECTORY}" "${CURRENT_DIRECTORY}"
# Run database migrations
drush updb -y
drush cim -y
# Install updated crontab
crontab ${RELEASE_DIRECTORY}/crontab
# Rebuild cache
drush cr -y
Integration testing is the phase in software testing in which individual software modules arecombined and tested as a group.https://codeception.com/
End-to-end testing is a technique that tests the entire software product from beginning to end to ensure the application flow behaves as expected.https://codecept.io/
A visual regression test checks what the user will see after any code changes have been executed by comparing screenshots taken before and after deploys.https://github.com/garris/BackstopJS
on:
push:
branches:
- master
- develop
Composite actions can be used to split workflows into smaller, reusable components.
This blogpost does a perfect job at explaining how to define and use them. Big up to the author James Wallis π
You can add several badges on the readme page of your repository to indicate the status of your project