Skip to content

Instantly share code, notes, and snippets.

@jaredallard
Last active February 27, 2022 22:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredallard/a4e1d2cc989db8e694b8448dfbdc4729 to your computer and use it in GitHub Desktop.
Save jaredallard/a4e1d2cc989db8e694b8448dfbdc4729 to your computer and use it in GitHub Desktop.
Balena IoT Project Blog Post
#!/usr/bin/env bash
# Configures balena to use our pre-created images.
DOCKER_TAG=$(awk -F ':' '{ print $2 }' <<<"$DOCKER_IMAGE" | head -n1)
echo "Setting version in docker-compose ($DOCKER_TAG)"
sed -i "s|{{ .VERSION }}|$DOCKER_TAG|" docker-compose.yml
echo "Configuring GHCR access"
cat >/tmp/image_pull_secrets.yaml <<EOF
'ghcr.io':
username: "$GITHUB_USER"
password: "$GITHUB_TOKEN"
EOF
#!/usr/bin/env bash
# Release deploys a given version of the app
# to balena.
set -e
if ! command -v balena >/dev/null; then
echo "Ensuring balena-cli is usable"
asdf install
fi
if [[ -z $BALENA_TOKEN ]]; then
# shellcheck disable=SC2016 # Why: We don't want it to be printed
echo 'Error: $BALENA_TOKEN must be set (get from https://dashboard.balena-cloud.com/preferences/access-tokens)' >&2
exit 1
fi
if [[ -z $FLEET ]]; then
# shellcheck disable=SC2016 # Why: We don't want it to be printed
echo 'Error: $FLEET must be set (e.g. org/name)' >&2
exit 1
fi
# Metadata
draft=false
tag="$(git describe --tags --exact-match HEAD 2>/dev/null || true)"
SHA="$(git rev-parse HEAD)"
# If not on a tag, then we create a draft which isn't
# automatically deployed to devices but still usable.
if [[ $tag == "" ]]; then
draft=true
fi
echo "Starting balena deployment: draft=$draft,tag='$tag',SHA='$SHA'"
tags=("sha" "$SHA")
args=("$FLEET" "--nocache" "--registry-secrets" "/tmp/image_pull_secrets.yaml")
if [[ $draft == true ]]; then
args+=("--draft")
else
tags+=("version" "$tag")
fi
args+=("--release-tag" "${tags[@]}")
echo "Logging in to balena cloud"
balena login --token "$BALENA_TOKEN"
echo "Executing balena"
set -x
balena push "${args[@]}"
name: deploy
on:
pull_request:
release:
types: [published]
env:
DOCKER_REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
FLEET: org/name
ENVIRONMENT: ${{ startsWith(github.ref, 'refs/tags/v') && 'production' || 'development' }}
REF: ${{ startsWith(github.ref, 'refs/tags/v') && github.ref || github.head_ref }}
jobs:
balena:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
deployments: write
steps:
- name: Update deployment status
uses: bobheadxi/deployments@v1.0.1
id: deployment
with:
step: start
token: ${{ secrets.GITHUB_TOKEN }}
env: ${{ env.ENVIRONMENT }}
ref: ${{ env.REF }}
- name: Checkout
uses: actions/checkout@v2
### ASDF Logic
- name: Setup asdf
uses: asdf-vm/actions/setup@v1
- name: Install asdf plugins
uses: asdf-vm/actions/plugins-add@v1
### END
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Generate Docker Image Tag(s)
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern=v{{version}}
type=sha
- name: Login to GHCR
uses: docker/login-action@v1
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v2
with:
context: .
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# TODO(jaredallard): Merge this step into the below.
- name: Configure Balena Deployment
run: .github/workflows/scripts/configure-balena.sh
env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
GITHUB_USER: ${{ github.actor }}
DOCKER_IMAGE: ${{ steps.meta.outputs.tags }}
- name: Deploy to Balena
run: .github/workflows/scripts/deploy-to-balena.sh
env:
BALENA_TOKEN: ${{ secrets.BALENA_TOKEN }}
FLEET: ${{ env.FLEET }}
- name: Finish deployment status
uses: bobheadxi/deployments@v1.0.1
if: always()
with:
step: finish
token: ${{ secrets.GITHUB_TOKEN }}
status: ${{ job.status }}
env: ${{ steps.deployment.outputs.env }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
version: '2'
services:
name:
build:
context: .
dockerfile: ./Dockerfile.balena
args:
VERSION: '{{ .VERSION }}'
# syntax=docker/dockerfile:1.0-experimental
FROM golang:1.17-alpine AS build
ARG VERSION
ENV GOCACHE "/go-build-cache"
ENV GOPRIVATE github.com/org/*
ENV CGO_ENABLED 0
WORKDIR /src
# Copy our source code into the container for building
COPY . .
# Cache dependencies across builds
RUN --mount=type=ssh --mount=type=cache,target=/go/pkg go mod download
# Build our application, caching the go build cache, but also using
# the dependency cache from earlier.
RUN --mount=type=ssh --mount=type=cache,target=/go/pkg --mount=type=cache,target=/go-build-cache \
mkdir -p bin; \
GOARCH=arm64 go build -ldflags "-w -s" -o /src/bin/ -v ./cmd/...
FROM --platform=arm64 alpine:3.15
ENTRYPOINT [ "/usr/local/bin/app" ]
ENV ZONEINFO=/zoneinfo.zip
COPY --from=build /usr/local/go/lib/time/zoneinfo.zip /zoneinfo.zip
COPY --from=build ./src/bin/app /usr/local/bin/app
ARG VERSION=VERSION_REQUIRED
FROM ghcr.io/org/name:${VERSION}
name: release
on:
push:
branches:
- main
env:
GPG_KEY_ID: GPG_KEY_ID
GIT_AUTHOR_NAME: name-ci
GIT_AUTHOR_EMAIL: name@email.com
GIT_COMMITTER_NAME: name-ci
GIT_COMMITTER_EMAIL: name@email.com
jobs:
semantic-release:
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Checkout
uses: actions/checkout@v2
with:
# We need to push w/ this token later
token: ${{ secrets.GH_PAT }}
- name: Setup Git Signing
run: |-
echo "${{ secrets.APP_CI_GPG_SECRET_KEY }}" 2>/dev/null | base64 -d | gpg --import
git config --global user.signingkey "${{ env.GPG_KEY_ID }}"
git config --global commit.gpgsign true
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "lts/*"
- name: Install dependencies
run: npm ci
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
run: npx semantic-release
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment