-
-
Notifications
You must be signed in to change notification settings - Fork 6
GitHub Actions
- Introduction
- GitHub Actions workflow
- GitHub env configuration
- Secret configuration
- Build testing
- Deploying to server
GitHub Actions is very practical for Continuous Integration. It automates tasks such as building the application, running tests and deploying the code.
Automating these tasks not only makes life easier, but also reduces the risk for potentially fatal mistakes when forgetting to run a task.
A GitHub workflow is a YAML file that defines a set of jobs and steps that GitHub
Actions will automatically run on specified events.
This file is typically stored in the .github/workflows
directory of the repository.
The workflow file contains the following main sections:
-
name
: The name of the workflow. -
on
: This section specifies the events that trigger the workflow (e.g. push, pull request) -
jobs
: Defines the jobs that the workflow will run. Each job runs on an operating system (e.g. ubuntu) and contains a sequence of tasks called steps. Steps can run commands, run setup tasks, or run an action in your repository.
The GitHub Actions environment has its own configuration file config/env/env.github.php
.
For settings.php
to load the values of this file, the APP_ENV
environment
variable has to be set in the workflow file.
env:
APP_ENV: github
In addition to the GitHub Actions configuration values, the test values also need to be loaded since tests are run on the GitHub server.
File: config/env/env.github.php
<?php
// `require` has to be used instead of `require_once`.
// The values have to be loaded for each test case not only once for the whole test suite.
require __DIR__ . '/env.test.php';
// Database
$settings['db']['host'] = '127.0.0.1';
$settings['db']['database'] = 'slim_example_project_test';
$settings['db']['username'] = 'root';
// The password in the workflow file is 'root' and not empty
$settings['db']['password'] = 'root';
Sensitive values that should be hidden in the workflow file, such as API tokens, are stored as GitHub secrets accessible in the repository Settings > Security > Secrets and variables > Actions.
They can be accessed in the workflow file with the syntax ${{ secrets.SECRET_NAME }}
.
With the following workflow file build.yml
,
the application is built, and the tests are run on every push to the master
or develop
branch
as well as on every pull request to these branches.
For a more extended quality and security analysis, I recommend using a cloud-based code review tool that detects bugs, vulnerabilities, code smells and makes coverage reports. There are many great services, and most are free for open source projects (e.g. Scrutinizer and Code Climate).
With this project, I'm using SonarCloud.
The matrix
section defines the different variables that are used in the workflow.
They can be accessed with ${{ matrix.variable-name }}
.
File: .github/workflows/build.yml
name: 🧪 Build test
on:
push:
branches:
- master
- develop
pull_request:
types: [ opened, synchronize, reopened ]
env:
APP_ENV: github
jobs:
run:
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: [ ubuntu-latest ]
php-versions: [ '8.2' ]
test-database: [ 'slim_example_project_test' ]
name: PHP ${{ matrix.php-versions }} Test
services:
mysql:
image: mysql:8.0.23
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: test
ports:
- 33306:3306
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0 #sonarcloud shallow clone warning https://stackoverflow.com/a/62500400/9013718
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, pdo, pdo_mysql, intl, zip
coverage: xdebug
- name: Check PHP version
run: php -v
- name: Check Composer version
run: composer -V
- name: Check PHP extensions
run: php -m
- name: Check MySQL version
run: mysql -V
- name: Start MySQL
run: sudo systemctl start mysql
- name: Check MySQL variables
run: mysql -uroot -proot -e "SHOW VARIABLES LIKE 'version%';"
- name: Set MySQL timezone to swiss time
run: mysql -uroot -proot -e "SET GLOBAL time_zone = '+01:00';"
- name: Create database
run: mysql -uroot -proot -e 'CREATE DATABASE IF NOT EXISTS ${{ matrix.test-database }} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install dependencies
run: composer update --no-ansi --no-interaction --no-progress
- name: Execute database migrations
run: composer migrate-prod
- name: Show test db tables
run: mysql -uroot -proot -D ${{ matrix.test-database }} -e "SHOW TABLES;"
- name: Run test suite
run: composer test:coverage
env:
PHP_CS_FIXER_IGNORE_ENV: 1
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
The deployment process involves multiple steps that can be automated with GitHub Actions.
The code should only be deployed to the server if the tests are passing and the code was
pushed to the master
branch.
With FTP-Deploy-Action
the files are uploaded to the server via FTP.
The paths .git*/**
, tests/**
and docs/**
are excluded.
The database migrations are executed on the server by making an ssh connection to the terminal via ssh-action and then running the migration command.
File: .github/workflows/deployment.yml
name: 🚀 Deployment
# Only trigger, when the build workflow is done
on:
workflow_run:
workflows: [ "🧪 Build test" ]
types:
- completed
jobs:
run:
# Run job only on success of the build test workflow, and if the event was a push to the master branch
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'master' }}
runs-on: ubuntu-latest
name: Deploy PHP application
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
# extensions: mbstring, pdo, pdo_mysql, intl, zip
coverage: none
- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v2
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: 📂 Sync files
uses: SamKirkland/[email protected]
with:
server: ${{ secrets.FTP_HOST }}
username: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
server-dir: /
exclude: |
.git*/**
docs/**
tests/**
- name: Executing database migrations
uses: appleboy/[email protected]
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
passphrase: ${{ secrets.SSH_KEY_PASSPHRASE }}
port: ${{ secrets.SSH_PORT }}
script: |
cd ${{ secrets.DEMO_PROJECT_ROOT }}
chmod +x vendor/bin/phinx
composer migrate-prod
Slim app basics
- Composer
- Web Server config and Bootstrapping
- Dependency Injection
- Configuration
- Routing
- Middleware
- Architecture
- Single Responsibility Principle
- Action
- Domain
- Repository and Query Builder
Features
- Logging
- Validation
- Session and Flash
- Authentication
- Authorization
- Translations
- Mailing
- Console commands
- Database migrations
- Error handling
- Security
- API endpoint
- GitHub Actions
- Scrutinizer
- Coding standards fixer
- PHPStan static code analysis
Testing
Frontend
Other