Automating tedious GitHub tasks with Serverless (part 1)
What is Serverless?
The Serverless framework operates as a very powerful wrapper around popular cloud providers. It allows you to quickly develop, test and deploy your product to the cloud.
I use it for most of my projects now, as it completely abstracts all the infrastructure and allows me to focus on my code.
Here are a few examples of what you can do with it:
- Create functions that respond to REST endpoints
- Schedule functions to run at a given time/interval
- Run a function whenever a file is added to an S3 bucket
- And much more
Our use case
I’ve been organizing little challenges on GitHub for Hacktoberfest, and have found myself having to label issues, and close/merge a lot of pull requests; that is why I decided to automate it as much as possible.
What we’ll try to reproduce together is the following scenario:
- Someone wants a challenge to be assigned to him ;
- He replies
/request
to the issue ; - Label the issue as
assigned
andassignee:johndoe
if it wasn’t already assigned to anyone ; - Reply to the user indicating him whether he’s been assigned or not.
Setting up your environment
Before diving in the actual code, you’ll have to set up your Serverless environment.
You first need to install Serverless as a global dependency:
npm install -g serverless
This will install the serverless
binary globally (and also create an sls
alias)
Next, you’ll need to install the AWS CLI tool, that will later be used by Serverless to manage all your resources.
You could use another cloud provider, but I’ll only be covering AWS as this is the one I’m the most familiar with.
Once installed, you’ll have to create an AWS user for Serverless and generate keys for it. Please refer to this section of the official guide. I usually name this user serverless-[whatever]
, for this article, I’ll name it serverless-admin
.
Now that you have your keys, we can configure Serverless to access AWS on your behalf:
serverless configure credentials \
--provider aws \
--key <access-key-id> \
--secret <secret-access-key> \
--profile serverless-admin
This creates an ~/.aws/credentials
file containing your keys under a profile named serverless-admin
.
At this point, you should have a fully-functional Serverless setup.
Initializing your project
Create an empty Serverless project named github-automation-project
, using the aws-nodejs
template:
serverless create \
--template aws-nodejs \
--path github-automation-project
There are quite a lot of templates available, for various languages (TypeScript, Python, Java, etc.), and various platforms.
Inside github-automation-project/
you’ll find a serverless.yml
file. This file contains the whole definition of your project, what are your functions, how they can be triggered, etc.
If you’re curious, here is a reference of all the things you can set up in this file.
For now, you only have two things to change:
- Edit the
provider
section so that it uses the correct AWS user - Add an
http
event to your function so that it can be triggered via aPOST
request on the/webhook
endpoint:
Another useful file is handler.js
; it contains all the logic of your function. For now, it only returns a JSON payload containing a message and the inputevent
object:
This event
object will later come very handy, as it contains the request payload, the request headers, and various AWS related metadata.
Running your functions
From here, you have two options:
- Redeploy every time you want to test a change (actually not that slow)
- Run your functions locally (this is my favorite solution)
1. Locally
To run your functions locally, you don’t need much:
# Initialize a package.json
npm init --yes# Install the serverless-offline plugin
npm install --save-dev serverless-offline
Once you have installed the serverless-offline
plugin, add this two lines at the root of your serverless.yml
file (do not nest it under any rule):
plugins:
- serverless-offline
That’s all there is to do! Let’s test it out:
serverless offline start# In another terminal
curl -X POST http://localhost:3000/webhook
This should return the JSON payload we discussed above.
2. In the Cloud
You can deploy your functions to the Cloud this way.
# Deploy all your stack
serverless deploy # Deploy a single function (a lot faster)
serverless deploy --function hello
After a short while, it will show you a summary of what’s been deployed, including the endpoint on which you can reach your function.
It will look somewhat like this:
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (19.45 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............................
Serverless: Stack update finished...
Service Information
service: github-automation-project
stage: dev
region: us-east-1
stack: github-automation-project-dev
api keys:
None
endpoints:
POST - https://<random-id>.execute-api.us-east-1.amazonaws.com/dev/webhooks
functions:
hello: github-automation-project-dev-hello
Once again, that’s all there is to it! You can test it out with a little cURL
:
curl -X POST https://<random-id>.execute-api.us-east-1.amazonaws.com/dev/webhook
Modifying our function’s behavior
We’ll see in part 2 how to connect our function to GitHub. For now, familiarize yourself a bit with Serverless, and look at what is available in the event
object.
For example, if you wanted your function to return the sum of the two numbers a
and b
passed as JSON, you could modify your function that way:
curl -d '{"a":123,"b":77}' -X POST http://localhost:3000/webhook=> { "result": 200 }
There’s no error handling, no payload validation, but that’s fine, we’ll discard all of this the next part.
In the second part, we’ll implement the actual logic of the lambda, and I’ll give you a few Serverless-related tips.