Automating All the Code & Testing Things with GitHub Actions


Code tasks like linting and testing are critical pieces of a developer’s workflow that help keep us sane like preventing syntax or style issues and hardening our core business logic. We’ll talk about how we can use GitHub Actions to automate these tasks and help keep our projects running smoothly.


Hey everyone, we're going to learn how we can automate all the things, all the code and testing things with github actions. So who am I? I'm Colby Fayok. I'm the one hugging BB8 and Kylo Ren over there. I work with the dev community as a developer advocate for Applitools. You can find me pretty much anywhere on the web by just googling my name because I'm the only one in the world. So we're going to just jump right into it. As developers, we have a bunch of tasks that we have to deal with day to day. And particularly, we're going to talk about code tasks. So let's see what some of those might look like. Well, first there's linting. Linting basically tells you if you write sloppy code. But on a serious note, it can be really helpful for actually pinpointing syntax errors. It's also generally helpful for making sure that your code looks like the same person wrote it, which makes your code easy to read and also work through as a team. The problem is, unless you're running some kind of modern framework with it out of the box, you have to remember to actually run it. Formatting is basically linting, but it will fix all those issues for you. Your linter, like ESLint, might even do it itself. But with formatting, you have tools like Prettier, where they will format the code and they'll even include an opinionated configuration. Linting and formatting come with a lot of super practical benefits. You also benefit from not having to actually spend time in code reviews where you're going to argue over syntax preferences. You can let the robots be the bad cop and actually do it for you. There's less that you have to think about and less that you actually have to stress about. But again, your worst enemy here is remembering to actually run it. Even more critical to a developer's workflow is testing. And while formatting and linting help prevent errors, it's more so static analysis. And it helps you find the syntax errors. But it's not doing a lot to actually test the functionality. Tests will help to make sure that you're not breaking things and end up losing money when your online store is down. The tests aren't just for your QA engineer to run every once in a while. You need to actually run these tests regularly. Otherwise, they're not really doing their job. And finally, the last thing that we'll talk about is deployments. And ultimately, you want your application to actually go somewhere so that people can actually use it. Deployments can be painful, especially for large, complex infrastructures where there's a lot of moving pieces. On top of this, you have to remember to deploy all those pieces. And missing one step can take the site down in a bad way. Not just because you're deploying a new version and have a little time between different migrations. A theme through all these tasks, though, are they're all important in one way or another. They help us focus on the real challenges of the work. Not the drama in the code review or stumbling through another code deploy or but another theme. They all require humans to do them. That means they're prone to human error. And whether it's simply forgetting to run something or forgetting to include a particular step, even forgetting to do it in the right order, what can we do to actually avoid those human mistakes? Well, we can automate it, of course. So let's learn how to automate all the things. How do we actually automate all the things? If we're deploying anything even moderately complex, we have a bunch of things that we typically have to do in order to actually deploy our full stack. Those ten commands or buttons that you have to run every time your Rails server and aws infrastructure, well, we can put that in a script. Scripts are probably the simplest form of automation. This helps us automate the manual task that we might have to take. So why not put them in a bash script? Or insert your favorite scripting language of choice, like Node. You can write it in javascript if that's your thing. The goal is to have a list of repeatable steps. And by using that one command with that script, you're able to do the exact same thing every single time. That way you never forget to leave anything out. But scripts also ultimately rely on something to trigger that. If it's still a human, you might forget to run that script in the first place. Or if somebody's filling in for a day, maybe they don't even know that you have a script to run this setup. A feature that comes baked into Git itself is Git hooks. With Git hooks, you can trigger something at different points of the Git process. For instance, you can set up a Git commit hook, where any time you actually run a commit, your hook will run, automating some part of your process, where a good example for that is formatting. Any time someone commits, you can set up Prettier or any other formatter to run before the commit actually applies. With your existing scripts to run linting and formatting, this is called a precommit, where with tools like Husky, you can hook in and manage all those Git hooks. We're also applying lint staged here, which allows us to get the path of all those changes that we actually get in the stage for Git. That way, when we run our precommit hook, we're not affecting the entire repository tree. We're only impacting the files that actually changed. But once that script finishes, those changes are added onto the Git stage before that commit is actually applied. So when the actual commit happens, all Git knows is that the code is pretty and formatted. All the developer knows is they got their job done. And when the code is actually pushed up to GitHub, it looks like that one person wrote it. But not everything makes sense inside of a Git hook. You don't want to run your tests as a precommit hook, for example, where that's going to be a huge pain for developers. Imagine you're working on a project with a huge test suite, and in order to actually commit, you have to run that entire suite. Nope, nope, nope, nope. So we can take that up another level by running CI-CD processes. CI is continuous integration. CD is continuous deployment, or sometimes continuous delivery. While some teams still require that to be kicked off manually, that kind of defeats the purpose, where the goal of CI-CD is to be automated within your code processes. So for instance, if you want to push your changes to the main branch, that triggers a certain set of functionality. And if you push to a featured branch, maybe that triggers a different set of functionality. Some popular frameworks for running these processes are Jenkins, Bamboo, CircleCI, and TravisCI. And depending on the framework, setup might look a little bit different. Most of them depend on some kind of configuration. But you can have various levels of scripting included right inside, where here we have a simple example. And I personally still have nightmares from actually working with Jenkins, where I'm primarily a front-end engineer. But the devops team swore by it. I think it's mostly because of the flexibility, though, not necessarily the ease of it. But setting up CI-CD can be super powerful. You can have large, complex orchestration of your entire infrastructure. Need to deploy some lambdas or dump a static site into storage? Add a script to your CI-CD processes. Need to spin up a VPC with strict networking rules? Well, maybe Jenkins can do that for you. Because you're really leveraging scripting and automation of that scripting, you should really be able to accomplish anything that you want. But like I mentioned before, I'm a front-end engineer, and I struggled with Jenkins. I didn't really have great documentation to look at, at least at the time. And waiting for builds to finish took forever. For a lot of those frameworks, you have to also maintain servers. And did I mention that I'm a front-end engineer? I can spin up a node server, but I don't really want to deal with servers. Servers are so last decade, am I right? Kidding aside, I want something that I can easily plug and play. Something that's close to the rest of my code that I don't have to fight with. And after all that buildup, that's where github actions comes in. github actions are still technically CI-CD, but in practice, they actually feel so much more approachable. The best part is they can be just as flexible. With Actions, you can start off by setting up the environment you want to run inside of the workflow file. You can add whatever commands you want, when you want to run them, and you're off. For instance, if I wanted to simply run some tests, I can set up a test file to run in Ubuntu or a variety of other options. Then I can check out that code where I'll be running the commands inside of a working directory. To set up Node, I can add another Action where I specify the version. I can even make this run on a few different versions of Node if I wanted to within the same workflow. And then finally, I install the dependencies and I run the tests. This alone will make sure that I have tests that run every single time a change is pushed, or if a pull request is created in this particular instance. As they run, they show up right in line with the rest of that pull request, letting you know by just a quick look if your proposed changes actually breaks things or not. This helps to get a great feedback loop for developers, when if something isn't working, they'll get that feedback quickly. I could also use github actions to deploy code. Once I install my project dependencies, I can build that project. Then configure my aws credentials as secrets, and I can then sync those artifacts up to aws to deploy. The aws CLI is actually available by default, which makes this really simple to do inside of Actions. And this includes any kind of commands with the aws CLI. I can even use Actions from the GitHub marketplace. Like if I wanted to post a message into Slack when a new pull request is made, I can use the post to Slack action, which I can configure in my project and set a note right to Slack. It's a helpful way to automate those little pieces of the development workflow. These last few were pretty simple examples, but it goes to show that with just a few lines, we can really do a lot of awesome things. So the cool thing, though, is we can take this up another level. We can create our own custom github actions. Once you create a custom GitHub Action, you really gain the flexibility to do whatever you want inside of that controlled environment. You have a few different options, like running javascript with Node, or you can even run it with Docker, where you get the ability to do whatever you want. For instance, if I have a common task that includes a bunch of complicated things, I might not want to have to copy and paste that little snippet through every single repository. I can set up a Node script and run that script as part of my new Action. That way, I only have to make that one reference inside of the Action workflow between all my different projects. That's exactly what I did with my Apple Tools Eyes GitHub Action. And just really quickly to add some context, Apple Tools is a visual testing platform where every time you run a test, Eyes takes a screenshot of your website or your mobile app, and it compares those images with AI. To do that, Apple Tools has a ton of SDKs, whether it's cypress, like you see here, or Selenium Java, or really any popular testing framework that allows you to drop the SDK into those existing tests. And this is already pretty easy to do, but I wanted to make it even easier. I wanted a solution where somebody who might not even have testing set up inside their project could easily drop this in and get visual testing, which is where we come back to Apple Tools Eyes GitHub Action. And hence, this Action just takes one snippet where you have to plug in your api key, which is required any time you want to run a test to Apple Tools to make sure it actually sends it up to your account. And that way, then you provide the URL for what you actually want to test. Once that Action actually starts off, it kicks off a process where it will first crawl that URL that you pass in. It does this kind of similarly to what you would expect from Google when it does it with robots for searching with seo, where it clicks around the site, tracks what links are available on your site, and it puts it together inside of a list. Or if you already have a custom sitemap, you can pass that in as the URL instead. But to crawl, I use a package called Sitemap Generator, where I can run a simple node command, and I can have that all inside of a list of URLs. Once I have collected all the site details and the configurations, though, I can run cypress right inside of that node script, including those URLs from the sitemap as an array that I pass in right into cypress, which allows me to collect all of my results that I can use for later, such as failing the action if the tests actually fail, or generally working with the results so I can provide a better developer experience inside of the GitHub repository. But finally, cypress runs those tests as it normally would, giving me my test runner and my results for the eyes check. The only difference being that this time, it's looping through all those pages to check for the sitemap, so it's running all those different tests dynamically based on the URLs. But that gives me my test runner and my results for that eyes check. With those results, I can actually comment right onto the pull request, whether they passed, failed, or if they're just unresolved, giving the maintainer of the project an easy way to know if the code actually introduced a bug, or at a minimum, introduced a breaking change. I even figured out a solution where we can plug this into an existing Netlify workflow. If you're not familiar with Netlify, they'll automatically deploy your static application all the way up from GitHub, and this happens any time that you actually push a change into your GitHub repository default branch. I was able to find an action where it waited for that deploy, and it actually stored that URL as output from that action workflow. I was able to take that and pass it right into my AppleTools eyes action, which it did its thing, it went through and it crawled the site, and it performed that visual test. But the entire project is ultimately a script that I need to run. Where running it between a few different projects is just a few input parameters of a difference. Instead of having to make sure that my testing framework is set up inside of every project, along with installing the SDK and actually writing all my tests, this makes it really easy to add AppleTools eyes visual testing to any project with just these few lines. So let's also check out a few other examples of github actions that are out in the wild. I also created a GitHub action template called content reminder. The one thing that I struggle with as a content creator and educator is simply remembering to share some of my old content and tutorials. So I put on my developer hat and I tried to do something about it. I built content reminder as a GitHub action, where it's simply a node script, but what happens is I run it on a cron so that it actually triggers two times every day. With actions, not only can you run on GitHub events, you can run on the same old cron syntax that you're used to scheduling other runs. But once it does run, I run a node script where I look through a few RSS feeds of my content, I find a random entry, and I send myself an email with that content pushing me to share it out to the world. For another practical use case, performance is, or at least should be, on the top of everybody's minds when building web apps. Lighthouse is a fantastic tool from Google's team that helps measure that performance. The Lighthouse CI action lets us drop all that in with the measurements right into our GitHub projects. And it's not too different from my Apple Tools action, where we can pass in the URL right inside of the Lighthouse CI action. We can also define a performance budget, such as max page size. But when Lighthouse actually runs, it'll take that budget into consideration. And if it finds any issues, it'll add annotations right inside of GitHub, letting us know what those exact issues are. Bringing things to the real world, though, Alfonso created Issutron 3000 for a GitHub hackathon. Whenever a new issue is created, Issutron 3000 will send a signal to an iot device. And while I'm not actually super familiar with the whole iot world, setup is similar to any other action, where there's extensive documentation if you want to actually figure out how to configure this. But ultimately, it's going to run a script that Alfonso set up inside that action, where it sets up the MQTT client and sends a payload depending on the event. And once received, Issutron 3000 is actually activated, and it blinks or lights up based on that configuration. It works pretty well in the video. I'll include it in the link in my talk notes. And finally, if that last one wasn't considered fun, here's a little bit of some. Where Tim created a community chess game on his GitHub profile page, all powered by github actions. To make a new move, anyone can click on one of the links to a specific space, where all it's doing is opening up a new issue with that location inside of the title. Once that action sees the open request, it ultimately runs a Ruby script to set up that play. Including actually parsing that title of the issue, where you're actually seeing where that next move should be, along with a ton of other stuff inside of that script that I'm not showing here. But as well, it's also going through and it's updating that readme with the new move. Where after the move is actually accepted, it will show up right on Tim's profile. And if you're persistent enough with all your moves, it might show up even on the leaderboard. But whatever you do with actions, the goal is to ultimately automate as much of your code as possible, or all your code tasks as you can. Again, let the robots do the hard work and be the bad cop. Spend your time on the unique challenges of your project. The great thing about actions, though, is that it makes it very approachable for anybody to use. I no longer get stressed out when I'm working with ci cd workflows. I love setting up a new action and figuring out what I can automate. I get excited having something run automatically that I no longer have to do myself. And I'm sure you will, too. If you want to learn how to create your own GitHub action, or if you want a quick intro on just simply getting started with actions, I have a ton of resources available, including an egghead course and a ton of videos on YouTube. And if you want to learn how to go from end to end from design all the way up to a full stack next.js application, including automating all of your code tasks with github actions, check out my free course over on YouTube, where you can also find it at And that's it. If you want to learn more or talk about the chat or chat about the talk, you can find me everywhere at Colby Fayok. Also tweet out a link with the stuff you've seen here today. Thanks, everybody.
19 min
25 Oct, 2021

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic