Edward Thomson

This is day 30 of my GitHub Actions Advent Calendar. If you want to see the whole list of tips as they're published, see the index.

Advent calendars usually run through Christmas, but I'm going to keep posting about GitHub Actions through the end of December. Consider it bonus content!

Yesterday we built an action that posted to a GitHub issue. It showed how easy it is to work with GitHub APIs from within an action. But actions are generally written in JavaScript, so they can easily integrate with APIs from other services. This lets you create actions that can create an issue in Jira, send a text message, or upload security information about your project.

One of the things that I want to automate is sending a tweet – this is something I do whenever I release a new version of something, to announce its availability.

And I'd love an action that I can use in a workflow to automate this. If you look in the GitHub Actions Marketplace, you can see that there is actually an action already that can tweet. Unfortunately, it's built as a docker container.

You can create actions as either a Docker container or a JavaScript application. It might seem easier to get started with a container-based action, but there are limitations: only Linux based workflows (those that run on ubuntu-latest) can use them. Using a JavaScript action ensures that your action can be used in any workflow.

So if I want to send a tweet whenever any of my projects are released – including the ones that run on macOS or Windows – then I'll need to create a JavaScript action that can send a tweet.

To do this, I'll start with the sample TypeScript action and add in the twitter npm package so that I can communicate with the Twitter API. This is quite straightforward – I'll need to take the authentication as inputs to the action and use those to create a Twitter API instance.

const twitter = new Twitter({
  consumer_key: core.getInput('consumer-key'),
  consumer_secret: core.getInput('consumer-secret'),
  access_token_key: core.getInput('access-token'),
  access_token_secret: core.getInput('access-token-secret')
})

Then I can send a tweet by calling the /statuses/update API. I'll take another input to the action that will be the status to tweet.

twitter.post(
  '/statuses/update',
  {status: core.getInput('status')},
  (error, data, response) => { if (error) throw error }
)

Once I've got that written, all I need to do is package it up – creating a distribution branch, pushing that up to GitHub, and then creating a release.

You can find the finished product at github.com/ethomson/send-tweet-action, so you can use ethomson/send-tweet-action@v1 as part of a workflow.

Now any time I push to this repository, it will send a tweet.

You can use this action to send a tweet whenever a release is created, a wiki is updated, or a deployment occurs. Or, just for fun, like what I've done.

It's easy to integrate other APIs into an action to let you automate other tools based on activities in your repository. I can't wait to see what you build!

GitHub Actions Day 29: Creating an Action

December 29, 2019  •  2:47 PM

This is day 29 of my GitHub Actions Advent Calendar. If you want to see the whole list of tips as they're published, see the index.

Advent calendars usually run through Christmas, but I'm going to keep posting about GitHub Actions through the end of December. Consider it bonus content!

So far this month we've looked at a lot of workflows for GitHub Actions. Almost all of them will run commands as part of the workflow. Some of them will also make use of actions to help set up the environment or expand the workflow.

An action is a piece of code – either a runnably docker container or a JavaScript application – that you can re-use within several workflows.

There are several actions that GitHub provides to use within your workflows. For example, the checkout action will clone your repository so that you can build and test it. The setup-node action will set up the requested version of Node.js in the PATH. And the clever github-script action lets you run JavaScript within the GitHub context.

But it's straightforward to create your own action that you can use in your workflows or share with others. GitHub provides a toolkit to help you get starting building a workflow in JavaScript or TypeScript.

I'm going to take the TypeScript sample as a start and create an action that listens for comments on an issue. And when it sees a particular comment – /octocat – then it will reply by posting a random Octocat from the Octodex.

First, we'll get some data out of the github.context, which contains information out of the workflow execution. From there, we can get the repository that we're running in, the issue that was commented on, and the body of the comment that was posted.

const context = (github as any).context
const repository = context.payload.repository
const issue = context.payload.issue
const comment = context.payload.comment.body

We also want to establish an outbound connection to GitHub – we can create an instance of OctoKit (the GitHub API), connecting back to our repository with the GITHUB_TOKEN. (Users will need to pass their GITHUB_TOKEN when they invoke the action.)

const token = process.env.GITHUB_TOKEN || ''
const octokit = new github.GitHub(token)

Now that we've got all this plumbing in place, we can add our business logic. We want to do some work when we see a comment that is /octocat (or /mona).

if (command == '/octocat' || command == '/mona') {
  // display an octocat
}

When we see that comment, we'll grab a random octocat image from a list of octocats.

const rand = Math.floor(Math.random() * octocats.length)
const octocat = octocats[rand]

Now that we have the Octocat that we want to post, we can use the Octokit API to post it, by calling issues.createComment(...). We'll post Markdown with an inline image to the Octocat that we chose at random.

octokit.issues.createComment({
  owner: repository.owner.login,
  repo: repository.name,
  issue_number: issue.number,
  body: '![' + octocat + '](' + octodex_url + '/' + octocat + ')'
})

I've put this all together and packaged it up as mona-action. When you add it to your workflow:

on: issue_comment

jobs:
  octocat:
    runs-on: ubuntu-latest
    steps:
    - uses: ethomson/mona-action@master
      env:
        GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

And you add a comment on an issue that is /octocat, you'll get a nice image as a result:

Octocat

Certainly this is a simplistic example, but this gives you an idea of how you can take advantage of the GitHub Actions API to retrieve data about the action execution within your workflow, and how to use the Octokit API to communicate back with GitHub. From there you can create an action that suits your workflow.

Want to see this action in full? It's at https://github.com/ethomson/mona-action

GitHub Actions Day 28: Repository Automation

December 28, 2019  •  2:47 PM

This is day 28 of my GitHub Actions Advent Calendar. If you want to see the whole list of tips as they're published, see the index.

Advent calendars usually run through Christmas, but I'm going to keep posting about GitHub Actions through the end of December. Consider it bonus content!

This month we've looked at a lot of different ways to build and test your code when a pull request is opened, or when a pull request is merged into the master branch. And we've looked at different ways to deploy your code to a package registry or to a cloud provider.

But GitHub Actions provides triggers for any operation that happens in your repository, not just the ones start CI/CD workflows. Here's some simple examples that display information about the event and are a good basis to build on.

Issue Comment

The issue_comment event is triggered whenever someone adds a comment on an issue or a pull request. The payload provides information about the issue and the comment that was added.

Here's a workflow that uses jq to get the issue comment out of the payload.

When this workflow runs it will print the comment.

Issue Comment

Wiki Changed

The gollum event is triggered whenever someone changes the repository's wiki. The payload provides information about the wiki pages that were changed.

Here's a workflow that uses jq to get the wiki information out of the payload.

When this workflow runs it will print the URL to the first wiki page that was changed.

Issue Comment

Scheduled

You don't have to run a workflow based on any activity in your repository at all; you can also run workflows on a schedule. When you use the schedule trigger, you can specify when the workflow should run using crontab(5) syntax.

on:
  schedule:
  - cron: 0 2 1 * *

In this case, the workflow will run at 2:00 AM every morning on the first day of every month (UTC). cron syntax is tricky, so be sure to consult the documentation if you haven't worked with it before.

Schedule

Although GitHub Actions provides great functionality for CI/CD workflows, there are triggers for almost every operation in your repository that you can use to automate functionality. With the last few days of the month, we'll look at a more concrete example.

GitHub Actions Day 27: Deploy to Cloud

December 27, 2019  •  2:47 PM

This is day 27 of my GitHub Actions Advent Calendar. If you want to see the whole list of tips as they're published, see the index.

Advent calendars usually run through Christmas, but I'm going to keep posting about GitHub Actions through the end of December. Consider it bonus content!

So far this month, we've looked at a lot of ways to build and test your software. And we've looked at a few ways to package up your software. But how do you actually get it running in your cloud service provider?

As my buddy Damian says, "friends don't let friends right-click publish". Instead, a best practice is to script your deployments so that they're reliable and reproducible.

Several cloud providers have created actions to help you deploy to their services as part of your workflow.

AWS

The AWS team has created several actions that will help you deploy. They've also created a sample workflow to show you how to push an image to Amazon Elastic Container Registry (ECR), update an Elastic Container Service (ECS) definition and then deploy that definition.

Azure

The Azure team has also been busy creating actions. And they also get the prize for creating the most complete set of starter workflows that you can use as examples and starting points. Want to deploy a serverless Function App? They've got examples for that. Deploying a database? Sure, they've got examples for that, too. Kubernetes? You bet. It seems that whatever you want to deploy to Azure, they have a sample workflow for.

Google Cloud Platform

The Google Cloud team has also created actions to help you deploy to Google Cloud Platform. They've got an action to set up the Google Cloud SDK so that you can use the gcloud command line application to script your deploys. Better yet – if you need help, you can drop by their slack.

And Anywhere Else

Of course, since it's easy to run scripts as a workflow, you can set up a workflow that will deploy wherever you want – whether that's using the AWS, Azure or Google Cloud actions to help you, or writing a workflow that scps your code up to your own servers.

GitHub Actions Day 26: Self-Hosted Runners

December 26, 2019  •  2:47 PM

This is day 26 of my GitHub Actions Advent Calendar. If you want to see the whole list of tips as they're published, see the index.

Advent calendars usually run through Christmas, but I'm going to keep posting about GitHub Actions through the end of December. Consider it bonus content!

This month I've talked a lot about the software installed on the runners that GitHub provides for running your workflows, and how to install new software.

But what if you wanted to customize your own runner instead of using the ones that GitHub Actions provides? You can use the GitHub Actions self-hosted runner to run workflows on any infrastructure that you have, whether it's an on-premises machine or a runner that you configure in the cloud.

Being able to set up a self-hosted runner is important if you have incredibly custom dependencies – some people still need to use software that has heavy license dependencies like hardware dongles. Or you might want to run a build on an ARM device that you have, instead of the GitHub Actions runners, which are amd64.

More commonly, you might want to talk to machines within your firewall to run tests against them. Or do a deployment step to servers within your firewall.

To set up a self-hosted runner, you first need to download the software to the machine you want to configure. To go the Settings tab in your repository, then select Actions in the left hand menu. There you can configure your self-hosted runners.

Self-Hosted Runners

Just click "Add Runner", and follow the instructions.

Setup

Once you've set up and started the self-hosted runner, it will start polling GitHub to look for workflow runs. You can configure a workflow to run on your self-hosted runner by setting the runs-on to self-hosted. Here I have a simple workflow that will run on my laptop, it just runs uname -a.

When I trigger this workflow, you can see that it accepts the job and then runs in.

Execution

And in GitHub itself, you can see the results of the run.

Results

It's easy to get workflows running on the GitHub Actions runners, or on a self-hosted runner within your network. And the GitHub Actions self-hosted runner will poll GitHub so that you don't need a hole in your firewall to be able to run workflows on machines located inside your firewall.