About this video
What You'll Learn
- Pin a CLI tool version in a project-specific aqua.yaml registry file
- Use a global aqua config to keep shared tools like gh and rust-analyzer
- Build a Go service and test it with Dagger, Hurl, and cached steps
Brian demos aquaproj for pinning CLI tool versions per-project and globally via its YAML registry, then David walks through a Go Dagger pipeline that builds a container, runs Hurl HTTP tests, and visualises cached steps.
Jump to a chapter
- 0:00 Introductions
- 0:33 aquaproj
- 0:35 Introduction to aquaproj
- 0:46 Installing a Project-Specific Tool with aquaproj (Hugo example)
- 2:33 Project vs. Global Configuration (aquaproj)
- 3:17 Searching and Adding Global Tools with aquaproj
- 4:38 aquaproj Summary
- 4:51 The aquaproj Registry
- 5:55 Dagger
- 6:32 Building with Dagger: Defining the Pipeline in Go
- 8:05 Creating the Final Container Image with Dagger
- 10:04 Testing the Application with Dagger (Hurl example)
- 11:48 Running the Dagger Pipeline (CLI & UI)
- 13:00 Visualizing and Caching with the Dagger UI
- 13:30 Dagger Capabilities and Reusability
- 15:05 Conclusion
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
0:00 Introductions
0:00 Hi, and welcome to AlphaBits. AlphaBits is an outlet for our inquisitive minds. We can't stop experimenting with new technology, and we wanna share what we're playing with with you. We're your hosts, Brian and David. Hi. I'm David, defender of the Rawkode Academy. I'm a perpetual learner, always playing with new cool tools that I'm gonna share with you as part of this podcast. And I'm Brian. I'm a developer advocate at Amazon, and I love all the shiny new toys in tech. It's kind of my passion is is testing out the new stuff and, seeing what's cool. Okay. So I'm in a
0:35 Introduction to aquaproj
0:35 project that is a Hugo blog. And what I intend to do is make Hugo available in only in this directory. So, if I ask which Hugo see, don't have one. So, I'm gonna do is aqua init and that creates the YAML file. Alright. So, it's it's set the registry. So, this is a pin version of the package registry that shows which packages are available and what versions of those packages are available. So, I'm gonna search for Hugo. And, it brings up a a fuzzy finder like search interface. So, I'll type Hugo. And, I get two choices.
0:46 Installing a Project-Specific Tool with aquaproj (Hugo example)
1:24 Basically, Hugo extended and Hugo. So, I'll just pick Hugo and it outputs this item, array item for YAML. So, let's mini our aquapro thing. And paste in the output. So Hugo version zero dot one one seven. Save it. The aqua I. Now, type it Hugo. I've got a Hugo. I move up a directory. Still there, but I could choose a different version in a different directory if I wanted to. Now, PewDie, it works, and I'm good to go. You can include that vocab. So you would include this in your git repository so that any person that was using this
2:30 would automatically get the same configuration. So that's at the project level. At the global level, there's also another config file that you can use for globally available tools. So, if you look at my globally available aqua yaml, I have the git hub CLI, fuzzy finder, and NeoGIMM installed. So, it's pretty wicked. I mean, you could use this to get a very repeatable environment, but it's it's very much focused on CLI tools, CLI applications binaries as opposed to libraries. Nice. So it's pretty slick with let's say I wanted to search for cargo. So I could just do aquaproj,
3:17 Searching and Adding Global Tools with aquaproj
3:23 and there is no cargo in here. How about Rust? Rust analyzer. So I could add the the Rust analyzer if I wanted, probably because Rust itself is such a large install. Is there? Let's see. Yes. It is. So try. We can add it. Because I'm at the global level, I have to specify this dash a. So it will be included in the global. So now I can which I'll try it. I could spell. That's weird. I swear it said 085. But come try reports 084. Strange. That's probably me. I probably forgot to update version string somewhere, but I'll look into it.
4:34 Possibly. Alright. So that's aquapro. It's short and sweet, but pretty powerful if you want to do some version pinning of particular tools at both the global and project level and share that with your team. Yeah. Awesome. I'm assuming their registry is just a GitHub repository that anyone can go along and contribute to. Is. Yes. So if you go to aquaproj, let's stop this screen, add a different. Alright. So here's their GitHub org. You can see the aquaproj thing itself. It's got an installer and the registry, and the registry has all of the package definitions. And and when I first looked at aquapro
4:51 The aquaproj Registry
5:22 roughly a year ago, it had 30 or 40. But now it's it's gotta be well into the thousands. So they have pretty much anything you need in terms of binary. So you can see the definition, the registry metadata, and this is how it knows how to get things out of GitHub. And then the package metadata. That's Starship version one dot 16 dot zero. Nice. Very cool. So that's what I got. Alright. So this is a rather contrived application of a build pipeline. It's a back end go app. It just has, like, some really simple endpoints,
5:55 Dagger
6:07 like slash ping slash ask, which speaks to, you know, the OpenAI chat GPT. You place in a question, it gives you a response. That's it. Nice and simple. Reason I set up like this is because I wanted it to use an API token and a secret to show how that worked with the Dagger build pipeline. So it doesn't need this environment variable to exist, and we'll see how we make that work or at least how we satisfy that with the Dagger pipeline. I'm always making sure that I have my build instructions as close to my application as possible. So
6:32 Building with Dagger: Defining the Pipeline in Go
6:38 you'll see here that in the back end directory and I know the sidebar is always really small and the s code, but, you know, we have a build directory with a Google some Google code called build.com. And this is our Dagger pipeline. So all of these publicly exported functions are things that can be consumed or used by a general purpose build tool. And and we'll see the general purpose part in a moment. But we see here that we have export, which is gonna call another step, build container image, and then publish it to my GitHub
7:12 registry. The build thing itself, we can ignore the runtime caller. This is just me doing some weird directory juggling. But we get this Dagger client where we say that we want a container. Remember, we're gonna use Golang latest as a base. And we add our current directory to slash source within the container. We set our work there, and then we say web exec. So we don't execute the command inside of this container. These five instructions are pretty much just a Docker file. Right? I'm building Yeah. Container image or potentially it could be building a container image.
7:51 All I'm doing is running go build, outputting a binary code back end and telling that that the source is main dot go. So now I've taken the instructions to build my application and made them available as a function within a Dagger pipeline. We then actually build the container. So this is like a multi stage container file or Docker file. Only this time when we ship it to prod, yeah, I wouldn't normally use Ubuntu 22, but, you know, whatever. Let's ignore that for now. We do an app update. We install the CA certificates. We grab a file from an alternative build
8:05 Creating the Final Container Image with Dagger
8:24 step. So here, we're actually using the output of this build as a variable and grabbing a file from anywhere inside of the container, which is just our source back end binary. We set an entry point and then that's just got a container image. And that's it. You don't know this GraphQL. You don't know that it's a container based build plane. You don't really know that it's Dagger other than that, you know, we have an import for some Dagger stuff. So I just find this a really nice API to define how to build any application. Now I could run this with go run,
8:57 but because it's a model repository, least it's a thick, can try it model repository in this case. I wanted to have, like, a centralized build instruction. So I've got the choice. Right? As a developer, I can build a small isolated part, which is just the back end, or I could just say go run everything and build the things that have changed. Because this is build cut under the hood and we're only mounting end specific things to each step, I can really take advantage of the way that build cut and container caching works. So I could point it down the time
9:25 on a repository with thousands of projects, but if one file is changed, it'll build to one thing, the thing has changed then and any dependence thereof. Okay. Which is very neat. So this build directory at the top level now, so outside of back end It's configured using a go workspace, which is my new favorite feature in a go, which means I can do local references. So I've got some helpers here. We have a function called get database with config and get database. So this is the explicit one that does some configuration, and this is one that just
9:57 injects a little bit of an override. So here, I'm just saying we default to d b 15 or postgres 15, but you can't provide the full config if you want. And then we just set it up so that we can access Postgres as the user Postgres with the password Postgres. This function isn't available for me to use anywhere. Same with secrets. This is actually just gonna do a local exec where it uses the one password CLI to grab a secret and then spits it back as a secret value. And then we have our different entry points.
10:04 Testing the Application with Dagger (Hurl example)
10:25 In this case, test is orchestrated with main dot go, which uses go workspaces to build in the back end builder, set up to Dagger client. So now we do know that Dagger's a thing. We use our Google Workspace dev tool project to grab the secret. We get our database and then we just call a few more things. Right? So I'm saying here's the build container image that we've seen. Well, with this time, I'm gonna change a few things. I'm gonna pass in the environment variable for the database. I'm gonna pass in the secret token, some port information. We can do service bindings
10:57 so that the different containers can speak to each other if you want. So in a way, you can actually use Dagger just to replace Docker Compose completely, which is very cool as well. Now there's a small hurl test suite here. So once we built the image, we actually just run it or at least we use a surface binding in this case. And then we run another container that has hurl available, which is like a HTTP testing framework because it's a really simple syntax. And then we run the test against it, and then we can inspect the output of
11:26 that and say, hey, this passed or this failed and then show you why it failed. The HARL file looks like this. Sorry. HARL file looks like this. We wanna have the ping endpoint and then we have the ask endpoint, which will actually go and speak chat GPT. So there's a lot of code and fed over that really, really quickly. I'm gonna show you it running and you have any questions, I'll I'll take them. So there's two ways to do this. One is, as a go developer, you could just do go run where we do build test main.
11:48 Running the Dagger Pipeline (CLI & UI)
11:55 We get that a few seconds. One password prompt. That was an error. Of course, that was gonna happen. Right? Make sure I'm on the right account. That's fair. Oh, here we go. Okay. And my test passed. Of course, never trust the passing test. So let's see. We change this to Brian. Run that again. So let's see if it fails, it'll tell us why it failed. We'll that output from Huddl. And then there's one last thing I'll show you and we'll wrap this up. Alright. Now it's saying we got back hello in German. It's hello. I couldn't tag and we
12:30 expected to find Brian. So let's change that back. Now I have to remove an alias because this is a very experimental feature of Dagger where they now provide an experimental UI where you could do Dagger run and then see go run build test main. Now what this does is it will actually run a Dagger client for you, which executes your command, but allows you to visualize the entire graph of containers and commands are running within the build instance. So here, we'll actually be able to see that most things are probably cached. I've run it a few times,
13:00 Visualizing and Caching with the Dagger UI
13:05 but we can see all of the jobs spawning and the output as we progress. So you got all these, like, fancy get branch symbols. Let's say, okay. Here's your directory. Now we gotta pull an image. Here's Golang, and you can see all these adults jumping about. But it really gives you good visibility into what's happening within your build pipeline. Yeah. So that's Dagger. Nice. So where is where's the passing? How do you know it passed? It isn't obvious that Well, it's all cash. What's it look like? Well, it's it's cached right now. It's somewhere in Oh,
13:30 Dagger Capabilities and Reusability
13:43 it it was way at the top. I see it. Was it? Oh, yeah. Okay. Right. Okay. Right. But this is all have been cached. So cached, cached, cached, cached, cached, cached, cached, cached, the way down. So actually, it really didn't execute anything because nothing had changed. If we change this again, hello, blah blah blah, whatever. Something just invalidate the cache. And we'll actually see that one of the steps is no longer cached and that'll be the hurl test suite itself. Because that's what we changed. Right? We changed the hurl test file. Yeah. There we go.
14:16 This one actually executed this time and then we got our failure. Oh, wow. Alright. I'm impressed. Yeah. It's nice that you can raise some go code, some TypeScript code, some Python code, some Rust code, some Alexa codes, you know, and theory could support any language. Some languages are obviously better suited than than others. First of the most first and most importantly, pick one that you're comfortable with, but then after that, try and pick the one that's gonna give you the the best pipeline. Like, been able to reuse functions across a mono repository is a really
14:46 important skill, but not every language actually supports that terribly well, like, no JS, unfortunately. The Go with Go Workspace does enable that kind of per user pipeline. Very impressive. I could think of a few uses for Dagger. Alright. Well, hopefully people enjoyed this episode. We'll see you all next week. Cheers. If you wanna hear why we showed off these tools, check out the podcast available at alphabets.fm. Until next week. See you soon.
Technologies featured
Meet the Cast
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments