th the way we do that, let's just get rid of these and go back to, there's another file called eval.nix that I've set up. So this is not a module. This is just a regular mix file. Uh, it's going to take packages because I need access to this function from the standard library called eval module. And the way that this works is that we give it an attribute called modules and modules takes a list of paths to all of the modules we've declared. And that's it. That's how you get, that's how you do your evaluation. It will handle passing in all of those automatically supplied arguments like lib to your modules for you. And it returns back an attribute set. There's a lot of, there's a lot of information in there for the sake of this. I'm going to look at the configuration output, the thing that we want to see that like we, we've set our, we've declared an option, we've configured it. We want to see that it looks right. So I'm just going to look at the configuration output. And, uh, for the sake of myself and all of you, I've included run scripts in all these lessons, which just, you know, runs nix eval on the file, automatically provides that imports the packages and applies it and gives it to the, to the eval.nix file and just formats it nicely in a JSON output so we can look at it. So let's run it. Okay. Go ahead and make a quick piece. Exactly what we wanted. Uh, so this was kind of trivial though. This is a little, a little simple, a little too simple. You know, we, we have an input. It's the same as our output. And we've declared it and defined it in the same space. We don't, we don't have any kind of interdependencies between different options. So let's look at that. Let's look at what we need to do, kind of in the next steps. Uh, let's look at something a little bit more useful.  Well, let's look at, uh, actually setting up a couple of, several options. Some we're going to use as inputs that we're going to configure. And then we're going to use the last one to actually use the values that we, that we define for all those options to create like a little message for us. So most of these lessons will have like an options, a config and a valid and a run script. Um, so in our options.nix, let's take a look up here. Okay. So we have some more arguments. I'm going to talk about config and it's like, but we still have live and we still have our ellipses. Um, so we have a few more. You can see we have a few more options now. We have a name, we have a title, we have an origin and we have a greeting. We're going to use the first three to configure something, to configure what we want our greeting to say. Um, and in this file, we're actually, we're configuring the value for greeting right away. We want this, we want this kind of part of like the module that we give to a user. Um, so how does this work? Well, you can see there's this CFG right here. So let's go back up and look at where CFG is coming from. It's coming from this line, which ultimately comes from config. So config is another one of those arguments that is automatically provided by the module system. It is kind of the lazy ultimate value of the configuration values that you set. So in another file, we'll configure like name, title and origin and config will be aware of what those values are. We can then take them and then use them down in greeting. This is all just kind of happens, you know, this is, this is the kind of like nice lazy nature of, of configs. We don't have to have these immediately available in this file. They can be in another file and we can use them here. Um, just as, as a note, this config is not the same as this config. This is the configuration that I'm, I'm setting up right now for this, for these particular option definitions. The config at the top, that's part of the module function argument is the, is the final evaluation of all your configuration. And this is, this is why you'll see this a lot if you look into anything that's created within the next module system. Um, sorry, that are like, if you look into any of the options that are on the search.nextOS website, you usually see this like config equals config dot something. And for us, like we're just looking at the top level, so I'm just gonna say CFT equals config. It kind of, kind of helps mentally separate the idea between like final configuration versus like configuration I'm setting right here. Um, but yeah, so I'm gonna say greeting is hello. My name is the configuration value for name. I am a configuration value for title and I am from configuration value for origin. And let's look at the config. Yeah, so here I just have to set up. It's similar to what we did before. I'm just gonna, except now I have a config for name, title and origin, along with these options. And I'm gonna use what we did, similar with the eval dot nix to combine them together and say evaluate both of these files together. Yeah. Actually, this is eval greeting. There's actually two evals here. This one is eval. And we'll see right real quick. So let's just exit out of this and run dot shell. Okay. So this is neat. So you can actually see like we have the configuration at the bottom. We have the configuration values for our name, origin, title, kind of like we just declared in the config dot nix file. But look at greeting. Greeting is, it's actually injecting those configuration values for name, title and origin into our greeting string. It is a little bit kind of hard to read because it's, you know, spit out from JSON or using GQ to try to format it as best we can. I have included, if we just look at the, there's another eval dot nix. There's eval greeting dot nix. I only want the greeting output. These are attribute sets. So you can just say, just give me dot greeting. There's another run script to go along with this. There's a run greeting. Just points to the run eval greeting that file instead of the regular eval file. But if you just look at that instead. Yeah, there you go. That's a nice, that's a nice pretty value. It says, hello, my name is Vody McBoatface. I'm an on-autonomous underwater vehicle and I'm from England. These are all very true statements. And yeah. So now, so now instead of just like using the configuration value or the configuration declaration and just setting the value for the same input or same output, now we've used three inputs to define what one output should be like. Let me just give a quick pause. Any questions so far? Can you say what's the instance of Nix packages? Which instance of Nix packages does the library that I'm using come by from default? So I'm using Flakes for this whole website. So it's whatever is pinned in my clock file. You mean like these ones up here? No, you can omit this. You could delete that. Let's do this and just delete this. And here I'm just going to change these to something. That's valid. That'll work. I use it mentally so I know which ones are which. So when I'm looking at them, that is my final evaluation. And also you usually see it, like I said, when you look in any kind of like NixOS module that's declared and like in the repo, they'll usually like try to access, they'll have multiple configs like accessing different layers of the configuration and it just gives you like a kind of a quicker way to get to it. Yeah. So he was asking in this right here, I'm adding the other one. Okay. And let's look at this one. Yes. Yes. So this is adding, yeah, these name, title, and origin are being added to the top level configuration. Yes. I could have taken these. I can do this. I can just do it like that. That's valid. But I'm showing you in a way that you kind of like for this something this simple that you would want to architect like you have an options. You give that to the user, they set the config. That's why I've kind of separated these things throughout. You see this a lot within like the module system.  The user wants to set these. But you want to define what the greeting is going to be because they want, they want that final output product. So that's, that's why they're set up like that. Yeah, go ahead. Yep. Well, they all get output. I mean, I think we saw that. Yes. Yes. Yeah, you can see, I think in, in, in these lines. Oh, I can't highlight. Let's go over here. Yeah. And all these, that's the entire duration. Yeah. And then the second one I just said like just gave me the greeting output so I can see the, the new lines. Yeah. Yeah. We have not talked about that yet. Merging. I didn't have, I don't know if there's enough time to talk about merging. Sure. And actually I've, I forgot to talk about this in the game. I am talking with the documentation team about this. I don't know if you guys have seen, there's one article on module system on Nix.dev which is the current official like outside of the manuals documentation. I've talked with the documentation team about bringing this all over. So if you guys have any feedback, good, bad, positive, critical, whatever, please find me after. Let me know what you think. This could really be helpful for future Nix users. But yeah, merging, the way that these are structured is to build like one little step at a time. Merging comes after enough foundation has been placed. So now we've got option dependencies. I hope that there is one. Talk to the architecture team. Okay. Okay, so let's look at, let's look at the next one. Let's look at the next, do I have anything? No. Splitting module. Okay. So you can imagine, this is the start. But you can imagine as you build up, you might have a module that gets quite large. Or you might find that you're developing several modules with option configurations in different places. And you find that you have some code reuse. Like, oh, that could be like a little piece right on its own. And I can, I mean, I could use the same kind of options in multiple places. And you can do that. There is the third kind of top level attribute. We've talked about top level options. Talked about config. And let's look at, well, let's look at some individual files first. I've taken kind of the previous lesson and split it up. So now we have a name.nix that just has name. We have a, let's see, what's the next one? Title.nix that just has title. This is kind of the same stuff we've all seen before. We're just declaring an option right here. But these are all just single ones. And we have an origin.nix. That's an option.nix. How about an origin.nix? There we go. Okay. So how can I combine these all together? Well, imports. So this is the third major attribute that's available, kind of the top level of the module system. So with imports, you just pass it a list. And you pass it, or you give it a list of paths to all the other modules you want to do. And here I've given the path the name, title, and origin, which are all in the same directory. And this is as if I had just declared them all in this file. It's exactly like the same as we saw in the previous lesson. But now, like, I have reuse. And that's basically it.  I've got the configuration for greeting locally defined here as well. If you look, there should also be a config.nix that has the same configuration as before. And if we run this, we'll get the same thing. But this is nice.  I want to, like, put it away. I don't want it to be, like, you know, injected into this code here. I want to be able to use it in multiple places. You can use imports, and you can just pass them, pass their paths. Yes. Yes. So the question is, is like, if someone's already defined a configuration value, can you override that? The answer is yes. There is a priority system. It is, I think, one of the last lessons that I want to talk about. And hopefully we can get to it. But yeah, you can't, you absolutely can't. Yes. Not necessarily. I'm just doing this because that's how the lessons are structured and to be very simple. You know, for this one, let's see, let's take a look at the eval. If everything at the top level, then they're pretty much the same. But we're going to look at some special types called submodules that allow you, like the name, they allow you to define submodules. And sometimes you want to do imports at the submodule level and not at the top level. Yeah. Of course. So if you're just doing, so the question is, like, what's the path availability for these, just repeating it so that people can hear better. What's the path availability for these modules?  I have to be generally, I think, relatively available. Like, all the stuff that I'm doing is, like, all relative directory. You could probably give it an absolute path as well. The only question is, like, if it's not in your repo. Yeah.  Just don't expect anyone else to do it, use it. One thing I'll just touch on, if you're talking about NICS OS modules, you have another special argument. I believe it's called modules path. And that is, I think, I believe that's another automatically provided attribute that I don't have in the lessons, but it's kind of important to talk about. It will give you a path in the NICS packages repository based off of whatever instance you're using to the directory that has all of the modules in it. So if you, let's say you wanted to extend NGINX or something like that, or extend some other package or some other service, rather than having to know where everything is in NICS packages or, like, having, drilling down from the top level of your NICS packages instance, you can just say modules path and just slash from there. Really, really handy. You can, that, I believe, is documented on the website, on, like, the official manual, so you can go look at that. Okay. So splitting modules was pretty simple. We're just doing imports. And the next lesson we want to talk about, ah, arguments. So let's go to options, arguments. So generally, without any kind of conditional logic, if you declare an option, you have to define it when you try to evaluate them. If you try to evaluate an option declaration that doesn't have a definition, in other words, if you have an option that doesn't have a configuration value, the module system will throw an error at you. But that's, I say, without any conditional logic. So there's a couple ways to do it. One of the ways I want to show you real quick is with, ah, using another automatically provided keyword, options. So this is the kind of final, you can think of it as the, like, the overall, the accumulation of all of the options you've declared. And it has a bunch of information in it. Like, if you go down inside options and we wanted to look at, like you said, options.name, it has information about its type, it has information about, ah, let's see what else. If it was defined or not, ah, see what else kind of stuff do we have. There's a lot of things you can look at that you can find in there. And also, similar to config, this options, it's not the same as this option. One is, one is all of the options together. This option right here is just what's local. Um, but what I can do, let's say, let's say I wanted to amend the things that we've done so far in this lesson. Let's say I wanted to give the user the ability to only define some of the things that we declared. Like, I didn't want to, you know, we have name, title, and origin. I didn't want to define title. I just wanted to name an origin. You can do that. And that's because mix is lazy. So I'm going to do something similar just for the sake of my own sanity. I'm going to say ops equals options. And what are we going to do down in my greeting, rather than kind of statically saying like, this is the string and I want you to inject these values and I'm going to put some conditional logic in. So I'm going to make a list of strings. And so if you haven't seen the optional function before, the first argument is a boolean. If that boolean is true, the second argument is passed in as a list, or it gets injected into a list and passed back. If it's false, it returns an empty list. So essentially what I'm doing is I'm generating a list. I'm concatenating all these lists together. And if I define, I'm going to say options.name, if it's defined, then pass the string along. And if it's not, concatenate an empty list, which will just be nothing. And then I use this other function called concatStringSep, which takes a larger and then concats a list of strings together for you. So I'm just building up the same kind of string that we saw previously, but now I don't have to define these options, or whichever, you know, these options that are the name, title, and config if I don't want to. So let's take a look at a config. Ah, there we go. So I've just defined a name and title, sorry, name and origin. No title. Eval is the same. Just pulling in options and config and giving back the config output. And the run is going to be the same. So let's just take a look. Doesn't crash. Works. I can go back in here and let's go back to the config. I can take out origin. Not all the way. Just that much. Doesn't crash. Which is exactly what I expected. So if I had done this before in the previous lesson where I didn't have those conditional conditionals in, the module system would have thrown an error at me. Let's actually go take a look. Here's a config. Oh, hold on. Let's go back again. Do I have something open? I do. Okay. Let's just see what happens real quick. Yeah. You'll see this error message. This is the kind of the facto error message that you will see at the bottom here. If you declare an option, you don't define it. So the option origin is used but not defined. A little confusing at first. Essentially what it means is I created an option called origin and I didn't give it a value. Now it's complaining. Yeah, go ahead. Ah. That is a really good question. Let's find out. Let's find out. I'm going to put origin back in. Let's open this options. Let's get rid of... Yeah, let's get rid of origin. Let's not import it. Come on. I probably screwed up something there. Hold on. I did. Thank you. I did delete the end of string. Yeah. Does not exist. So they go hand in hand without any conditional logic. That option has not been declared yet. And it even tells you where you messed up. Which is really nice. It's like you messed up right here in this config file. I guess you guys can't see that. Right there. So make sure that there's... It'll tell you right away. It doesn't take that long to figure out that you messed something up. But those tools are there. Just recognize that the error messages might not necessarily be what you have expected. You've defined something here but you didn't declare it. Or you declared something and you didn't define it. These are kind of the translations. This option origin does not exist. It's not declared. So we've got that. Okay. So far, all of these have just been kind of contained within the next language. Strictly within the module system and just extending on the next language. I try to keep it as much in here as possible so that you're kind of always touching something. Something that's always familiar. Or something that I didn't want to add packages or external things where you have to pick up something new like some API or Nginx or some service that most of you might... Some people might not be familiar with. We're going to take a little step out and we're going to use packages for something. So let's go over to... Let's just make sure I didn't... Okay, nothing's still there. Let's look at extra arguments. We've talked about things that are automatically provided so far which are lib, options, and config. Packages, if you're just writing standard Nix-level modules, not NixOS modules, you don't get packages for free. If you're writing a NixOS module, you do. But I'm writing just something really simple that just puts out strings so I don't get packages for free. So how do I get access to it? Well, first, I'm going to assume that I do get it. I'm going to call it packages. And I'm going to use it. So what I'm doing here is similar to what we did before. I'm going to provide it a name and a greeting this time, just two. The name is going to be my input and greeting is what I'm going to see in the output. And I've configured this kind of silly little thing that uses Cowsay, shoves the name into Cowsay and then spits it out to a text file that we can just cat out and look at later. This is just kind of stuff that I had to do to make sure that I could integrate it all with the lessons site and the build, because this whole site is built with Nix and verifies that everything runs before the site can deploy. So this isn't the way that you would normally write something like this, but I just did it for the sake of having it work for my CI. But the short is name goes in, Cowsay says hello, name. But I need to give it packages somehow. We're not going to find that config. Configure just those names. The way that I'm doing it right now. Let's take a look at eval because the way that packages are provided, you'll soon need access to packages somewhere. You need to pass that in. And since I know previously that when we were looking at our eval files that were just regular Nix files and I was using the run scripts to provide packages to it, I know that I have packages available there. Similarly, this is a little bit different because now we're not going to use Nix eval or we're going to use Nix build for this particular example. I needed a more pure version of Nix packages available. So don't worry too much. This is just a purity thing. Just know that right now, at the top level of my eval file, I have packages. And this is unfortunately not documented too well. I believe the only place that this is actually fully documented is in the source code. The documentation team is working on it. I promise. They're doing a great job. But within config, so within that same config that we were, actually, this is not the top level of the config. This is like the regular module attribute config. There's a kind of sudo underscore, sudo hidden argument called underscore module. Within that, there's another attribute called args. And args, you can pass it arbitrary things that you want to be provided to your module system. So I'm going to say inconfig.module.args inherit packages or packages equal packages. And this is what allows me to pass in anything that I want. So let's say you were building up some library. Let's say you had some really elaborate module that had your own library called mylib and had all these cool functions in there. If you had that available here, I could just say like and mylib, you know. And then I could go over to my my options file and say like, oh, now I have access to mylib. Since I actually have these, I'm going to undo that real quick before this thing breaks. But yeah, you can pass in arbitrary things right through that argument right there. I don't have it in the lesson yet. You can also look in the documentation for something called special args. It is another way to do it because this particular way has some limitations. It is the more recommended way. But if you run into those limitations, special args is there as a backup. Look in, I think it's the Nix manual. No, Nix package is manual or the Nix OS manual for the documentation for special args. But yeah, now we have packages. Well, at least I'm telling you I do. I've tried to apply or make it available. So if I click at my run file real quick, this is just going to be a oops, that is not it, run.shell or build.shell. Sorry, this is a build file. Just runs Nix build on the aval.Nix file. If we try to build it, it might take a little more. Okay. It gives us a result. If we look out here, there's something in result and if I cat that out, it says hello in the code page. And that's it. So yeah, this is how you can give anything you want for the module system that you've tried. Yes. Let me see if I understand. So yeah, when you go to the source code that builds for the derivation for a particular package, usually you see as a function argument, but you don't usually see module options in there. But are those Nix OS options or are they just like function arguments? The module system? No. Ah, right. I think generally for things in Nix packages, you just want to use simple functional arguments. You don't want to use the module system. I don't think I've ever seen something from Nix packages that you just use as a module system. Generally, where you see options is for Nix OS. When you want to configure services, that is not a mistake. I started using what recently? Really? Okay. Okay. So something just said that recently people have started using the module system for packages. Cool? The typing is nice. So this covers the basics. This is just enough to kind of get you started. But let's continue on. I want to talk a little bit more about MkOption. That was that generic option builder that we had. Some of the other things that are available for us. Let's see. I believe this is let's go to default okay. So doing all this is really nice. You saw in some of the previous lessons that we had, we could configure a value for things like reading in the same thing that we had our option declarations, which is really nice. There's also another attribute that is available from MkOption that we can use to set developed values. Just as a thing, they are completely overwritable. But they make your users lives easier if you kind of know what the fault they might like. So here's a really simple module in my options.next file. I have an option called A, which is integer. B, which is integer, and sum, which is integer. And the configuration for sum is A plus B. Simple. But I've added a little default value here for which one is this? For B. And that's it. This uses the priority system. We will talk about that in a bit. But this has this priority possible within the module system. This is the most overwritable thing. Easiest to override. But is available if the user wants to use the default values. So let's take a look at a config. Just a regular config. Let's do that. So in this configuration, I've only set a value for A. So I expect that it will use the default value for B, which is 2. Let's give me a sum of 5. Let's take a look. Run.shell. Perfect. So what do I need to do to override it? Well, there's another configuration file in here called config-override. That's it. Just say B equals 4. B is whatever. B equals 40. There's a run override in here as well to help you out. And there you go. 40 plus 3? 43. These next couple ones are fairly quick by some of the other ones. Go for it. You could totally do that. You have the power to do whatever cursed things you want. Not these. These are all just example files. But I could have defined it in another file. I could have defined it in somewhere. Somewhere it has to be defined. I just set these up as the way that I have defined it to the user. And then what they would have in their config.x file to configure those options. But yeah, like we've seen with like imports. Like we saw, you know, you can split these files up as much as you want. Just how readable do you want them to be? Any other things? That's a pretty simple one. I'm going to quickly talk about some of the other attributes that are involved with documentation. So if we go over, there is option documentation. Directory. Okay, there's a couple other really handy attributes.   If you look at the options tab, you can see all the options that are available for NextOS. There's a couple of extra attributes that are held with documentation. So one of them is called description and one of them is called example. So description is like it sounds. You just can write it. Text, sentences, paragraphs. You should just describe what this option does. Please be thorough. Some of, as you might have noticed, some of them are not super well documented. The more information you give to the user, the more they can understand the better. An example is just just another example. This, you know, strings are kind of simple, but like, we'll look at some extra types and like why you might want to put an example and to kind of show the structure of what your option can look like. And all of these, if you look at the outputs for documentation, will be included. So let's take a look real quick. We don't have a config this time. We're not caring about config. Actually, let's see. Is there a config? No. What else is here? There's docs. Yeah. So now we're going to use some new functions. So when, generally, when you're trying to create documentation for all of these options that you've created, you don't need to have a config. Wonderful laziness of Nix that allows you to do things like this. So we're going to probably want to use this function called NixOSOptionsDoc. So this takes an attribute set and one of the attributes for this is options. And here, we're going to pass in the evaluated options options attributes. So this eval options is the same kind of eval options that we've been using before. Normally, we had our config.nix right here next to options.nix, but we don't need to configure it. We're actually fully evaluating these things. We're just going to look at the options output. So we can take the evaluated options. We can look at the options. This line right here is up to you if you want to implement it. The generated documentation includes all of the documentation for that little hidden underscore module thing that we saw earlier when we passed in packages. It doesn't really generally add a lot of value. So for the stuff that I generate when I build documentation for my modules, I remove it and it just cleans up the documentation a whole bunch. If you're using it in really cool ways that it's meaningful to leave it in there, feel free to leave it in there. When you're writing your first module, the function provides a couple of different outputs. It provides an adjacent output. It provides an ASCII doc and it provides a common mark output. Maybe one more, but I think that's it. We're going to look at the common mark output. I'll build it and then we'll build it without that line and we'll see what happens. Let's see. I think I already have it built. Let's build it just in case. Okay, it built really quickly. Let's take a look. Okay, so this is the common mark output. So it's going to look like markdown. But you can see right here at the very top is the name of my option which we also name. The description for it which was a name for a user. The type which was a string type. It's the fault value which in this case was empty. An example which I call Voting with Boatface. I'm going to have this declared by which is actually going to show you basically a link to your system where this option which file was declared in. This is maybe less handy generally for things on your system. Definitely handy when you're looking at NixOS options because it tells you where in the repository it's defined. Or it's declared, I should say. Exactly. It's showing manipulation. For some of this documentation that I've generated I will actually look at the JSON output and create my own common mark output. Which is not terribly hard. You can go look and see how this is generated in the Nix packages repository. And I kind of just took that and massaged it a little bit and made it my own flavor. Another question? Thinking. Do you have documentation in the doc? Can you say that again? Who do you have a doc that's available to build this project? Do you run streams of documentation in the doc directory? Yeah. Is that in the Nix thing? If I'm documenting some other thing and I assert on a file it's used in the doc directory. If the doc builds they can fail the build. That answers your question. When I build this site I take in all these Nix files I evaluate them and I inject them into the Markdown lesson source code and that will build the site. That's how I know that all of these work. Is that answering your question? Okay, great. Let's answer this the other question we had earlier. What happened? Oh, no, sorry. Docs. Options. Equals. Eval. Options. Okay. Build. Let's see what this looks like. Actually, let's look at it in less because it's probably going to be big. There you go. Actually, if you wanted a quick access to the documentation for this, this will give you some of it at least. But, yeah, you can see this is we're still on the way. Oh, wait, here we are. Now we're at our stop but we had to get past all of this so it's already bigger than what we had for a single option. If you had a lot of options, yeah, the percentage goes way down so you don't need to know or care. Unless you get to more advanced things. But if you're already in the advanced stuff, you know that exists and it's always there so you don't really need to document it. We're going to talk a little bit more about documentation and try to help you guys avoid a potential pitfall which you can run into. Not the biggest pitfall but just some funny things happen when we try to use a default value as a package or something that's not like a simple type like a string or an integer or a list. So we've got a new options.next file here. So this one is using packages again, we already know how to pass that in that's great. And we're going to, we're creating a new option called random package. So random package its type isn't string anymore it's from the typing system it's a package type. It's the default value and we're going to use the hello package. The description is a random package and the example is going to be a cal say package. So basically saying just give me a random package, it doesn't really matter. We're not doing anything with this we're only kind of, this is just an exercise to look at what does the documentation look like when we try to build documentation with these values. So the docs file is exactly the same the regular docs.next there's a fixed version of this but we'll look at that in a sec. Let's look at what happens when we build all this. So we build docs.shell and let's look at the results. Okay, so yeah, we've got our title which is random or the heading of this output is random package which was the name of our option. The description was a random package type is package but look at the default and examples, they're variations. That's weird that isn't how we configured it it was package.hello or package.cal say but this is the kind of problem like the string output of a package is this. So how do we fix these kinds of things because we don't want to give this to our user and be like, what does that come from? So, instead of that let's look at this file. Fixed. Oh sorry, not the doc. Options fixed. So there's a couple of functions that are here that are really handy. One of them actually first before we talk about the functions, there's another attribute, default text. So default has to be left alone. The default value of the module system will use. But you use default text for your documentation if you need to do these kinds of things where you need to fix up, like if you're using a package or some other kind of derivation. So in default text what we can do is say put packages.hello in a string and pass it to literal expression. So this will extract the kind of code within the string outside of it and put that in a code block for you within your documentation. Because if you didn't include literal expression it would be a string and we don't want to say package in string quotes. We want to say packages.hello. You need something? What's up? So real quick announcement. So since everything kind of got shifted this morning, the next talk will start at 3.30. Not right now. So you still have time. There's a bunch of people in here for the next talk. You're going to get in. It's fine. We're going to have an hour behind. So I just wanted to call that out. So you still have time to go use the restroom or whatever. Good? Or you can stay. Yeah, you can stay. That's also totally fine. Yeah, you should stay. You can go back to Pierre. I might need him. Okay. So one other function that's really handy but not necessary unless you're writing a lot of writing and it's better to maybe explain an example rather than trying to write out what an example could look like. You can use this other function called literalMD or literal markdown. And you can write markdown inside of your string now. And that'll just be injected as is into your documentation. So you can see here I'm saying like any italicized random boldface package. And it should show up like that. It should just basically just translate directly into the markdown file. Yeah, so let's see. We've got default text for overwriting the default value as some sort of text.value. We've got a loader expression when we want to use things like the packages. And we've also got literalMD for when we just want to write markdown. I believe literalMD is generally used for example and either default.text or like the documentation. I don't remember I think if you put it in the wrong one it might fail. You'll find out. But so we've got another build file that looks at let's see where is it. Yeah, we've buildbox fixed to build these locks but with all these things that we have fixes now. So let's take a look at the result for this. Okay, look at the bottom. We have our default and we have our example and it doesn't say derivation anymore. It says packages.hello exactly what we wanted, exactly what we said. If you assume that they had the package namespace available, like just kind of spot it out, you could just say hello saying like that's a package. But it's kind of nice to say like this comes from packages, something packages.hello and our example is any random package with the markdown of a file is already injected in there. So that I think is all we want to talk about about empty options and documentation. Any questions before we move on? Yeah. This is a totally new one. Oh wait, did I take out the case? Oh, this was just to show that like instead of having like actual expression I can just throw like a description in there. It's not part of the, I could have used literal expression the same I did for the default text. But I just wanted to show you that I didn't want to have like a third example in here. I just wanted to show you. Default text is the fix for showing what the default value looks like in the documentation. But example doesn't get evaluated. It's just for documentation purposes. Moving on. So we're going to quickly talk about some really simple types. Because I want to talk about some modules. I think we have enough time for all that. So if we go to basic types. Just wanted to show you guys some more basic types that we can look at. So we've looked at string. We've looked at integer. We've looked at package. And there's the kind of normal set of types that you would normally expect from a typing system. So we've got Booleans. We've got enumerated values. We've got integers. We've got strings. If we look at just a simple configuration for all that. You can see, like, I have an example Boole is true. Example integer is 42. Example enum is left, which is one of the values in the list that's provided to enum type. By the way, that is how you declare enumerated values as you pass in a list of all the enumerations you want to the enum type. And then we'll handle that for you. An example string is hello. If we run this evaluation for this, we get exactly what we want. Nothing surprising there. Let's go to compose types. These are a little bit more interesting. That's not the right file. Options. More trivial. So we can build things of things. Compose types like we have list of at this very top one. Example list of this type is a list of integers. I can have an attribute set of strings. I can have null or boolean. You can either give it null or boolean value. It's called either. So it can be either string or int. Another really nice one. It's one of. So you give it a list of all the types you want it to be able to be. So it could be either a string and a integer or horrible. Similarly, we can configure these ones. We'll look at the configuration trivial. And if I evaluate this, it's the same thing as before. It evaluates, it verifies. But you can see we have our list of integers. We have our attribute set of strings. This one was the example that could have been null or boolean. For this case, I set it to true. Either it could have been string or integer. And like the text says, I could have been integer. This example one of could have been string, integer or boolean.  You can nest these pretty deeply. Yeah. A list of one of string, integer or boolean. So you're basically creating like, I could have a list that has the first element of a string, the next element of an integer, the next element of a boole. Attributes of null or string. So I can have an attribute set where the values are either null types or string types. Attributes of a list of either integer strings. And then like the last one, I'm not going to go through all that. You can get some really, really crazy stuff and create some really interesting architectures and structures. Look at the configuration for that. And this all works. This all evaluates.  Okay. So why do all that? Well, there are other ways to do things like this. And this is where we're going to start talking about submodules. So rather than kind of creating these complicated structures that might be hard to parse, might be hard to debug, might be hard for users to even understand, there's a better way. So let's go over to the submodules lesson. Submodule. Yeah. And this is arguably one of the most powerful types in the whole typing system. Is the ability to create modules that go inside of other modules. So actually, before we look at the options on the next, let's go look at the character dynamics. This will look really familiar. I have an options of name, title, and origin. I have a greeting. And I have a greeting configuration that is conditionally dependent on whether name, title, or origin are present. This is the same thing we saw when we looked at the conditional using the options argument to conditionally set what the greeting would look like. So this is all continuing within this file now. It's called character.nix. And if we go look at options, let's go look at the options file. We're creating a new option in this file called characters. And its type is a list of submodule and a path to characters.nix. So now I can create kind of arbitrarily many characters as I want. And they'll all be contained within this list. The way that you configure sub-options, think of options that are very adder-like. So let's just take a look at the configuration so we can see what this looks like. And we'll keep this up, actually. Yeah, let's config. Right, so here's my character. Remember, it was going to be a list of, and it's a list of submodules. So the things that we have exposed are name, origin, and title. So the first one is going to be podium and foot face. It's origin from England. And then the second one is going to be me. Titles is in Nix Enthusiast from the USA. And now I've, this is kind of what we had a question earlier about imports. This is where you would want to use imports when you're using them for a submodule. Because then it's not the same thing as importing them at the top level anymore. Yeah.  But yeah, so let's take a look at the run. There we go. We have hello from podium and foot face. We have hello from me. But it's, I don't know if I can stress this enough. This is so absolutely powerful. Because now I can have a character and I can, for each character, let's say you wanted to go like a video game route. Like I could create like a job system or something like that. And job could be its own module. Or the character module. So you could have arbitrary lines of things. But this has a lot more structure. When you generate the documentation for this, it's a lot easier to read. It's a lot easier to parse. So get familiar with some modules. They're really, really handy. This is the big one. So any questions before we move on? Yes. I haven't played with trying to go up a level. Because I know you can go down if you're aware of what's below you. And within character.next, this configuration I have right here, it's nested underneath like my top level options. It's here. But that config is going to refer to like locally myself. I don't know if there's a way to drill up. Unless you like override the fault. Like if you try to like import the character or do something strange. But you are able to like if you look at the options.next, it is aware of what's below it. So if you with attributes it gets like let's see. You can go through some indexing and stuff like that. If you're doing like an adder of characters, it might get a little dicey because you don't know what the end user is going to set the value for the end user. But I guess you could still iterate over the adder values. But yeah. So the option because it's above is it config is aware of some module below. But I don't not sure if character can be aware of it's parent module system. Here do you know anything about it? Okay. Is that good? Anybody else? What's up? Does somebody else have a question? I believe the last thing we're going to talk about when we're running out of time. So this is a good thing. So priorities. So this goes back all the way back to the beginning when we were talking about overriding things. Okay. What did you find? Yes. There are some other functions that we don't have time to cover. There are other ways to do submodules. We did it the most simple way which is using the submodule type. There is another function called submodule width which is what I think you're looking at. It exposes a lot more arguments that you can use to pass more arbitrary arguments to your submodules. That's documented pretty well on the manuals. Check that out if you want to look at other ways. It's really handy. I've used it before and it's super powerful. Yeah. Let's see what we have here. Okay. Okay. So default values plus priorities plus overriding things. Okay. So we looked at before using the default value inside of mkoption. Instead of having it like you can say there's a default value here, we have a default value of default equals something. Deep within the module code, there's a priority system. And anything that's defined here within mkoption as a default has a priority of 1500. The lower the priority the higher the presence or the more preferred it is. So 1500 is basically like anything will override it. There are different functions that we can use to kind of change the priority that we're setting for particular option such as this one. So if you do something within config using lib.mk default, it will have a priority of 1000. So it will automatically override this one because it's 1500. If you don't use mk default and just do it like this sneakily, the module system will give this a priority of 100. So even even and the kind of idea behind that is that like sometimes you need to like create your default values in config and not within the option itself. And so they created kind of a secondary function. So that's why this is 1500 if you use mk default it's 1000. But generally users don't use mk default this is usually when you're creating a module for someone else to use. When you're doing it like this it has even higher precedence over a predefined module. So this is what users would use. If someone else I think someone else had earlier asked like if someone else has done this but then you wanted to override that even further well there is one called mk force and it has a priority of 10 which is I believe the lowest one that's kind of defaultly available. If you want to set up more there is I believe something called mk override. The first argument you pass it is an integer which is going to be its priority. The second one is the content you would provide like the string value here so I could say let's see lib.mk override and then 1 beat that. I don't know if you can go negative with the value I guess you could say like 0 but you can essentially just create any kind of priority level that you want for a particular option. If you're doing this for things you want to expose to the user and you actually want to configure it don't do this. Either use mk default or put it in here. Everything past that like mk force is used or just like not even using any function just configuring it in the config attribute is generally used for the user. Yeah so let's just take a look real quick. We've got our option here. We have a config let's see maybe we don't have a config oh right we just have this one this one's already got a config within it. Okay no big deal let's take a look at the eval file since the one module that we have has both the option and liquefaction definition we don't need anything other than that options file and so then if we run that we're going to get both of my code paste remember that was uh this value right here. So just to kind of push it forward to like really show you that like yes you can overwrite things there are some let's see then it will complain so you can see this one I'm not using mk default this is just configuration value we call this one had a priority of a hundred whereas mk default had a priority of a thousand we expect this one to supersede the previous value so what was that called a boat overridden okay so if we run overwrite we get exactly that there's one more um sorry the way that that was being used there's another eval file called override where it pulls in that option file and the override the last one we're going to look at is uh force I think yeah so force is going to use mk force which we're calling out a priority of ten and then we have another eval file called you know force which uses all three and you can use all three but it's going to pick the one that has the lowest priority yes fifty is is there one that's okay sorry what was the name of the one that has ten make vm override okay sorry so corrections thank you for that um apparently uh mk force has a priority of fifty not ten yes all the way in the back it doesn't or not overriding it's not important at all why design um and then if we look at the run for m force a force there you go um let's take a look at real quick what happens if I uh what happens if I have them at the same level I believe that should just be run let's look at actually a run overwrite I believe there you go so the option name has conflicting definition values and you can actually it nicely gives you a path to each one of those files about overridden and voting my vote base conflict because they have um the same priority and the str type cannot be merged unfortunately we don't have the time to get into merging but if you continue looking at the documentation for this there are types that can be merged attribute sets you can have multiple definitions for I believe certain types of lists I don't know if all lists can be merged it should absolutely say on the NIC packages documentation whether or not it can be merged I think the default list won't come out um it's in the source code sorry sorry what was that okay oh they actually have it because I found it in the source code but I think that's all the things that I had planned is there any more questions before we wrap up and prepare for the next session yes so I know there are everywhere in the NIC packages sure are there any other NIC packages that I mentioned like if you said I'm writing a package that's still in the NIC then I also think you also think it's in the NIC yeah so not just in the NIC but basically anything everything within NICsOS is module configured yeah so actually I do want to point out if we look at where do we want to go seven I want to go eight yeah I do let's go here NICs.dev so this is the official documentation so the question was asked about like more practical things these lessons were created with the idea of keeping you within the NIC system not exposing you to too many other things that might add some complexity but if you go here you should have enough information now to read there's this article called module system deep dive which is one of the maintainers for the module system also lead of the architecture team here they create a set of modules to interact with Google's Maps API they configure some options for like zoom and scale and they show you like it's not like kind of split up they work from like nothing that kind of useless.NICs empty attribute set and build up all the way to a fully fledged module system if you're interested in like kind of a thought process for someone who does a lot of NICs I highly recommend reading this article it is also where I pulled a lot of good information from and it's the outside of the manuals is the only other official documentation on the module system that you should go read up on so that's at NICs.dev module system deep dive anybody else? can you speak up? it's a little hard to hear you main trade off of the module system just writing a package or a function well you don't get type checking you don't get type checking that's one of the major trade offs users can just inject arbitrary things into the arguments of a function if you're having a regular like kind of package that you want to go the other way like why would you want to pick up just a function package versus using the whole module system? simplicity it's a lot quicker and simpler to write up something like that and the real question is do you need all of this? I don't know that would probably be a good question for someone who's committed a lot more to NIC packages I've only made one so are you good? thank you all right the next talk will start in just a couple of minutes so let those speakers get set up in the meantime if you're entering or leaving this room do everybody a favor and close the door very quietly it's very distracting so that's the only real announcement there I would like to bestow upon Daniel Baker a Nick's brick there you go where are the next speakers? right here cool I'll let you guys get set up we have your bricks here hello one more quick announcement so here's how lightning talks are going to work all right everybody quiet down for a second I have a mic but I don't want to yell at you so lightning talks that will happen after this talk so the way that's going to work is if you want to give a lightning talk you can line up against that wall after they're done please don't interrupt them while they're finishing up their workshop but you'll just form a line over here first come first serve I'll give you five minutes to present your lightning talk as five minutes approach I'll be uncomfortably close to you to force you to finish and at five minutes I'm yanking the cord so first come first serve five minutes weird shit anything you want fair game you guys ready to go? hello all right thank you everybody for coming today I'm Ryan Trinkel this is John Erickson we've worked together for a long time in the Nix community Nix really made a huge impact on how we work I started moving stuff to it in 2012 and that was in the context of a company that wanted to provide a really high quality application to some commercial market so obviously one of the most important things about running a high quality application is knowing that it's actually high quality and how do you determine that when you're constantly changing it? well continuous integration is sort of the main and first step for most organizations in that I'm sure probably almost all of you know what continuous integration is but I'll just reiterate it in case basically continuous integration is a system that continuously builds and tests your software as people make changes so that you always know whether what you've got on your main branch is something you can actually push to production and it can enforce all kinds of other rules and sometimes they do automatic deployments and all kinds of other automation but basically you want to know that the work that you've been doing continues to do continues to meet the quality standards within the company and CI is the framework for that now Nix gives us an amazing foundation for this because Nix gives us a very high degree of reproducibility so with a lot of non-Nix based continuous integration systems even if CI says your code is good it might not be good because the thing you put in production might not be the same thing that your CI system tested that's no good and so obviously people have ways that they try to approach this like using Docker containers or really carefully holding on to artifacts that the CI system produces making sure those are the things that get pushed to production there are many ways to do these things but Nix gives you the advantage that the default and kind of only way is already pretty good at any given step now that being said Nix is not really that great to use with traditional continuous integration systems for a couple of reasons let's see I'm going to skip ahead to this one the two big reasons basically being that these Nix is based around sandboxing everything an absolute total chain of custody for everything that your software depends on which often is a lot of stuff so when we build at our company we do a lot of Haskell work and the Haskell compiler tool chain is like a few gigabytes and so when you try to go build stuff in a traditional CI well it needs to fetch all that stuff and every CI system tends to have its own caching stuff they all tend to be not as principled as Nix and not interoperate with Nix super well so people who use Nix the long and short of it is we tend to use Nix oriented continuous integration solutions I'm going to go back and talk a little bit about the benefits of CI I already mentioned testing but the other big one with Nix is that Nix in addition to doing reproducibility and all the sort of robustness things that we like it also has this awesome caching capability which you know whereas outside the Nix community you might pull Docker images with Docker compose or whatever but then as soon as you make a tiny change the whole thing has to rebuild from scratch with Nix you can have very fine grain binary caching and so if you have a bunch of different people on your team they're using a bunch of stuff you can pull from binary caches and everybody's development environment can be set up very very quickly I mean when I converted my two companies ago from like Ubuntu and just traditional stuff to using Nix we went from having usually a few days to a week for a new developer to set up their laptop to 10 minutes however long it takes to download basically so that's another huge advantage and that's another place where the traditional CIs even if they have their own internal caching that cache is not usually exposed to developers to be able to pull it directly onto their laptops so we want to have Nix solutions to take full advantage of Nix and also to deal with the sort of different performance profile that Nix has from traditional builds and so we have a few different tools in the marketplace here I've only covered the ones here that are well known and publicly available for some time and sort of tried and true there are a whole bunch more there you know if you go look on the course you're going to find more it's a great open source community lots of people do this kind of stuff but so Hydra is probably the best known CI solution in the Nix space that's the one that builds Nix packages so anytime you're seeing whether you know a given version of something is building on Nix packages or whatever you might find yourself on hydra.nixos.org and that is that and this is an open source project you can use yourself in my experience people find it kind of difficult to run difficult to keep running it tends to be I mean it has every feature pretty much that you could imagine but it's kind of a beast to work with Hercules is probably the best known most mature managed CI service for Nix so Hercules will manage the actual interfacing with your GitHub project it will manage assigning jobs out and it will give you a user interface for all of this with Hercules you do run the actual builds on your own hardware which is nice it also is a thing that you need to set up Garnix is a little bit less at least it's a little younger I guess I don't really want to comment on exactly how mature each of these solutions is since they are competitors with each other but I think that Garnix is a newer competitor and they host the runners for you as well so they'll manage not only the build but also getting the machines ready to actually build the software and then we have some more specialized tools so there's Cachex which is just for caching and it also has some deployment features but it's focused on caching as the name implies and then you've got NixBuild.net which is effectively like a remote builder as a service as far as I know they don't do anything with the actual CI sort of traditional loop but they'll give you as much compute as you need on demand so that's the basic lay of the land here of course there are other tools and I'm not discouraging you from using any of those but these are the ones that seem to me to make sense to say like this is the normal stuff in the ecosystem so when you're setting up CI for yourself you're going to need to try to decide a few different things for example which of those tools do you want to use if any of course you can always roll your own as well you're also going to need to know which provider you're on not every CI tool supports every Git forge so you actually would potentially want to consider moving your repo to one that is better supported by the CI tool but not if you've got a lot of issues and everything going on so anyway that's one thing to take into account when you're picking a provider then the Flake support is pretty good nowadays but there is usually a different way to do things for the Flakes or non Flakes way so release.nix is the main way traditionally before Flakes came around and now there's a lot of support for Flakes in all these different tools but this is another decision that you want to make fairly early on when you're doing your CI deployment you know probably Flakes is the more modern way to do things and so then yeah you're going to want to choose one of each of these tools to make sure that you've got something that lines it up so that's basically all the talking I'm going to be doing here I do want to ask how many people in this room already are running some sort of CI integration that they manage on a repo of theirs okay great so that looks like about 40% of the people in the room so great you all just volunteered so how many people have a project that needs CI but doesn't currently have it I think probably there are a few people who put their hands up and didn't but anyway all of you are the ones who we're really here to help today so we want to make sure that by the time everybody leaves here everything they've got is being CI'd at the very least everything should be getting built ideally you have a workflow that includes you know green check marks and red X's for all of your pull requests and merge requests and things like that and hopefully you are populating a cache and if you are working with other people on a team you've given them instructions for setting up the cache so that when you push some massive change in your merge request they don't have to take 4 hours to build it before they can test it similarly you know with continuous deployment that's something you might want to consider even if you've got CI set up maybe you don't have CD set up that's something that we can also help with basically I want to encourage people to ask as many questions as possible we're going to have people wandering around everybody who knows me here better be doing that and so yeah we're just going to try to help out answer questions if there are questions that seem like very generally relevant I might turn on the mic and announce them or something but I think for the most part it's going to be about like how do we get through each person's individual issues with their repo because there are various situations you can run into where the CI won't work as well as it might otherwise and hopefully everybody leaves with something that's working and running so before we break into that are there any questions there? yeah I would say all of these do a considerably better job with that than just like typical like for example if you go on github or gitlab and you just use one of their standard runners and say nix build this thing then yeah almost all of your CI time is going to be dominated by downloading dependencies every single time and then you can get kind of fancy and try to like shove that stuff into their cache and pull it out it's sort of a mess these tools because they're nix focused from the beginning trying to do that better and typically they will have if you're running on a cluster ultimately things will need to be downloaded and moved around sometimes but like these tools are going to get it pretty nice from the get go you're not going to have nearly the amount of downloading and uploading time for example with Hercules most of the users of Hercules are going to have one or several large builder machines and those machines are just always going to have most of the stuff in their local cache so that works super nicely I think Garnix has something sort of internal to their cloud that does a similar thing I don't know the details of how Hydra works but I mean it works at the scale of all of nix packages built multiple times a day so it's getting that right too so yeah and that is one of the big big reasons that it's better to use a nix-specific CI than to just use a typical average mainstream CI so in terms of convincing people usually people who are operating CI systems which often falls under the DevOps category care a lot about speed so I've heard one of my clients for example they have a KPI for are the CI CD builds under five minutes so that's something that's got management visibility for them so I think that just bringing those speeds down because really with a nix-focused tool you can beat the pants off the speed of traditional CI tools you can get down to the point where it's like well very little changed so all I'm actually doing is evaluating the nix expression seconds or tens of seconds as opposed to traditional CI even if it's got caching it's usually going to relink it's going to at least do something bigger than that and then in terms of the actual operation of transitioning I think it depends a lot on how the existing CI system is set up but for example on GitLab so GitLab allows you to specify a directed cyclic graph of jobs in their GitLab CI.yaml file and what you could effectively try to do in that is you could say well my nix-specific CI is going to be one of the nodes in that graph and then I'm going to make everything that might want to pull from that cache be a dependency sorry be a dependent on that node and then you can make sure that everything's in the cache before you know maybe some third party tool wants to I don't know run a virus checker or something right and so then it can always deterministically pull from the nix cache because it's afterwards in the CI pipeline go ahead for the most part yes although I think that there's a pattern that I've come across in the nix community that can be very nice which is to have a derivation that builds a script to do something so for example at our company we produce iOS apps a lot of the time and that can't be done the final signing step can't be done inside of the nix sandbox and so we just return a bash script and then I think the bash script invokes nix again so it's sort of like a continuation passing style of the idea bash scripts for the things that can't be sandboxed and pure so that's the kind of thing you can do so you could produce an artifact from the nix CI tool that then becomes it embodies a lot of the logic of some other step that needs to happen even though that's an impure step and Hercules for example has a way of specifying impure steps to do after your build successfully completes in the nix language so you can kind of do what I was just describing you can produce a script and you can say you know whenever the master or main branch you know successfully finishes building then run this script which maybe is a deployment script or something like that it tends to be very specific to your CI system how you do anything like that but I think it's great to integrate it with nix I think that nix doesn't have a lot to say about the very final impure step but it can produce that step for you and that can be a really useful way to do it go ahead well I think I've seen a lot of projects that involve integrating nix with basal and or buck I think that can be a little bit awkward obviously those systems do have advantages in some areas and I think also disadvantages in other areas I think the key with nix to structuring those kinds of like code quality tests and other kinds of tests is to leverage the fact that it's a directed acyclic graph try to get as much parallelism as you can there's no reason you can't invoke a basal build inside of nix or invoke some other tool and there's no rule that the output of your nix expression needs to be a package so for example you could I don't want to get too specific about particular tools but let's say you had a code coverage tool that produces some sort of report at the end so you could write a very small nix expression that says hey take my source directory take the tool on the source directory and then just whatever its output report file is copy that to dollar out and that's my output and so now you've got this artifact produced by the nix system that has that report or you could even go a step further and say well the report had better be all good otherwise I fail as a build so I think if you look at Haskell.nix which is the next generation proposed Haskell infrastructure for nix it takes every Haskell package and splits it into a library component a test component various other benchmark components things like that and each of those is a separate nix derivation which means that if you have a library that depends on another library it can start building before the tests finish running for the first one so I think that's a very valuable technique as well like just because something like is one package doesn't mean it needs to translate into one nix derivation it can be a dag of nix derivations that come together at the end and then you can leverage a bunch of parallelism in the nix build as well as avoiding rerunning things that haven't changed yeah just a small addendum there nix packages Hydra is going to take a while before it even starts to evaluate we haven't really thought about latency so much but I think some of the idea with the AI use case is if we are going to run something every commit or every PR we do care about these issues and we're going to care about them more than we traditionally have and the good news is just as Ryan says nix is ultimately just a graph of jobs there's no rules about how fine-grained or coarse-grained those jobs have to be and so just naturally in trying to tune our CI to be faster and faster we're going to end up having more and more fine-grained steps and push the CI system harder and harder and like Ryan said earlier yes that will take some engineering to make sure the intermediate artifacts are efficiently moving between the runners and whatnot and the place where nix really is competing head-to-head with the bucks and basils in the world much more than it is today so I guess I would just invite everyone to have faith that we can if we care about this use case we can start using nix in ways we haven't really as a community done yet and the technology will rise to meet the challenge yeah definitely is that ATTIC? okay cool so yeah I guess that should go on the list of things people should consider here any other questions? alright go ahead yeah I think well actually in terms of free open source options I would throw that one back to the crowd I know Hercules and Garnix both support flakes and do you know I think I know that Hercules has a capability to do something like that I think it's 29 bucks a month so outside you know for a hobby project that's probably a bit much for an enterprise it's probably not such a big deal but yeah it would be certainly nice if Hydro had that capability as well and maybe someone will contribute that functionality go ahead right okay that makes sense yeah did I see another question okay well then let's get into it to make sure that everybody who is interested in getting their CI rolling right now has the chance to do so and has the support if you are doing that and you have a question please raise your hand that anytime we'll be looking for hands up and yeah ask questions early ask them off and don't worry about anything being dumb and the second set of eyes always helps with that kind of thing so yeah let's get to it and anybody in the audience who's willing to help please help the people around you or just start walking around and looking for hands thanks test test I can help someone someone raise their hand who needs help hello hello we've got the whole Garnix team here by the way guys if you're interested in setting that up please there we go yeah we'll be over there and if anyone has questions or wants help we'll be over there alright folks the time has come it's time for lightning talks just flashbang myself with the projector okay if you want to give a lightning talk if you're not at all first come first serve let's do it hello hi I think that's very loud yeah okay sorry so we're Garnix I'm Julian this is Alex and that's Junke so we thought we'd give a talk immediately after the previous workshop because we're Garnix and we thought that we could show you how hosting is a new thing CI caching and hosting works with Garnix from signing up to having your checks on github and on the website to caching to deployments which are a new future sorry and the premise here is these lightning talks are supposed to be five minutes long so I'm going to be measuring the time myself and we're going to try to do this in five minutes so from not having an account to having servers running, NixOS servers running in the five minutes cool so we're going to start by signing up here Garnix currently uses github for authentication so Junke is going to sign up through github and then we finish sign up agreed to those terms of service and then the next step is to enable Garnix on repo so going to choose the repo that we want to build and we prepared repo with some small Haskell project ahead of time we're going to set that up and as you can see there's no build yet and that's just because we haven't pushed any commits since setting up the repo so as it is now going to push a commit to this repo that we just set up and the first thing Garnix is going to do is evaluate your flake Nix that it finds Garnix specific in this repo there's just a flake file in the root that has some packages, some NixOS configurations and some checks in it and Garnix is just going to immediately start evaluating this and you can see our check has failed here so if you go in here there's an issue there it goes and you can see that this was a check to make sure that there was no 2 to 2s in the codebase and it failed because of that all these checks are also visible on github so you can go there and see them all there Garnix is reading through your flake file and you can see that there's 2 NixOS configurations it's building those it also sees that there's a check it builds that the check is just a regular flake check so you can run this locally with the standard Nix tools Nix flake check which solves the problem that you have in other systems where reproducing SCI failure or whatever locally is quite hard this is just Nix so if we look at what it says here the check is just checking that there's no 2 to 2s in the source code and it failed because there is in fact a to do so we're going to fix that and that's the to do I don't know if you can see it but the to do says say hello to Nixcon this is a small Haskell server so we're going to change that to say hello to Nixcon fix that and we're going to do one more thing we're going to add a Garnix.yaml which is the first Garnix specific thing on this repository and that Garnix.yaml is going to have I don't know if you can see this but it specifies what server is to deploy so this is something new everything that we've shown so far is available you can sign up it doesn't cost anything you can sign up right now and use it this is still behind an alpha wait list if you want to use this sign up for the wait list and this is going to deploy your servers to Garnix's own infrastructure so if we push this we have now presumably a second entry in the builds this is the second commit that we've pushed and we have to wait a little while and we see the same things as before because the deployment is going to come in as a new check after everything else is done so if we wait a few seconds hopefully you still have a few seconds if we wait a few seconds we'll see these things finish by the way all of this is cached so Ryan and John talked about caching and all of these things so we have a local cache so we make sure that between builds everything is much faster and we see that everything's succeeded the cache you can use it as a remote cache so you can download it locally as well so you don't have to build everything and now we see the two deployments at the end of that we're going to have the two servers the back end and the front end talking to one another and every commit the update with zero downtime this will take a few more minutes before it's deployed but once it's deployed the server will be running and we'll say hello Nixcon when you do a curl thank you well done for five minutes next person up if you've got a demo get in that line the line's looking pretty short come on hey Tom do a Nix Rebel demo do a Nix Rebel demo get in line to do it so these tokens too if I use one of these tokens can I get on the Garnix waitlist a little bump okay so I'm going to go through a project I've been working on for the past two months it's basically what I'm trying to do is make verse hell for Nix I'm not going to go through the entire thing like the Garnix people just did but I'll just give a brief overview of what it's trying to do and what you can do with it today so I've got to find my mouse so I'm just going to go over deploying a Nixflake on Flakery and connecting to Tailscale so what you can do is you create a Tailscale service using this Flake template and add your service to your configuration with an auth file key and can I zoom in? I don't know let's see there we go now I'm going to find the mouse again here you go so yeah it's not too crazy and then I already have one here so you would take this copy it head over to Flakery.dev and then this would say sign in with github if I wasn't already signed in you would sign in you can paste it here and hit create but I already have one created with the right syntax you have to make sure you use a valid Flake URL and then when you have that where is that link right there what you can do is then add your auth key which I would show you it but I don't want to give that to everybody here but you can add a file to this template here with your Tailscale auth key and then go ahead and create a deployment and what this will do is provision you a VM and apply that Flake to the VM with this secret file there and then after you wait a few minutes you should be able to see it in your Tailscale and then connect to it via SSH so it's basically it's like if you have a Nix Flake that configures a NixOS module you just put it into this put it into this form hit go and then you'll get a VM with that configured and one day it's going to have auto scaling and load balancers and lots of cool stuff but I'd really love you guys to check it out try it out and if you want you can join and give me feedback that's all I got question it's open source it's scattered but I'm going to get it all in one place so yeah alright thank you very much let the next one get set up if we don't get more people in line I'm just going to make Tom do more stuff I know no I'm pretty sure Tom would love to give plenty of demos yes after Nix REPL we can do a Nix output monitor we can do a one liner with parallel to do some crazy stuff you ready? cool hey there this is a very humble demo I haven't built anything but I thought about sharing this which happened last week is sort of like motivational for newcomers and just general Nix appreciation so last week I picked up this ticket our Kafka client would stop processing messages and never recover and I couldn't rep locally and basically it would freeze with this log message that the application MaxBall Interval was exceeded and not being able to rep or I tried to trace this message and I managed to find it inside the already Kafka source code and my naive approach to this was well can I print debug my way to understanding what is happening here like why does the prod consumer hit this line of code but mine doesn't does this function ever get called do we always reach this return here and my first problem is this is my dependency chain I have my application I have the company's wrapper for the Kafka library I have the Haskell Kafka library and then I have already Kafka the C library that HW Kafka client uses how can I swap already Kafka for my local version without Nix I have no idea how to do this with Nix I know there's a chain of derivations that takes me from my application to already Kafka and then what I do is I just go to search.nixsoas.org and you hear me now awesome alright so I click source and I find the definition of HW Kafka client here and inside the library system depends I find already Kafka and then I go to search.nixsoas.org I go to the source for already Kafka and I see it's fetched the source is fetched from GitHub and I think hmm can I just point that source to directly I know make derivation can be can be changed with override errors so I do that pointed to a local check out of already Kafka but how do I make HW Kafka client use it though can I use an overlay can I make my Nix packages already Kafka use this overridden already Kafka which points to my local check out I don't know why I thought overlays allowed me to replace packages but well it's not using my local version with my print debugs but I have other tools can I override adders on the HW Kafka client well I try it it doesn't error out it still doesn't work it's not bringing my debugs Google Google I find a slides from Nixcon 2015 talking about override cabal I use override cabal library system depends points to my already Kafka and it works my takeaway I would have no idea how to do this without NixOS like how to replace a C library deep in my dependency chain without Nix who knows maybe doing this without Nix is easy perhaps even easier but it's not discoverable with Nix the dependency chain is all declared and readable and I can poke at it so in a sense for the average programmer like me Nix is a little bit like a super power here's to hoping that this battery doesn't die I just hold it so hello everyone I'm Nix how and I just decided to do this lighting talk maybe five minutes ago since I feel like a lot have been covered not for the upper levels of Nix but few have been covered on lower levels of Nix so like how does Nix actually works under the hood a lot of content in my talk can be found on that Nix Pills webpage but I feel it's just easier for me to talk about it so first of all we know about view systems like NUMAKE so what is it NUMAKE you just write a make file and you say that on the left side is the output so our output is main.l on the right side is all the input file so main.cdev.h below that it's the command to run to generate the output from that input so that's a view system if we check one of these view rules we can see that it can be separated into three parts as I've just described so it's the target it's the prerequisites so the prerequisites so the dependencies or inputs are referenced in the recipe and so they should be considered part of that recipe so what I mean is that so you do cc compile utl.c utl.c is both an input and part of that recipe and target is the consequence of the recipe so like whatever recipe whatever command you just run you produce the identical output given your input and the command you're using so the output is actually a function of that recipe think of like among math functions sine and an input 2 you know sine 2 that's the output or wherever you run that function it always gives the same output so we know that a rule in glue make file is the smallest minimal unit of build so we name the minimal unit of build as a derivation as in nix so the codes below are just a rough approximation of how nix really works don't try them they won't work so given that makefile line utl.o is built from utl.c and divs.h and how to build that we use cc utl.c so that's a derivation and the builder is just that command we use to build the output so how do we name that output first of all we evaluate the derivation so we drop all the syntax triggers of nix gets the final output is just like json a flat output and we normalize it so like non-code json kind of thing but in nix it's called derivation and we hash that output so we get a shahash and we use this to name that output so to ensure the reproducibility of nix derivations we also have to track all the inputs so like in the previous command we use cc as the compiler we use utl.c as the input so we have to track these inputs as separate derivations this is a very natural design so we can just do cc is some derivation utl.c is from some derivation and our resulting derivation is a deck composed of these input derivations you can see that there's generally no impurity from this invocation everything is just encapsulated in derivations and this can be normalized or evaluated to this so you can see every derivation pass is replaced by the hash let's ignore this part


to you most with the way that Nestor has been throwing the ball to start the season? I mean, I'm unbelievable. It's so fun to play defense behind him too. He works fast, throws strikes, but I'm really glad he's on our team. I don't have to face him, but he's been, he's been lights out. So hopefully he keeps doing that and he's going to give us a good chance to win every time he takes them out. As a hitter, what would be the most difficult thing in trying to face him? Face him? I have no idea. I actually never faced him and like I said, I'm glad I have and I've told him that too. I'm like, I'm glad you're on our team because he doesn't look like a fun at bat, but gets a lot of weak contact and like I said, for right now I'm on his team, so I'm just happy and when he picks him out for us. You had an important walk in the third and then obviously provided some insurance runs later in the game overall. How are you feeling at the plate right now? Yeah, I feel, I feel good. I felt pretty good all year. I'm getting some results now, which is good. So, you know, that's a big inning for us and I think that's what's important about walks is, you know, they're not as valued, but you know, they can start innings like that and that was with two outs and then it just gives the next guy a chance and that was a big inning. It seemed like after that inning, the game was kind of in our hands the rest of the way. So that was nice to get that going with the walk. You know, sometimes you don't need a swing and you can help the team out. So you're always different about contact that inning besides, you know, is it just the matter of control and the other side? Yeah, I mean he had really good stuff. I think just sometimes that happens. There was a couple pitches that he threw close that we had just good takes on and you know, I think from there it's kind of tough. You start losing the feel a little bit and we just try to capitalize and get as many runs as we can. But like I said, he fits really good all. You know, I just had that one inning where things didn't go as well, but he puts a great game.


And outside the continent, the United States has announced an additional 30 million dollars in aid for the victims of recent floods that displaced hundreds of thousands of people across the country. The Prime Minister's office said the aid was announced by U.S. Ambassador to Pakistan Donald Blum during his visit to flood-heat areas in southern Sinu province, the hardest heat region. Now, Washington's total assistance to Pakistan for flood response, food security, disaster preparedness and capacity-building efforts now stands at 97 million dollars. The new fund then will expand efforts to address immediate needs, scaring up assistance to the most affected communities, and enable the United States Agency for International Development, U.S. AID, to reach more than 4 million people.


Hello! Hello guys! Hi! I invited today Tiba! Nice to meet you guys, my name is Tiba and I am from Iraq. But I also live part of my life in Sweden. Sweden? Yes, I have two nationalities. I have Swedish and Iraqi. So today I want to talk about woman rights, is it okay? Oh, yeah, yeah, of course. Yeah, I'm so interesting because you studied about human rights. So you have more of a heart perspective, maybe? Yes, yes, yes. I think people generally think woman rights in Islam is very low because they wear D-Job and they can't like Saudi Arabia. A few years ago they cannot drive, they cannot enter the stadium. The kinds of things make non-Muslim people think, oh, maybe they're right, but when I meet my Muslim friends, they think really differently. They think they deserve equally. So I'm curious about that. I want to ask you, and you're Muslim too. So do you think women right in the Muslim countries in general? They are like this low, do you think? I feel like the problem that we are experiencing is a mixture or misunderstanding between culture aspects and religion. I feel like they are both being mixed in the Middle East because there are so many things that in Islam that fights for women rights. But then when you come and think about it in a culture perspective, then the woman is being repressed by the society, but not because of the religion, but just because of the society. And I feel like that's how it was even in Korea before. There is so many repression on women, but that doesn't necessarily mean it's religious. So it's not because of religion? No, no, it's more like a cultural male dominant society or sometimes justified by the religion. It's like, ah, but this is how it says in Quran and this is how it says in here. But they just exclude other aspects which is fighting for women rights. We don't talk about them because it doesn't benefit men. So that's one of the most problematic issues. But I feel like it hopefully is getting better more and more with the new generation in the Middle East. The many laws are changing. Yes, many laws are changing and the new generation is more open minded, reading more, studying more and realizing that everyone is equal and everyone should be giving the opportunity because the Islam, the religion we are proud of is actually fighting for this, fighting for equality between men and women. So it will only make sense if we do as well. The Quran said that in front of God every human being in service have same rights. Yes, yes, yes, yes. I heard that if you're late, their family think that's not good at their reputation. So they killed, all are killing. But this is very sensitive. What do you think about that? I think it's a very cultural thing again. It's in different cultures. That goes back in history to the tradition. But that doesn't have anything to do with Islam. That's the thing. That's not a thing. Absolutely not.   And actually even dishonoring the relationship between parents and children. It's very pure. So the Islam even says that if your parents are not Muslim, just be nice to them anyway because they are your parents. So sorry if I quote it not correctly, but it's something along the lines. So it's a religion that really forbid killing just like that. That's forbidden. So absolutely not. There are so many misunderstandings because Islam spreads so many countries. They make so many cultures. Yes, yes, yes. Many people misunderstand about that. Exactly. There's so much misunderstanding and I really hope in the future with YouTube and other things we can explain to the world that there is differences. Religion is not the same as culture. And it's not just happening in the Middle East. This problem is happening everywhere. There are so many... In Korea too. Yeah, there are so many things. The gender conflicts are so serious. Exactly. And that doesn't have anything to do with religion. Yeah, why? It's not because of religion. No, exactly. I want to ask, many countries their feminism is very big issue in the worldwide. Yes. Worldwide issue. I'm curious that the Middle East or other Islamic countries, is there any movements? Fighting for women's rights. Fighting for women's rights. Yes. I actually haven't been in Iraq for 18 years. But I have been in Jordan, for example. I visited a few times, yes. And in Jordan, for example, I can say I feel like there is movement and there is also organizations, non-governmental organizations who are actually fighting for women's rights. But of course, everything that happens regarding a human rights is in a very slow process.  Everything, it takes time in order to change something, to change people's mind. They need time. They need time, absolutely. So I'm sure I'm positive that the change will happen because I see it in certain countries. Even in Saudi Arabia, for example, the changes are happening. There is a positive outcome in the future. But it's just a matter of time. It might take time. Yeah, so it is changing. Yeah, I think so. I believe so. I think that it's changing. They are trying to give the same opportunity to men and women. It's definitely getting better. Unfortunately, it will take years. It will maybe take decades. Even in Sweden, about 100 years ago, they were one of the worst when it comes to women's rights. But now they are one of the best. So it does take time. So I'm sure, and beside Iraq and other countries, they have experienced so much more. So the people didn't have time to think of social problems. Because when you don't have food to eat at home, you cannot think of all about women right. It's like a priority. You can solve the problem at the first and then think about it. Exactly, exactly. So I think when the war calms down a bit, the Middle East situation and the political situation calms down, I'm positive that changes will happen. I think it should be more about qualifications more than gender. It's not gender conflict. It's human beings. Exactly. Even when it comes to the different skin color, women should be treated differently just because you are born in a way that you can never change. You cannot change your gender. You cannot change your identity. You cannot change your skin color. So there are things that is you and you don't want to change it. And that should be your right. You have all the right to be you. I think there are many misunderstandings about Islam even nowadays. But if there is some problem, it's just their problems, not because of Islam or because of... Exactly, exactly. I don't want to say that. Yes, yes. And the thing is, religion for me is a very personal thing. My religion is different from yours. Both of us are, for example, Muslims. My relationship with God is different from yours. That's why in my opinion we should never judge someone just based on the way they look or the way they dress or the way they show their religion in a way. I feel like everything is very personal. It's a relationship between God and the person. And it's all depending on myself and on my intentions. I believe in intentions. If I have good intentions, as long as God knows my intentions are innocent, even if the other person in front of me is misunderstanding my actions, just because of the way I look, that doesn't mean that I'm wrong. Nobody can judge them. Exactly, nobody can. Nobody can say like you are more religious and you are less religious just because you do one, two, three. No, there is way more religion and it's a life. It's a life thing.  It's a life process. I'm so impressed that Islam focuses on the religion you and God. Exactly, exactly.  No, I would think about big thing. I would care about God thing. That's one of the most important things I believe. That's cool. So, today we talked about girls' life in Islam. It was so interesting. Yeah, it was really interesting. I want to talk more things later. I would love to. Thank you for saying really well about this topic. Yeah, thank you so much. No, it's no problem. Yeah, it can be really sensitive, but it could be. But as long as we keep in mind that everyone has different perspective, different opinions, there is no such a thing as with our bad opinions. If we don't talk about this, nothing changes. That's true. Yeah, we have to talk about this. Absolutely, absolutely. Yes, yes, yes. Okay, so thank you for watching my video. So, take care and be safe. Bye-bye. Bye-bye. Ma'a salam. Ma'a salam.