GitHub recently launched the new Actions CI/CD feature (which is still in beta so you may need to request access) which allows you to run the usual CI/CD checks and builds that you would normally use via a third-party platform. The documentation is still a little light at the time of writing this, but below we’ve documented how to set the usual tasks:
- Yarn/npm install & build
- Composer install
- phpcs (with WP & VIP Coding Standard)
- phpunit
- committing the built code to a {branch}-built
To get started you’ll need to create a .github
folder in your repository root. Inside, you should create another folder, called workflows
. You can use multiple workflows, but for this example we only need one so we will create a file main.yml
. The next step is to add the following to the file:
name: CI Build
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v1
- name: Show files
run: |
ls -la
name
is just what we want to call this workflow, and on
is the action which triggers it (we will use push for now). Actions will run on a VM, therefore, you will need to specify the type. runs-on
lets you select Linux, Windows, OSX. We will use Ubuntu as it provides the most flexible solution for us.
Under steps
you should add each task you will need to carry out. There are two types of steps, one that uses
an action and the other is a terminal command. The action type Checkout Repository
calls an action to check out the repository. These work by pointing to a GitHub repository:
uses: {org}/{repo}@{version}
The action
organisation is managed by GitHub and you can find lots of actions there. The other step Show files
runs a command, or it can be a list of commands each on a new line.
Ubuntu VM comes with a lot preinstalled including PHP, Yarn and Composer on there making the set up relatively quick and easy. The only other action step we will use it to control the Node version actions/setup-node@v1
name: CI Build
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v1
- name: Set Node version to 12
uses: actions/setup-node@v1
with:
version: 12
- name: Run yarn install
run: |
yarn install
- name: Run yarn build
run: |
yarn run build --if-present
- name: Run composer install
run: |
composer install --prefer-dist
- name: Install PHPCS with WordPress Coding Standards
run: |
composer global require dealerdirect/phpcodesniffer-composer-installer:0.5.0 wp-coding-standards/wpcs:2.1.1 automattic/vipwpcs:2.0.0
- name: Run PHPCS Coding Standards
run: |
/home/runner/.composer/vendor/bin/phpcs $GITHUB_WORKSPACE
- name: Create a built branch
run: |
BRANCH_NAME=$(echo $GITHUB_REF | grep -oP '(?<=refs\/heads\/).*')
rm .gitignore
mv .deployignore .gitignore
git config --global user.email "ci@company.com"
git config --global user.name "Company CI"
git remote set-url origin https://$GITHUB_ACTOR:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY.git
git checkout -b $BRANCH_NAME-built
git add -A && git commit -m "built from $GITHUB_SHA"
git push --force -u origin $BRANCH_NAME-built
The above should seem similar to using any other CI, but a few things to note is the environment variables – you will find a list of them on the outdated docs. GITHUB_WORKSPACE
points to where your files will be, so it is needed when running certain tasks. The final step is a little bit more complex as we work out the branch name, remove the .gitignore
to allow to commit files in the vendor and build folders. However, there are still things we will want to ignore, such as node modules, so we use .deployignore
. GITHUB_TOKEN
is passed down to us automatically, which allows us access to the GitHub API and allow the ability to write to the repository. If using something like Travis CI, you would need to manually provide access to each repository.
Unfortunately, there are a few issues with the above set up – we have no control over the PHP version and if we wanted to run PHPunit we would need set up MySQL. As it’s just Ubuntu, we can write a command to uninstall and reinstall a different version of PHP, and we could easily set up MySQL if needed. However, GitHub provides a better platform to extend and work with whatever set up you need, you can use Docker. Taking the simple example from earlier and adding Docker:
name: CI Build
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
container:
image: php:7.3-apache
env:
NODE_ENV: production
ports:
- 80
volumes:
- $GITHUB_WORKSPACE:/nas/content/live/bigbitev3
steps:
- name: Checkout Repository
uses: actions/checkout@v1
- name: Show files
run: |
ls -la
We add the container
section and select an image from Docker Hub. We will use the official PHP image php:7.3-apache
. env
which allows us to set any environment variables we might need, in this case, I set the NODE_ENV
to production
. Then ports
lets us map ports from host to the container, which in this case 80 is mapped to 80. Then lastly, we can point to the workspace to apache root.
Now that we are using a Docker image we no longer have Yarn and Composer installed by default. So we must do some setup of the container:
name: CI Build
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
container:
image: php:7.3-apache
env:
NODE_ENV: production
ports:
- 80
volumes:
- $GITHUB_WORKSPACE:/nas/content/live/bigbitev3
steps:
- name: Set up container
run: |
echo "Update package lists."
apt-get update
echo "Install base packages."
apt-get install -y build-essential libssl-dev gnupg libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev libicu-dev libxml2-dev vim wget unzip git subversion default-mysql-client
echo "Add yarn package repository."
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
echo "Update package lists."
apt-get update
echo "Install NVM."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
. ~/.nvm/nvm.sh
echo "Install node."
nvm install 12.9.0
nvm use 12.9.0
echo "Install yarn."
apt-get install -y yarn
echo "Install composer."
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
echo "Install PHP extensions."
docker-php-ext-install -j$(nproc) iconv intl xml soap opcache pdo pdo_mysql mysqli mbstring
docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/
docker-php-ext-install -j$(nproc) gd
pecl install mcrypt-1.0.2
docker-php-ext-enable mcrypt
- name: Checkout repository
uses: actions/checkout@v1
- name: Run yarn install
run: |
yarn install
- name: Run yarn build
run: |
yarn run build --if-present
- name: Run composer install
run: |
composer install --prefer-dist
- name: Install PHPCS with WordPress Coding Standards
run: |
composer global require dealerdirect/phpcodesniffer-composer-installer:0.5.0 wp-coding-standards/wpcs:2.1.1 automattic/vipwpcs:2.0.0
- name: Run PHPCS Coding Standards
run: |
~/.composer/vendor/bin/phpcs $GITHUB_WORKSPACE
- name: Create a built branch
run: |
BRANCH_NAME=$(echo $GITHUB_REF | grep -oP '(?<=refs\/heads\/).*')
rm .gitignore
mv .deployignore .gitignore
git config --global user.email "ci@company.com"
git config --global user.name "Company CI"
git remote set-url origin https://$GITHUB_ACTOR:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY.git
git checkout -b $BRANCH_NAME-built
git add -A && git commit -m "built from $GITHUB_SHA"
git push --force -u origin $BRANCH_NAME-built
So we install some base packages then NVM to allow use to select a Node version, then yarn and composer. The last section add PHP extensions, this can be very slow so only do so if needed:
echo "Install PHP extensions."
docker-php-ext-install -j$(nproc) iconv intl xml soap opcache pdo pdo_mysql mysqli mbstring
docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/
docker-php-ext-install -j$(nproc) gd
pecl install mcrypt-1.0.2
docker-php-ext-enable mcrypt
After that, the steps are pretty much the same. Now to add MySQL we could install in this current container (but that’s not how Docker is meant to be used) so instead we add service, which is other Docker containers. Taking the short example from earlier:
name: CI Build
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
container:
image: php:7.3-apache
env:
NODE_ENV: production
ports:
- 80
volumes:
- $GITHUB_WORKSPACE:/nas/content/live/bigbitev3
services:
mysql:
image: mysql:5.7.27
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306
volumes:
- $HOME/mysql:/var/lib/mysql
steps:
- name: Checkout Repository
uses: actions/checkout@v1
- name: Show files
run: |
ls -la
Adding the services
section allows us to add a list of named containers. In this case, we will just be using one, so we will name it mysql
and specify the image mysql:5.7.27
which is another official image. The rest of the setup is setting MySQL password and setting the correct port and volume. So now we can add PHPUnit:
name: CI Build
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
container:
image: php:7.3-apache
env:
NODE_ENV: production
ports:
- 80
volumes:
- $GITHUB_WORKSPACE:/nas/content/live/bigbitev3
services:
mysql:
image: mysql:5.7.27
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306
volumes:
- $HOME/mysql:/var/lib/mysql
steps:
- name: Set up container
run: |
echo "Update package lists."
apt-get update
echo "Install base packages."
apt-get install -y build-essential libssl-dev gnupg libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev libicu-dev libxml2-dev vim wget unzip git subversion default-mysql-client
echo "Add yarn package repository."
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
echo "Update package lists."
apt-get update
echo "Install NVM."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
. ~/.nvm/nvm.sh
echo "Install node."
nvm install 12.9.0
nvm use 12.9.0
echo "Install yarn."
apt-get install -y yarn
echo "Install composer."
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
echo "Install PHP extensions."
docker-php-ext-install -j$(nproc) iconv intl xml soap opcache pdo pdo_mysql mysqli mbstring
docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/
docker-php-ext-install -j$(nproc) gd
pecl install mcrypt-1.0.2
docker-php-ext-enable mcrypt
- name: Checkout repository
uses: actions/checkout@v1
- name: Run yarn install
run: |
yarn install
- name: Run yarn build
run: |
yarn run build --if-present
- name: Run composer install
run: |
composer install --prefer-dist
- name: Install WordPress Test Suite
run: |
bash bin/install-wp-tests.sh wordpress_test root root mysql 5.2.2
- name: Install PHPUnit
run: |
composer global require "phpunit/phpunit=7.5.15"
- name: Run PHPUnit
run: |
~/.composer/vendor/bin/phpunit $GITHUB_WORKSPACE
- name: Install PHPCS with WordPress Coding Standards
run: |
composer global require dealerdirect/phpcodesniffer-composer-installer:0.5.0 wp-coding-standards/wpcs:2.1.1 automattic/vipwpcs:2.0.0
- name: Run PHPCS Coding Standards
run: |
~/.composer/vendor/bin/phpcs $GITHUB_WORKSPACE
- name: Create a built branch
run: |
BRANCH_NAME=$(echo $GITHUB_REF | grep -oP '(?<=refs\/heads\/).*')
rm .gitignore
mv .deployignore .gitignore
git config --global user.email "ci@company.com"
git config --global user.name "Company CI"
git remote set-url origin https://$GITHUB_ACTOR:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY.git
git checkout -b $BRANCH_NAME-built
git add -A && git commit -m "built from $GITHUB_SHA"
git push --force -u origin $BRANCH_NAME-built
The last remaining issue we have is the above, and luckily is the easiest to update. If you wanted to change the version of PHP or even if you needed to test against two versions of PHP or WordPress, GitHub provides a solution to this called a matrix. You can add variables which can be one value or a list of values, and if a list is provided the action is run once for each value. For example, if you provided two versions of PHP and two versions of WordPress it would run a total of four times. Taking the short example from earlier:
name: CI Build
on: [push]
jobs:
build:
name: Build
strategy:
matrix:
php: [7.3,7.2]
runs-on: ubuntu-latest
container:
image: php:${{ matrix.php }}-apache
env:
NODE_ENV: production
ports:
- 80
volumes:
- $GITHUB_WORKSPACE:/nas/content/live/bigbitev3
steps:
- name: Checkout Repository
uses: actions/checkout@v1
- name: Show files
run: |
ls -la
Adding a strategy
section with a matrix
section within. We then add each variable, in this case, I used php
and give it two values, which I can later use by calling ${{ matrix.php }}
. Adding this to the final example you should get:
name: CI Build
on: [push]
jobs:
build:
name: Build
strategy:
matrix:
php: [7.3]
node: [12.9.0]
mysql: [5.7.27]
wordpress: [5.2.2]
phpunit: [7.5.15]
wpcs: [2.1.1]
vipcs: [2.0.0]
runs-on: ubuntu-latest
container:
image: php:${{ matrix.php }}-apache
env:
NODE_ENV: production
ports:
- 80
volumes:
- $GITHUB_WORKSPACE:/nas/content/live/bigbitev3
services:
mysql:
image: mysql:${{ matrix.mysql }}
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306
volumes:
- $HOME/mysql:/var/lib/mysql
steps:
- name: Set up container
run: |
echo "Update package lists."
apt-get update
echo "Install base packages."
apt-get install -y build-essential libssl-dev gnupg libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev libicu-dev libxml2-dev vim wget unzip git subversion default-mysql-client
echo "Add yarn package repository."
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
echo "Update package lists."
apt-get update
echo "Install NVM."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
. ~/.nvm/nvm.sh
echo "Install node."
nvm install ${{ matrix.node }}
nvm use ${{ matrix.node }}
echo "Install yarn."
apt-get install -y yarn
echo "Install composer."
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
echo "Install PHP extensions."
docker-php-ext-install -j$(nproc) iconv intl xml soap opcache pdo pdo_mysql mysqli mbstring
docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/
docker-php-ext-install -j$(nproc) gd
pecl install mcrypt-1.0.2
docker-php-ext-enable mcrypt
- name: Checkout repository
uses: actions/checkout@v1
- name: Run yarn install
run: |
yarn install
- name: Run yarn build
run: |
yarn run build --if-present
- name: Run composer install
run: |
composer install --prefer-dist
- name: Install WordPress Test Suite
run: |
bash bin/install-wp-tests.sh wordpress_test root root mysql ${{ matrix.wordpress }}
- name: Install PHPUnit
run: |
composer global require "phpunit/phpunit=${{ matrix.phpunit }}"
- name: Run PHPUnit
run: |
~/.composer/vendor/bin/phpunit $GITHUB_WORKSPACE
- name: Install PHPCS with WordPress Coding Standards
run: |
composer global require dealerdirect/phpcodesniffer-composer-installer:0.5.0 wp-coding-standards/wpcs:${{ matrix.wpcs }} automattic/vipwpcs:${{ matrix.vipcs }}
- name: Run PHPCS Coding Standards
run: |
~/.composer/vendor/bin/phpcs $GITHUB_WORKSPACE
- name: Create a built branch
run: |
BRANCH_NAME=$(echo $GITHUB_REF | grep -oP '(?<=refs\/heads\/).*')
rm .gitignore
mv .deployignore .gitignore
git config --global user.email "ci@company.com"
git config --global user.name "Company CI"
git remote set-url origin https://$GITHUB_ACTOR:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY.git
git checkout -b $BRANCH_NAME-built
git add -A && git commit -m "built from $GITHUB_SHA"
git push --force -u origin $BRANCH_NAME-built
Hopefully, this will get you started in the right direction, however, if you need more information, you can find some documentation here. I’m sure the documentation from GitHub will improve and we will start seeing more example in the wild. If you do know ways improving my examples feel free to reach out.