Last month, GitHub announced new improved CI/CD with GitHub Actions. Since then, I have experimented with the new Action workflows and created an Action that wraps the faas-cli so that you can easily build and deploy your functions directly from GitHub.


My initial interactions with the new GitHub Actions were not great, they really meant it when they said “public beta”. BUT!!! Having said that, there is clearly a lot of promise in GitHub Actions. People seem to love it. The integration into the UI is great. And, while the first week was tough, the documentation and starter workflows have seen a lot of improvements. Today, I want to introduce the OpenFaaS Actions that I wrote and show you how to set up a simple “build & deploy” workflow for your OpenFaaS functions.

Back at the beginning of the year, I wrote a short walk-through on the OpenFaaS blog showing how to cleanly split Python functions into multiple modules. Today, we will extend that example repo with a GitHub Action Workflow to make sure that the latest version of the function is available to deploy.

TL;DR

If you are a GitHub Actions expert already and just want to see the workflow files:

name: CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Login to Docker Hub
      uses: actions/docker/login@master
      env:
        DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
        DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
    - uses: LucasRoesler/openfaas-action/template_store_pull@v0.1.0
      name: Fetch Template
      with:
        name: "python3-flask"
    - uses: LucasRoesler/openfaas-action/build@v0.1.0
      name: Build
      with:
        path: "wordcount.yml"
        tag: "sha"
    - uses: LucasRoesler/openfaas-action/push@v0.1.0
      name: Push image
      if: "success() && github.ref == 'refs/heads/master' "
      with:
        path: "wordcount.yml"
        tag: "sha"

The OpenFaaS Actions

Currently, the OpenFaaS Action repo defines 6 common actions that expose the faas-cli commands of the same name:

  1. login - login to an OpenFaaS cluster
  2. build - build the functions in the named function stack configuration
  3. shrinkwrap - assemble the function build context, merging the function implementation with the template, but don’t build it
  4. template pull - pull the function templates from the named template repo
  5. template store pull - pull the named function template from the template store
  6. push - push the built function docker image to a docker registry

It also provides a generic cli action that allows you to pass any command to the faas-cli. This is the escape hatch that allows you to use the faas-cli directly instead of via the pre-defined actions described above.

What is an Action?

Each step in the above workflow “uses” a different GitHub Action. These are predefined … well… actions that can be performed, in this cases

  1. clone the project,
  2. login to docker hub,
  3. fetch the python function template,
  4. build the function, and
  5. push the function image.

Each of these actions is effectively a Docker image, a command, and some optional metadata. For example, the template pull Action is just this bit of yaml

name: 'OpenFaaS CLI Template Repo Pull Action'
description: 'Pull templates from a remote repository'
author: 'Lucas Roesler'
inputs:
  repository_url:
    description: 'repository to pull templates from, defaults to the core templates'
    required: false
runs:
  using: 'docker'
  image: 'docker://openfaas/faas-cli:0.9.2'
  args:
    - 'faas-cli'
    - 'template'
    - 'pull'
    - '${{ inputs.repository_url }}'

In which I specify that it runs the faas-cli template pull command in the prebuilt openfaas/faas-cli:0.9.2 image.

Setup the workflow

Configure you repo secrets

Before we dive into the GitHub Action Workflow, we need to configure two secrets for the repo DOCKER_USERNAME and DOCKER_PASSWORD. This is done in the Secrets section of the Settings tab, like this:

Setup docker credentials as repository secrets Setup docker credentials as repository secrets

Write the workflow

In your GitHub repo, select the Actions tab to get started. If you do not have any actions yet, it will look just like this

Actions welcome screen Actions welcome screen
If you have already setup some actions, it will show the previous workflow runs. But, as of Sept 26 GitHub has add a “New Workflow” button that brings you back to that welcome screen.

This is a custom workflow, so select “Setup a workflow yourself”, which brings you to an editor

Actions text editor Actions text editor

Now we simply need to update the steps to use the new OpenFaaS Actions.

For this example, we want the workflow to

  1. “clone the project”,

    This step is the simplest and uses an the official GitHub action

     - uses: actions/checkout@v1
    
  2. “login to docker hub”,

    this again uses an official Action, but requires some secret parameters which are passed via the env, which we configure in the next section,

     - name: Login to Docker Hub
       uses: actions/docker/login@master
       env:
         DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
         DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
    
  3. “fetch the python function template”,

    This is the first OpenFaaS step and calls the faas-cli template store pull command, but is as simple as the docker login. This time we need to specify the name of the template we want to pull. Unlike the docker login action, these are passed by the with section because the OpenFaaS action has predefined input parameters

     - uses: LucasRoesler/openfaas-action/template_store_pull@v0.1.0
       name: Fetch Template
       with:
         name: "python3-flask"
    

    Before going further, the action name specifies a folder/action template_store_pull in my action repo LucasRoesler/openfaas-action at a specific SHA/tag v0.1.0. All actions have this structure, which makes it easy to find and review what the code is actually going to do before you use it or to find inspiration for your own actions.

  4. “build the function”,

    The faas-cli build action requires the relative path to the stack YAML file and it also accepts the tag flag for the build command:

    Override latest tag on function Docker image, accepts ‘latest’, ‘sha’, or ‘branch’ (default “latest”)

     - uses: LucasRoesler/openfaas-action/build@v0.1.0
       name: Build
       with:
         path: "wordcount.yml"
         tag: "sha"
    
  5. “push the function image”,

    Finally, with the the functions built, we can also push them to the Docker Hub using the faas-cli push. This step also uses the step conditional statement to ensure that it only runs when the build step was success AND when the workflow is running on the master branch.

     - uses: LucasRoesler/openfaas-action/push@v0.1.0
       name: Push image
       if: "success() && github.ref == 'refs/heads/master' "
       with:
         path: "wordcount.yml"
         tag: "sha"
    

Altogether, the workflow should look like

name: CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Login to Docker Hub
      uses: actions/docker/login@master
      env:
        DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
        DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
    - uses: LucasRoesler/openfaas-action/template_store_pull@v0.1.0
      name: Fetch Template
      with:
        name: "python3-flask"
    - uses: LucasRoesler/openfaas-action/build@v0.1.0
      name: Build
      with:
        path: "wordcount.yml"
        tag: "sha"
    - uses: LucasRoesler/openfaas-action/push@v0.1.0
      name: Push image
      if: "success() && github.ref == 'refs/heads/master' "
      with:
        path: "wordcount.yml"
        tag: "sha"

Save and Run the Workflow

Save the workflow using the “Start Commit” button, if you are feeling brave you can commit the workflow directly to master, but you can also easily test the workflow from any branch

Committing the workflow Committing the workflow

Once it is committed, you can see the workflow results and logs in the Actions tab

Workflow logs Workflow logs

Next Steps

This is just an experiment while GitHub Actions is in beta. But the next step will of course be a GitHub Action to keep the OpenFaaS action up-to-date with the faas-cli releases.

Additionally, there is some interesting work currently going on that should help simplify this workflow even more. We want to add the ability to save the template dependencies to the project repository so that we can pull all of the dependencies with a single command faas-cli template pull stack. For a standard OpenFaaS project using the default stack.yml, the basic workflow will be completely standardized and require almost no configuration, like this

name: CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Login to Docker Hub
      uses: actions/docker/login@master
      env:
        DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
        DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
    - uses: LucasRoesler/openfaas-action/template_pull_stack@v0.1.0
      name: Fetch Template
    - uses: LucasRoesler/openfaas-action/build@v0.1.0
      name: Build
      with:
        tag: "sha"
    - uses: LucasRoesler/openfaas-action/push@v0.1.0
      name: Push image
      if: "success() && github.ref == 'refs/heads/master'"
      with:
        tag: "sha"

Ultimately, I want to make the OpenFaaS experience on GitHub as easy as possible.

If you have any questions, feedback, or you want to help: