 All right, so thank you all for coming. So as you see, running my actions and introduction to GitHub and GitLab workflows. So what we're going to do today is just introduce the concept of GitLab pipelines and GitHub actions. So to start things off, I'm principal emerging technologies developer at Red Hat. Pretty much what that means is we just try to identify cool tech that's coming out and put it into our products. So that makes me a gluer of things together. So I try to identify, this piece should go with this piece to this piece, and that's why you have the infinity sauce gauntlet there. That's how I view what I have to do. So the agenda we're going to cover today is, we're going to go through the concepts of the whole thing, and then we're actually just going to do demos after that. The demos are definitely more fun than the concepts, but if we didn't have the concepts, it'd probably make things a little more difficult. All right. So why? Jenkins has been out there, Travis CI, all of these products have existed, they've been successful, they've done everything you need to do, but why? So the quick advantages that I've actually seen thus far, you can have cleaner code, you can put linters in front of your process as part of your merge requesting to fire off, hey, you have white spaces in your code, please stop doing that. Then it's a repeatable defined process. So many times, in my past, I bring in the software was like, okay, we'll do this to the code, and if it's not operating the way that you think it should, do this, and then this. The nice thing about the repeatable process and get-up actions is that every user of your code repository is going to see your actions. So they're going to be able to actually see and follow your steps and it's a nice way to validate that it actually works on someone else's laptop. So there's two types of testing that I'll show today. Schedule-based testing, you can have a kickoff every morning at 5 AM or 6 AM. The nice thing about that is there's a failure, then you can actually catch it before your day starts. I ran into a go-lang issue because I was using another repo, and so I knew that morning before my day even started. Then there's pull request testing, which probably everybody's used to today. It will run through your pull request, test it against the code infrastructure, test try to deploy it, and merge it as successful. So this is like the happiest scenario that you have. Like green check marks is like a great day and success, because that means all the code has passed checks, it built, it deployed, everything is happy. So that's what you always want to aim for, but I'll show you an example, a little bit of not-so-happy example. All right. So the key concepts that we'll talk about, we're going to talk about the GAREPAL story, which is self-explanatory, we've all been doing this for a long time. Then we're going to talk about runners. Runners are the actual containers or hosts that actually run your code or do any of your testing. Then we'll talk a little bit about integrations, actions and pipelines, which are the workflows that we're used to dealing with that we're going to deal with. Then templates and marketplace, like the templates and marketplace are so nice, because they're like a store for you to actually just grab code from and just, you don't have to develop it, it makes it so much easier. So GAREPAL story, it's our application code, we know that part. But it's also going to be where our workflow actions live. You'll see a dot file within the GAREPAL story, and that's going to list out the YAML for your runs. You can also have integrations as well, where you can poke some main slack that a new PR came in, and then another thing that we'll show and demonstrate later is secrets. Secrets are things that you don't want to really show in your workflow runs such as access to a Kubernetes cluster or a password to Quay, which we're also going to show today. So there's two types of runners that you actually have to deal with. Self-hosted, these are the runners that are provided by GitLab and GitHub. So the reason I want to talk about this thing is, I can recall early in my career where we had this cool idea for a project, but it was like, where are we going to host it? Where are we going to test it? Then you'd have to put the ticket in to get a Jenkins server for this MVP product that you might not even be able to go anywhere with. So the cool thing about self-hosted is you can request like Ubuntu, Mac OS, and then run your code against it, and it is maintained by GitHub and GitLab or you can spin up a specific container. It's just nice because it's offloaded and you are started today. You don't have to wait around. On the other side of that, there's hosted. That's where you bring your own infrastructure to the party. This infrastructure could be a host, it could be a Kubernetes cluster. So the nice thing about this versus the GitLab and GitHub provided, you have a larger variety of operating systems. If you're a well shop, then you could just have a host of well environment. You can access internal resources because since you're part of your trusted network running your hosted runners, then you can access things over all over the place in your internal network. Then greater control over versioning. When you use the GitLab runners that they provide for you or GitHub, you don't really have a lot of control of what version is this package. So you can nail down and identify the exact things you need to do if you bring your own host. So integrations as I said earlier, these are just your standard notifications that it requests came in, build broke. These are nice clickable things that you can either get from the marketplace or templates in GitHub and GitLab. Then you can add in PR requirements and this can be done through a workflow and this can make it so that, all right, someone didn't sign off on a commit. So there's a really common one that I've seen called DCO. What it does is it just checks your pull request and make sure it's signed off to there. The nice thing for me because I make the mistake all the time, it tells me how to fix it. Then there's also integrations around merging assistance, such as squashing or doing any cleanup after the fact deleting branches after the PR has been merged. So while we're all here, okay, actions and pipelines. They're called actions and workflows in GitHub, pipelines in GitLab. So when you think about this at the end of the day, you could build your code, you could test your code, deploy it and then create releases, and this could be like automatically done, predefined in your YAML. One thing that we've done a lot with our builds and our testing and operator for the test part of it, using the runners provided by GitHub and GitLab, we can actually spin up a kind cluster, deploy everything to it, and validate that it's actually working in a way that we expect before we even go to our required environment. So it makes PR process a heck of a lot easier than just kind of running a couple like lints against it. I will say that the option exists to do way too many things. When I was creating this presentation and even doing the code that you'll see at the end, I started going down the rabbit hole of like, oh, I could put like a release that automatically generates at the end of this PR run or I could do this, that and so have a defined path and where you want to go before you start because the possibilities are endless. You can hit API somewhere, so many options. All right. So the templates at Marketplace, the cool thing about this is GitHub and GitLab, they want you to get started. They want you to start trying to use this. So what they provide for you is these templates, and it takes like all of the guesswork out of it. They will provide such things as like an automated like predefined Go template to test your Go code. You can deploy Kubernetes environments either in kind or actually provision one against the Cloud provider, and then you create and push images and then test that code. The Marketplace is cool because you'll see some of the major vendors out there that will produce assets and then provide them to the community to be able to use, and so it makes the barrier entry a lot lower. So let's take a look at our repository. This one is a project that I can work on, and that is the wrong link. Let's try this one. All right. Sorry about that. Okay. So we have actions enabled here, and as you see, I tried to do a commit two days ago, and it caught that the version of Go I was using didn't have IOS capabilities. So I knew that the commit that I was needing to use wasn't going to work because I was using Go 115. So this was like almost a stopper before I actually did something wrong of building the image and just pushing it out. The one thing that I will say is it makes it easy to be a lazier developer because I don't actually have to have a Kubernetes environment to try to run this against or I don't have to have a cluster to use. I can just put in my code, throw it over to GitHub, GitLab, wish for the best, and it'll tell me if it's correct or not. So it really shows the importance and the strength of spending the time to just nail down your actions in a way that benefits you. A few months ago, I was actually sitting in traffic writing code for this and the passenger seat. Sorry. Sorry. That would have been bad if I was driving and writing code. It was in Chicago, so the traffic wasn't moving anyways. But no, I was in the passenger seat and the nice thing was I could just throw it while coding on my phone, I could just throw it to GitHub and it just tested for me and let's me know if I'm in the correct place. It was not. There was a lot of failures because I was typing on a mobile phone, so that's to be expected. But yeah, the nice thing is, you get almost a history as well. I can see not only, here's the schedule runs I was talking about, but I could see the branches that were merged at the time that had the failures, and I could see when it ran, how long it ran, which is nice as well because if you introduced a build that deploys and it goes from taking six minutes to 35, probably should not go in. So back to the slides real quick before we jump into demo time. All right, so we're actually going to go grab demo. All right, so how are we going to get started? We're going to have a simple go application, and we're going to put that in GitLab, and then we're going to build some testing around it, and we're just going to see how the PR process goes. Then we're going to do an image, build push, and deploy, and we're going to do that on GitHub. We're going to use a couple of the items from the marketplace that I talked about from third-party companies that provide how to do things. So hopefully, this will give us a clear picture in how you can get started today. So to start us off with, I have a simple stock application that I have preloaded in here. I'm going to jump to my terminal real quick, and all right. So all this application really does is it tells me the current stock price of any ticker that I put in and that text is really small. All right, so it tells me the stock price of any ticker I put in. So that's kind of fun. As you all remember at the beginning of this year, there was a lot of fun stocks to follow and such. So that's good enough, but what if some of the other people want to use it within my company, within my organization, friends. So let's put a pipeline around it. So here is the templates that I was talking about, and since I have a Go application, I can scroll down here and it's the CI CD test and deploy your Go project. So I'm going to use this template and as you see here, it's going to do all of the things that I expect a project to do. I'm going to test the code first, and then we're going to build it, and then the nice thing is you'll see right here, the highlighted artifacts. So what that actually means is by the time this is complete and every time that there's a run, we are going to end up with this stock binary. The fun thing is there, then you can just point people to that. They don't have to know how to compile Go or build Go on their system. They can just grab this release asset. So we'll take a look at the first pipeline run just to get an idea. We're going to do the format or the testing, and that's just going to run our Go test scenario that we have. As you see with running on GitLab right now, we are pulling down the latest GoLang image and then running our code against it. So right now we're doing the pull of that Go image, and then our code will just get thrown into that image, and we will begin the testing cycle. Then this is probably one of those times that you make the debate of whether you have a self-hosted runner or if you just use what's available, especially when you're a time crunch such as this. So you have to wait, and then the format was successful as I was clicking around as you see jobs succeeded. Then we will go back to the pipeline and I will show you the assets that were generated once they finish. So right now it's on the build process, it's on the compile. So what we're going to do right after this is we're now actually going to do PRs against this workflow once it's been established that we know that the pipeline is working. Yeah, sorry about that. I'm going to put this down here. So as you see, we have a successful run. This is exactly what we wanted. There is the ability to download the artifacts after the fact, even if we go to this, I can get my artifact, so I can get my stock artifact, and so I can give that to friends or whomever. So let's take a modify this on fly. So we will go here and I will take and open up edit. So I would like to get the 52 week high and low, just because when you're dealing with stocks, that could be an important thing. So I'm going to grab this code here that I have, and we'll go back to the live ID here. We will put it in here, and then what I will do is I will create a branch for this called weeks, commit it, and create the merge request, and then the cool thing is we will start the process of testing the code. So the nice thing is this is officially offloaded off my system. I can just modify the code and then send it up to the repository, the testing building takes place, and I am no longer in charge of that. So let's go ahead and from here, wait for the test to complete and then we'll add just like a little bit more functionality. Like I said, this one runs in containers, the second demonstration today, we will use host and use the Ubuntu latest as our base for starting off. So waiting for this pipeline to finish, we have the format complete and now we're in the compile process, and we'd automatically selected to merge upon a successful run. So going back to our PR, you'll see this, we'll click that there, and we're just waiting for it to be complete and then it will auto-merge. So once that merges, hopefully pretty soon now, then we will add just like a little bit more fun to it. Take a look at that and job succeeded. So this should auto-merge into our repository, as you see it did auto-merge into the repository. Let's see here, merged. So the cool thing is, let's see what happens when we don't do things correctly. So going back to edit, we'll take a look at this and we will do just like a simple if statement. If symbol. So we will make yet another PR that I know that's going to break because I removed the bracket there and we'll just say broke. That changes and we'll create another PR and see what happens. So the nice thing is, this is going to cash us from putting out a bad code or then it's going to tell us exactly what happened along the way. So we'll just give the test just a moment to get spun up and tell me that I cannot code and go correctly, which is definitely true. Then the second one, we will move on to it right after this. So as you see, we failed because we expected this bracket to be at the end. So going back to our merger request, we can fix that and patch that up really quick and click the right button. Sorry. So open IDE and you just see here, here's the change, we're going to patch it and commit it. Then it will go through the process again and we will just go ahead and just check to auto-merge it and move on since time is ticking down. I don't want to leave a little bit of time for questions at the end. So merge when successful, so we will just go ahead and set it and forget it. All right. So the next one. So this is a web app that I found interesting. It's a PHP application and what you can do is you can throw files to it and test persistent data. The bad thing is this doesn't contain a Docker file and I can't remember how to do Docker files for PHP applications. So we're going to lean on what I was talking about that marketplace and those vendor provided actions. So here's one for S2I and which pretty much all this is going to do is it's going to take and look at our code, figure out what it is and then compile an image for us. So any ideas you have for a GitHub action, you could type in what you're attempting to do and then GitHub, and sometimes you'll get lucky and you'll see something like this where there is already a vendor provided option. So jumping into this, we're going to use S2I to build this. So let's go to our actions and we're going to set up a workflow. I'm going to cheat just a bit and grab some code that I have. All we're going to do is for this run is we're just going to build the code. We're going to baby-step this just to make sure that we've done things correctly. So what we're going to do is we're going to start commit against main and you will see that we are now attempting to build this image anytime that there is a commit. So while this runs, I want to highlight a few things while that build actually occurs. So looking at the file, we want to run this anytime that there is a pull request against the master branch or whenever we actually push code or merge it into master or as you saw that I did a moment ago, push directly to master which don't ever do. Then there's also this cool workflow dispatch button flag or field. What this actually allows us to do is if you go to actions, this means that I can execute that job at any time by clicking run workflow. So it's like if you don't have a pull request that's going to hit it or if you just want to test your pipeline, you can just hit run workflow and against the branch you want to use and boom, it'll go. So as you see our container build was successful. So we had a setup and build and we created an image that had two tags on it. We had V1 and the GitHub Shaw. So let's expand that. It doesn't really help us if this image doesn't go anywhere. So what we're going to do is we're going to add in functionality to push this image directly to Quay after the fact. So going back to my cheat sheet here, we're going to see that we have this block here that we're going to push to Quay. There are a couple of things that I want to call out and we will do these, set these up. So as you see here, we have these fields called secrets. These are just items that you don't want kind of showing on the command line while this is actually running. So I have already predefined my password because that's the difficult one. So let's go ahead and create our secret for our Quay username. This is just the equivalent to a Docker Hub credentials. So I'm going to add my secret in. So that was our Quay username secret and then if we look at our current secrets that we have defined, we also have our password predefined. So we will go ahead and commit this against master again, and it will trigger the same set of workflows. But at the end of this workflow, this time once we have our build, it's going to pass it next to the push to our repository. One really cool item that I showed on the GitHub is artifacts. How we had and ended up with our compiled script, our compiled stock application. Those artifacts can be saved for a specific run and presented to the user when you click the Actions button, or you can actually use those artifacts to pass it to a job that's later on down the line. So you can do things where it'll save you just a bit of time and kind of make the code a little bit clearer because you can take and save that and pass it, and GitHub and GitLab provide the ability for you to just upload that into their temporary cache for it. So as you see here, we are pulling and pushing our image. We're pushing the V1 as well as this GitHub Shaw, and this will be successful in a moment. Then from here, we'll at least have it in our repository. This would allow us if we're just a company that works and uses Docker containers or Podman to deploy this image because it would be publicly available in our repository, then we can just grab it and use it as we need to see fit. We're going to take the next step and just define our Kubernetes cluster and we're going to deploy the image anytime we build it directly to it. Okay, so that was a clean successful run. So let's go ahead and add the activities for pushing to our Kubernetes cluster. We're going to cheat a bit and use yet another one of those third-party integrations. This one I believe is provided by Azure. So once I highlight this here and bring it into my environment. Okay. So as you see here, I want to point out this deploy line here that I have messed up the formatting on. Okay. The deploy line and what this means is, this identifies almost like a break in the process. Our first process was the build process and now our next step in the process is this deploy. So the nice thing is we have it so that if we require and we need to build the pass because we don't have the build that we don't have the assets to actually deploy. Then the other part is this if statement here. So we don't actually want to run this against the PR because we ran this against a PR and somebody had malicious code in or if the build actually didn't really work the way we expected it to, it would automatically get deployed to the environment. So we can stop that with this set of lines here. It's just a simple if statement says if push or if it's scheduled, go ahead and run this process. The nice thing is if you think about it this way, you could break it up to say deploy it to a test environment first just out of the way and done. Here is the plug-in that I was talking about that I looked up from the Marketplace. This is just a way to provide your kube config and then issue Kubernetes commands against your cluster. So what that means is we need to actually build our secret for the kube config. So put it here and then this is a ginormous wall of text. So just give me one second on it. So what I did was I grabbed my kube config file and I base 64 encoded it which is the expectation of that Marketplace item. So we'll go ahead and create the secret and then go back to my actions and then as you see here, kube config now equals the secret that we just created. Once that secret is loaded, then we are going to set the image for our deployment based on the image that we just built. Here's the GitHub shot. So we'll start the commit and push it yet again to master. Then what we're going to do is we're actually going to take and modify the code and I will show you that in a moment. I'm going to get the app just to show you what it looks like right now and then we will make a change on the fly. So we have our application here. I said it's just a simple file uploader, nothing really crazy. So we'll go to our index.php and we'll just modify this and say okay, we'll go ahead and put a quick note in there and we're going to create a branch and call it note. So similar to what we saw with GitLab, we are going to run through the same exact process. You'll see that we're going to execute the build piece, and then the interesting piece after that is we're going to see it stop, and we need that stop because we don't want to deploy to the environment, and we don't want to really push to quay. So this is going to run momentarily and due to us having about 15 minutes left and I want to leave some time for questions, I will hopefully try to hurry this up. Okay, so the build job is complete. As you see, we did not push to quay and we did not deploy to the environment. And that was based on those flags that we set that unless it is a merge into master or main, then don't perform those actions. And so it's a nice gating mechanism to just kind of make sure that things stay safe, and then now that we have merged the pull request, we will actually see that the actions for deploy and push to quay are actually going to happen this time. And so we're going to pop up in our terminal here, and I'm going to kind of see if I can have both of these at the same time running. So if we do get pio.w against our Kubernetes cluster, once the deploy takes place, which will be in a few moments, we'll actually see that this one's been down and the new one's been up. And this is just all directed by the GitHub actions that we've kind of defined at this point. So we're on the push to quay step, and hopefully this should be pretty fast because layers should already exist, because we haven't really modified the code all that hard. And so with this, while we're waiting for this to go, because it's probably going to be another minute, does anybody ask any questions right now while we fill the time just a little bit? Or yes, yes, so the question was pushing this to quay, this will deploy it to production. And yes, so because of the way that we set up the action, and we'll go ahead and take a look at that, and we'll go back to that really quick. To say that anything that we had that is like a merge to our main branch, we'll go ahead and push it to the repository, and that same statement is on the deploy as well. So anytime that we feel confident that the image or item meets our requirements, then push it to production. Or this could be a testing environment or so on. But I think one of the cool things is, if we would have had a little bit more time, we could actually deploy this against kind right straight in here, and then did some testing on it. And then we could go to production after that to have like a safer experience. So I mean, one thing that I really like about it is the possibilities are endless in regards to what you can kind of glue together in peace in in this situation. So pushing the quay means it goes out to the deployments where it only goes out to a certain percentage, and then scales up to the rest of what. Yeah, so with that, you would probably, so when we're pushing quay, we're just pushing to our image registry. And then from there, anything that I just kind of want to highlight this line here. Anything that you want to modify with your environment, as such as setting like blue-green items, would fall in the Kubernetes environment itself, but you can manipulate it within your GitHub actions. So whatever you define or hard code or soft code define within the GitHub actions it can do. And what you could actually do for your scenario, for example, you would modify the traffic for the Kubernetes cluster using maybe specific SDA rules or some sort of ingress rules. You could have it so that it deploys those bits and then have further testing down the line of that and continue that testing framework to where you can increase those percentages of that traffic and then switch completely over it once you establish confidence and in where you are. So, like I said, the possibilities are endless because I know a lot of bash, like it's probably the only language I know. So the nice part is, anything that you can really kind of think of, they have the capabilities with, I do not have one in here, all right. They have the capabilities with what they call the run line and I will pop one up. I, oh, wow, let me just push the PR to my repo, okay, sorry. So let me see if I have a run line here just to kind of show that off. Okay, cool. All right, so this is kind of what I really mean when the possibilities are endless. They have the capability to do these run lines. It could be one to many, as you see this in many. So we have the pipe in here, but like I said, it's like anything bash you could throw at this thing. And it will do kind of what you really wanted to do. Since we have a little bit of time, kind of show off as well as part of this process, I was talking about the archives earlier. So this is a workflow run that will archive a container image. Then we go down the line a little bit further in my build. And we will see that we're gonna take, and we're gonna actually download this image, download the artifact within our jobs. And then we're gonna take and import them straight into our kind cluster, which is running on the GitHub runner. And so, like I said, it's anything bash you could throw at it. Here is setting up kind and here is actually creating a cluster with kind. And this is all on infrastructure that GitHub provides you. And so, this being the open source summit and thinking about open source in general, this really removes a lot of the barriers to entry for those people that are starting to code. And they don't actually have anywhere to run the stuff. And so this is a good way to be able to test it and make your stuff look very mature before you really have to invest a ton of money into it. No, and that is the reason why I'm so amped about GitHub and GitHub Actions. This has all been on infrastructure that they provide. And that's the only reason why, to me and the open source community, this feels so powerful because they're removing that step. Me needing to find infrastructure to test and run my code, they're providing it for me. Okay, so the question was, how do you add in your own integrations, own testing framework, while still kind of keeping up with the fork? So the way that I would actually kind of approach that is the fact that there's no really kind of hard limit on workflows. So that I could add a second workflow to my application. And the nice thing is I would keep it separate, cuz here's the CI flow. I would keep it separate from the fork, but I'd also have my own spin on things. Yes, so anything that you want to my knowledge, I'm sorry if I'm wrong, to my knowledge, anything that you want to run in the actions has to be kind of within a repository, that's how it builds up the, I guess the list of things that it wants to run along the way. Yes, yeah, you could have that and then I don't as long as your workflow didn't mesh or had a different name than even upstream, you could potentially keep it in main because I don't think the fork would ever complain and then you could always do your pull from upstream and then merge in and I think you'd still stay safe. So I mean that is actually a really good idea like that cuz I usually modified it on my own and then I'm stuck like trying to diff along the way. So going back to our container, as you see here a little while ago, we did the deployment update because we patched our image. And then going back to our web app, we can see hopefully our new note. Yep, so here's our new note that we kind of pushed through the GitHub actions, built the image and then deployed it straight to our environment. And all in all, it's kind of fun as well that with kind of a security idea in mind, I potentially wouldn't need to give developer friends credentials to get into my environment, to poke at my environment. I could have this service account or a cube config setup specifically to deploy for a namespace or a specific part of cluster and upload that and just let the actions take place. And it really removes the need for someone to push the button to patch a release. Since we just have another moment, where I was actually gonna go last night when I was playing with this is I was thinking about, I don't know if anybody's heard about GitOps, but GitOps is the idea of all of your Kubernetes objects are maintained in your repository. So those EMO files are the direct picture of your cluster. So what we can kind of do with this scenario, rather than our push to the Kubernetes cluster itself, you can provide your actions in GitHub token. And then make a merge by the robot itself or by the GitHub actions that would update the deployment definition. Which then your GitHub or GitOps tool would then take over and actually deploy it to the environment. So it doesn't, I guess where I'm going with this, it doesn't require you to kind of change the way that you do things. You can still use your GitOps tooling that you have today or any release engineering tools that you had today. But you can actually use GitHub to update those tools. So we got about, I think, seven minutes left. Anybody have any questions? Yes. So the question is, are there any major differences between GitHub and GitLab? The ones that I've only really noted are regardless, like along the lines of the templates and marketplace. Sometimes you won't get lucky enough that you could find it in both. The other thing is the format is just slightly different between the two of them. But then again, when you're looking at those run actions, you can just move it over and match the format and the line spacing. And just everything works. Everything that I've seen and kind of switched back and forth, I initially had this presentation only to a GitHub stuff. And then I brought it into GitLab. And it's seriously just some line spacing things. And then if you take advantage of this third party integrations, such as plugins or marketplace items. Which makes it nice because some organizations like to buy GitLab and deploy it internally to keep their code local. So if you started out in GitHub, you can just bring it in. And then still just kind of set up your line spacing and be back in business very quick. I think that anything, the question is, could you generate the format for both GitHub and GitLab from a single source of truth? I think you could. I don't know a solid answer on it. I think the nice thing is because you have the freedom of the actions in one place, you could kind of fix the deficiencies and changes. I could have GitHub just kind of scrub the code and do a couple sets to make it match what I need in GitLab. And then just kind of, like you said, like automatically just build it on the fly, dynamically have it. So I think you can actually depend on the actions to create your actions almost. It's how many layers do you kind of go with it? It's like inception of GitHub actions. And so it's really fun. I've seen some really cool stuff around people building documentation and web pages, like automatically from this. So if you use read the docs, you can actually just have your code in here. It'll do a little bit of testing, make sure the lines and the lint are fine. And then boom, update your web page. Like it's, most of the possibilities are endless. Like you could grab it whole four weeks at a time and still just be like doing really cool stuff. So like I said, this has been the most exciting thing. And it feels very approachable as well. I had to attempt to use a newer version of Jenkins and they would not let me use the freestyle GUI. And so having to kind of figure out how to put groovy and do the things I needed to do was difficult at that time. And so that's why actions just feels so approachable that it's just YAML and you just throw bash at it and it's right there. Any other questions? I did want to put this up as well. Both of the repos that are in this presentation are available for y'all to kind of take a look and poke at after the fact. Just to kind of see what we did or if there's like things you want to go back to and look at. I also included just kind of introduction to GitHub and get lab actions in case you have those or you're using those in your organization today. You might be able to just kind of take these quick links and really attempt to get started today. It's I have not had any regrets at this point with deciding to go the routes of using this for my code testing. And I really appreciate if anybody knows at GitHub or GitHub tell them that I really appreciate them offering this free for people to test their code or to use this to kind of make their lives a little bit easier because you think about the people all over the world. The ability sometimes doesn't exist for them to have testing frameworks in these environments. And then removing that barrier entry I really think allows everybody to win. I also included my email in Twitter on here if there's like anything after the fact if y'all go home and you're like I really want to poke at this. Any of any questions, feel free to reach out. I will share any code I have with you, jump on a Google Meet, etc. Just to kind of get you off the ground because I mean, once you get started, it is hard to stop playing with this because it is just way too fun. With that, I think we have like a minute left. If anybody has any like last minute questions, very cool. Well, thank you all for.