A GitHub Actions workflow to deploy to Google Cloud using the Serverless Framework

How to set up continuous deployment while keeping your Google Cloud credentials secure

I've written about the Serverless Framework before, and how it plays very nicely with AWS. With Google Cloud, I've found things are a bit trickier, and there's less documentation, probably because it's relatively new. But fear not! Here's a complete step by step to set up a GitHub workflow to deploy to Google Cloud using the Serverless Framework. The process shouldn't be too different if you are using another CI provider.

If you haven't, set up a starter project:

    serverless create --template google-nodejs

First, you'll need Google Cloud system credentials. The Serverless framework has a complete guide on how to set up a service account with the right roles.

By now you should have downloaded a JSON file with your credentials, and added its path in your serverless.yml. But this path can be different if you deploy from your machine, or a fellow developer deploys from theirs, or if it's deployed via CI, right? So we want this to be an environment variable. Your complete serverless.yml file should look something like this:

    service: my-project
    
    provider:
      name: google
      stage: dev
      runtime: nodejs8
      region: europe-west1
      project: my-project
      credentials: ${env:CREDENTIALS_PATH}
    
    plugins:
      - serverless-google-cloudfunctions
    
    package:
      exclude:
        - node_modules/**
        - .gitignore
        - .git/**
    
    functions:
      first:
        handler: http
        events:
          - http: path

Now, from your machine you can easily deploy passing the correct path:

    CREDENTIALS_PATH=~/path/to/your/keyfile.json serverless deploy

Now we are ready to create our deploy workflow! We'll be using the official Serverless action. In the example workflow, they show how to pass secrets as environment variables, but as we've seen with Google Cloud we need to pass a whole JSON file. And we know it's not a good idea to commit sensitive information, so we'll need to commit an encrypted version of it, and add a script to decrypt it on deploy time. That might sound complicated but don't worry, we'll do it! 💪The GitHub Actions documentation includes a guide on how to do that, but we'll go step by step here too.

First, encrypt the file (with Mac I installed GPG Tools)

    $ gpg --symmetric --cipher-algo AES256 keyfile.json

You'll need to enter a passphrase. Make sure to keep it somewhere safe as we'll need it to decrypt the file. Commit the encrypted keyfile.json.gpg file to your repository.

Create a shell script to decrypt the password. We need to use the $GITHUB_WORKSPACE path instead of $HOME (as the documentation example) because the Serverless action runs in a docker container and is not able to access any directory, just the workspace.

    #!/bin/sh
    
    # Decrypt the file
    mkdir $GITHUB_WORKSPACE/secrets
    # --batch to prevent interactive command --yes to assume "yes" for questions
    gpg --quiet --batch --yes --decrypt --passphrase="$SECRET_PASSPHRASE" \
    --output $GITHUB_WORKSPACE/secrets/keyfile.json keyfile.json.gpg

Save this file in .github/scripts/decrypt_secret.sh, make sure it is executable by running chmod +x decrypt_secret.sh, and commit it.

In your GitHub repository settings, add a new SECRET_PASSPHRASE secret with the passphrase you used when encrypting the file.

Now we are ready to decrypt the keyfile in our workflow, and pass its path to the Serverless action as an environment variable (inside the container the $GITHUB_WORKSPACE path is accessible via /github/workspace/ ). Here's the complete workflow file:

    name: Deploy
    
    on:
      push:
        branches:
          - master
    
    jobs:
      deploy:
        name: deploy
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@master
        - name: npm install
          run: npm install
        - name: Decrypt secret
          run: ./.github/scripts/decrypt_secret.sh
          env:
            SECRET_PASSPHRASE: ${{ secrets.SECRET_PASSPHRASE }}
        - name: serverless deploy
          uses: serverless/github-action@master
          with:
            args: deploy
          env:
            SERVERLESS_ACCESS_KEY: ${{ secrets.SERVERLESS_ACCESS_KEY }}
            CREDENTIALS_PATH: /github/workspace/secrets/keyfile.json

Finally, your project is set up for continuous deployment! You can find the whole code in this repository.

Of course, you should also have workflows to run tests or lint your code. At Codegram we love GitHub Actions, and we have a repo with useful workflows. Make sure to take a look!


Cover photo by Daniel Mayovskiy