Why automate code style0:00
You know what the biggest problem working on a team is? It's not the million meetings every week. It's not the fact that your boss calls you at the weekend to tell you one of your colleagues pushed a bug into production and you need to fix it. No, it's that every developer writes code in a slightly different way, with a slightly different style. And we hate this so much that we've actually developed automated tools to solve the process. For example, Laravel Pint, which is built on top of a tool called phpcsfixer, well that automates the process of restyling code to match one defined, uniform style. But you try getting every member of your team to run that manually before they push code?
Create Pint CI workflow0:36
automates the process of restyling code to match one defined, uniform style. But you try getting every member of your team to run that manually before they push code? Yeah, it's not going to work. Which is why we have continuous integration. So let's create a workflow that will automatically run Laravel Pint for us, so that we never miss that step again. Rather than starting from a blank slate, let's go ahead and copy and paste noplanstomerge.yaml, and I'll name it LaravelPint.yaml instead. We can change the name to Laravel Pint. And then I want to select when this workflow should run.
We can change the name to Laravel Pint. And then I want to select when this workflow should run. I do want to run on pull_request, but not when a pull_request is closed. Instead I want to run when a pull_request is created or updated. Now if we go back to the documentation for pull_request and we scroll down just a little, you'll see that by default a workflow only runs when a pull_request event's activity type is opened, synchronized, or reopened. In other words, the default for this event is exactly what we're looking for. So all we'd actually have to do is declare that we want to run this on pull_request. And that's enough to get us off the ground.
So all we'd actually have to do is declare that we want to run this on pull request. And that's enough to get us off the ground. We'll now go ahead and update the ID here of the job to Laravel Pint. We'll want to run on Ubuntu latest. We no longer need that if check. And of course, whilst we do want steps, we don't want any of these steps. So let's delete that code and we can make a fresh start here. Now before we get started, why don't we actually create our steps with just the names so that we know what we want to accomplish by the end of this episode. How would we get Laravel Pint working if we were on a fresh install of Ubuntu?
Outline workflow steps2:17
we know what we want to accomplish by the end of this episode. How would we get Laravel Pint working if we were on a fresh install of Ubuntu? Well, the first thing we need to do is check out the code base. Can't do anything without the code. We'd also need to set up php and Composer. Once we've got that configured, we'd be able to install Laravel Pint. Two ways we could do that. We could do it as a local install if it was already part of the Composer dependencies for the project, or because Laravel Pint is a PHAR file, then technically we could just install it globally, which might actually be a better option here.
for the project, or because Laravel Pint is a FAR file, then technically we could just install it globally, which might actually be a better option here. Once we've got Laravel Pint installed, obviously we can run Pint. And then finally, if there are changes based on the running of Laravel Pint, we'd want to push the changes back to the repo. So we have five steps that we need to satisfy in this action. Let's go ahead and fill them out one by one. As you can imagine, most of your workflows are going to require checking out the code base. So there's an official action for it.
Checkout and setup PHP3:21
base. So there's an official action for it. I'm going to copy this User's statement here. We'll drop that into our YAML file under the name. And the defaults that it's set up with will suffice for 99% of use cases. So you almost never have to do anything else with this stage of the action. Now we need to set up php and Composer, which might seem daunting, but again, actions to the rescue. Search for set up php. And this third one down, set up PHP action, is brilliant.
Search for set up PHP. And this third one down, set up PHP action, is brilliant. It's not official, but it is so, so well put together. And if you look at the contributors list, you might even recognize a few avatars from the Laravel community. So come down to the usage section, and if we scroll down a little, here we go. We want to use set up PHP at v2, and I'm going to pass some custom input to say that we're interested in PHP 8.3 as the version to download and use. Of course, depending on when you're actually watching this series, that may change. But yeah, at least for now, PHP 8.3 is the latest version of PHP.
Of course, depending on when you're actually watching this series, that may change. But yeah, at least for now, PHP 8.3 is the latest version of PHP. Believe it or not, that's PHP installed and configured. Could not be easier. But we also said we were interested in Composer. Thankfully, if you take a look at the documentation for this action, it actually has tools support, and one of those tools is, in fact, Composer. If you scroll a little further down, there is even an example of how to do that. So let's copy this line here. We define a custom tools input, and we just specify that we're interested in Composer.
Install and run Pint4:50
So let's copy this line here. We define a custom tools input, and we just specify that we're interested in Composer with a version of 2. So now we have php 8.3 and Composer v2 available as binaries that we can execute inside this GitHub action. Well, from here, you don't even really need me anymore. You know how to install Laravel Pint. Let's do it globally. composer global require laravel/pint. Then we want to execute that binary.
Composer global require laravel/pint. Then we want to execute that binary. So to do that, we literally run pint. And just to get us started, how about we use the --test flag so that we don't actually make changes to the file? And for pushing changes back to the repo, we'll come back to that in just a moment. For now, let's just echo to do. I think that should be enough to get us off the ground. So we'll push that back up to the repository. And then why don't we come in to create a new User on a branch here?
So we'll push that back up to the repository. And then why don't we come in to create new User on a branch here? And I'm going to put an opening curly brace on the same line as the method declaration, which not only will make many of you unhappy, but it will also make Laravel Pint unhappy. I'll push this up in a branch and we'll test it from the front end. After opening a pull request, you can see we did execute the Laravel Pint action and it failed. Let's make sure it fell for the right reasons. We'll go to details. Yes, successful.
We'll go to details. Yes, successful. So it failed on the run Pint step because when it came to this create new User action, the brace position made it upset as it should make all of you upset. And then it exited with a code of one, which basically instructs GitHub Actions to fail the entire thing. Now you could actually leave this here, right? If all you want is to inform your developers, your teammates that they need to run the action, you don't need to do anything else. But I think it would be cool to just run it and push the changes back into the branch.
Auto-commit and push fixes6:38
you don't need to do anything else. But I think it would be cool to just run it and push the changes back into the branch automatically to save our developers a step. Let's add that in next. We'll start by removing the test flag from Pint so that changes to files are actually made. And then in push changes back to repo, I'm going to start with a pipe character, which will allow me to do multi-line scripts. What would you actually do from the terminal? You do git add . to add everything into git.
What would you actually do from the terminal? You do git add . to add everything into git. Then we could commit with an inline message, Laravel Pint. And then finally we would say git push. The only thing to keep in mind is that we don't have any user credentials configured in git. So let's also do that. git config. We'll set user.name and I'm going to set it equal to gh actions and then we'll do the same thing for the email.
We'll set user.name and I'm going to set it equal to gh actions and then we'll do the same thing for the email. git config user.email. And let's set that to gh@laracast.com. That's not an actual email address. Don't send anything there. It will fail. You'll get an undelivered. Don't waste your time. I'm just telling you because I know some of you will think about doing it.
Don't waste your time. I'm just telling you because I know some of you will think about doing it. Okay. With that done, why don't we push those changes back up to the repository and see if that works. All right. So we got an error. Let's take a look at the details. And the error was on push changes back to repository and taking it run pint did make a change.
Fix detached HEAD push8:00
And the error was on push changes back to repository and taking it run pint did make a change. Yes, it did. It updated the file. And in fact, we changed one file. But when we tried to push, it doesn't know where to push because we have a detached head. Right. There is a simple way to fix this. Our checkout action allows us to pass a ref as custom input, which will actually fix our error.
Our checkout code action allows us to pass a $ref as custom input, which will actually fix our error. If we go back to the code base, then we'll use the width parameter on checkoutCode, and I'll pass the $ref in. But of course, we can't hard code a value because we don't know the $ref ahead of time. This is where we'll use some of that special syntax again with a GitHub context object. So GitHub.head_ref, which is in reference to the branch that was used for this pull request. It's exactly what you need to fix this error. Push that up and we'll try again.
It's exactly what you need to fix this error. Push that up and we'll try again. Let's take a look at the details of the action this time. pint is running. You should see that error and fix it. And boom. Yes, you can see that this didn't fail. It actually passed with flying colors. If we go back to the pull request and refresh, here we go. Here's the commit that was made by GitHub Actions.
If we go back to the pull request and refresh, here we go. Here's the commit that was made by GitHub Actions. And clicking into it, we should be able to see, yep, the only change was moving that brace down onto its own line. Perfect. So this is working without issues. But there is a little edge case bug you'll want to be aware of. Let's go ahead and make a change that won't upset Laravel Pint. So I'll just say a new comment here. Nothing out of the ordinary.
Handle no-change commits9:34
So I'll just say a new Comment here. Nothing out of the ordinary. Laravel Pint will have no issues with that. We're going to commit and push this code and then take another look at the action. This time the action failed. And the reason was that when we attempted to commit our changes, there was nothing to commit because Laravel Pint didn't change any files. Laravel Pint ran without error. So we need some escape hatch here because obviously if Laravel Pint doesn't change anything, that doesn't mean that the action should fail.
So we need some escape hatch here because obviously if Laravel Pint doesn't change anything, that doesn't mean that the action should fail. There is an easy way to do this. If you go back to the codebase, jump into our YAML file and head down to where we actually commit, we're going to use a little bit of OR syntax from the command line here to say that, look, if there's a problem, we're just going to exit with a code of zero, which is a success code inside the terminal. Go back and push that up and you should find that now, even if there are no changes to be made, the action won't fail. And there we go.
Consider running after merge10:33
be made, the action won't fail. And there we go. Problem solved. Doesn't take much work, but you do want to be aware of edge cases like this and get them fixed as quickly as possible because there is nothing more annoying than not being able to merge your code because some GitHub action is failing. Something just to keep in mind. Now there is one issue left with this GitHub action, and that is that the code is updated, changed every time the pull request is updated, which means the developer working on the pull request will have to constantly pull down changes to their branch to avoid merge conflicts.
changed every time the pull request is updated, which means the developer working on the pull request will have to constantly pull down changes to their branch to avoid merge conflicts. I think the experience would be smoother if we actually executed this action after the code had been merged into the main branch. So some homework for you. See if you can work that out. See if you can change the action so that instead of running when a pull request is made, it actually runs after the code has already been merged. Simple hint, doesn't take that much work. If you find yourself changing the whole action, you've probably gone wrong somewhere.
Simple hint, doesn't take that much work. If you find yourself changing the whole action, you've probably gone wrong somewhere. See if you can get it working, and I'll see you in the next episode.
