GitHub Actions is a tool for automating routine actions from your package on GitHub.
From personal experience, I will tell you how, without experience and knowledge about setting up CI, I learned to automate a routine in my Open Source project in just a day and that in fact it is really not as scary and difficult as many people think.
GitHub provides really convenient and working tools for this.
Action plan
set up CI in GitHub Actions for a small PHP project
( )
, / , PR-s ( ), Check Suite PR, .
, , packagist.
, , , Open-Source , , -, . , , .
CI
CI , , PHP Postgres CI . , .
:
.github/workflows/ci.yml
name: CI
on:
push:
branches:
- master
pull_request:
types:
- opened
- reopened
- edited
- synchronize
env:
COVERAGE: '1'
php_extensions: 'pdo, pdo_pgsql, pcntl, pcov, ...'
key: cache-v0.1
DB_USER: 'postgres'
DB_NAME: 'testing'
DB_PASSWORD: 'postgres'
DB_HOST: '127.0.0.1'
jobs:
lint:
runs-on: '${{ matrix.operating_system }}'
timeout-minutes: 20
strategy:
matrix:
operating_system: ['ubuntu-latest']
php_versions: ['7.4']
fail-fast: false
env:
PHP_CS_FIXER_FUTURE_MODE: '0'
name: 'Lint PHP'
steps:
- name: 'Checkout'
uses: actions/checkout@v2
- name: 'Setup cache environment'
id: cache-env
uses: shivammathur/cache-extensions@v1
with:
php-version: '${{ matrix.php_versions }}'
extensions: '${{ env.php_extensions }}'
key: '${{ env.key }}'
- name: 'Cache extensions'
uses: actions/cache@v1
with:
path: '${{ steps.cache-env.outputs.dir }}'
key: '${{ steps.cache-env.outputs.key }}'
restore-keys: '${{ steps.cache-env.outputs.key }}'
- name: 'Setup PHP'
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php_versions }}
extensions: '${{ env.php_extensions }}'
ini-values: memory_limit=-1
tools: pecl, composer
coverage: none
- name: 'Setup problem matchers for PHP (aka PHP error logs)'
run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"'
- name: 'Setup problem matchers for PHPUnit'
run: 'echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"'
- name: 'Install PHP dependencies with Composer'
run: composer install --prefer-dist --no-progress --no-suggest --optimize-autoloader
working-directory: './'
- name: 'Linting PHP source files'
run: 'composer lint'
test:
strategy:
fail-fast: false
matrix:
operating_system: ['ubuntu-latest']
postgres: [11, 12]
php_versions: ['7.3', '7.4', '8.0']
experimental: false
include:
- operating_system: ubuntu-latest
postgres: '13'
php_versions: '8.0'
experimental: true
runs-on: '${{ matrix.operating_system }}'
services:
postgres:
image: 'postgres:${{ matrix.postgres }}'
env:
POSTGRES_USER: ${{ env.DB_USER }}
POSTGRES_PASSWORD: ${{ env.DB_PASSWORD }}
POSTGRES_DB: ${{ env.DB_NAME }}
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
name: 'Test / PHP ${{ matrix.php_versions }} / Postgres ${{ matrix.postgres }}'
needs:
- lint
steps:
- name: 'Checkout'
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: 'Install postgres client'
run: |
sudo apt-get update -y
sudo apt-get install -y libpq-dev postgresql-client
- name: 'Setup cache environment'
id: cache-env
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ matrix.php_versions }}
extensions: ${{ env.php_extensions }}
key: '${{ env.key }}'
- name: 'Cache extensions'
uses: actions/cache@v1
with:
path: '${{ steps.cache-env.outputs.dir }}'
key: '${{ steps.cache-env.outputs.key }}'
restore-keys: '${{ steps.cache-env.outputs.key }}'
- name: 'Setup PHP'
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php_versions }}
extensions: ${{ env.php_extensions }}
ini-values: 'pcov.directory=src, date.timezone=UTC, upload_max_filesize=20M, post_max_size=20M, memory_limit=512M, short_open_tag=Off'
coverage: pcov
tools: 'phpunit'
- name: 'Install PHP dependencies with Composer'
run: composer install --prefer-dist --no-progress --no-suggest --optimize-autoloader
working-directory: './'
- name: 'Run Unit Tests with PHPUnit'
continue-on-error: ${{ matrix.experimental }}
run: |
sed -e "s/\${USERNAME}/${{ env.DB_USER }}/" \
-e "s/\${PASSWORD}/${{ env.DB_PASSWORD }}/" \
-e "s/\${DATABASE}/${{ env.DB_NAME }}/" \
-e "s/\${HOST}/${{ env.DB_HOST }}/" \
phpunit.xml.dist > phpunit.xml
./vendor/bin/phpunit \
--verbose \
--stderr \
--coverage-clover build/logs/clover.xml
working-directory: './'
- name: 'Upload coverage results to Coveralls'
if: ${{ !matrix.experimental }}
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_PARALLEL: true
COVERALLS_FLAG_NAME: php-${{ matrix.php_versions }}-postgres-${{ matrix.postgres }}
run: |
./vendor/bin/php-coveralls \
--coverage_clover=build/logs/clover.xml \
-v
coverage:
needs: test
runs-on: ubuntu-latest
name: "Code coverage"
steps:
- name: 'Coveralls Finished'
uses: coverallsapp/github-action@v1.1.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
, 3 (lint, tests coverage)
lint - , , , , .
code style , CI . , umbrellio/code-style-php, ( , ):
"scripts": {
"lint": "ecs check --config=ecs.yml .",
"lint-fix": "ecs check --config=ecs.yml . --fix"
}
test - (os, postgres php), include - ( ).
, :
experimental ( ) , CI, , PHP, . ( ).
sed -e "s/\${USERNAME}/${{ env.DB_USER }}/"...
, phpunit.xml.dist phpunit.xml, , ENV, :
phpunit.xml.dist
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
>
<php>
<env name="APP_ENV" value="testing"/>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="pdo_pgsql"/>
<var name="db_host" value="${HOST}" />
<var name="db_username" value="${USERNAME}" />
<var name="db_password" value="${PASSWORD}" />
<var name="db_database" value="${DATABASE}" />
<var name="db_port" value="5432"/>
</php>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
<exclude>
<file>./src/.meta.php</file>
</exclude>
</whitelist>
</filter>
<testsuites>
<testsuite name="Test suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>
coverage - .. , .. Postgres, , 100% . , composer, Bandler- Ruby, .
.. badge:100% coverage, , , . , coveralls.io .
CI, .
- (labels)
( ):
.github/labeler.config.yml
type:build:
- ".github/**/*"
- ".coveralls.yml"
- ".gitignore"
- "ecs.yml"
- "phpcs.xml"
dependencies:
- "composer.json"
- "composer.lock"
type:common
- "src/**/*"
type:tests:
- 'tests/**/*'
- 'phpunit.xml.dist'
- 'tests.sh'
theme:docs:
- "README.md"
- "LICENSE"
- "CONTRIBUTING.md"
- "CODE_OF_CONDUCT.md"
, , PR, PR.
, Summary PR ( patch, minor major). , -, , (pull-) .
.github/workflows/labeler.yml
name: "Auto labeling for a pull request"
on:
- pull_request_target
jobs:
triage:
name: "Checking for labels"
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@main
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
configuration-path: ".github/labeler.config.yml"
-
( ):
.github/assignee.config.yml
addReviewers: true
numberOfReviewers: 1
reviewers:
- pvsaitpe
addAssignees: true
assignees:
- pvsaintpe
numberOfAssignees: 1
skipKeywords:
- wip
- draft
, - , GitHub.
.github/workflows/assignee.yml
name: 'Auto assign assignees or reviewers'
on: pull_request
jobs:
add-reviews:
name: "Auto assignment of a assignee"
runs-on: ubuntu-latest
steps:
- uses: kentaro-m/auto-assign-action@v1.1.2
with:
configuration-path: ".github/assignee.config.yml"
- PR
:
.github/workflows/auto_merge.yml
name: 'Auto merge of approved pull requests with passed checks'
on:
pull_request:
types:
- labeled
- unlabeled
- synchronize
- opened
- edited
- ready_for_review
- reopened
- unlocked
pull_request_review:
types:
- submitted
check_suite:
types:
- completed
status: {}
jobs:
automerge:
runs-on: ubuntu-latest
steps:
- name: 'Automerge PR'
uses: "pascalgn/automerge-action@v0.12.0"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MERGE_METHOD: 'squash'
MERGE_LABELS: "approved,!work in progress"
MERGE_REMOVE_LABELS: "approved"
MERGE_COMMIT_MESSAGE: "pull-request-description"
MERGE_RETRIES: "6"
MERGE_RETRY_SLEEP: "10000"
UPDATE_LABELS: ""
UPDATE_METHOD: "rebase"
MERGE_DELETE_BRANCH: false
, PR, approved, CheckSuite .
Squash, .
- PR
PR, approved, :
.github/workflows/auto_approve.yml
on: pull_request_review
name: 'Label approved pull requests'
jobs:
labelWhenApproved:
name: 'Label when approved'
runs-on: ubuntu-latest
steps:
- name: 'Label when approved'
uses: pullreminders/label-when-approved-action@master
env:
APPROVALS: "1"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ADD_LABEL: "approved"
REMOVE_LABEL: "awaiting review"
-
( ) :
.github/release-drafter.yml
template: |
## Changes
$CHANGES
change-template: '- **$TITLE** (#$NUMBER)'
version-template: "$MAJOR.$MINOR.$PATCH"
name-template: '$RESOLVED_VERSION'
tag-template: '$RESOLVED_VERSION'
categories:
- title: 'Features'
labels:
- 'feature'
- 'type:common'
- title: 'Bug Fixes'
labels:
- 'fix'
- 'bugfix'
- 'bug'
- 'hotfix'
- 'dependencies'
- title: 'Maintenance'
labels:
- 'type:build'
- 'refactoring'
- 'theme:docs'
- 'type:tests'
change-title-escapes: '\<*_&'
version-resolver:
major:
labels:
- major
- refactoring
minor:
labels:
- feature
- minor
- type:common
patch:
labels:
- patch
- type:build
- bug
- bugfix
- hotfix
- fix
- theme:docs
- type:tests
default: patch
, MAJOR, MINOR, PATCH
.github/workflows/release_drafter.yml
name: Release Drafter
on:
push:
branches:
- master
jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v5
with:
publish: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GitHub Settings
Check Suite GitHub
GitHub , , , , , 100%, , Check Suite.
,
. , , :
approvals
, - , PR, , , Code Owners .
, approvalls
Check Suite
( CI , , , , Coveralls / Scrutinizer, ), .
, PR .
, Check Suite
, , Settings => Options Squash, "Automatically delete head branches"
- packagist.org
, packagist , .
, webhook packagist
packagist (Show Api Token).
, - OpenSource , Contributor- ( ), CI , , , workflow .
coveralls / scrutinizer , Check Suite % 100%, Readme , :
I will be glad if my tutorial is useful to someone, because Before writing this post, I first encountered GitHub Actions, I am not DevOps and I am not engaged in setting up CI, I myself had to google more than one site to set up such a workflow that I needed.