 Hello, all right, if you're just watching this after the stream, make sure you check the description below for time codes. We're not going to get going immediately here, so check that out. We'll do hellos and stuff first, and I'll pause all the videos that are coming back down at me. It's good. I'll say hello to folks. Hello, unexpected maker. Hello, Randall. Hello, Dishipoo. Hello, Doctor. Hey, Mark. Long weekend time. Yes. I just sent the email of like, I'm taking Monday off. Hi, Bruce. Hi, David. Hi, Hems Labs. Hope all is great. Yes, things are great. I am ready for weekend though. I'll tell you that. All I want to do is play video games. I should get at least something else done. Hi, Mr. Dawgarde. Is it cow day? If so, moo. Everyday's cat day here. Hey, Gary. Going to my niece's two-year-old, two birthday party. Well, my family's getting together for her two-year birthday tomorrow. Hi, Linux 203. Hi, Rod. Hi, Dylan. Hey, Keithy. I almost forgot you. Yeah, I was playing Valheim last night with a friend of mine. It was fun. Hi, Piata. All right, we got tweeted out. All right, I guess we don't have to wait any longer. I think the camera's a little low because I raised my desk a little bit, but we'll go with it. Mark's been playing Valheim as well. Hi, Bo. All right, let's get going here. Let's do some housekeeping. Hello, everyone. My name is Scott and I work on CircuitPython. I'm paid to do that and to stream like this by Adafruit. Adafruit is an open-source hardware and software company based out in New York City. CircuitPython is a version of Python designed for microcontrollers, which are little inexpensive computers. It's meant to make programming really easy for folks. That's really fun. Your chin isn't out of frame. Your camera is high enough. Okay, perfect. If you want to support me and Adafruit, those are one and the same, please go to adafruit.com and purchase something there. All right. Good. I'm glad I got it right, Bo. If you want to chat with me and a lot of others outside of this stream, I encourage everybody to join our Discord server as well, which you can do by going to the URL adafru.it-slash-discord. Is my mic too hot? It looks like it's getting close to speaking. I just adjusted the position a little bit. Did I say the URL? If you want to join the Discord, you can go through the URL adafru.it-slash-discord. We're in the live broadcast chat. David says the mic is fine. This is a deep dive. It happens every week unless I take the week off. Normally it's Fridays at 2 p.m. Pacific, but occasionally it's shifted to Thursdays at 2. Hi, Mark. Next week is on Friday as well. We go for typically two hours or so. If you have questions, please let me know. I'm happy to answer them. In fact, today's another day where I'm not in the middle of anything. So if you've got questions, bring them. I'd love to talk about whatever people want to talk about. So yeah, questions are welcome. The last bit is that Spook here, the cat in the cat cam. I'll switch to him full. This is Spook. He is epileptic, which means occasionally he has seizures. He's been doing really well, so I don't expect it to happen, but I just want to give people a bit of a heads up. He's going to be sad, I think, because the sun went away. But yeah, he's doing good. So yeah, this is Deep Dive. I have a couple things I want to talk about. David says Twitch is having a problem. My mic isn't focused more than me. The camera says it should be focusing on my eye, so I should look okay too. It's just nice and shiny. Is anybody else having trouble with Twitch? Twitch looks good. Even the camera looks equally focused to me, I think. Cool, that's fine. I've been super happy with this camera. So yeah, I'm not worried about it. It has two modes. It has that product mode and the person mode, the eye tracking mode, and it's an eye tracking mode, so it should be okay. Ever so slight fuzz because 1080p on a 4K. Yeah, well, you could GitHub sponsor me if you want me to have a 4K camera. Mark's getting new glasses. Okay, so I put a couple things on the plan. First off, I wanted to cover just briefly talk about the release. I did two or like one and a half releases this week of Circuit Python. It's been a while since I did the release. Dan's been doing them, so that's been great. But Dan is on vacation this week, so I was like, oh, this is a perfect chance for me to do a release. And lo and behold, I kind of messed it up. UnexpectedMaker asks, just confirming deep sleep is still faked on the ESP32s too when USB is connected, right? Correct, that is the policy across all boards, is that if you have USB enumerated, then if you deep sleep, it will not do a true deep sleep. It's essentially a light sleep. Yeah, the cats aren't even in 4K, so sorry about that. The cat cam is my old webcam. But yeah, so if you're on USB, it's meant to, the problem, you can't have both because deep sleep turns USB off. So the policy is that if you're on USB and you do code the deep sleep, it fakes it, so that you can test your deep sleep code while staying connected to USB. Yeah, and that didn't change at all. All right, so maybe let's just go down this list. So a lot of this list here is additions to 7.0 since 6.3, so a lot of these we may have seen already. BLE development workflows, the headline, it's still a little in progress because of all the apps and stuff, so I'm going to have to work on that again next week, but I'm kind of done this week. I don't really want to pick up the BLE stuff. I've been having a lot of allergies this week, so it's not super focused. Camera support on the S2, QRIO, which is QR decoding, keypad, real-time custom, runtime customization of USB devices, merging of MicroPython up to 116. 117 is actually out, so we are now behind. PixelBuff got renamed, ColorWheel got moved, TixMS is added, simplifications to the status LED, clocking fix for a few of the RP2040s, VectorIO was reworked, we got an ad exit, get pass, and traceback modules. Make sure you don't sneeze on the camera. Actually, I should probably take the allergy meds in an hour or two, so hopefully I won't, but I did take Tylenol sinus to clear me up. Supervisor get previous traceback, board LED consistent, pulse out, Unicode file system file name support. We have board.board ID now, and AESIO is new. You think we'd run out of things to set on fire, right? Ah, never. So, two good questions we just got here. Nerodoc, hello Nerodoc, didn't say how do you yet, I don't think. There's four boards added in the last couple of days that don't have board.board ID, I propose to PR that after or before the Lolan S2 mini fix is merged, maybe, and add dunder name while I'm there. Either way, either way is fine. I wonder if we should have some sort of check that can make sure, make sure that we have a dunder name in new boards. Hi Jian. Doctor asks, can you control the board frequency within CircuitPython? I think you mean like the CPU frequency, and the answer is not currently. It is something that people wanted, but generally we don't. It's really complicated, like changing clocks is not simple. Especially if, as we would want to do in CircuitPython, if you want to do it dynamically, that's even a trickier thing. Because like, if you have a particular clock and you're changing that clock, that clock may also run like your spy device, like your spy peripheral. CPU frequency, it would be good for power management. I don't actually think it's, my understanding is that it's not that important for power management, because generally with power you either want to be running and running efficiently to get through your computation that you need to do and then go back to sleep and turn just everything off. That's generally my understanding, but I'm sure there's cases where I'm wrong. But yeah, I was saying dunder, it's Python slang for double underscore. So like, yeah, double underscore, it's pretty common in, like all of the kind of special methods in Python have two underscores at the front and the back. So they call it dunder, D-U-N-D-E-R. Okay, let's keep going down here. Port status didn't change. So fixes since beta, fixed the USB hidden custom devices. This was like a report checking thing. Added, oh, I never fixed that. Wow, it's like I didn't even proofread these. I didn't add the display IO dots. Let's fix that right now. It is, so in the readme we have display IO, e-paper display, update refresh mode. I copied this elsewhere, so it's going to be wrong in other places, but that's okay. e-paper display, update, refresh, something, mode. This is a community contribution and it allows you to do, like some e-ink displays have different, they have the ability to do like fast refreshes, where they are much faster, but they're not perfect. So adding this allowed the folks to have like, do seven fast refreshes and then do a full refresh, kind of mixing the two. So that was pretty neat. Hi, Yanni. Hi, Minnesota Mentat. Keithy says, I just thought dunder was a thing in and of itself. Yeah, I think it's double under. Okay, so I just fixed that. I'll probably find more issues. Parallel Bus is now moving to its own module. This is because Jeff is anticipating having to expand Parallel Bus and it's going to grow in size. Jeff added the 0x prefix for printing pointers, which is to match CPython. StageNow uses keypad. BoardID is now in boot out text and board.boardID. Dan fixed some WAV file validation. We support multiple.sars as status LEDs. This is the main thing. TAC has been doing USB compliance testing on TinyUSB, which is the USB stack we use in CircuitPython. We actually did an RC0 and then somebody was like, hey, you realize it doesn't work on the RP2040. And so I updated TinyUSB further and it had been fixed. I'm actually expecting to do another update of TinyUSB probably before we go stable, because I believe I saw this presence might be broken. So we might have to pull another TinyUSB, but generally that's just like fixing more bugs. So that's good. Added the AESIO stuff. Thanks to MicroDev and UnexpectedMaker. Updated the NeoPixel module as well. So yeah, a lot of my week has just been hunting down these issues and getting them all merged in. We did get down to zero open pull requests, which is really awesome. And then the next day I woke up and there were six again, which is how epic and I merged them all in. So I think if we look at the insights tab here for this week, it's going to be pretty strong here. We've had 36 merged pull requests. We have two open and we've had 14 different authors, which is great. So thank you to everybody who's contributed. Weblate's not actually a person. So is there documentation on how to add a board that could be updated on GitHub or is it only in the Learn Guides? Unfortunately, I think it's only in the Learn Guide. Yeah, but I could just do that right now. How to add a new board to CircuitPython. Is there a spot here? Get set up to add your board. This is good. I think generally it just has you copy existing ones. Here's testing it. Doctor says, so looking at this eInc display product 41.97, I don't see anything about partial updates. Is that a limitation of the display controller or CircuitPython? There are eInc display controllers that are able to do partial updates. It doesn't really save you time from my understanding though. It just preserves parts of the image. CircuitPython does not support that right now, although you could probably use this update refresh mode to actually do it. Maybe I haven't tried. But generally it's not universal and so CircuitPython doesn't support it. And it's not really a speed thing either. UnexpectedMaker says, a lot of your time has been fixing my PRs. Yeah, that's no problem. I know you were anxious to get it in, so I wanted to make sure and get your new board in. And this ship has been doing stuff too. It's all good. We're going to keep releasing. We're just going to keep moving forward. Reviewing is helpful though, especially this week because both Dan and Jeff are on vacation. Thanks to Mark. Shout out to Mark who's been doing some reviews for me. But yeah, if you want to get back, we can give you review permissions so you can review other people's PRs as well. Doctor says, I thought partial updates were faster. For eInc, I don't think that's true. That is true for TFTs where you have to send the new pixel data. But on eInc, the reason eInc's are slow to refresh is because you have different voltages that you apply to different voltages in a sequence. And that sequence length is like, it does all of those sequences in parallel, I think. Yeah, it doesn't help when I'm sleeping when you're working. Yeah, it's no problem. Oh, Unexpected Maker, now that I'm thinking about it though, you could add me as a collaborator on your fork. You have the unexpected CircuitPython org. You can add me and Dan and Jeff as collaborators on that and then we'll be able to edit your PRs because I had to fork yours and do it from mine. And that's because it's in an organization, not on your personal account. If it's on your personal account, you can check to allow editors to edit it or something. And so we can just fix your branch. But I can't do that on your org, so you could invite us to that so that we have access to fix yours directly. But it worked out. It wasn't too big of a deal. Although there's a sneaky technicality with that. If I'm able to edit an existing PR, it means I can approve it even though I've edited it. So yeah, that's a little annoying that if I have to fork it in order to get it in, then somebody else has to do the review. But Mark's been helpful doing those. I don't think partial updates are power saving, but I could be wrong. Generally, my understanding of power saving is just like you want to sleep as often as you can for as long as you can. But if you're worried about power savings, get a power analyzer before you even start. Make sure that you're grounding yourself in the actual power usage before you worry about it. Oh, here's a pins.c. So this is where we could add the note about board name. It would be kind of nice if it wasn't an issue. But yeah, there's this. This is what we point people to for adding a new board. Do we want to keep going about this? I mean, it's a lot of just bug fixes. I don't think any of the fixes I did this week were that interesting, but maybe. I don't know. Like I said, I'm like a little bit out of it. So let's see. What did I do? I fixed a USB issue. I updated the IDF. Fixed a crash. Fixed count IO reset. I switched circuit pythons for the Beely workflow off the Nordic UU IDs. Fixed two watchdog crashes. Fixed NRF light sleep when on USB. It would just like constantly wake up. One Beely workflow. One for linking. Oh, I should highlight this. Was this the latest recommended power analyzer, the PPK2? Yes, it is. I highly recommend it. I'm overcooked. Time for a holiday. And then straight on to circuitpy.aid. Yeah, right. I'll get a holiday this weekend. I'm not going to take myself too seriously. I got some good. I have some Beely workflow like protocol updates to do. And I got most away through that yesterday. I just like a few sites say partial updates do save power, but no info on how much. Just measure it. Yeah, that's what I would say. You got to measure it and see. Okay. Oh, I found this cool thing. And I thought I should point this out. Let's look at this. So as many of you know, like we constantly are filling up the. We are constantly filling up the flash with circuit by the code. And so whenever we add something, we end up like not having enough flash space anymore. And so you'll, you'll see us like hunting for bites is what I call it. I need to figure out if my display can do that and get that power profiler. Yeah, you can take a look about your display. So this is a really cool trick that I just kind of discovered as I was browsing the linker documentation. I was doing what I was doing last week and trying to figure out areas where I think it was last week or the week before where I was like actually looking at the hex values of the binary. I found a couple things. I found this week I found a couple spots to save some memory. So one thing was that we were configuring the USB HID stuff to allows up to six different report IDs per descriptor, which made like we have three standards things baked in and that saved a couple hundred bytes to just say like on samd 21, you can only have one report per descriptor. And then I found I was thinking about what is it? I added a knob. So for queue strings, there are two different, maybe I should just show this. There's there's two different tables. There's for queue strings, there is a queue strings attributes table, which is the hash and the length of every queue string. And then we have a pool table, which is a list of pointers to where the strings are in memory for the queue strings. And I was looking at that and I thought, you know, what we could really do is we could for a given string of pointers or a pool of pointers, we could have a base pointer and then make the rest of the point the rest of like per per pointer only be two bytes instead of four bytes. We could do it like as long as all of the strings are near each other within 60 64 K. You can have a like the lowest base address and then add the offsets. And that would basically get us a thousand bytes back because there's this table that is like 2000. It's 2000 long. Actually, let me just, I'll just pull that up. So as I was I was browsing the docs for, I know you can't see this all. See if I can find it. So here we are. We're in Octetta. This is the, this is just a hex viewer. So this is like actually circuit Python. And if I were, what are the odds I remember where it is. So here's a bunch of strings. You can see here that this is like four, try, deaf, 7 0 beta, blah, blah, blah. So this is, this is where the linker chooses to put all of the strings. So these are key strings. Or these are the strings behind the queue strings. And then there's a table further down. So there's a lot of strings, right? And there's an open issue that's one of the oldest open issues, which is compressing these strings instead. The linker does do some sort of compression kind of, it does this thing where if, if one string is a, is a suffix of another, it will just store the longer version with two, like if you have the longer version and then you'll have two pointers, one that starts at the start and one that starts in the middle. And that way you can like, because they overlap, you can share that memory. But they're still all null terminated. So that's, that's that. And then here you can see a lot of zeros. I don't know what's what, but I think this is the table I was looking at here. So this area here has kind of like a lot in, well, this is at least an example of what I was seeing. So these are all pointers where it's like zero, two, such and such, zero, two, such and such. And I was thinking, well, if we, if we have this range where we know they're all pointers, we can actually figure out the lowest address and then make them all offsets and like basically get rid of this zero, two portion and just store those last two, last two instead. It'll like require like one extra piece of computation if you actually need to get the pointer. But the idea with queue strings is that you can do a lot of what you usually do with strings without actually needing the string, right? Like there's a hash and a length that you check for equality before you ever get to the string itself. So yeah, I was, I was thinking, oh, wouldn't it be cool if I could like figure out a way to make this table half the size? And it's like, it's complicated. It's really complicated because all of these pointers are determined by the linker. So they're done at link time. And so you can't really take those pointers that haven't been determined yet and subtract some other thing that hasn't been determined. I tried it and it just throws an error. So I was like starting to look into how like, what if I take this one struct, this queue string pool of pointers, put it in a separate section and then I can load that section, modify it and scored it back into the elf at like a much smaller size. And potentially this is, this is an optimization that we could potentially do dynamically at runtime and circuit Python as well, which would allow, which would reduce the size of the queue string pool stored in RAM as well, which could be awesome. But yeah, it's complicated. So I was looking through, I was looking through the linker docs, the like docs for LD. And I stumbled across the ability to sort sections by to the ability to sort sections. And I, I'd always seen that if I find, let me just look. I'm sure I have an old map here that's been like, I haven't built this in ages. If we just look at this map is a map file and we have Phil. So here's an example where there's this, so text, the text area is like function code. Right, like functions or code is just like assembly values. So GC collect root is, is not 16. This is hex, but this big. And because the functions need to align, there's two more bytes after that function that are empty. That's what Phil is doing here is Phil is filling in the gap between like, if functions need to start on a four byte boundary, and this previous function took like two extra bytes, there's two spare. And if you look here, or it's very small here, it says 185 matches. So if we just start searching, you'll see that like this happens kind of a lot. So imagine that we have we, it says we have 185 fills and each of them is two bytes. That's like 100 or like 360 bytes that are wasted just because of the, these extra fills. And so as I was going through the, going through the linker docs, I discovered there's this sort by name, sort by alliance, alliance, alignment thing that you can do. And basically what this does is it, it sorts all of the things so that, so that the things that are misaligned kind of like cause each other to line up. So if we look at, I think, what was I building? So this is an old build that I did. And let me double click it so it stays open. But I was building the Arduino Nano 33 IoT, I think. And so now if we look at this map here, and I do fill, oh, it's still got a lot. Let me rebuild it. So take the things that take two extra and don't need 4k boundary and rearrange them so you have less fills. Yeah, basically. That's my theory. Okay, so I just rebuilt it. And now if we do this search here, now we only have 25 of them. And they're not in the text section. Here's the read only section instead, which we could actually fix as well. I think there's one BSS. So that's RAM that's taken up. I did. So this PR here is actually, you can see that it's not adding it. It's fixing it. He says that's such a better way of saying what I was typing. Awesome. But yeah, I was pretty happy to find that. It saved like a few hundred bytes, which is all I needed. All I needed at that point. So that was great. So that's pro tip if you ever bite hunting on embedded stuff. Yeah, so much fun. I did add a knob to circuit by thumb that I actually don't have on, but I added a knob. The problem is I fixed... They're not separate pull requests. They're modifications to existing ones. So if we just look at... I know one of them was one byte task. This one... This is the one that was having trouble. So I... Was it here? No. I had to do this on a couple of them. So I added this other knob. If we look... I know where it is. It's in py queue string. So like I was saying, there's two tables. There's one table that is like the lengths and hashes of all the queue strings. And there's one table that's all the pointers to where the strings are. And this is just for the queue strings that are built in. That we already know exists. And what I realized based on a comment from T.O. Mitch was that you could... Instead of building that table of lengths and hashes into your firmware, you could make a trade and you could say, I don't... Andy Roberts says, I missed why the fills happened in the first place. Are they to align on work boundaries? Yes, that's why they're there. They're there to align whatever happens afterwards. I thought the linker was smart enough to do this, but it turns out it's not. It actually does it just in order, I think. So it takes things in a particular order, and then it has to fill in the places where the next thing needs to be aligned. And so instead, what in your linker script you can do is say, hey, sort this set of things by alignment, and then you get a lot less fill. But I added this knob that we're not using yet, but this pre-compute queue string attribute table, it moves this... I think it's about a K of... It's in Flash. It moves it from Flash to RAM and then computes it on startup. So you save a K of Flash at the expense of a K of RAM, which in circuit Python terms impacts how big your heap is. And so I was like, I saved it on Sandy 21 and I was like, oh, but that's like one K out of 20 K total. That's not very cool. So I added it and then didn't actually turn it on for editing. It's not that much code to add. It's just like it does take RAM to do it. It moves this const atter table from the read-only data section to the BSS section, which is in RAM, which is good for your Flash size. It's bad for RAM size. And it takes a little computation power, but I don't think it's that much. I don't expect it to be that much. Neodog says, oh yeah, don't take RAM off the Sandy 21. There's already not enough. Yeah, exactly. But I was thinking for cases where... So for the Sandy 51, if you don't have external spy flash, we still try to fit in 256 K flash. We use the other 256 K flash for the internal file system. So that's a case where if we have 192 K RAM, we might actually be willing to make that trade of 1 K flash for 1 K RAM. So yeah, I think there are places that we're going to want to use it, but I haven't actually turned any of them on. And those are for the cases where there's a lot more RAM than there is Flash. All right. Any more questions? I thought that was a pro tip that I thought I should give people. I think that was one of the more interesting things this week. It's been a lot of just running, making sure tests work and things like that. Hi, Paul. Pro tip, worthy price of admission. Thanks. You're welcome. It was funny though. David asks, what is a Q string? So we covered this a bit, I think a couple of weeks ago, but I'll give you the brief answer. So strings are often used in... They're very often used in Python because even internally when you have like a class, a dictionary is used internally to map like a name of something on that class to the object that it points to. And so Q strings are a way of making sure that you only have one copy of a string, even if multiple things name things that. Does that make sense? So like, if two classes have a foo, that's a bad example. Like, most of the time, classes will have dunder and net. Slow underscore, underscore, and net underscore, underscore. And by having a Q string mechanic, you can say, I only want one copy of that. And furthermore, key strings are only 16 bits and not 32 bits. So they are smaller to store, which is great. So they deduplicate the strings you use and they are cheaper to store ourselves. And then on top of that, we pre-compute the length and the hash of Q strings so that if we need to do comparisons or like dictionary lookups, we can generally get pretty far without ever having to look at the string yet. So like, you only look at the string if the hash matches and the length matches, then you have to check the string. But like, that's going to be pretty rare if you're like looking through a bunch of strings. So yeah, a Q string is an optimization to deduplicate strings that we use and then store them as 16 bits, not 32 bits. Keith says, it's really good to know though, especially for those I need an edge case optimization. I'm really curious to see if it helped with compression slash decompression for the error strings. I don't know. There is this whole pool of Q strings as well that we could compress. You're welcome, David. So yeah, I'm kind of checked out. But I have some things that we could poke at. One thing that's really kind of top of mind since I've been babysitting the CI is one thing that MicroPython's done a good job of. Oh, and I guess I should say MicroPython released 117. So let's take a look at that. I haven't actually looked in depth here. So I released two days ago, MicroPython 117. We won't switch CircuitPython to 117 for 7 out of like, but probably for 7-1, we'll do it. We'll try to keep more up to date. So this adds F strings, which we already had, but we'll need to rectify what we merged in versus not. Other improvements to the core runtime include pretty printing OS error when it has two arguments, an error code and a string, scheduling a keyboard interrupt on the main thread, and support for a single argument to the optimized form of stop iteration. I2S, which we don't have, JSON module has support for the separators, which we kind of don't use. Bitstream function is also added where you can output a stream of bits with configurable timing. Interesting, that's used for NeoPixel. Like, we have NeoPixel right, which we inherited from them. Restructuring of the repository directory layout. This is going to be a challenge for us, but it's a good reorder restructuring. We should do it, too. Docs, we don't really care. This is cool, the terms master and save have been replaced with controller and peripheral, mainly relating to I2C and spy usage. The U module references have been replaced with just the module name without the U prefix to help clear up the intended usage of modules of MicroPython. Hidden networks are now included in the WLAN scan. Initial support for the ESP32C3. Yeah, some other support-specific stuff. STM Lego Hub stuff. And lots of people contributing. So thank you to everybody who contributed to MicroPython. And I won't read through all these. But yeah, so we'll up to date to that pretty soon, but not immediately, because we're trying to get 7.0 out the door. We'll do it for 7.1 or something. But yeah, I also was up late last night. I didn't get to sleep that early last night, and I got up kind of early. I was googling around for this We Balance Board thing. So yeah, that's something I could do is I could try to get the We Balance Board to talk to me that is not that related, and I don't know if that will be successful. Another thing that it's kind of on my radar is I really want to tweak slash redo our GitHub Actions setup. So when we were doing the merge for MicroPython, we noticed that the MicroPython redid their stuff, and it's quite nice. So they have individual actions for every port. And then they also say what paths have to be changed to run it. So that is something I would like to do on CircuitPython because we get some of these pull requests where it's like changes to a single board, and yet we build all 200 plus of them. And that's just outrageous. We shouldn't need to build a board that we know will not be able to build a board that we know won't be impacted by it. In the case of translations where the new translation will be on every board, then we kind of have to do it. So yeah, I think this is kind of the thing that I'd like to poke at. During another review deep dive missions, I saw that you had visual a lot more detailed info. Do you have a J-Link device or so? Yes, I have a J-Link base. So I use that with GDB to load code and do things like that. So yeah, there's kind of like three options that we could poke at. We could try to poke at the balance board, but that's kind of tricky because it's pretty large. We could do this get-of-action stuff, which is probably the most interesting to me. Or the other thing that I've kind of been wanting to do is we could start looking at the Raspberry Pi stuff. So yeah, what do people think? Mark, I'm not sure what you're saying. Mark on YouTube. David says get-of-actions and then Pi. Get-of-actions, I think, is one of those... Bruce says, is there any support for parallel connected display controllers? I don't see it, but I only started looking. Yes, it is. That's the thing that's moving from display IO to the parallel bus. The only guy says I would vote for actions. I'm not super familiar with it. I'm always interested in learning more. Alright. David's voting our Pi. We'll get to the Raspberry Pi stuff. Actions is probably one of those things where it's not very interesting work, but it's going to be very, very nice if we have it. If we can make our CI run a lot faster for certain PRs, then that's a huge benefit. The sooner you do that, the better. We'll get to the Raspberry Pi stuff on a deep dive at some point, but I think it's wise to do some of this action stuff. Where do we start with this action stuff? I had this docs pulled up. There's two levels that I was trying to figure out. There's workflow level, and then there's... Hi, Dave. There's workflow level, and then within workflows you have jobs, and then jobs have steps. And so I think that I... I was trying to figure out... So MicroPython... MicroPython took the idea of doing it at the ports or at the workflow level. So at the workflow level, do this stuff based on what's edited. So this is the on push or pull request. If any of these paths is modified, then this workflow runs, and they've factored it out by having this ci.sh shell script thing that runs. So that's the approach that... MicroPython has taken. And I'm not... So here they're doing... So this is what the build command is. So they're building all boards in a single job, it looks like. So here's a job, here's one job, and here's the steps for it. So they're building it all in the same place. Whereas we've switched CircuitPython to be very parallel. So we build... Pull up CircuitPython's existing build. So in CircuitPython, if we just look, see there's this green check mark here, and this will be on a bunch of commits. We could say it says 100 successful checks, but that's actually too low. It caps out at 100 for some reason. But for us, it's more like two-something. So this is for a single workflow, our build CI workflow. It was triggered by a push, by mark. It was successful. It took an hour and 16 minutes, and it produced 242 artifacts. Now, those are actually zip files of other artifacts, though, I think. And this is kind of how it's set up. So each of these boxes here is a job. So these jobs can run in parallel here. So there's two that you start with, and then the way that we have it set up right now is that we run all of our tests first. And then only if those tests pass will we build all of the individual boards. And the reason that we do that is just that we don't want to have to build all the boards if we can do a smoke test up front and know that something's wrong. So that's why we have this dependence. And this is one of the reasons, like, MicroPython does not do this as far as I know. And I don't know of a way to do it between workflows, right? Like, this sort of graph structure is a thing that is a function of... It's all within a single workflow. It's within the jobs in a single workflow. And you can see here that there's this matrix thing. So if we, like, click there... Whoa. You can zoom out. So I didn't know you could do. You can see that this build extensa job is actually used as this matrix to build every board in parallel. So you can see that they take about between 11 and 15 minutes apiece. So we're using, like, a lot, a lot of CPU time. I think it ends up being, like, 24 hours or so of CPU time. But GitHub Actions lets us do 60 jobs in parallel, which is why it only took us an hour and 16 minutes. So not only will this make our lives better with improving our CI, but it will actually make us better citizens if, like, a PR comes in, like, some of these ones that we've been, like, tweaking a single board. Like, if that means that we only build, like, the tests and then the single board, like, we're actually saving a lot of our time, but also, like, CPU time as well. So the question is, how do we actually set it up so that that's the case? So the way that MicroPython does it is MicroPython takes advantage of this PAS argument to the on triggers. Unfortunately, I don't think that will work if we want to do this prerequisite job, this prerequisite test. I don't think that workflows themselves can kind of, like, reference each other. It's only within jobs that you can do that. Although maybe we can do that with triggers. Bruce says, I have the same actions on my fork. Microsoft, I must love how I waste cycles building. Yeah. It's really nice that we're able to build everything, but it's like, especially if you look at what we spend some of our time doing, it's like, we do a get check out on every job. Like, it's not... It could be done better. And maybe what we should look at is, like, using a custom Docker image as well that might help. But for now, my goal is with this to just ideally scope what we test closer to what we think will... closer to what actually is different based on the files that have been edited. So... See, they're talking a lot about example workflows. Oh, here we go. Understanding. So this workflow syntax for get-of-actions is going to be really useful. So I'll open that up. Name. So on, specify the event that automatically triggers the workflow file. So I wonder if actually what we could do is whether we could use triggers to actually do derivative... derivative stuff. Check bats version. Runs on Ubuntu. Steps uses... Okay, so... Visuallying the workflow file. Da-da-da-da. See, so much of this is actually, like, actions. It's like within a workflow. So let's look at workflow syntax. Kind of see what our options at the top level are. So... This is... Or maybe we want this events that trigger workflows. Okay, so this reference is really handy for those getting into... into get-of-actions. This, like, is... it's kind of exhaustive. This is kind of, like, the API reference for get-of-actions. That sounds awesome. Thanks for stopping by Handslabs. Okay, so name on... And then events that trigger workflows. Let's actually look at that, because maybe that's what we can do, is we can use one... one workflow to trigger another. And then we could still do it at the workflow level. Although if that were the case, then we wouldn't be doing it based on... like, then it wouldn't be a push directly. Using a single event. So you can filter it by branches. Scheduled events, we don't want to do that. You can manually trigger workflow runs. Use the workflow dispatch event. More than one workflow in a repo and create custom events and event types, use the repository dispatch... event. Custom define input properties, default input values, and required inputs for the event directly in your workflow. When the workflow runs, you can access the input values in the github event.inputs context. You can manually trigger a workflow using github API and from github. When you trigger the event on github, you must send a post request. This example defines the name and home inputs, prints them, and it provides a default. On workflow dispatch, name, person, input. So I wonder if... well, what was the repository level dispatch? Repository dispatch. So I wonder if we can do conditional stuff on that. Yeah, maybe I had to trigger a webhook event called repository dispatch. And for more information, do this, open that and look at that in a sec. By default, all event types trigger a workflow to run. You can limit your workflow to run when a specific event type value is sent in a repository dispatch webhook payload. You define the event type sent in the repository dispatch payload when you create the repository dispatch event. So that... I'm just trying to think of, could we have one main workflow that runs all the tests and makes MPY cross and then triggers a workflow for every board, potentially? So like one central place would decide all the boards that we need to build, and then it would trigger the workflows to build those things. Webhook events. See webhook events and payloads. Check run. Check suites. Create, delete deployment, deployment status, discussion. It's pretty cool. You can do it basically on anything. Issue comment, issues, labels, on pull request types. Is that where they cover paths, push registry package, status, watch? That is not right. That should be start activity. This event occurs when a workflow run is requested or completed. It allows you to execute a workflow based on the finished result of another workflow. Workflow run is triggered regardless of the results. If your pull request workflow generates build artifacts, you can create a new workflow that uses workflow run to analyze the results and add a comment to the original pull request. We use this... I use this briefly. Activity types. You can filter based on branches. To run a workflow job conditionally based on the results of the previous workflow run, you can use the job if or steps if conditional combined with the conclusion of the previous run. That's only success or failure, which is not that useful for us. We can use this if thing, potentially. What is a repository dispatch event? Here we are in the REST API. This is annoying. This is annoying because if it's a repository dispatch event and we have to have all of the types up front or something, that makes it really hard to add new boards. We want to make sure that whatever we do, it makes it easy to add new boards. I don't think that's what we want. I suspect we're going to want it all within a particular workflow. The challenge then is to figure out what files have been edited. Webhook events and payloads. Organization installation. I don't think any of this is really what we want. We're triggering it when we want. We just need to be able to narrow it down. I did see... Let's take a look at this jobs if. You can use the if conditional to prevent a job from running unless a condition is met. You can use any supported context and expression to create a conditional. For more information, look at context and expression syntax for GitHub Actions. That sounds more like what we want of just saying, here's our big long matrix that we already have, but then the job doesn't want to run because of this or that. Weather a step should run. If conditional is true, the step will run. It's also for jobs. If expression... Contexts. Information about the currently executing job. The job context. Runner. Secrets. Strategy. Matrix. If you configure a matrix build with OS and node versions, the matrix context includes the OS and the node versions of the current job. This is probably what we want. This is the way that we get the left-hand side. This is the way that we get access to which board we're trying to do. Is matrix. Environment variable. Needs. Needs is kind of interesting. That might be something that we could use to do it. Determining when to use contexts. See, like, if GitHub ref is that, then do that. Hey, Johnny, no worries that you're late. We're taking a look at GitHub Actions, and if folks have basic questions about GitHub Actions, please let me know. Brent was going to do some action stuff, too, so I might come to this later. Right now, when CircuitPython does its CI build, it does this prerequisite step of running all the tests, and then if that passes, it builds every single board. What I want to do is get us to the point where, if the PR, if the change only changes, if the change only changes, if this is, like, files in one board's directory, then we should only build that single board. We shouldn't build all of them. And an intermediate thing would be to just limit it based on, like, the port that does it. So, yeah. Oh, and here's all of... I clicked these context things, but it looks like I'm... Oh, no, these are references. So, Job says, Service Containers. I was wondering if a Service Container could be useful for Ccache, actually. Clearly stating the requirements. Yay. Current status of the job. This is the same file. Okay, let's close that. Okay, so let's just see what we've got at our disposal. We have Action, Action Path, Actor, Base, Ref, Event. So, one of the challenges is figuring out what files changed. Question one. Sure, Actions can actually do that. Well, so we have Git, right? Like, we have access to Git, so the first step, well, one of the steps for us is going to be figuring out how to enumerate all the files that changed. Environment, Job, Steps, Runner, Needs, the value of a specific output for a job that the current job depends on. So, this could be really interesting. If we say, I need, like, this job depends on this prerequisite job saying an output of this is true, right? So, like, if for every board, we say yes or no in a central job. So, I wonder how we do job outputs. Because then we could just say, so the answer is maybe. Yes, the answer is maybe. The result of a job that the current job depends on. So, we know this will be true. So, how do we do job outputs? Literals, environment, operators, contains. So, we could say if the output from the previous one contains our board name. Hi, Dinkelberg. Hash files. Job status checks. This is an interesting de-drive. It's me reading the docs. Go figure. Okay. Object filters. Fruits. Dot. Star name equals that. I picked a deep rabbit hole. Yeah. Bare metal circuit pipe on Raspberry Pi would have been easier. I doubt that. I doubt that. Context availability needs is available. So, we want to know what's available for if. How do we set an output? Can't have actions set output. Workflow commands when running shell commands in the workflow or in actions code. Actions can communicate with the runner machine to set environment variables. Output values used by other actions add debug messages to the output logs and other tasks. Finding the right documentation is key. Yeah, totally. Actions toolkit includes a number of functions that could be executed as workflow commands using the double colon stuff. So, here we can say set output name equals selected color colon colon green. And then we can say the output of selected color is green. So, okay. Get input. Set output. You can also declare output parameters in actions metadata file. Open it. Optional. Output parameters allow you to declare a data that an action sets. Actions that run later in a workflow can use the output data set in previously run actions. If you don't declare an output in your action metadata file, you can still set outputs and then use them in a workflow. Workflow commands forget have actions. Outputs for composite actions. Oh, interesting. So, this is like value is steps. So, I think that's I think that's kind of what we want, but at the same time it may not be if like, I don't really care if we start the job and immediately decide that it doesn't need to be done. So, I found this other thing. GitHub actions. Path filter job. So, I found this other thing. GitHub actions. Path filter job. Path filter job. Um, there's this. Conditionally run actions based on files modified by the PR feature branch or push commits. Um, here's a GitHub community thing too. Path filtering a job and step level. Let's see what people say. People say has changed path. Solve this with pass filter. Okay, that's the one we have open. Looks like there is no more official way. A deep dive into pull request. Okay, so let's see what this other option is. It has 139 stars. This says 305 stars. Outputs whether path or combination pass has changed in the previous commit. That's not what we want. We want potentially more than one commit. Deep dive into nap time next. Optimize all the parallel checkouts or does each job need to detect if it needs to be done? Uh, well, I think that's kind of what we could do is like if we have a central job that sets outputs for all of them. I think then we could say or no, where was that thing that said it needs needs depends on context availability. So interesting. Strategy has access to the needs context. Very interesting. I think that means that maybe we can create the matrix based on the needs context. Where is the workflow syntax? Okay, so if we look at job strategy matrix to find a matrix of different job combinations as the value of the runs on keyword right. Running multiple versions. But I wonder if we can it would be nice. Here's another thing that would be nice is if we have that big long list of boards right now in the GitHub actions. It would be nice if we could actually not need to update that. If at the tail end of that test job that we do we determine all the boards that need to run and then we suck that into the matrix suck it into the matrix. Allow memory of the checkouts. I mean I'm okay writing a Python script that just says here's all the ones I want to run. All the boards to build. So I wonder if I can do that. See how runs on here is referencing this context. I wonder if we could say for the build job we could say strategy matrix board. But then put this like dollar print or dollar crilly crilly needs previous job dot outputs dot board or like arm boards. I bet we could do that. Yes. I bet we could. Oh yeah Johnny that one way to find more bytes is to sort your sort your sections in your linker scripts. Oh interesting. You can exclude individual things. Using environment environmental variables include strategy fail fast max parallel I wonder if we could do that. How does an action know the previous outputs. Well that's what I'm saying I think I think because we have it set up let's pull it up. So let's close this close this like I'm okay if we could centralize it that would be plenty good. So if we do github workflows build so we have this first job that is our test job so this is what runs first. And then we have NPY cross that runs at the same time but this is on Mac so that's how we build NPY cross for Mac. That's okay but then we have this giant build arm one that has this giant board matrix. Now I'm wondering if we could just replace this board matrix with a needs output from test so see there's already this needs thing here. I wonder if we could do that. We use dependencies on the outputs to decide if we need to rebuild something what is the equivalent of github actions model. It's like you can say if for a job you can say if this is true but then there's also this matrix thing so I'm wondering if we could pipe it. Let's start experimenting shall we? So let's do gith status where are we? We can delete the release nodes. Let's do a new bridge. Gith switch. CI opt. Let's update. So let's play around with this. So let's just say I'm going to put three and I'm going to put them this is the bottom of test. Here's the bottom of test. So let's just put that there for now and then let's delete all this and say this is going to be needs dot test dot outputs dot arm boards and then RIS 5 only has one board so let's just leave that there but then here let's just crib this down to here as well. So that should be enough. Oh but let's do this so let's see if we can't name set boards to build so let's see get have actions toolkit how do we set an output see so needs job ID outputs output name although it says type is string so we may not be able to say to make it a list unfortunately job that would be so such a bummer oh but you know what I think where is the functions it's a string here's set output name value but if we look at there's this to json from json oh look at this that's exactly what we want it's a set output matrix to this json and then from json to the matrix that's exactly what we want we want oh that's perfect that's exactly what we want okay so we want to say um json was in the matrix yes from json we'll just do it as a list of test outputs arm boards did I capitalize it right? I did and then what we're gonna say is outputs so for test outputs steps set matrix we'll call it set matrix but maybe like build arm goodnight Dave boards is what I call it boards okay so now we're going to call this last step um id equals set matrix what we called it right and then we're gonna run let's go back to our example this but not this so we're gonna run set output equals arm boards and it's going to be so it doesn't look like we actually have to encode it it's doing the encoding for us but it does look like we have have to escape the quotes so it must auto convert it to or it knows it's a string so we're gonna do Arduino nano 33 so this is just for our test if this works we should be able to have some python generate this list instead and in fact we already have um we already have that python that can figure out a list of all the boards so we can repurpose that and in fact we're just doing echo here so we could actually output this bit um oh alright let's just do these two cause I don't want to direct any others so let's delete this stuff cause that's gonna break it alright so here we are printing this output for set matrix we're calling it arm boards and then arm boards goes into here output matrix let's call it arm boards as well so I think this is connecting a step output to a job output um and then the job output gets input into here we decode it from json and put it in there so let's save that so the thing with get up actions is you have to just test it so first try and this is gonna take a little time cause that first step takes a little time unfortunately luckily I've got all you folks to just uh sorry snag in my GPG password um let me actually remember to copy something else into my paste buffer um so now we'll get push 10 new ci opt and we'll take a look at what it does I'm very excited about this we could use this um we could use this get up actions to figure out all the modified files or we could just do it ourselves in a python file um which I kinda like I kinda like doing it ourselves but there are some things to be aware of if you're on a PR and there's extra stuff on the main branch there will be a merge commit and we don't wanna do it based on that merge commit we only wanna do it I think based on the files that are changed by the other stuff um yesterday I wrote 3 lines of python resulting in 6238 icons in jpeg format for the free touch deck standing on the shoulder of giants that's the brilliance of python is there no fish console for windows? I did a lot of console stuff I found this really awesome the problem is that I'm so there's this thing called McFly that allows you to do like different history searches which is very very cool um and so I set that up um I also set up um I tried a different there's a different command prompt thing called um Starship um Starship thanks Bruce I don't actually like it as much as this one because this one has this cool thing where it changes color based on your get state and Starship actually doesn't do that it'll change icons but it doesn't really um it doesn't do the it changes icons but not style based on on state which I was kind of disappointed about um but there's some modern modern rest ci things that looked pretty cool like fd is a short modern find and then um I can't remember all of these there's like rg for ripgrap which is like ag that I use um there's also there's an ls replacement that I problem is like I can't remember them all there's a replacement for cat called bat which does syntax highlighting and line numbers which is pretty awesome um yeah there's there's some really cool command line to the rest community is awesome like I need to do rust at some point I need to take a look at it it's pretty neat um what is the what is the ls one I think it's fish does this really nice autocomplete that you see here um X thank you Nate that's what I'm thinking of like it's very cool cool exit exit exit exit exit this is like the second time today I've tried to remember what it was I really like fish I recommend fishing you can do it um yes there's some really cool tools you know I was complaining about the history thing earlier and that's why I I realized what I want is when I'm doing air up arrow I want it in the current I want it in the current shell unless I just opened it yeah David Exa the exa website that's clever um but if I'm doing like a search like a prefix search then I wanted to cover everything it's kind of what I decided all right let's see if this get have actions is running and it failed so what did it say oh the new boards check failed haha I should turn that off um because I deleted the list that it checks check so that new boards check is probably almost what we want to repurpose to create the list of boards so I'm actually going to move it down here there's a chip shortage to go with your fish and chips okay so let's disable that check turn off boards check can't think of what I'm doing we'll look at that in a second did I close it I think I hit home I think we're on to something Bruce is getting hungry with the chip top oh they do have windows support awesome I really like I'm really happy with it with fish so here's where we're checking everything out this step takes a while takes a while but we'll keep an eye on this this is this is what happens when we're doing get have actions testing gotta end up waiting for it okay while we're waiting let's take a look at what this does conditionally run the actions based on files modified by the PR feature branch or pushed commits path filters don't allow this because they don't work on a level of individual jobs or steps it's used by google chrome which is pretty awesome so this might be really handy to just it probably gives us just a a list ways to reuse the previous checkouts yeah like there is some caching stuff but even downloading the cache can take time I'm sure there's more optimization we could do there so example uses path filters with filters blah blah blah if steps output source equals true see the examples section by using the pico match library quote your path expressions with quote or double quote local execution with act works only with the alternative runner image what is act? run your get have actions locally that's cool list files csv format configure a matrix job to run for each folder with changes using changes output interesting usage filters we could have base ref enables listing of files matching the filter file paths are formatted as a json array I almost want to like just use the part that it gets it changes is a json array with names of all filters matching any of the changed files so the problem with this is that um oh there we go there's the examples alright we're building the docs if we like this filters set up for this action thing is done up front whereas like we know the structure of circuit python we know where all the boards are so we really shouldn't like we would have to hard code all the boards of this uh in this case so passing a list of modified files is json array well there we go this this would at least get us all of the changed files so so this is what we could do is we could say alright well like we're going to just get all of the files and then we're going to pass it to a python script that actually so we don't have to figure out what all the changed files are um command line args or a json array i wonder how this works oh man it's like all this type script stuff yeah so this is what i think we should do let's use this as our file listing input into a python script that we write and just make decisions on does that make sense oh man we're almost out of time we have 10 more minutes i'm gonna i want to do this today though this is i'm i'm stuck in which is good okay so here's the thing that says get us all of the file pass and we then pass it to some other action we should be able to it's just going to be a string we could throw it in an environment variable um kind of remember how i built qmk does qmk do this or are you talking about something else we still have to see if this worked for some reason the other build went really fast oh it went really fast because it failed earlier that's why maybe that's another thing we should do if this test step is pretty slow but like the checkout took a minute and a half the dependencies took two minutes building the docs took a minute and a half now we're building the lautec pdf version which is a minute and a half which is fine we're not optimizing for overall overall speed and this test step can run concurrently if we have multiple prs or commits coming in um so this is i think what we want like let's let this thing worry about what the change stuff is and then we can pipe that into our we can pipe that into our our own Python scripts and it as a chase and array yeah let's do that so we'll use this thing to figure out like given our context what what changed so and I think it will repurpose this new board's check so we'll no longer need to do that which would be very nice um ID filter filter name files output has what we okay so now what we're going to do is we're going to do a multiple step run I don't think we actually need the echo anymore I think what we want is a echo can we put I think we might you know I think we might need to throw it in the environment but that could be a lot of files I don't know how big an environment variable could be um how do we pass in something ginormous potentially I'm probably getting ahead of myself ah you're still talking about fish like command line and args what if it's like super large oh you know what we could do if we did this earlier we could use this to not build the docs that could be nice actually like we could actually speed this step up if we knew that like nothing changed alright that's I'm getting ahead of myself that's not what we're trying to do today um file was added modified or removed oh interesting so you can put like prerequisites there um filter rules in your own file but again that's not like I want them to separate how's this going ooh we're almost there ooh and it worked so we've got the two Arduino nano builds the foam and the one extensa um super large things are not appropriate for environment variables right that's my assumption like I mean worst case we like imagine we're doing like a micropython merge and we're like changing a ton of files um like I wonder how we get that kind of out an output back into us back into it like can we write an output to a file alright where's our workflow syntax so we're talking about what we can do with oh here we go with see that puts them into environment variables with args passes it to the containers entry point start up not what we want with entry point I mean maybe we just try it I don't see that like this can't write it to a file unfortunately I don't think we saw that haha I mean I don't think we have a choice I don't see any other options and like uses is like using a separate action actions environment variable size limit I mean there's 64k in size here let's just we'll just do it that way for now and if it breaks it breaks will it be large enough that we need a database or a file to store the list of changes you know what I think we could do either it will fail and we'll have to fix it or it'll be a partial and it won't it won't be parsable as json and so if it's not parsable as json we could fall back to just generating all of them right so like if there's so many things that change that we ran out of room we just run it all like we're just optimizing for the case where it's only a few files probably um so yeah we don't need to be perfect about it and that's one of my strengths I think is not being perfect about things um okay so what we're going to do here is then this is going to be with uh an environment variable we're going to call changed files and that's going to be steps dot filter dot outputs dot changes changed I think we're on the right track so let's do let's go back to here and changes output and then this filter name see there's this here the example we copied has it to one thing that's interesting is that it calls it changed files filter name files steps filter outputs changed files cool okay so now I think what we want to do is actually do this but let's do um let's copy this so we're going to rename new boards check to changed board list does that make any sense tools ci new boards check rename changed board list and then we're going to print this so this is what we want to print out of here is these set output things um because if that's all gab is doing I think is it's just like seeing it come back um so let's see here so what are we doing we need os we don't need this anymore we don't need to know the workflow file we don't need to do the yaml we don't need to read this board matrix and we don't need to sort them and we don't need to see what's missing because we're just going based on this list now um if missing boards over time and it is kind of hot in here but I'm on a roll I'm on a roll folks okay so we're going to have a changed files changed files is equal to json.loads os.environ changed files any recommendations for new circuit pipeline users uh what do you want to do with circuit python the learn guides are really good both the welcome to circuit python and the circuit python essentials I definitely recommend starting there but then if you have a particular project in mind find a project on learn cheers Minnesota mentat thanks for hanging out changed files so let's dump that out and then I think what we might want to do is slurp these back in as we can basically undo it and it's like if see how do we want to do it do we want to do it exclude exclude exclusively or inclusively so I guess what we could say is for every path if the path matches like ports port name, boards, board ID then we include that board if it's just in ports we include everything from that port if it's outside of that we include everything I think that's pretty much it right if it's in port only then we include everything in the port if it's in port only we only include that board does fish support the fish operator that's like the default prompt is like a fish alright I think I'll call it I'm going to keep working on this but I'm kind of hot and need a snack so keep an eye out if you want to follow along that's the branch that I'll be poking at so if you want to check that out go ahead and do that um doctor needs to go for their walk the audio pseudo code sounds like a good plan okay yeah the inclusive like for every path that changed if it's in this bucket include this group if it's in that bucket include that group um cool so then what the script will end up doing is it will end up printing out these set outputs will do arm boards risk 5 boards and extensive boards um and we'll just output these big long lists and then those should get slipped into the matrix and so we'll only trigger matrix builds for the boards that changed um which should be pretty neat I hope we can I mean yeah we should be able to give entry mate empty matrices but um if not we can always do one for every sub job or whatever that would be fine um alright so let me switch to the camera uh everybody have a great weekend here in the U.S. it is a long weekend so um if you're in the U.S. and are able to take Monday off please do um I'm going to take Monday off so the circuit by the weekly meeting that happens on Mondays normally will be next week on Tuesday at the same time at 11 a.m. pacific 2 p.m. eastern um the ship is says what about commits that add a board if a commit adds a board it should build that board it shouldn't need to build any other boards so this get board mapping should hopefully work on the current the absolute newest thing and then we'll see that those files were added to because change changes including additional new files I think we'll double check it I'm excited about this I'm ready to I'm going to keep working on it for sure because it would like really make our builds a lot better so I'm excited about this um if you want to support me uh well if you want to chat with me and a bunch of others outside of this stream feel free to join the discord server by going to the URL adafru.it where they're all week and weekend um if folks want to follow along on discord we can keep chatting about it um but I think we have a plan it's just making that plan happen now um and if you want to support me you can go to Adafruit.com and purchase stuff there uh they pay me to stream they pay me to work on circuit python so uh supporting them supports me uh there's lots of cool stuff um check out the release Canada as well like Keith is talking about um please test it the BLE stuff will will continue to push it forward um so give that a trial though um there's more work to do there and uh next week is on Friday as normal and I think that's all of it um rain here for a long weekend but not a monsoon yeah we're going swimming in the pool with my two year old soon to be two niece so hopefully it's not too bad tomorrow although the pool will be uh emptier I'm sure if it's raining um anyway uh thank you all we'll see you on discord and I'll I'll pet the cat to wrap us up here since he's since he's up here I'm gonna take a break and before I keep poking at this too anyway thank you all uh we'll see you next weekend on the discords I kept shocking him alright have a great weekend everybody