GitHub Automation with Octokit
with Gregor Martynus
GitHub has powerful APIs, and with Octokit we can do just about anything on GitHub through code. Gregor Martynus will teach us how!
Resources & Links
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
Hello, everyone. And welcome to another episode of Learn with Jason. Guess what I forgot to do today? Turn on my lights. It's all right. We're going to go with natural lighting today. You're going to have to deal with my shadowy face. Welcome to the show, Gregor Martynus, how are you doing today?
I'm doing great. It's an honor to be on your show.
I'm super happy to have you here. I feel like you and I have kind of run in a few of the same circles for a while now. I've done a lot of little projects using, you know, the GitHub API and every time I use the GitHub API it leaves me around the Octokit. And Octokit is something we'll talk about in a minute. But before we bring that up. I want to hear a little bit about you. So for people who aren't familiar with your background, could you give us a little bit of a history?
This is not an official GitHub project, right? Didn't start as one?
Yeah, no, that's great. And I think it really does. You see that when you, you know, because I feel like I built a few little apps where I was trying to use the APIs and because GitHub does so much, I found myself chaining a lot of things together and having to keep track of tokens and all of these different things that it was like, all right, there's a lot going on here. But I, then I reached for Octokit and that kind of all happens under the hood. I was able to chain commands that did I freeze? Why am I frozen? What are you doing?
I muted myself and you froze. So try, again, and to unfreeze you?
Why is my camera frozen. Give me a second to bounce out here and maybe we'll come back and the camera will magically fix itself. Nope, give me just a second, everybody.
OK. I can talk a little bit more about SDK and why it's useful.
Yeah. Why don't you do that? We'll see if we can figure out why my camera.
REST APIs are stabilized, they are great in and of itself, but with SDKs can do because they're running on the client, they can keep state between these requests. And you can do things like throttle request, for example. You can do things like request retries. You can obviously keep state in terms of authentication. You can on some strategies require you to renew tokens once per hour and so on and SDK can do that transparently.
Yeah. Absolutely. And I have no idea what happened. So great. Hello, I'm back. Hi. That was odd. So I mean, we could talk about this forever, but honestly, there's so much we wanted to cover today. And I'm so excited to jump in. And also, when you're working with an SDK, it's, you know, seeing is believing, I think.
Why don't we just hop right over and immediately start working on this? Let's jump in and a little bit of housekeeping here. So first and foremost, today is episode 200 of Learn with Jason. So thank you all so much for hanging out, learning with me, thank you Gregor for being
(Laughter) Oh, there's a sticker! Oh, that's amazing. I'm so happy. OK. So if you want your very own stickers, you can go over to the store and get yourself a pack of sticker asks boop the heck out of anything. Thank you for dropping the boops, Chris. I can't believe it. It's been a wild ride. Thanks, y'all for coming along and learning with me. And as usual, we've got the whole show being live captioned. We've got Diane with us today. She's helping us out. Thank you so much. And that's from White Coat Captioning. The captioning is made possible through the support of our sponsors, netlify, all kicking in to make this accessible to more people which I very much appreciate. Make sure you check that out. If you want to watch the live captioning, you can see that right on the home page on LearnwithJason.dev. Then, great, yeah, Twitter. Make sure you follow Gregor on Twitter. Gr2n, make that happen. Smash that follow button and ring that bell. And we'll be talking about Octokit today. So this is the Octokit JS, specifically. OK. So that is a lot of things. And in the, like, most amazing act of overpreparedness, Gregor has created an issue on GitHub with our agenda for today, which I am, this is thrilling to me. This is the best planned episode of Learn with Jason that we ever could've had. And look at all of this stuff we're going to try to get done. We're going to see how far we can get. We'll start here and then, we might have to do, we might have to call it at a certain point. But let's see if we can go fast. I think a speed run sounds like a whole lot of fun.
Congratulations, again, to episode 200. I'm very humbled by all of the other amazing guests you had. I try to compensate to live up to this high bar of your show to come prepared.
You made a hat, you're the most prepared guest of all time.
Not every episode is episode 200. I might need to take it off, this is for my kids, it's a little tight. I tried.
Yeah, I appreciate, you don't have to commit to the gag. But OK. So we are looking at doing an Octokit kit speed run here. So the first thing that we want to do is probably get Octokit set up, right? I think I'm probably out of date here. What's the first thing I should do if I want to
Get Octokit running?
I would say, we hop into your terminal and create a new directory for today's show. And we will boop your nose, so maybe create a directory called boop Jason's nose or something like this.
OK. Is that the right thing to do in this case?
Yeah, yeah, sure. Get in it and then NPM in it dash Y.
OK. Anything else?
And is it just like this? Or is it
OK. This is the it's pretty new. I didn't really officially announce it, but this is now the all batteries included Octokit package.
OK. And now, I will create. Let's create a CLI.js file.
And we can open it.
Open this right up here.
And one thing we can do is we will kind of bump, counter on your repository, and I started a pull request to add this number to your J link story repository.
OK. Let's check this out.
So we can edit it if you don't like it.
I love it. Let's get it in here. So we're going to submit the review. And we'll do a squash merge.
Now you make the
I love it. I need to do a whole episode on why I think those are cool.
I do maintain the release, whenever you want to do that.
OK. So we've got this new line added. We've got a link to the show that people can figure out what the heck's going on after we turn it off.
But once we're there, then we've got this empty Kelli file, what should I do next?
Our goal here now is to automate the process to bump the to go in and edit yourself, right?
You start out by importing the Octokit package so you can use require and then
Thank you for the bits.
What is it called? The object?
Yeah, destructure it. I need more coffee. OK. And it's a capital O.
Got it. Capital Octokit.
OK. And now, we can create instance. It's a new Octokit.
OK. So in, is there a convention for this? I always, I end up doing GitHub is new Octokit.
You can do whatever. In all of my docs, I use lowercase octokit.
Let's follow the docs so it doesn't look like my preferences here. And so I've created a new Octokit, do I need to configure it?
Yeah. You need to for our use case, you need to pass authentication, and the most simple strategy is to just pass a token so you're passing an object with an auth property to a present access token. And we talked about this before the show, so we don't need to expose your token to configure it to make it available globally.
So what I did was put this into my environment, like in my CLI, like my rc, this will be available whenever we run the CLI.
We will write an asynchronous code. I usually define a function called run and execute it right away. Exactly. Now, what we can do just to test your authentication is to request your own user. So you can say const response.
Equals await octokit request.
Oh, look at the autocomplete.
Oh, wait for this. Now, start a string and press, I think
So you know, Octokit has 600 plus REST API endpoints and there it is.
Oh, my goodness.
So we spend a lot of time on this magic because gosh, it is really nice when you use it. But it is a pain in the butt to maintain all of this magic. But I'm glad it works. This is the moments I'm doing this for. I've been sweating blood to make this all possible. All right. So this will request your current user account using your authentication.
Before we do that, Brittany asked where you get a token. And I'm going to go show where you get one. So we go to settings, and then we go to is that right?
No, that's not it. Developer.
Just tokens. I just remember URL is/settings/tokens. There it is.
Session settings, developer settings, personal access settings. I created a token here. So if you want to generate a new one, we just need to check, we check public repo for this?
And add a note to describe it. So some description there, and that'll give you a token. Once you create a token, you'll never be able to see it, again, which is why I was able to open this. We can't see this token, I can change the scope but I can't change the token. But if I wanted to, I could regenerate it to get a new one. So that's how you get that. So then, what I did, I have a .env file on my whole computer. And I can put things in that get loaded that are available in the environment. If you're curious about how that works, there are some good Google y articles which is how I learned if you're still not there, feel free to DM me, and I'll walk through. I've requested the user. Do I have my, am I done? Is that can I console log that?
Yeah, let's log, authenticate.
And then, it would be response.
Response.data and you should get the magic. Like the login.
Oh, this is so good. So then
We just run it. Node CLI?
Mm hmm. Whoops.
Requires authentication. That means, source my
Yeah. Exactly. That's most often how it is the problem here. It's not.
Let's see, GitHub token, all right, let's do it this way. Let's do, pull this off screen really quick and check it actually does what I think it does.
It does. You know what I might need to do? Let's do this. Let's do GitHub token = GitHub token, which is ridiculous.
You don't need to do it, usually. But let's try.
OK. So I think I'm using nvm, and through the course of 200 episodes the internals of this machine are a wreck. At this point, I think I have like 19 different versions of node running and all of this are talking to a slightly different flavor of environment. So this is me kind of doing things the bad way. But what I could also do, if I wanted, is I could do a .env file in here. And we could do it that way. I'm not going to do that. Let's just yeah, let's be OK with that. And now that I've done this, now, so I'm going to have to include that GitHub token. So why don't I do it this way? I'm going to put this in as a run command. And that way, we won't have to do it every time. We can just npm run run. Whoa, why are you like this?
It doesn't know that's fine. We'll just do this. My computer will fight us kicking and screaming and we will beat it into submission.
It's not a problem of Octokit. It must be something odd.
This is 100% a problem with my machine. Yeah. This one's not Octokit by any means.
They tell us we should remain script to DMC test. Rename it to forest. But I guess there are a lot of, OK.
Because it's tricky.
All right. Let's go back to it. Hey, you wanted to invite me to your visual
Yeah, let's do a live share.
I want to give you some snippets that that are not relevant to Octokit, like reg X.
Yeah, let's get this shared over. There is a live share link for you.
OK. I've got it.
And if you haven't used it, if you're seeing this the first time, VSCode liveshare is the coolest. It lets people, like, join in the session and they can do stuff on your computer. It's very, very good for remote pairing. So we're going to let, this here. So now, this thumb tack, I can follow you around.
Cool. So I can
Hands free. Let's do some stuff.
Just magically appear.
What I always wanted. OK. So we've got a bump boop counter and this is going to do a content replace. So we're doing regular expression to pull out the number, and this is our name group because it's in parentheses. And then, we run this function to handle it. And we take our counter, which is the named group, the captured group, I should say. And add one. So it's just an auto increment function because we're working in a readme because we make great decisions. If we run this, we should see, assuming we pass in the readme text, we'll see the readme text bump to plus one.
So now, we have to update repository content. You can leave the authenticated as it won't hurt us. And next, let's do an update request.
This is so cool. I can watch you live here now. Yeah, now await Octokit, request.
Am I getting another response here?
Yeah. You get another response.
So let's change this one to user
Destructor above to data and rename the data to the user, that's what I usually do.
Oh, yeah. We can do that.
Data: User and it has a good name, right?
That's nice. Look at that. What a handsome destructured variable.
At this point, maybe we can show GitHub's REST API documentation and then just kind of go from there. Because this would be the usual flow that people would do if they don't know the
And this is to get
We're in the REST API and we would be looking for our endpoints and we would want to update
Repo, repository. It's called repositories CS. Can you see, there's so many things in here in this article. So basically, what you want is updates file, there's a section called contents when you scroll down.
Oh, wow, there's really a lot in here. Commits contents. Creator update file contents?
Yeah, that's it.
And every endpoint has all of this option. The cool thing, when you scroll down a little bit in the examples now include Octokit code examples, as well. So you can basically copy and paste this.
Oh. Look at that. That's slick. OK. I can take this out. Let's look at what it does. Whoops. Whoops. Here. So we're going to Octokit the owner would be me. Path is root, right?
No, it's readme.md. The root is a folder, right? You have to update a file, which is this is the full file of it. Boop Jason's nose, I guess.
And the content is going to be. It will not blow up, it will not also, when you see the request, what's happening there is that 3 of the parameters on the repo path are URL parameters. And you can pass all parameters no matter if they're body requests parameters the same way as they are documented. This is what Octokit request is abstracting for you. You don't need to the difference how the parameters are transported to GitHub. When you run this now
Response might be too big. So we can response data.
Let's see what we get back. I'm going to run this, again. And it says I'm authenticated as me, invalid request.
Gives you a 4 to 2 or there's a conflict because it's all git and cannot update the file. We need to tell GitHub, what's the current version you want to update, right?
So before we can update a file, we need to get it. So we need to add another request first, which has the same path, but just a different method. Instead of put, it's get.
So we'll get data as readme, that's OK.
Octokit request, and I'm going to do the repo
What if I do content.
You have to pass the route first. So it's get all capital letters.
So repos owner, then I need to pass in some arguments. So the owner.
Yeah. The first three.
Go away. Not the button I wanted.
You could copy the first three and use them there.
And I don't need these others.
Yeah. Exactly. Now, readme has a property called sha. It should give you a type ahead.
And this is now
I'm going to take this part out for a minute so we can look at this sha. If I run this, again, we should see, yep. And then, if I go look at my readme on GitHub, I should, theoretically
It is not the same as a commit sha yeah, it's a little different. And you don't want to go too deep into GET because I don't understand GET like anyone else.
That's a deep hole. OK. We've got our readme, should VE have the readme loaded at this point and we have a sha but we also have, like, the content.
Oh, that's a good one.
Is it content? Yeah.
Mm hmm. Because that's another surprise, oh, interesting.
It's 64 encoded always. You have to keep in mind, now, I wanted it to be a surprise but you're too smart for me. You need 64 decode it. You can do buffer from as a function. You probably did this a million times and as a second parameter, you you hackers, you dirty hackers.
Oh, wait, I did that all wrong. Two string.
And then, to string. That should be enough.
Now, we've got our, this is our readme content, we're pulling everything in. You can see here, this is the content you want. If I change this out to be content I run our boop, am I getting ahead of you?
No, please, go ahead. This is great.
Bumpboopcounter content. We should see this will go to one.
Look at it go. All right. So now, we have a boop counter, and then, we want to get back here. And this is the part where I have no idea what is going on. I do think I want this to be updated.
Unfortunately, not quite because you, again, have to base64 on code it. This is how GitHub talks to you. No, this is not, this is UTF8 now. Buffer from, and I think it's a default.
String base64, is that right?
And you need to add the sha argument.
And that's just like this?
No, read me that sha.
Oh. Right. OK.
All right. This is the first big moment.
OK. Let's see if we can do it. Let's get ready, let's do a boop. Oh, doesn't look it. Didn't like it because oh, because I typoed. Like a doofus. Let's try that one more time, no typos.
If this was a live show
Ooooh, I think things happened.
Ask if we could use B to A. And an answer is sometimes. It's another browsers and also Node 16 have B to A.
I saw a whole thing why you shouldn't in Node, doesn't it do double encoding?
B to A is unicode, and it's a rabbit hole as deep. We do not want to go there. I've run into this problem before today because now we will see if we can actually run it together.
I'm just excited this worked. How great is that?
It worked. I'm sorry, I missed it. I had to put off the hat, it helped me a little bit.
Well, I appreciate you suffering for the show. Also, thank you for the sub, Luke, I appreciate that. Yeah, B to A is base 64 encoding and decoding built into the browser. Handled the same way in Node, but it's not UTF8, which is very confusing when you're trying to do anything that's not standard Latin character.
All right. I would like to show you plugins now.
I would like to see plugins now.
Octokit itself only does the bare minimum, grabs all relevant APIs, everything else we put into plugins and if you have, you know, like a use case like now you basically want to update and file, it is likely that you're not the first and last person who has this use case. So we created a plugin for it. And I can send you, I don't know how to send to you. I'll put it in the chat. Actually, I can paste the link into the code directly.
You sure can, yeah. So I will let's get this thing out of the chat here. All right. So I have create or update text file.
Yep. And we have GitHub actions outage right now, of course, of all days and times. Anyway, so this is a plugin and you'll see Node user, which is what we want and install the package, not Octokit call, we don't need that. But you can install the Octokit
Yeah. You have the Octokit package, which is build an Octokit core.
I'm installing create or update text file. And now, this, is this going to just kind of make what we did easier or
Yeah, it's basically, like a function that contains the getting and updating logic for you and the whole buffer management and stuff. And also, the abstraction of how to use the on code and decoding. And there's a lot more pain with the API which we don't need to get into. But, basically, what you now have to do is register the plugin after you import it.
OK. I'm going to no, not
All the Octokit packages destructure.
Wait. TFTS here somewhere.
In the usage there. In the table.
Oh. Right here. Yep. So we've got
Basically, the second line. So the two methods are exposing, only will need one.
Which one we're going to create?
The first one, yeah. The second one is basically stateless. So you can use it within other plugins.
So the plugins are composable but this is what we want right now. Right now, you have to define your own structure, const my Octokit. And then, = Octokit.plugin. And then, you pass in the plugin directly as parameter. And you can also add a .default, which is kind of best practice. You can pass in default options and you want to set your user agent. Should be type ahead. It's an object. And then, user agent and just set it to Jason's nose booper or something like that. So, this is now sent as the user agent in all requests and GitHub knows who you are. And asks you to do it. Defaults to Octokit, but ideally, you should set it to your own if you build something.
OK. And now I just switch this here.
And so, effectively, what I've done we've taken Octokit and we've extended it with this plugin, updated some defaults and now we're just instantiating Octokit with these updated settings?
OK. That makes sense. And so now, when we do this stuff, we are, like, I don't have to change any of the other code.
But basically, we can remove all of the code or common it out or whatever you want.
Yeah, let's do this here.
And you should be able.
All right. Let's go from here. I'm going to do what like response equals await Octokit.
It should have the response I had for the new plugin exactly. And then, I think it's more or less the same parameters that you have in lines 33 to 48. .
We're going to be missing some pieces.
The sha you can remove. This is something the plugin does for us. It can send multiple requests for you and you don't know, right? And content is now a function. Or you can set it to a function and the function is called with an object which has a content property. I think it's content.
Oh, does it? Oh, yes, content and it has an exist.
Yeah. Exist basically tells you to file exists, or not, so you can treat it differently. But we don't need that. Now, we just return this. Yeah. And that should
We return it straight up.
Straight up. There is no sha, no buffer, that's it.
That's significantly easier. And I guess I don't really need to keep this response.
You can log it out because it has different information. You can see. I think it'll tell you if it actually updated a file because it doesn't update a file if it doesn't have to. It does magic for you.
Do I want to log the entire response or just the data?
I hope the API is not down or something.
Doesn't like, I see, not a legal HTTP header value. I used a non
Oh. Does this need to be alphanumeric or something?
I don't know. That should be OK.
There we go, that's better. So we've got nose booper and if I go back to my profile, we have now booped twice. Hey, hey, it's doing what we want. And that is significantly cleaner than what we were doing before. It's one nice well contained function as opposed to load the data, change the data, send the data with all of these other pieces that we just needed to know that as opposed to this which feels a little more predictable, I guess. Like we want to load that content and make a change to it.
Yep. OK. I would say the next problem here is, we all want to boop your nose, it shouldn't just be you, right? But you're the only one that has access.
It is good that way. What I would like to do next is to show you the OAuth device flow which permits you to awe authenticity case as the current user and the OAuth will use the authentication. And there's no redirect in it, which is perfect for CLI applications. This is a new flaw that GitHub published in the past year.
I'm getting rid of my auth?
Basically, we will no longer need the token, yes.
And if I try to run it now, just to clarify it's going to say you can't do that? You're not authorized?
Yeah, it'll give you a 401 or if the repository was private, it would give you a 404, it would not expose it as repository exists.
You need to install maybe open this, added in line 6. Oops.
Opening the wrong window. Let's open it in this one.
There's all kind of strategies and this is one of them.
And we wanted the apps.
For OAuth apps. Yeah, the usage for OAuth apps?
And device auth is the one we want?
Exactly. This is the method we will import from the package and this is the authentication options. Basically, instead of passing string as a token, this is what you set it off when you use it together with Octokit. Install the package and import it like that.
OK. What's get this installed. OK. That's installed, now we've got it, I've got this here. Do I do another .plugin? Or a .plugins method, as well? Is there a pattern for adding more?
No, it's a little bit different. You in line 14, you now set a new property called auth strategy. So basically, now, before it was a token strategy, which was the most simple, now we get a more sophisticated one, create auth advice. And you can uncomment the auth parameter and set it to the parameter you saw in the readme of the strategy. Because we have to set a few things yeah. Basically.
Wait, aisle not, so is this create OAuth device?
I keep losing you here. Yeah, starting with types, these are the options you will need.
OK. And so I'm going to copy.
We will mostly use it as these.
And I put this right in here?
Nope. No, you set it separately. You want to be able to set a strategy separate from the authentication options. Auth comment and auth object with the same properties.
Oh, OK. I understand.
Sorry, yeah. It's a little complicated.
Got it, so we're here. And I don't have a client ID, do I need to create it?
Yes, go back to the same place you created a token in GitHub settings developers, there is an option for OAuth apps. And this is what you're going to do. Settings and developer settings and we're going to go to OAuth apps. New.
Yep. And then, OK.
And the home page URL will for now, I think, just set to dev.
Subscription doesn't matter.
And the callback URL.
You can set it to the same or it's a great place to Rick Roll people if you don't use yeah, basically, you can now copy the client idea. It's not a sensitive credential. It's not a you can set it there. There is a different type called GitHub app. The GitHub apps also have an OAuth feature and work differently. But OAuth app is the default. And then, we need to set scopes so the token that's created has the right permissions and we need public repos similar as you created token manually before.
And then, on verification is called and can be synchronous if you prefer with information where you can instruct the person, go to this website and enter this code. And once you enter this code, I'll be able to get my token for you. It pulls in the background and waits for you to enter this code. Once you did and grant it access, it will be able to exchange its own one tide code for a token.
Got it, if I do that, then, I run this now and it's going to do it?
Let's hope so. Open GitHub login device and copy this code.
Yes, and open this.
You can share it. It's basically and this is your OAuth app tells you want it wants. And now, go back to your terminal.
And it did the thing.
Yeah, it did the thing. I thought it looked like an error.
This did the thing for me because I own my repo, but is it going to do the thing for you, for example, because if you log in, you wouldn't have access to write to this.
Exactly. This is kind of a segue to the next section. What I would suggest we do is if you are you, if you can, you just boop your nose because why should you not? But if I am me, I create an issue, basically, asking, hey, may I boop your nose?
OK. I was thinking of making this way more of a free for all. A serverless function and let people do whatever they want.
Basically, I think what we can do is automatically, I just create an issue and your nose gets automatically booped because there's no approval process because your nose can take it. But the CLI will create an issue and then the second automation maybe work on if we still have time will be handle new issues and
Oh, OK. I'm ready, let's do it.
Let's go back to the code.
And basically, leave it as it is, that's fine. And go down where we called create our update text file function. So now, wrap this into a try error, the whole thing. What we expect to happen is that this will fail with a 401 error. We can check if error status is not 401, then, just it's unexpected. Like an early exit thing. Throw error. And with that out of the way we can create an issue.
Basically, a way perfect.
I'm learning. I'm learning. So request and now, I'm going to guess if I start typing issue, it's going to let me post an issue.
And this is the one I want?
This is very intuitive. And that is not true of most REST APIs, so that makes me really happy. Now, I can do the store, I could put this in a config object we could've shared around, but who cares. And then, what happens next? We've got
You can do a control space. We want a title P and maybe a body. May I boop or please boop or something? You can set a body because it's nice and you can set it to some markdown. Underscore something. So when would you use OAuth app instead of GitHub, it's also another rabbit hole, OAuth use scopes for permissions and GitHub apps don't have the concepts of scopes, it's working all different and I hope we will get to it in a moment and I can explain the differences.
OK. Do I need any other pieces here?
No, I would kind of log out and say, issue created at and then data.HTML. Did I do something?
No, that was me hitting buttons. So we'll do console log
Issue created. Yes, exactly. I think actually line 53, let's replace the huge console log and do something like just console log now boops successfully, that's all we care about, I think.
OK. So now, as me, when I run this, it'll say, oh, I have to enter my code.
Yeah, it'll be the same as before. We can publish this to NPM and have our visitors, viewers run this thing and see what happens.
So I've done booped but if I run this without my token, oh, it's still going to have me run as me.
Yeah. You will need another, basically, if you do you have another test user? You can sign in the private window?
Great question. Let's find out.
If not, we don't have to. I think we can ask the viewers to do it in a moment when you publish.
I feel like I do have a test user that I can use. Let's see what's in one password. There's probably a throwaway user in here. Let's find out. Any of you throw aways? No, these all look like things I probably don't want to log into.
Oh, there was a throwaway right at the top. Let's use this one. Throw away 00. Uh oh. Guess not, don't have that code. Don't know when
It's not two factor authentication, it's sending you an email because you have not logged in in a while. But let's not open your email and find this unless you want to.
No, let's take test in production.
Yes. Of course. This is how we roll. We are experts, we never make mistakes.
That's me, never made a mistake in my life.
What could possibly go wrong? Let's do the following.
OK. So on the top of, your CLI.js, we have to add the (inaudible).
Oh, yeah. It's the hash bang, which is why it's called a shebang and vin node? I was close, almost there.
Because we want to publish this as a binary now, right? In your packets Jason now, can you open packet JSON file, what you call a bin key.
You know, below the description bin for binary.
I heard it as binkie. What is a bin key?
This is how package can define binaries and you can execute. This is basically what we get when we run NPX, which we want to do in a moment. So boop Jason's nose, that's fine. Let's change the version from 1.00 to 1.00 alpha.1. Ideally, I want to use this later.
OK. I think if we didn't forget anything, that is it. Let's do NPM publish because why not?
OK, this is one of my favorite things about NPM package management. We just wrote a couple things in here and now, we're just going to, good bye. Let me find that really quick. Because you don't want to give away that security. That was my I think it might be in my one password, one moment, please, everyone, let me get it off screen.
I should have warned you about this.
No worries at all. This is, you know, this is, if this is the hardest part of the story, I'm in pretty good mood here. We just published boop Jason's nose.
I'm trying to run it and see.
If y'all want to try this, you can go to your, let's just do a downloads and I'll make a new directory called test and we'll move into test and NPM and net.
You don't need to do any of
You don't need to do any of this.
Oh, you're just using the NPX, aren't you?
Well, I'm trying to do this right now. Let me post it. Yep, folks, just try NPX boop Jason's nose. And let's boop some noses and hope it works.
Beauty. Beauty. OK. So let's see if it works off my machine.
Incorrect password. What is going on here?
OK. So I'm me. So I've done been booped. Let's see if this is going well for everybody else.
It tells me not found for some reason. And I don't know OK. Can you
Did I typo something in here?
Let's go back to the code. I don't see any issues open. Got a 404, boop incoming. Oh, you can do it, come on little booper. What did we get wrong here? We did something.
Oh, man, that's disappointing. I was hoping this would just work.
So probably do we need to give it access. Oh, it needs issue access to create an issue.
I don't think so, there's no dedicated scope for that.
Are you sure? Let me check. Because I could have sworn that to create issues on somebody's behalf, you needed to give an issues where is it? Somewhere in here.
So, doing cool things in GitHub world.
Yeah, let's do it. The error that we're getting, oh, somebody just hacked it invalid character error.
So the invalid error, this is now the B to AO to B. It's already fixed but actually, GitHub actions outage prevents me from publishing my fix because I'm automating too much. And that's the downside of it. So use node 14, please, and you shouldn't get the error.
Does that mean, I need to also capture the error here? From Octokit?
No. It will throw an error. You can do for the simplest thing would be line 69, add catch and then console lock.
Yeah. Dot catch. Using, you can directly pass in console lock as a call back, you know, as a shortcut. But that's OK.
Nice. Nice. We don't need to publish it and test it and publish it and test it. So let's try to maybe throw an error online 45 to get to catch one. We have to do it above line 45, because we have to boop your nose. That's probably not a problem.
Throw an error. And that needs to be a 401, right?
Can I fake it like that?
No, you will need to do const error equals new error with a string message. And then, set the error.status to 401. = 401 and throw error.
401 and throw error.
And so now, it should
That should do what we want, it should give us and hit our data issue. Let's just give this a try.
I don't know what's going on there. Issues
OK. I've got this. Going over to log in device. I wish I could keep that.
Yeah, we could probably also go back to just user token for debugging purpose.
No, it's OK. I'll just that's doing its thing. Authenticated issue created. So it just works when you're me. And then
We get an issue. Do I need to do will NPM publish handle the CH mod stuff? Do I need to make it executable or anything?
No, and we also wouldn't get that error. So hold on, let me just double check HTTP error not found. Hold on, let me debug this my side really quickly directly in the code from NPX if that works, I don't know.
We can publish this and see what happens. I can also
Hold on, maybe, maybe it gives for some reason it gives a 404 when you use the in the try block.
This gives a 404 because you don't have access?
You know what, let's remove this call check. And then, publish it, again. And remove the error above and if that doesn't work, I suggest we move on and everyone believes me this should just work. This is another thing, you can increment this and now, NPM publish. Here, grab this. OK. Alpha 2 is up and running. Boop Jason's nose.
There is sometimes a short delay, but yeah, now it's available.
Thank you for the sub, thank you both very much for the subs. I very much appreciate it. OK. So it still works for me as me. Let's see if we get some issues here.
And if y'all want to try this, you can do that right now. With NPX boop
It's your creator. Oh, OK.
Hey, hey, look at that go. Everybody get it in there. Get those boops in. Why am I frozen, again? Why, why?
Too much booping.
Yep. Over booped.
I'm going to have to restart everything when this is over. Figure out what's going on. But let's see Fadden still has a 404.
I don't know.
It might have been cached. NPM caches sometimes. Here's some issues, this is great. This makes me really happy. All right. So from here, we want to make this more automated. I don't want to have to review issues and run this command. So how do we go even a step further?
Most definitely not that. That's like the whole automation thing. I went on GitHubstatus.com still shows us that GitHub actions are gone.
They're still down, down.
Yeah. What I can promise your viewers is I will make a follow up to this and I will show you the kind of actions part. But right now, we have to skip it because even when they say it's recovering, I kept trying and all of my actions right now are not working.
And I really wanted to show how it works. I would just like to move on maybe to GitHub apps and the advantage of it is we can call some Netlify deployment there.
Yeah. Let's make it happen.
Now, what we want to do is whenever an issue is created, we want to receive a request from GitHub notifying us. And then, we want to run our Octokit code and in between, somehow, get magically the right authentication, right?
And what we will start out doing is, hold on. I have to adapt a little bit. OK. Jump to GitHub apps.
Heading over to here? Here?
GitHub settings, developer settings.
And you see GitHub apps and OAuth apps. And OAuth apps is the old school way and it's only authentication, it's nothing else. And GitHub apps are different because they are their own actors. They can act as themselves. Can create a comment and do stuff. OAuth app cannot do this. Plus the GitHub application has a much granular permission system. It cannot just access globally. You don't get this pop up and ask you to grant access to the organization. Instead you have to explicitly install it, which is really nice. Let me, I have taken some notes to make sure. We need, where are you right now?
I'm just on the register new app page. Given it a name, description and homepage URL.
Let's call it Jason's nose booper. Just to make it more explicit.
Let's go down, that is fine. This is for authentication, we don't need to worry about this right now. Post installation, don't need to worry about this, as well. This is now where we want to go. The thing is for development, locally, you want to receive requests from GitHub on your login machine and that is not easy.
I have a way.
You have a way?
Let's use your way.
OK. So if I set up a Netlify function. So if I go here and I do Netlify functions, we'll do handle issue.js and I'm going to export .handler and an async that gets the event. And then, what I can do here is let's console log the event and then, we'll for now just return status code of 200 and a body of OK. But then, I can here run Netlify dev live. And this runs my function server, oh, but I don't have my site ID set up. So let's see, let's echo node modules into a git ignore, and I'm going to add everything, good. Going to GitHub repo create using the GitHub CLI and call this one learnwithJason/boop Jason's nose. Public, yes. Then, from here, I can nit, we don't need it to do anything, we just need to create it so Netlify knows what to call it. Boop Jason's nose. We don't need a bit command, we can deploy the current directory, it's going to do the functions. So all of that set.
Your viewers all know it pretty good, don't need explanation. I definitely do a lot of that. And if you're not familiar with this, I'm definitely taking, I went very fast through this. But, check this out. If I go through here and go to .netlify functions handle issue, aisle going to get that OK status. And so this is running off my local machine. But Netlify set up a tunnel. You can try that on your on if you want to see that work. This will let us do testing. I'm going to drop this in. We don't need a secret.
No, actually, let's set a secret, but you know, set it to development. We said, you know what, if you want to use the cool things, be responsible. And now permissions, it's permissions and it's more granular. And what we need is read and write access to issues. And hold on one second. We need single write access. There's a down. You only need access to one file. Single file thing. Right access.
Read and write.
And you need to configure a path.
Very cool. Finally. And now, the next part is this is permissions what the app can do, it can receive webhooks, but which events do I want to be informed about? So when you go further down, this organization permissions we are not worried about these. User permissions, we are not worried about these. All the way down, now you can subscribe to your events. And suggests you events you can subscribe based on the permissions. We just need the issues one. And then, yes, create it and uncreate it on your own account. So if that basically means only you can install it and not everyone else. And just as a security measurement because I don't know what we will build here.
That makes sense.
OK. So you need to generate. Hold on one sec. Locally, the Netlify picks it up automatically, correct?
Let's collect from this screen, then. This is not sensitive. You can create the .env file. And then, also webhooks secrets.
And am I going to need it?
And this is something you shouldn't show, but it's 12,000 characters, I don't know how people can copy it.
So when I generate this private key
It will download a file.
You hackers, you dirty hackers.
So there is a trick here, because you have to open the .pmv file and it has line breaks and what you have to do is replace the line breaks with backslash N.
I can see your file. Maybe put in double quotes around it just to be sure. I hope this will work.
Okay. So what I'm going to do, then, because this is a big, long thing. This is what it looks like, an RSA private key. It's very, very long I'm not going to show the whole thing. But that's what you're looking at. And each of these is a line break that got replaced.
With that, I'm going to add a line break at the end, save this, close it and we are done with secret keys. And so, what I will have to do, though, is stop and restart this to pick up the new thing, which I am going to have to update the webhook URL. You can see here that it picked up those keys and now, now those are there. I'm going to be able to copy/paste this part. And head back to our GitHub app setup.
Which is where webhook secret. I need to edit this somehow.
There it was. Webhook URL. Yeah.
Whoops. Just that part. OK. I'm going to save the changes.
So you get a new URL every time you restart? Or
Yeah. It's, we won't have to restart that often. But when you change environment variables, you do.
If we keep restarting, there's another way I want to show you to do it.
So now, what I think will happen, or we have to install this, don't we?
You have to install the application. And installing an application, like this is kind of tricky with the wording. Because it is really just a means of authorization now. But installing you say, basically, you can access but only this single repository. So even if you have permission to edit the readme or create issues, you will only be able to do this on this repository. So that is great.
If I create an issue, I should see a notification, right?
You should see a notification basically, tells you your app was installed.
Here's the details. So now, if somebody wants to run that NPX boop Jason's nose, we should see another notification show up?
Yeah, well shall I create an issue?
Yeah, go ahead.
Issue created. There it is.
And here it is. Oh, bunch of them came in. Looking here, we've got here's the body. And we can see like the labels URL, comments URL.
Yeah, these webhooks are pretty big.
Yeah, what's great about this is shows here's the title of the issue, here's the person who sent it. This is pretty great. I'm pretty fond of this whole general setup. Pretty convenient to be able to do that. And from here, then, we have the issue. So we I assume we can just say I have an issue, I would like to update the boop count?
Hold on, can I play some code?
We have like 7 minutes left.
There is so much more I wanted to show you, folks. So much more. OK. So
Oh, we got a lot in here. So let me pin to where you are. And you can kind of walk us through.
OK. So first of all, I wanted to create a plugin out of the code that you already did. Like a custom Octokit plugin called boop Jason's nose, but we didn't create that yet. So we might just need to copy the code that we have right now to directly boop you or create this plugin in a moment. We use from the Octokit package, we use new export called app.
And this is now the app SDK specific to GitHub apps. And it does a lot of things. We pass in the credentials here you recognize which is app ID and private key. And the webhook secret. And then, you have an API which is app.webhooks on. And it has a lot of I don't know if you, if I type it
I think I have to. Yeah. So check run completed.
You see, there's a ton of things there. Right?
And we can see here that we're using issues.open, which is what I would have expected.
Exactly. Oh, sorry. And then, within this callback, you get an Octokit instance.
And the payload. And for the payload and the Octokit, you get all of the intellisense. It's authenticated for you. There's a whole lot of stuff that needs to happen. It receives the webhook and the webhook includes installation information. And using the installation ID and the app ID and private key, it requests an authentication token so it can act as the app in the score of the installation and does all of that for you. So you don't need to worry about it.
And so what we're doing here is registering a handler for issues.open. And here, we verify and when you say receive, it is received, quote, unquote, triggers any handlers for the given events?
Exactly. If this is the handler you have before. And this where you can kind of separate the logic to receive it from the logic where you act on the events that you care about because you could kind of put the whole app webhooks code in another file. And you could make it work easily with deployment or traditional server deployment. Also, the code would be the same. Only the way you receive an app on the webhook would be different. That is quite nice, I think.
Now, that we've got this, we need to bring over this code here. So basically, our whole, our whole run function we copy/paste it. I can bring in here.
I put that in the wrong place. Let's put it above.
That doesn't matter. And let's just pass in an Octokit instance there. OK. Octokit and let's remove the user request. This would fail. Like, if the installation tries to access this, it will fail and tells you I'm not a user.
OK. So we get the readme. Don't want to do this part anymore.
We want to create or update now. It'll work, it should work. We shouldn't fail.
So we can get rid of these pieces, let's simplify this as much as we can here. OK. So then we've got this boop Jason's nose and instead we would.
Exactly. Which really is how the Octokit's code for the plugin would look like, too, actually. It's not harder than what you just did. But
Yeah, that's perfect. So then, what we need to do is bring over the other pieces that we are importing.
No. Why? You already get an instance, right?
Oh. We already have that instance, but I don't have the plugin. Right?
Oh, I'm sorry, you are right. That's true. The my Octokit thing, yes. You are good, Jason. Exactly. So we need to add this plugin and then, online 17. This is where we tell you don't use the default constructer, instead, use my custom one, which can have a custom user agent and custom plugins, as well. So in theory, this would just work. So let's hope.
OK. This has been successfully reloaded. If you want to run this one more time, what we should see happen is
Run the thing
Create an issue?
I'm getting nervous, Jason.
Bump boop counter is undefined.
Oh, the function, we didn't copy the boop, the function.
OK. OK. We can fix that. That's a problem we can fix quickly. Let's grab, where is bump boop counter? Where is it? No.
All the way at the bottom hidden from view. We'll take this one and we'll drop that. All right. One more time everybody. One more issue. This one's going to work. Show me potato salad.
OK. Let's go. Look at it go! All right. It did the thing. Let's go look at this. We've got our one more how many boops we got? Seven? Somebody boop us. Another one.
I created an issue, let's reload. Whoa!.
Chaos. I love it. I am so into this. This is so much fun, y'all. So yeah. OK.
We're running out of time.
We don't have time to add any more features. But from here, if somebody wants to do more. What would you, where should they go next? What should the next step be?
Go to this issue, again, that I created for today's show.
So go subscribe to this issue. Because I want to, basically, pick it up and fill in the gaps. So what we did today is when you look at part one, we did most of it, we didn't create our own plugin, but that's it. And we skipped over part two, which is all GitHub actions. I was optimistic how much we could cover. In part 3, we didn't do the OAuth app. I wanted to do the actual OAuth flow, as well, so we're basically users can use your OAuth app or GitHub app to authenticate as themselves. And then, a new issue would be created not as the app but as themselves. Because this is a use case you often times have, as well. Right?
Just to show that. And it's a really slick, as well. And I will do that in myself. And then, the last thing is how to use Octokit in the browser, like, how can you do a login with GitHub button, which is what you would use the OAuth flow for. And how you basically load the same Octokit packages directly in your browser. Like all of the Octokit packages in the usage have instructions how to install or import all of these packages directly into your browser from SkyPack, shout out to SkyPack, they made these ES modules available and work in the browser, at least the ones that are meant to be used in the browser.
OK. So what I'm doing here is I'm just adding the nv to the gitignore.
Did that not work? Where are you at? I'll tell you what, I'm going to have to do this at the end. Netlify, this is a bug, we don't put a line return. Which is the I'm not going to deploy this right now. The webhook will be deployed. What I have to do is get it up to netlify which will just take pushing and go update the settings on the app wherever that is. Once those are updated, then the issues will start triggering, again. With that, Gregor, thank you so much for spending time today. Make sure you go and follow Gregor on Twitter and you will get infinite insights into amazing things around Octokit and so many other things. We also, as always, have had live captioning going all day, we've had Diane with us. Thank you so much for being here. That's through White Coat Captioning. White Coat Captioning is here because of the generous support of our sponsors, netlify, Fauna, AuthoO. Make sure you look at the schedule. The schedule has a lot of good stuff coming up. Including stuff I forgot to talk about. But we're going to learn Kotlin JS. Which is going to be funny I'm not good with typed languages and I have to use an entirely new IDE, find out more on Thursday. It's going to be chaos. Go join the Google calendar to make sure you get updated from that. With that, thank you all so much for hanging out for episode number 200 of Learn with Jason. Here's to at least two or three more, right? We're going to find someone to raid, Gregor, thanks for hanging out. Chat, as always, thanks for hanging out with us. We will see you next time.
Thank you, Jason. Thanks, everyone.