Build, distribute and run binaries using GitHub

github-badge go-badge

In short

In this example we show how to use GitHub to deploy a binary file to your fleet of Raspberry Pis. The specific binary is written in go. The process starts by pushing the changes of your source code to your GitHub repository. Then, using GitHub actions, your source code is cross compiled for your Raspberry Pi and finally the binary is placed into your qbee.io file manager. From there on you can distribute and run it on the group of devices you have specified.

If we want to build an executable (called hi-pi) for our Raspberry Pi we can use the following code

main.go

package main

import "fmt"

func main() {
    fmt.Println("Hello Norway")
}

we can easily do that locally using

   $ env GOARCH=arm GOOS=linux go build -o hi-pi main.go
thanks to go's powerful cross compilation capabilities.

However, we aim to setup an automatic workflow, where we

  • use git as for version control management
  • setup a GitHub runner (using GitHub actions) to build our code once a push is triggered
  • copy the created binary to the qbee.io file manager via API calls
  • use the qbee.io file distribution to the distribute (and run) the executable on a list of devices

How to create a repository on GitHub

Check out the official documentation on how to create a repository on GitHub.

Once a repository is set up, before creating a runner you need to setup GitHub secrets as shown in the following screenshot.

There we specify our qbee.io username with QM and our password with QP, as we do not want them to be exposed.

After the passwords are defined we start GitHub actions which executes the YAML file on the runners on GitHub (or you can use a custom runner). GitHub actions are created under "set up a workflow yourself".

This is the script:

go-package-distribution.yml
name: Go package distribution

on:
push:
    branches: [ master ]
pull_request:
    branches: [ master ]

jobs:  


build:
    runs-on: ubuntu-latest
    env:
        APPNAME: hi-pi   

    steps:
    - uses: actions/checkout@v2

    - name: install jq
      run: |
        sudo apt-get install jq

    - name: create go executable
    env:
        GOARCH: arm
        GOOS: linux
    run: go build -o $APPNAME main.go

    - name: copy to qbee file manager
      run: |
        successful_status_code='200'
        auth='false'
        i=0
        token=""
        while [[ "$auth" != "true" && $i -lt 10 ]]
        do
            echo "try number $((i++))"
            output=$(curl --request POST -sL --url 'https://www.app.qbee.io/api/v2/login' \
          --header "Content-Type: application/json" \
          -d "{\"email\":\"${{ secrets.QM }}\",\"password\":\"${{ secrets.QP }}\"}"\
          -w "\n{\"http_code\":%{http_code}}\n")
            http_code=$(echo $output | jq -cs | jq -r '.[1].http_code')
            echo $http_code
            tokenValue=$(echo $output | jq -cs | jq -r '.[0].token')
            echo $tokenValue
            if [ "$http_code" != "$successful_status_code" ]
            then
                echo $tokenValue
            #uncomment next line to do nothing
            #: 
            else
                auth='true'
                apiOutput=$(curl -i --request "DELETE" -d "path=/$APPNAME" -H "Content-type: application/x-www-form-urlencoded" \
                   --url 'https://www.app.qbee.io/api/v2/file'\
                   --header 'Authorization: Bearer '"$tokenValue")
                echo "API output is:\n$apiOutput"
                apiOutput=$(curl -i --request POST -H "Content-Type:multipart/form-data" -F "path=/" -F "file=@$APPNAME" \
                   --url 'https://www.app.qbee.io/api/v2/file'\
                   --header 'Authorization: Bearer '"$tokenValue")
                echo "API output is:\n$apiOutput"
            fi
        done

Comments on the yaml file

  • this action is triggered on pushes to the master branch
  • the runner is a virtual machine with an ubuntu OS as we specified ubuntu-latest
  • The actions/checkout@v2 option will check out your code on the virtual machine. This needs to be done every time as the virtual machine is not persistent
  • JWT authentication jq is needed for JWT authentication
  • we set up a step, where we build the go application using cross compilation (due to the specified environment variables GOARCH and GOOS
  • the output is placed into the root directory of your qbee.io file manager (c.f. file distribution via API)

Note that your credentials aren't revealed as we use GitHub secrets to encrypt them as you can see in the action output

Finally, we distribute our files to the remote devices as usual with the standard file distribution.

Automatic deployment

Using this workflow, every time you push your code changes to the repository the output binary (or binaries) will always be replaced within the qbee.io file manager. If the binary file differs from the one previously placed in the file manager, then the qbee.io file distribution is triggered along with the run command that you provided.

Hence, all your edge devices are updated by a simple git push :)