GitHub Actions for PHP Developers (HCL)
This article is old.
Some information might be out of date, as I don't always update older articles.
The demo application mentioned in this article has been updated to work with the new YAML syntax. Checkout this Pull Request to learn what has changed.
On Augusth 8th GitHub announced some major updates to Actions. Switching from HCL to YAML syntax, CI/CD support and much more. This blog post was written before this announcement, so stuff might be different, depending on when you're reading this article.
If you're a developer using GitHub, you probably have heard of GitHub Actions. GitHub's new automated workflow system (similar to the CI feature of GitLab). It has been announced in October 2018 and is currently still in beta (You can sign up here to get access).
I recently got access to the beta and began developing Actions suited for my projects. As I currently spend most of my time writing code in PHP, the Actions mentioned in this article are focused on that language. The logic can be easily ported to other languages though.
What are Workflows and Actions and what can I do with them?
A Workflow is a collection of multiple Actions which can be triggered by multiple GitHub webhook events (for example when code is pushed to the repository, a Pull Request has been merged or when a new Issue has been created)
An Action can basically do everything: run your test suite, publish a package to npm, deploy your site to your server, send a Slack message. You name it.
The code for an Action can live in the project repository itself, in a separate public GitHub repository or in a Docker Hub image.
Your Workflow is defined in the
main.workflow file in the
.github folder in your repository. This means your Actions are written in code and are in version control. If you like to work with a GUI, Workflows and Actions can also be configured and edited in a visual editor on github.com.
In this post I'm going to cover 3 Actions which I think could be useful for PHP developers in their daily workflows:
- Run your
I've published a sample Laravel application, with all three Actions configured, on GitHub. You can clone it, fork it and see for yourself how the Actions are set up.
The process of adding those Actions is documented in this Pull Request.
As mentioned earlier, your Workflow and Actions are defined in a
main.workflow file. The final file for our sample application looks like this:
GitHub Actions are not written in YAML or JSON, but in HCL. If you've worked with Terraform in the past, the syntax might look familiar to you.
I won't go deep what each keyword in the Workflow syntax does (
secrets, etc.). You should rather read the documentation.
The most important keywords for us right now are:
uses: Set which Action in a Workflow should be used
needs: Set which Action must successfully run, before this Action runs. (Similar to Dependencies)
env: Environment variables defined in the Workflow file itself. Allows you as a Action consumer to change things within an Action
secrets: Secret environment variables like API keys which should not be stored in the repository
Dockerfile? Oh no!
Each Action is executed in a Docker container. Therefore, each Action needs a
Dockerfile. If you now think: "Oh no! I don't know Docker!", then we have something in common.
I've read and heard a lot about Docker over the years, but never really worked with it.
The good thing is that you don't have to be a Docker expert to create or work with Actions. All you need to do is set a base image and then you're good to go. The "core" Actions code can basically be written in any language.
One caveat you have to keep in mind when working with Actions, is that even though each Action is run in its own container, the underlying filesystem is shared with other Actions. Meaning: If one Action changes repository files, these changes are also available in other Actions.
So let's get started with our first PHP Action.
Install Composer dependencies
To do anything with a Laravel project, we first have to install its dependencies with
This can be accomplished by using the general composer Action developed by MilesChou.
As all following task depend on composer dependencies, all other task should have "composer install" defined in the
needs keyword. The "composer install" Action is therefore executed before all other Actions. (GitHub is clever enough though, to only run the Action once).
Action 1: Run phpunit test suite
One of the most common things to do in Continuous Integration is running your projects test suite on each code push.
As the test suite setup differs from project to project, I won't use a publicly available Action to run
phpunit. I will use an Action defined in the project itself.
In the root of my project I create an
actions/run-phpunit-folder and within it the following files:
FROM php:7.3-cli-alpine LABEL "com.github.actions.name"="phpunit-test" LABEL "com.github.actions.description"="Run phpunit test suite" LABEL "com.github.actions.icon"="check-circle" LABEL "com.github.actions.color"="green" ADD entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]
#!/bin/sh -l set -eu # Setup Laravel App cp .env.example .env php artisan key:generate # Run phpunit Tests vendor/bin/phpunit $*
Dockerfile we tell Actions that we want to use PHP 7.3 and that we want to execute the
entrypoint.sh-file is where the logic of the Action lives.
To run the test suite, we first create a copy of the example Laravel environment file and generate a fresh application key.
phpunit is executed. Any provided arguments (
args) are passed down and will be placed where the
$* variable is.
If your project needs more PHP extensions or a MySQL database, the
entrypoint.sh would be the place where you set these things up. (That's why I didn't use an already existing Action. The setup differs from project to project.)
In our Workflow file, we can now add the Action:
Even though this was quite easy to set up, I personally wouldn't use GitHub Actions to run the test suite for my bigger projects yet.
Other CI services like Travis or Circle CI are much better suited for this task. These services give you richer notifications and better performance features like parallelism and dependency caching out of the box. With GitHub Actions you would have to implement this on your own.
However, I think Actions is perfectly fine for smaller projects.
Action 2: Run php insights
Our next Action is going to run phpinsights on each push. As we don't have to set up a database or PHP extensions to run insights we can use an existing Action. As no phpinsights Actions existed when I wrote this article, I wrote my own: phpinsights-action and laravel-phpinsights-action.
As we're working with a Laravel app, we're going to use the Laravel version of the Action.
As you can see, I'm passing the
--min-xxx arguments to the Action. If the code quality of my app would drop in a future Pull Request, the Action would return a "failed" status code which in turn would mark the Pull Request as failed.
To see the reported issues of
phpinsights you can open the log on github.com
Action 3: Run php-cs-fixer
Another common use case to use a CI service, is to check if your code follows the code convention and style guide you or your team has defined.
Here we've used another way to define an Action: By using the
docker:// protocol, you can directly point to an image on DockerHub.
The Action uses your existing
.php_cs configuration and runs
php-cs-fixer on your project. However, the Action always returns a "successful" status code. It doesn't matter if violations happen.
But if violations would happen,
php-cs-fixer automatically fixes them. So wouldn't it be cool if the fixed files would automatically be committed and pushed back to your branch? 🤔
Action 3 Bonus: Commit fixed files
As I said in the beginning of this article, the underlying file system is shared between multiple Actions in a Workflow. So committing the fixed files is just "an Action away".
I've created a git-auto-commit-Action which commits all changed files and pushes the commit back to the repository.
main.workflow file now looks like this.
Now on every push, possible style changes are automatically committed and pushed back to your repository. No need to run the command manually or for a Third-Party-service.
We now also added our first secret:
GITHUB_TOKEN. This is a special secret which is available to all Actions in a repository. But it's not enabled by default. You have to add it manually in the visual editor on github.com
As this Action uses the GITHUB_TOKEN to authenticate the
git push-command, GitHub won't trigger a second run of the Workflow. (Keep that in mind!)
I think this covers the basics of GitHub Actions for PHP developers. I'm very excited about Actions and what the future holds. I hope the feature leaves the beta soon, so that more people can use it in their projects.
GitHub already announced support for Scheduled Workflows which opens a big realm of possibilities. (Jason Etcovitch writes here how he uses Scheduled Workflows to automatically create weekly meeting notes). Or for example, you could also build an Uptime-Monitoring Action which is triggered every few minutes and would send Slack or Email notifications.
Personally, I would like to use the Scheduling feature to fully automate Laravel Download Statistics (a side project of mine). A workflow could trigger the update of download numbers, create a new HTML export and push everything to Netlify.
No need for humans any more 🤖🤯.
Start developing your own Actions
If I could excite you about Actions and you want to start developing your own Actions, here are a few resources I've found while working on this article:
- act: Run GitHub Actions locally
- sdras/awesome-actions: A big list of available Actions
- Jason Etcovitch's blog: Jason is an engineer at GitHub and has several long posts on how he uses Actions in different scenarios. I learned a lot from him.
- developer.github.com/actions/: The official documentation
- github.com/JasonEtco/actions-toolkit: Toolkit to build rich Actions in Node.js
And here's a list of Actions I found, which I actually would like to implement in my projects.
- github.com/shivammathur/setup-php: An Action which will help you install required PHP extensions for Actions.
- github.com/peaceiris/actions-broken-link-checker: Check a domain for broken links or images
- github.com/banyan/auto-label: Automatically label Pull Requests based on file patterns
- github.com/pullreminders/label-when-approved-action: Automatically add a label, when the Pull Request has been approved
- github.com/actions/npm: Actions to work with npm
- github.com/maxheld83/rsync: Use rsync in Actions
- github.com/cirrus-actions/email: Send status emails when a GitHub Check Suite completes