Files
firezone/.github/workflows/elixir.yml
2023-06-27 13:11:36 -06:00

536 lines
19 KiB
YAML

name: Elixir
on:
pull_request:
paths:
- "elixir/**"
- ".github/workflows/elixir.yml"
workflow_call:
# Cancel old workflow runs if new code is pushed
concurrency:
group: "elixir-${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
unit-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./elixir
env:
MIX_ENV: test
POSTGRES_HOST: localhost
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
services:
postgres:
image: postgres:15
ports:
- 5432:5432
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "25"
elixir-version: "1.15"
- uses: actions/checkout@v3
- uses: actions/cache@v3
name: Elixir Deps Cache
env:
cache-name: cache-elixir-deps-${{ env.MIX_ENV }}
with:
path: elixir/deps
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- uses: actions/cache@v3
name: Elixir Build Cache
env:
cache-name: cache-elixir-build-${{ env.MIX_ENV }}
with:
path: elixir/_build
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
- name: Install Dependencies
run: mix deps.get --only $MIX_ENV
- name: Compile Dependencies
run: mix deps.compile --skip-umbrella-children
- name: Compile Application
run: mix compile --warnings-as-errors
- name: Setup Database
run: |
mix ecto.create
mix ecto.migrate
- name: Run Tests
env:
E2E_MAX_WAIT_SECONDS: 20
run: |
mix test --warnings-as-errors
- name: Test Report
uses: dorny/test-reporter@v1
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && (success() || failure()) }}
with:
name: Elixir Unit Test Report
path: elixir/_build/test/lib/*/test-junit-report.xml
reporter: java-junit
type-check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./elixir
env:
MIX_ENV: dev
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: erlef/setup-beam@v1
id: setup-beam
with:
otp-version: "25"
elixir-version: "1.15"
- uses: actions/checkout@v3
- uses: actions/cache@v3
name: Elixir Deps Cache
env:
cache-name: cache-elixir-deps-${{ env.MIX_ENV }}
with:
path: elixir/deps
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- uses: actions/cache@v3
name: Elixir Build Cache
env:
cache-name: cache-elixir-build-${{ env.MIX_ENV }}
with:
path: elixir/_build
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
- name: Install Dependencies
run: mix deps.get --only $MIX_ENV
- name: Compile Dependencies
run: mix deps.compile --skip-umbrella-children
- name: Compile Application
run: mix compile
# Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones
# Cache key based on Elixir & Erlang version (also useful when running in matrix)
- name: Restore PLT cache
id: plt_cache
uses: actions/cache@v3
env:
cache-name: cache-erlang-plt-${{ env.MIX_ENV }}
with:
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-plt
restore-keys: |
${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-plt
path: elixir/priv/plts
- name: Create PLTs
if: steps.plt_cache.outputs.cache-hit != 'true'
run: mix dialyzer --plt
- name: Run Dialyzer
run: mix dialyzer --format dialyxir
static-analysis:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./elixir
env:
MIX_ENV: test
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "25"
elixir-version: "1.15"
- uses: actions/checkout@v3
- uses: actions/cache@v3
name: Elixir Deps Cache
env:
cache-name: cache-elixir-deps-${{ env.MIX_ENV }}
with:
path: elixir/deps
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- uses: actions/cache@v3
name: Elixir Build Cache
env:
cache-name: cache-elixir-build-${{ env.MIX_ENV }}
with:
path: elixir/_build
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
- name: Install Dependencies
run: mix deps.get --only $MIX_ENV
- name: Compile Dependencies
run: mix deps.compile --skip-umbrella-children
- name: Compile Application
run: mix compile --force --warnings-as-errors
- name: Check For Retired Packages
run: mix hex.audit
- name: Check For Vulnerable Packages
run: mix deps.audit
- name: Run Sobelow vulnerability scanner for web app
working-directory: ./elixir/apps/web
run: mix sobelow --skip
- name: Check Formatting
run: mix format --check-formatted
- name: Run Credo
run: mix credo --strict
migrations-and-seed-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./elixir
env:
MIX_ENV: dev
POSTGRES_HOST: localhost
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MASTER_BRANCH: cloud
services:
postgres:
image: postgres:15
ports:
- 5432:5432
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Install package dependencies
run: |
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget -qO- https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo tee /etc/apt/trusted.gpg.d/pgdg.asc &>/dev/null
sudo apt update
sudo apt-get install -q -y postgresql-client
- uses: erlef/setup-beam@v1
with:
otp-version: "25"
elixir-version: "1.15"
- uses: actions/cache@v3
name: Elixir Deps Cache
env:
cache-name: cache-elixir-deps-${{ env.MIX_ENV }}-${{ env.MIX_ENV }}
with:
path: elixir/deps
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- uses: actions/cache@v3
name: Elixir Build Cache
env:
cache-name: cache-elixir-build-${{ env.MIX_ENV }}
with:
path: elixir/_build
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
- name: Install Dependencies
run: mix deps.get --only $MIX_ENV
- name: Compile
run: mix compile
# - name: Download master branch DB dump
# id: download-artifact
# uses: dawidd6/action-download-artifact@v2
# if: !contains(github.ref, env.MASTER_BRANCH)
# with:
# branch: ${{ env.MASTER_BRANCH }}
# name: db-dump
# path: elixir/apps/domain/priv/repo/
# search_artifacts: true
# workflow_conclusion: completed
# if_no_artifact_found: fail
- name: Create Database
run: |
mix ecto.create
# - name: Restore DB dump
# if: !contains(github.ref, env.MASTER_BRANCH)
# env:
# PGPASSWORD: postgres
# run: |
# mix ecto.load
# - name: Run new migrations
# run: |
# mix ecto.migrate
- name: Dump DB
if: contains(github.ref, env.MASTER_BRANCH)
env:
PGPASSWORD: postgres
run: |
pg_dump firezone_dev \
-U postgres -h localhost \
--file apps/domain/priv/repo/structure.sql \
--no-acl \
--no-owner
- name: Upload master branch DB dump
if: contains(github.ref, env.MASTER_BRANCH)
uses: actions/upload-artifact@v3
with:
name: db-dump
path: elixir/apps/domain/priv/repo/structure.sql
- name: Run Seed
run: mix ecto.seed
acceptance-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./elixir
env:
MIX_ENV: test
POSTGRES_HOST: localhost
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MIX_TEST_PARTITIONS: 4
strategy:
fail-fast: false
matrix:
MIX_TEST_PARTITION: [1, 2, 3, 4]
services:
postgres:
image: postgres:15
ports:
- 5432:5432
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
vault:
image: vault:1.12.2
env:
VAULT_ADDR: "http://127.0.0.1:8200"
VAULT_DEV_ROOT_TOKEN_ID: "firezone"
ports:
- 8200:8200/tcp
options: --cap-add=IPC_LOCK
steps:
- uses: nanasess/setup-chromedriver@v1
- uses: erlef/setup-beam@v1
with:
otp-version: "25"
elixir-version: "1.15"
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/checkout@v3
- uses: actions/cache@v3
name: Elixir Deps Cache
env:
cache-name: cache-elixir-deps-${{ env.MIX_ENV }}-${{ env.MIX_ENV }}
with:
path: elixir/deps
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- uses: actions/cache@v3
name: Elixir Build Cache
env:
cache-name: cache-elixir-build-${{ env.MIX_ENV }}
with:
path: elixir/_build
key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ steps.setup-beam.outputs.otp-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }}
- uses: actions/cache@v3
name: pnpm Deps Cache
env:
cache-name: cache-pnpm-build-${{ env.MIX_ENV }}
with:
path: elixir/apps/web/assets/node_modules
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }}
- uses: actions/cache@v3
name: Assets Cache
env:
cache-name: cache-assets-build-${{ env.MIX_ENV }}
with:
path: elixir/apps/web/priv/static/dist
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }}
- run: |
export DISPLAY=:99
chromedriver --url-base=/wd/hub &
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
- name: Install Dependencies
run: mix deps.get --only $MIX_ENV
- name: Compile Dependencies
run: mix deps.compile --skip-umbrella-children
- name: Compile Application
run: mix compile
- name: Install Front-End Dependencies
run: |
cd apps/web
mix assets.setup
- name: Build Assets
run: |
cd apps/web
mix assets.build
- name: Setup Database
run: |
mix ecto.create
mix ecto.migrate
- name: Run Acceptance Tests
env:
MIX_TEST_PARTITION: ${{ matrix.MIX_TEST_PARTITION }}
E2E_MAX_WAIT_SECONDS: 5
run: |
mix test --only acceptance:true \
--partitions=${{ env.MIX_TEST_PARTITIONS }} \
--no-compile \
--no-archives-check \
--no-deps-check \
|| pkill -f chromedriver \
|| mix test --only acceptance:true --failed \
|| pkill -f chromedriver \
|| mix test --only acceptance:true --failed
- name: Save Screenshots
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && always() }}
uses: actions/upload-artifact@v3
with:
name: screenshots
path: elixir/apps/web/screenshots
- name: Test Report
uses: dorny/test-reporter@v1
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && (success() || failure()) }}
with:
name: Elixir Acceptance Test Report
path: elixir/_build/test/lib/*/test-junit-report.xml
reporter: java-junit
# draft-release:
# runs-on: ubuntu-latest
# outputs:
# tag_name: ${{ steps.release_drafter.outputs.tag_name }}
# steps:
# - uses: release-drafter/release-drafter@v5
# id: release_drafter
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
web-container-build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./elixir
permissions:
contents: read
id-token: "write"
needs:
- unit-test
- acceptance-test
env:
APPLICATION_NAME: web
REGISTRY: us-east1-docker.pkg.dev
GCLOUD_PROJECT: firezone-staging
GOOGLE_CLOUD_PROJECT: firezone-staging
CLOUDSDK_PROJECT: firezone-staging
CLOUDSDK_CORE_PROJECT: firezone-staging
GCP_PROJECT: firezone-staging
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- uses: actions/checkout@v3
- id: auth
uses: google-github-actions/auth@v1
with:
token_format: "access_token"
workload_identity_provider: "projects/397012414171/locations/global/workloadIdentityPools/github-actions/providers/github-actions"
service_account: "github-actions@github-iam-387915.iam.gserviceaccount.com"
export_environment_variables: false
- name: Change current gcloud account
run: gcloud --quiet config set project ${GCLOUD_PROJECT}
- name: Login to Google Artifact Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: oauth2accesstoken
password: ${{ steps.auth.outputs.access_token }}
- name: Build Tag and Version ID
id: vsn
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
run: |
TAG=$(echo ${BRANCH_NAME} | sed 's/\//_/g' | sed 's/\:/_/g')
echo "TAG=branch-${TAG}" >> $GITHUB_ENV
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
platforms: linux/amd64
build-args: |
APPLICATION_NAME=${{ env.APPLICATION_NAME }}
APPLICATION_VERSION=0.0.0-sha.${{ github.sha }}
context: elixir/
cache-from: type=gha
cache-to: type=gha,mode=max
file: elixir/Dockerfile
push: true
tags: ${{ env.REGISTRY }}/${{ env.GCLOUD_PROJECT }}/firezone/${{ env.APPLICATION_NAME }}:${{ env.TAG }} , ${{ env.REGISTRY }}/${{ env.GCLOUD_PROJECT }}/firezone/${{ env.APPLICATION_NAME }}:${{ github.sha }}
api-container-build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./elixir
permissions:
contents: read
id-token: "write"
needs:
- unit-test
- acceptance-test
env:
APPLICATION_NAME: api
REGISTRY: us-east1-docker.pkg.dev
GCLOUD_PROJECT: firezone-staging
GOOGLE_CLOUD_PROJECT: firezone-staging
CLOUDSDK_PROJECT: firezone-staging
CLOUDSDK_CORE_PROJECT: firezone-staging
GCP_PROJECT: firezone-staging
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- uses: actions/checkout@v3
- id: auth
uses: google-github-actions/auth@v1
with:
token_format: "access_token"
workload_identity_provider: "projects/397012414171/locations/global/workloadIdentityPools/github-actions/providers/github-actions"
service_account: "github-actions@github-iam-387915.iam.gserviceaccount.com"
export_environment_variables: false
- name: Change current gcloud account
run: gcloud --quiet config set project ${GCLOUD_PROJECT}
- name: Login to Google Artifact Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: oauth2accesstoken
password: ${{ steps.auth.outputs.access_token }}
- name: Build Tag and Version ID
id: vsn
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
run: |
TAG=$(echo ${BRANCH_NAME} | sed 's/\//_/g' | sed 's/\:/_/g')
echo "TAG=branch-${TAG}" >> $GITHUB_ENV
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
platforms: linux/amd64
build-args: |
APPLICATION_NAME=${{ env.APPLICATION_NAME }}
APPLICATION_VERSION=0.0.0-sha.${{ github.sha }}
context: elixir/
cache-from: type=gha
cache-to: type=gha,mode=max
file: elixir/Dockerfile
push: true
tags: ${{ env.REGISTRY }}/${{ env.GCLOUD_PROJECT }}/firezone/${{ env.APPLICATION_NAME }}:${{ env.TAG }} , ${{ env.REGISTRY }}/${{ env.GCLOUD_PROJECT }}/firezone/${{ env.APPLICATION_NAME }}:${{ github.sha }}
# TODO: add a sanity check to make sure the image is actually built
# and can be started