Menu
About me Kontakt

Github Actions as a Replacement for Typical Continuous Integration Software (video, 26 minutes)

In the latest video on the Swashbuckling with Code channel, the author discusses the fundamentals of Continuous Integration (CI) in the context of GitHub Actions. The video starts with a reminder about a previous video where the author briefly explained what CI means. After this introduction, they proceed to create a simple project that incorporates linting elements. The process starts with the author creating a directory named 'CI GitHub Actions', followed by creating an index.js file and an attack.js file, which contains a function simulating an attack in an RPG game. They also mention that they will not use prettier in this project to keep the code as written for demonstration purposes.

Next, Swashbuckling with Code installs ESLint, a tool for checking code quality. They use 'npx' for a one-time installation of ESLint and its configuration for syntax checking. The focus here is more on preparing for GitHub Actions implementation rather than teaching linting. After configuring ESLint, the author runs the lint script to check for errors and warnings in their code and makes an initial commit with a suitable description.

The next step involves creating a repository on GitHub using the GitHub CLI. The author shares their experiences with this technology, showcasing how easily you can manage projects now. After creating the repository, they add CI configuration using GitHub Actions. A suitable folder and YAML file structure is created to define the steps for running the linting whenever code is pushed to the repository. Swashbuckling with Code emphasizes the importance of action statuses and explains the meaning of exit codes, enhancing the understanding of what happens when CI encounters errors.

At this point, the author makes adjustments related to the CI process, demonstrating how to fix code errors to allow the CI to run successfully. They walk through the process of creating a pull request to seamlessly integrate changes into the code. The author underscores Continuous Integration's significance in the everyday coding lifecycle, particularly as many platforms like GitHub, Bitbucket, and GitLab gain built-in CI capabilities. Lastly, the video reveals how to set up rules for merging pull requests into repositories, ensuring all statuses are checked before acceptance, thus contributing to better code quality and security.

In conclusion, the author summarises the video, noting that according to current stats, the video has 4,150 views and 150 likes at the time of writing this article. Check out the material on Swashbuckling with Code to learn more about Continuous Integration and GitHub Actions and how they can be applied in coding work. The author also promises more videos on linting and related CI topics, so it's worth following the channel to not miss upcoming episodes.

Toggle timeline summary

  • 00:00 Introduction to the series on continuous integration.
  • 00:20 Beginning to create a simple project for demonstration.
  • 00:29 Creating a directory for the project.
  • 00:41 Initializing git and npm for the project.
  • 00:50 Proceeding with additional project setup in the editor.
  • 01:07 Creating initial JavaScript files for the project.
  • 01:21 Defining a simple function within the attack.js file.
  • 02:13 Ensuring prettier is not applied to the project.
  • 02:34 Exporting the attack function using CommonJS syntax.
  • 03:21 Confirming the basic implementation works by running the code.
  • 03:24 Adding ESLint for code linting.
  • 03:40 Installing ESLint with specific configuration options.
  • 04:49 Setting up a lint script in package.json for running ESLint.
  • 05:34 Committing initial setup changes to the repository.
  • 05:48 Using GitHub CLI to create a new repository.
  • 07:22 Setting up GitHub Actions for continuous integration.
  • 07:36 Creating a .github/workflows directory for action files.
  • 08:08 Defining a YAML file for CI configuration.
  • 11:15 Setting the steps for the CI pipeline to install packages and run ESLint.
  • 02:03 Verifying the linting command produces correct output.
  • 02:07 Debugging errors and warnings generated by ESLint.
  • 24:25 Closing remarks on the evolution of CI tools and future enhancements.

Transcription

If you haven't watched the previous video in this playlist and you're not sure what continuous integration actually means, you might want to go check that video out. I give a little quick description of it and what we're going to be going over in this series. I'll of course link that in the notifications and description in case you didn't land here from the playlist. Otherwise, let's start out by creating a project. It's going to be a real simple little minimal example and we're going to be adding linting to this project. So I'm going to start by creating my directory, of course. You can do that however you like. And I'm going to name it CI GitHub Actions. I'll cd into that directory and then I'm going to start off by just git initting because we know we're going to do that. I'll do an npm init dash y. Just kind of doing some quick setup here. Say yes to all the questions, we don't care about that. And I'll actually do the rest in my editor. So here in the editor, we're going to probably want a lint script in a moment. For now, I just won't put anything for that just so that you know that's a placeholder. And then over here in the files pane, I'm going to add a new file. We're going to add an index.js like normal. And then I'm going to just add all at the root to make it simple. I'm going to make an attack.js. And in this, we're going to create a little function that you don't really need to pay too much attention to how this works. But here we go. So I'm going to make a fun little attack. Pretend like we're making a game or something, an RPG game. So I'm going to have creature name as one of the arguments. And then I'm going to have the damage. And then I'm going to have is critical as a boolean. Again, this doesn't really have a whole lot of purpose. It's just to have something that I know the linter is going to be mad about basically and want to fix. So I'm going to immediately return an interpolated string here. I'm going to say creature name dealt, let's see, is critical. So if it was critical, we'll do damage times two just to keep it simple. And then otherwise, we'll just return damage, we'll write the word damage with an exclamation point. And that's pretty much it. I want to make sure in this project that I do not have my prettier enabled because I do that by default. Yeah, it's not enabled. Okay, cool. So when I save this, it shouldn't format. Then over here, oh, you know, actually, we need to export that, don't we? So we'll say module.exports, we're going to use common.js for this, attack. And then I'll say const attack equals, this is an index.js, require. And we'll require that attack. And then we'll console.log running it. So let's do attack, we're going to say smelly ogre is the creature, we're going to say did 23 damage, and we're not going to pass the third argument, I'll say that it's optional. And one more thing, let's while we're here, add a .getignore, because we're going to need to install some node modules, and we of course want to ignore them, just keeping this pretty simple. Now come back to the terminal. And I'm just going to do actually node index.js, just to make sure it works. Okay, the code runs perfectly fine. So now what we're going to do is we're going to add linting. So I'm going to do like the fastest way, which is just to do eslint, that's kind of what I prefer. I'll just do npx to, you know, install at a one time go, if you're not familiar with that. And to check syntax, yeah, let's do that. You don't need to be too concerned with the options that I'm picking. If you're not familiar with eslint, pretty or any of that stuff, I will do some videos of that in the future. But the purpose here is more to get to the GitHub action side of things. So I'll pick JavaScript modules. Actually no, we don't want that. We don't want ESM, because we did common.js, didn't we? So let's go back, check syntax and find problems, we're going to make it strict, we'll do common.js. We're not using any frameworks, not TypeScript, I'm just going to press enter to that, use a popular style guide. And then Airbnb, I'll use that, and then JavaScript. So it's going to install all these things real quick. And you can see all my answers here, you know, if I went a little fast there. Okay, so now, I actually have eslint already on in this. So it's going to show us kind of like what's wrong with it. But what we can do to see that kind of wholesale here is, we'll go to es, we'll make our command lint in our package.json, just run eslint on the current directory without passing any, you know, extensions or flags or anything like that. So now when we do npm run lint, we will see this output. And you want to pay attention to this, because this is what we're going to be running as part of our continuous integration steps in GitHub actions. So we'll run this command, and we have this output right now. And you can see that these are actually errors, right? With one warning. Okay, so what it's basically saying, it's not really important, but you know, by default, it wants semicolons, you know, new line required. And then this is kind of the big one is that it wants to, this is such a long one, it wants to implicit return right here in the attack RPG file. It wants this to all be one line since we immediately return and we don't have any reason to have braces. That's just a style preference there. Okay, so I think it's time to make our first commit. So let's do git add dash A, I already know all the files. And then what we're going to commit here is we'll say initial setup with eslint. Now I don't have a git repo yet. What I'm going to do, that's a neat little tool if you haven't seen it before is I'm going to use the GitHub CLI. So we'll say gh repo create. And if you haven't already installed this, you know, I'll link to the documentation, but you do have to authorize yourself first before you can do this. So the repository name, yeah, CI GitHub Actions, I'm fine with that. Description, a cute demo repo for actions. And then we're going to make that public so that you all can see that. Yeah, created my current directory. Okay, so you can see it created this repository. So I can actually go and copy this right here. And my browser, I'll do www.github.com slash, and I can just paste that. That should take me right there. Cool. So we haven't pushed anything up yet. So it's giving us the, you know, the usual stuff here, but it has been created. So all I can do, I can just say git push origin master here. It's using all the defaults. Okay, and then when we reload, we will see that that repo has been pushed up. It's pretty cool, huh? As opposed to doing it through the web browser. If you do this a lot and create a lot of repos, it's a really nice tool. I've actually done it with an npm init, like auto-creation. So when I init, it'll ask me if I want to, you know, create the repo, and then it uses GitHub Actions to do that. It's a really nice little thing that you can do. Maybe we'll do a video on that sometime. But the GitHub CLI is worth checking out. It's pretty sweet. Okay, so right now, we have this basic repo set up. We have linting and all that stuff, but we don't have any actions. We don't have any continuous integration. And so if we come to here, if we go to this Actions tab, we'll see that it kind of starts us out with some nice little helpers that are suggested, which is pretty neat. But I'm going to show you how it'll just auto-detect a certain folder structure that you can just do every time. So here in our files right here, and I'm just using keyboard shortcuts. I should probably kind of just do it like this so you can see it. But if we create a new file, you can do a .github directory. Make sure the period's at the front of it. And then you can do workflows. That's the directory they're going to be looking for. And then you can name this file whatever you want. Usually, if you're doing multiple types of actions and job runners, this will make a little more sense as you see them. You might do multiple files. I'm going to start with just a YAML file, ci.yaml. And if you're not familiar with YAML, it's very similar to JSON, except it uses indentation instead of like braces and such. Braces and brackets, I should say. You can kind of think of it like the SAS, SAS indentation version to SCSS, which would be, you know, YAML to JSON. But, you know, if you're not familiar with that, you'll see. So we're going to start with a name. And that's the name of this like whole sort of suite of stuff that's running. So I'm just going to name it ci. I'll make it really generic right now. You know, later on when we have multiple actions, we might make it a little more specific. So we're going to say on, and this is when you want it to run. And this is sort of like, do you want it to run on push? Do you want to run it on like merges? Do you want to run it on specific branches only or pull requests? For now, we're just going to start with push. So every time we push up our code, any branch, it's going to run. So then you have a jobs key that's required here. So we're going to say eslint is the job. And that's just kind of like a key for us. It'll use that as the name if you don't put a name. But let's say that we want to do a name, and I'm just going to name it lint code. I'll name this all uppercase base. And I'll just do the first letter. What does this say? Missing property runs on. That's really cool that they gave us hinting for that. So we're about to do that now. So you need this runs on next. And so that's for this whole task here, job. And the runs on will actually give you a little bit of auto completion. There's a bunch of stuff that you can do here. You can see all these, which is really nice in VS Code. But we're going to do Ubuntu latest. That's what I like to do. If you're curious what we're kind of selecting here, this is sort of the image that we're going to, well, GitHub is going to build this image, so whatever operating system they want it to run on. And then it's going to check out our code, and then it's going to run a bunch of stuff on our code. What's really nice about this is it makes it kind of replicatable because if you are having your stuff hosted on an Ubuntu server, and you know the version and everything, you can kind of run it on that so that it's the exact same, which is pretty neat. So that's why we're just doing Ubuntu latest to keep it simple, but Ubuntu is pretty common. Now, steps is going to be next. And that's the actual, you know, each step that we're going to take, it's a good name. So the first step, we're going to give it a name. And I believe all these names are optional, by the way. It's just to kind of make it more clear. And like I said, it will make more sense when you see it first run. We'll point out where all these are. So first, you need to check out your code. Okay, this is a required step. So you're going to do uses for each step. And that's going to be action slash, I shouldn't say, I should say, you're going to be using uses for this step. You can use uses or you can use run. So uses is kind of like some prebuilt type of stuff, like GitHub actions has like a checkout at V2. And that's a really common thing to use right now to just check out your code really easily. And the next one, I like to put a break here. Where's my indentation? The next one we're going to do is, we'll have a name of install packages, just so I know what it's doing. And I like to make these separate. Let me show you. So npm i, and then I'll explain that. So this is just running npm install. And then we're going to do a name of run, es lint. And then this one will actually run this command, npm run lint. Okay, so that's the whole file. Let me go over this last couple of pieces. So you could actually do this all in one step if you want to. You could do like npm i and n, like you might do in your package JSON, or you can actually use like the slash to escape and then do like a new line type of thing. So I'm not going to do either of those. I like to make them their own thing. Oops, I have too many lines here, one sec. I like to make these each their own command because they give me a timeframe of how long they ran. And so I like to know like exactly how long my linting ran. That's just my preference. All right, that's that. So now when we add this and we push this up, we're going to name it add ci for linting. Okay, and this npm run lint, you know, it's just the command that I ran locally in the terminal package JSON. So it's checking out our code base, it's installing, and then it's running it just like we would locally. I think you get it. Okay, so I pushed that up now. This little guy down here, if you can't see that. And now when I go back to my browser, sometimes it takes a little bit, sometimes it's pretty fast, but this will actually, you can just refresh every so often. If it doesn't pop up, you might want to go one to another because I think it kind of caches it. Change tabs, but here you go. It automatically picked it up and it's running this action. So we can actually click into it. This one will probably be pretty fast. And I'll go back and explain what that is. I just want to catch this before it finishes. So you can see it's on the installing packages. Just kind of moves me down and you can see we get this error. And that one error caused like the whole job to fail, which you'll see in a moment. So let's come back here and start from the top. Oh, that was interesting caching. Okay, so that's like from a different project that I was demoing. So add CI for linting here. This is the step. So if you just start at the top, you go to actions, you'll see this. And then the CI right here is like what we, you know, named the whole thing, right? It's kind of compare and contrast here. So that's this name right here. And this is the commit name and we did it on master. So we click into that and here it's going to show all the different jobs in case you have multiple. So we just have this one job, lint code base. So you can see jobs and then the ES lint, which we named it lint code base. So then from here, we can click into this job and see the details about it. And it tries to take us like right to the failure, which is pretty nice normally. So you can see it runs this setup job, which we didn't name. That's just part of the setup. The checkout code we did name, which uses this run actions checkout at V2. And that's just kind of how, you know, GitHub knows to look for our files and check things out. You don't really need to know too much more about it than right now. Install packages to the next one that we made because, you know, we need to install before we can run our linting command, if that wasn't clear, since it uses ES lint to run. And then finally we have this run ES lint. I wish you could get some syntax highlighting here, but it's pretty tricky in this type of world. So it's the same output that we saw locally in our terminal. You can see it's got this missing semi-colons and all that stuff. The one thing that is a little bit different here that we didn't see is this error process completed with exit code one. So if you're not familiar with that, I think it's probably important to explain that right now before we even get further into any CI stuff, because you'll be seeing that a lot or things like that when you're, you know, running things in a Unix-like environment. So exit codes, if you're not familiar with them, it's pretty, it's like standardized minimally and then not really standardized past that. And what I mean by that is zero is considered a success. This is a standard in the Unix-like world, Linux and it's going to be on Mac and all that stuff because it, you know, based on Unix. So whenever you get the exit code zero, we can assume that's a success. And then anything other than zero is going to be a failure. What those codes mean are not really standardized. There are some like kind of set of best practices. So one like typically means like a general catch-all error in my experience, but they might have more specific ones that they'll decide like, you know, two is, you know, some sort of specific error. I can't think one off the top of my head right now and three, four, five, whatever, however many they want to do. But the idea here is that the program decides what each number means. So I know that doesn't give you like a ton of context, but hopefully, you know, kind of turns this arbitrary error. If you've never really understood what that means, why is it exit code one type of thing? You're looking for a non-zero exit code if you're looking for that to actually be a failure. So we get this generic failure, but it does actually have the log dumped here to show what it is so we can act on that. Let's actually kind of make this more useful for a moment because we've just done an action, but it's like, what purpose does that have? I mean, yeah, if you go to the code, you'll actually see this little X here, which is pretty neat that it is saying on the master branch, like the latest thing failed, but you know, I'm not going to go to the repo and check that every time. There's kind of multiple ways you might do this, but I'm going to show you what like the normal workflow for me is that makes this useful. Let's actually fix this first and show a success, and then I'm going to make a pull request. So in order to fix this, we come back to our terminal, and all we really need to do is do npm run lint, and then we use dash dash to pass an additional flag. This should work on Windows too. I've tested it online and it works, but then you do fix. Now, if you're doing yarn, you don't need this additional dash dash. I think it doesn't hurt, but with npm, you do need that to say, I'm going to pass more arguments here, or you can make a script in your package JSON. Either way, that will auto-fix everything that it can. It can't always fix everything. So if you're doing an example different than mine, you might have to fix some manually, but there you go. And if we go over to our code, we look at this attack. You can see that it has formatted it for us, and it's made it this big long line because we don't have prettier, and so we don't have like a line length minimum and all that stuff. We're just using ESLint. So with that, we fixed everything, and you do probably see that warning. You might be wondering about that. We'll get to that in a second. So when we commit this, and I'm showing all the commits that I'm doing here so that if you happen to be following along and you want to check out the repo afterward, which I will, of course, link in the description, then you can know what commit to check out if you want to see a very particular set of code changes. So we're going to name this FixLinting, just something generic. So we'll push that up, and gup is just my git push command shortcut. Probably should have done the full one for you, sorry. And we go back here, and if we go to Actions, we will see a new action run. So you can always go and check out the history and see the previous one, but we have a new one running called FixLinting. And if we watch this, it's going to be the exact same set of steps, but we should see a success here. Now, the warning that you saw before, you can see got that check, and we got a green check. Now, the warning will still be here. You can actually check it out here and see. It does say it has a problem, but it's not an error. It's one warning. Warnings are okay. They're not going to return that non-zero exit code. They're going to return a zero. You should probably fix that, but it's not going to hold up your code, basically, from passing. So now that we have that passing, and you've seen kind of failing and passing and so on, let's make a new branch real quick. So on this new branch, let's name it PR Example. I don't know why I put that in quotes. So GCO is my Git checkout. Sorry, habits. Yeah, let's just revert, essentially, what we did here. So I'm going to go to my editor, and I should be able to undo. And then I can do Save Without Formatting in VS Code so that it doesn't auto-use my ESLinter, because if I just save right now, it's going to auto-format it, right? So I'm going to do that. If you happen to have ESLint set up in your VS Code, the extension, you might not. And I can't go over that right now due to lengthening the video, not being directly the topic, but I do plan to do some linting videos in the future. So when I get those out sometime in the future, I'll link that here. But if it's not there, then you're just going to have to go and look it up in the docs. So we have saved, and now we're going to just commit this, basically, breaking the pipeline to show an example. So I'll say revert lint fix to break action. Not really a break, but... Okay, so we've pushed it up. Now we're still doing on push. We haven't changed that at all. It's going to run every push. So we go to actions, we'll still see that here. But check this out. I'm going to create this pull request. If I just go to PRs, it's going to suggest it for me. You know, pretend you're creating a PR. You're going to PR from this example branch into master. You know, I might write some comments here, whatever. When I create this pull request, you'll see that it says all checks have failed. So it actually puts this like right in the conversation, which is really neat. So whoever's looking to kind of merge this code can see that you basically messed up their stuff. Whatever they said was their minimum set of guidelines to merge in as broken here. Now, by default, it actually doesn't block merging. I can still click this merge pull request and it will merge it. So let me show you how you can set it up to actually block it for a second on your repo. So if you go to settings and then you go to branches. So I want to add a new rule here. The branch name pattern, I'm just going to do a master. You can do like, you know, feature if that's how you do your things or whatever. So I'm saying master is going to have the specific rule. And then we can go to require status checks to pass before merging. I wish they would put something about actions here to draw more attention if that's what it uses, but. So we require status checks to pass and then require branches to be up to date before merging. This is pretty common to do. So I'm just going to flip that on for now. You have these two layers and then you want the test that you want to run. So in this case, or the action or check. I can type lint and we have that lint code base check if you recall. It'll just auto complete for you, which is pretty nice. So once you've added this with these two checks you can come down here, you can save changes. And then when you go to your pull request should have automatically updated. You can see now that it says this little required statuses must pass before merging. And the circle will actually get filled up like proportionally if you have multiple checks which is cool. As an administrator, you may still merge this pull request. So it's saying I can click merge and then you can make sure that you want to do it but only if you're the admin. So I'll cancel that real quick and then we'll show this auto updating by going back to our terminal. We're going to do the fix command that we did before. Oops, this one, npm run lint fix. We'll add that again. Fix run dash dash fix for PR. Example. Push that up to our base. And then as soon as we do that, this will just live reload, which is really cool. So once it has, it'll still say some checks are, you know, haven't been completed yet. So they're running. And then we have this one which usually should give us a link. Let me reload. Yeah, it's kind of weird. Sometimes you just have to reload it like loads half the things. So we go to details. Oh, wow, that was already, that was fast. So you can show the checks. It'll just kind of like truncate them in case there's multiple, but you do show all checks and then you can see this individual one passed. And we could of course click details. And that's the same thing as kind of going to actions except in this case, it's on this pull request. It will show it right here. So now that our stuff's passed, you know we get the green light to click merge. And that means that, you know anyone who has authorization to merge can merge there, not just an admin. Since it passes all of your checks. So there you have it. We've merged this pull request, should be closed. Go back to our code and we're currently up to date with that latest. And so that is a quick, basic example of doing GitHub actions. You know, in the past, you would have had to rely on some really awesome tools like Travis to do continuous integration. And those are still great tools. They have a lot to them. We might go over them in this series, depending on interest. But now what's really neat is a lot of them like Bitbucket and GitHub. And I would believe GitLabs, but I haven't tested it out have their own way to do continuous integration or some sort of automated services kind of just built in. And so that's what I use now by default. And then if I need a little bit more I might add some other services and stuff like that but actions will get you really far actually or in Bitbucket it's called pipelines. Now, if you want to see more about that, you know you can of course check out the GitHub actions documentation. I don't know why I didn't just type docs, but whatever. I will link this in the description but of course you can just Google it like I did. They have a nice little quick start guide. I thought about showing that, but I figured I'd do something you know, a little more normal to what I usually set up but you can obviously follow that. And they have a ton of information here that you can check that out. Also the GitHub CLI was the thing I used to set this up. And I'll link that as well, which is really cool. It goes through just a quick example of all the neat things that you can do with that. Like I said, if there's any interest I might do a video on that in the future. So just let me know. And I believe that covers the topic for this video. So I'll hopefully see you in the next one.