Combining GitHub Secrets and Actions
Tags: showdev, git, devops, webdev
Published: 2021-08-19 21:33:39

---
title: Combining GitHub Secrets and Actions
published: true
description: Lessons learned using the GitHub Secrets and Actions features.
tags: showdev, git, devops, webdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wjijfvzng5nw0i1t12m.jpg
---

While searching for a [CI](https://docs.microsoft.com/en-us/devops/develop/what-is-continuous-integration)/[CD](https://azure.microsoft.com/en-us/overview/continuous-delivery-vs-continuous-deployment/) solution for a side project of mine, I discovered the [GitHub Secrets](https://docs.github.com/en/actions/reference/encrypted-secrets) feature. Using [GitHub Actions](https://docs.github.com/en/actions), you can setup automated builds triggered in various customizable ways. If part of your automation pipeline includes deployment or delivery to a third-party, you’re probably going to be using some kind of authorization token. Common knowledge dictates that storing super secret authorization files in plain text is a big no-no, so in comes GitHub Secrets to save the day!

![GIF of a cat dressed as a musketeer](https://media.giphy.com/media/dZMLcjyy5Vave/giphy.gif?cid=ecf05e47y7vl0alv2cjkuwjky2zsc1i4umz4ysqpkpe6wsq9&rid=giphy.gif&ct=g)

## Table of Contents
- [Actions](#actions)
- [Secrets](#secrets)
- [Credits](#credits)

## Actions
GitHub Actions can be as simple or as complicated as you need them to be, but here’s an Action that runs a Bash script from the target repository whenever a commit is directly made to the `main` branch:
```yaml
name: Deploy Instance

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      - name: Deploy
        run: ./script/deploy
```
In this case, the Action runs a git-checkout on the repository and then executes the deploy script.

## Secrets
In order to define a GitHub Secret, you must navigate to the *Settings > Secrets > Actions* Page for either a specific repository, or if you’re logged in as an organization, all repositories under that organization. The naming convention for this page is a bit odd. GitHub calls it, “Actions secrets”, but the URL points to `<GitHub repository url>/settings/secrets/actions`. 🤔

![A screenshot of the Secret creation form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5znhchq7vvo94zfjzg3c.jpg)

The documentation is quite clear regarding the rules regarding Secrets, such as:
- Names may only contain alphanumeric characters and underscores (e.g. `A-Z`, `a-z`, `0-9` or `_`)
- Names may not start with `GITHUB_`, as this prefix is reserved
- Secret values may not exceed 64KB
There are a few other rules, but they’re not relevant to this post.

In order to use a Secret in an Action workflow (pipeline), the format is: `${{ secrets.NAME }}`, where the `NAME` is whatever you called the Secret.

For example, if you create a repository secret named, `MY_SECRET`, and give it a value of, `helloworld`, the value output by `${{ secrets.MY_SECRET }}` in any Action would be `helloworld`.

Since Secrets are censored in log output on GitHub, you can’t explicitly see that value being interpreted when the workflow is run. However, by executing an `if` statement in a Bash script run by the workflow, the actual value of the Secret can be confirmed. 😎

See, `check.sh` below:
```bash
#!/usr/bin/env bash

if [ "$MY_SECRET" == "helloworld" ]; then
  echo "that's the one!"
else
  echo "secret does not match"
fi
```

By committing the above Bash script, the Action workflow from previously can be updated to use the new Secret and check that its value is as expected. See `check.yml`:
```yaml
name: Check

on:
  push:
    branches: [ main ]

jobs:
  check:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      - name: chmod
        run: chmod +x ./scripts/*

      - name: Check
        run: MY_SECRET="${{ secrets.MY_SECRET }}" ./scripts/check.sh
```

After committing the above workflow to the `main` branch, the Action should be triggered, and it will pass the current Secret value to the `check.sh` script for validation. Since the Secret value is simple, the check should be a success!

![A screenshot of the workflow log, showing a successful check of the Secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bh8lhdnic3vkww5y1hjg.jpg)

Now, let’s spice it up a bit. Say you’re trying to pass along some authentication key, e.g. an SSH key, a Azure DevOps/Google Cloud Authentication token, a password, etc. Does the check succeed the same way? Let’s see…

If the value of `MY_SECRET` is reassigned to something more complex like, `apple_microsoft_$123`, the `check.sh` script should be updated accordingly:

```bash
#!/usr/bin/env bash

if [ "$MY_SECRET" == 'apple_microsoft_$123' ]; then
  echo "that's the one!"
else
  echo "secret does not match"
fi
```

Remember that the quotes for the new value in `check.sh` should be single quotes (`’`) so that the script won’t interpret `$123` as a variable instead of part of the full value.

Oops! Here we see that the check failed. 😕 The Secret is *not* equal to the value that it was reassigned to.

![A screenshot of the workflow log, showing an unsuccessful check of the Secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/68wpawv6u5fm7s02wkw4.jpg)

So, what happened? Well, it turns out that Secrets embedded in workflows are actually interpreted separate from the job commands. So, when `${{ secrets.MY_SECRET }}` is run GitHub has already interpreted the value of the Secret and inserted it into the job command. The job ends up running, `MY_SECRET=”apple_microsoft$123” ./scripts/check.sh` instead of `MY_SECRET=apple_microsoft$123 ./scripts/check.sh`. Since the Secrets are interpreted first, the same change that was made to `check.sh` needs to be made to the workflow. Use single-quotes instead of double-quotes!

```yaml
name: Check

on:
  push:
    branches: [ main ]

jobs:
  check:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      - name: chmod
        run: chmod +x ./scripts/*

      - name: Check
        run: MY_SECRET='${{ secrets.MY_SECRET }}' ./scripts/check.sh
```

Now, the check should succeed and the special characters in the Secret won’t break the intended behavior!

It’s the tiniest change, but wow…I spent many hours figuring that out. 😅 Learn from my mistakes y’all.

![Phew](https://media.giphy.com/media/Q9vL9BBfoFD1c0o8vn/giphy.gif?cid=ecf05e47eleyk9ppxxwrq80zsfezmf904wdz5o761hm4y3mz&rid=giphy.gif&ct=g)

If you want to learn more about [GitHub Secrets and the limits they have](https://docs.github.com/en/actions/reference/encrypted-secrets#limits-for-secrets), check out the bottom of the documentation page, which explains how to encrypt larger secrets with GPG. Until next time, thanks for reading!

## Credits
Cover image by [Stefan Steinbauer](https://unsplash.com/@usinglight?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/secret?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)! :D