Skip to content

Fix dir

Fix dir #66

Workflow file for this run

---
name: CI Matrix
# Build and run tests in containers
# Store test and prod images to GitHub Container Registry (GHCR)
# Build prod image and push to AWS ECR
# Run tests and quality checks on test image
# Run security scans on code and prod images
# Run tasks in parallel
# Use GHA caching
# Use test matrix for multiple versions of Elixir, OTP, and OS
on: push
# on:
# push:
# branches: [main]
# paths-ignore:
# - 'README.md'
# - '.github/**'
# - '.vscode'
# - '.gitignore'
# pull_request:
# # branches: [main]
# types: [opened,synchronize,reopened,labeled,unlabeled]
env:
# Name of image
IMAGE_NAME: foo-app
# Name of org in GHCR Docker repository
IMAGE_OWNER: ${{ github.repository_owner }}
# Name of org in external Docker repository
ECR_IMAGE_OWNER: cogini
# Tag for release images
# IMAGE_TAG: ${{ (github.ref == 'refs/heads/main' && 'staging') || (github.ref == 'refs/heads/qa' && 'qa') }}
IMAGE_TAG: latest
IMAGE_VER: ${{ github.sha }}
# Docker build args
ELIXIR_VER: 1.15.5
OTP_VER: 26.0.2
BUILD_OS_VER: bullseye-20230612-slim
PROD_OS_VER: bullseye-slim
BASE_OS: debian
# Variant if test matrix is not used
VAR: '1.15.5-erlang-26.0.2-debian-bullseye-20230612-slim'
# Variant that is deployed
PROD_VAR: '1.15.5-erlang-26.0.2-debian-bullseye-20230612-slim'
# Registry for test images
REGISTRY: ghcr.io/
# Registry for public images, default is docker.io
PUBLIC_REGISTRY: ''
# Give GitHub Actions access to AWS
AWS_ENABLED: ${{ true }}
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
# AWS_ROLE_TO_ASSUME: arn:aws:iam::XXX:role/cogini-foo-dev-app-github-action
AWS_ROLE_TO_ASSUME: ${{ secrets.AWS_ROLE_TO_ASSUME }}
AWS_REGION: ap-northeast-1
# Health check port for app
APP_PORT: 4000
# Datadog
# DD_API_KEY: ${{ secrets.ACTIONS_DD_API_KEY }}
# DD_ENV: ci
# DD_TAGS: "environment:ci"
# Docker
DOCKER_BUILDKIT: '1'
COMPOSE_DOCKER_CLI_BUILD: '1'
COMPOSE_FILE: docker-compose.gha.yml
DOCKER_FILE: deploy/debian.Dockerfile
ELIXIR_MODULE: PhoenixContainerExample
ECS_CLUSTER: foo
ECS_SERVICE: foo-app
ECS_CONTAINER: foo-app
CODEDEPLOY_APPLICATION: foo-app
CODEDEPLOY_DEPLOYMENT_GROUP: foo-app-ecs
TASKDEF: ecs/task-definition.json
# AWS SSM Parameter Store name prefix
AWS_PS_PREFIX: cogini/foo/dev
# Name of environment for resources created by Terraform
TERRAFORM_ENV: dev
# GitHub Advanced Security
# https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security
# https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning
# https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github
GITHUB_ADVANCED_SECURITY: ${{ true }}
DEPLOY_DOCKER_HUB: ${{ false }}
jobs:
build-test:
name: Build test image
permissions:
contents: read
# Push to ghcr.io repository
packages: write
# Cancel previous runs
actions: write
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Specify versions of Erlang, Elixir, and base OS
# Choose a combination supported by https://hub.docker.com/r/hexpm/elixir/tags
os: [alpine, debian, ubuntu]
otp: [25.3.2.6]
elixir: [1.14.5]
include:
- os: alpine
otp: 25.3.2.6
build_os_ver: '3.17.0'
prod_os_ver: '3.17.0'
- os: debian
build_os_ver: bullseye-20230612-slim
prod_os_ver: bullseye-slim
snapshot_ver: 20230612
otp: 26.0.2
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 25.2.3
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
env:
DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile
VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }}
steps:
# - name: Dump event
# run: cat "$GITHUB_EVENT_PATH"
- name: Cancel previous runs in progress
uses: styfle/[email protected]
with:
access_token: ${{ github.token }}
- name: Check out source
uses: actions/checkout@v3
- name: Set variables
run: |
echo "GITHUB_SHA_SHORT=$(echo $GITHUB_SHA | cut -c 1-7)" >> $GITHUB_ENV
echo "run_id=${GITHUB_RUN_ID}" >> $GITHUB_OUTPUT
echo "run_num=${GITHUB_RUN_NUMBER}" >> $GITHUB_OUTPUT
- name: Get branch name for main
if: github.event_name != 'pull_request'
run: echo "BRANCH=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
- name: Get branch name for pull_request
if: github.event_name == 'pull_request'
run: echo "BRANCH=$(echo $GITHUB_HEAD_REF | tr '//\\' '.' | cut -c -55)" >> $GITHUB_ENV
- name: Log in to GHCR
uses: docker/login-action@v2
# https://github.com/marketplace/actions/docker-login
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Pull public images without rate limits
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# - name: Configure ssh keys
# uses: webfactory/[email protected]
# # Configure machine key or deploy keys to access private repos from build
# # https://github.com/marketplace/actions/webfactory-ssh-agent
# # https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys
# # ssh-keygen -t ed25519 -m pem -C "[email protected]:reachfh/api-utils.git" -f api-utils
# with:
# ssh-private-key: |
# ${{ secrets.SSH_PRIVATE_KEY }}
- name: Set env vars for builds
run: |
echo 'DATABASE_HOST=postgres' >> ./.env.test
# - name: Set up QEMU for multi-platform builds
# id: qemu
# uses: docker/setup-qemu-action@v2
# with:
# platforms: linux/amd64,linux/arm64
# - name: Display available platforms
# run: echo "${{ steps.qemu.outputs.platforms }}"
- name: Set up Docker buildx
id: buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Build test image and push to GHCR
uses: docker/build-push-action@v3
# https://github.com/docker/build-push-action
with:
file: ${{ env.DOCKER_FILE }}
target: test-image
build-args: |
ELIXIR_VER=${{ matrix.elixir }}
OTP_VER=${{ matrix.otp }}
BUILD_OS_VER=${{ matrix.build_os_ver }}
PROD_OS_VER=${{ matrix.prod_os_ver }}
context: .
builder: ${{ steps.buildx.outputs.name }}
push: true
# https://github.com/moby/buildkit#export-cache
cache-from: type=gha,scope=${{ github.workflow }}-test-${{ env.VAR }}
cache-to: type=gha,scope=${{ github.workflow }}-test-${{ env.VAR }},mode=max
# ssh: default
tags: |
ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:test${{ env.VAR }}${{ env.IMAGE_VER }}
# secrets: |
# "access_token=${{ secrets.DEVOPS_ACCESS_TOKEN }}"
# "oban_key_fingerprint=${{ secrets.OBAN_KEY_FINGERPRINT }}"
# "oban_license_key=${{ secrets.OBAN_LICENSE_KEY }}"
test:
name: Run tests
# permissions: write-all
permissions:
# Read from ghcr.io repository
packages: read
# Upload JUnit report files
# https://github.com/EnricoMi/publish-unit-test-result-action#permissions
contents: read
checks: write
pull-requests: write
issues: read
needs: [build-test]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [alpine, debian, ubuntu]
elixir: [1.15.5, 1.14.3]
include:
- os: alpine
otp: 24.3.4.6
build_os_ver: '3.15.6'
prod_os_ver: '3.15.6'
- os: alpine
otp: 25.2.3
build_os_ver: '3.17.0'
prod_os_ver: '3.17.0'
- os: debian
build_os_ver: bullseye-20230612-slim
prod_os_ver: bullseye-slim
snapshot_ver: 20230612
otp: 26.0.2
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 25.2.3
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 24.3.4.9
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 25.2.3
# ci_node_total: [2]
# ci_node_index: [1, 2]
env:
DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile
VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }}
steps:
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Check out source
uses: actions/checkout@v3
- name: Pull repos
run: |
docker compose pull --quiet --include-deps test
docker images --no-trunc
- name: Start services
run: docker compose up --detach test
- name: Show docker logs
if: failure()
run: |
docker compose logs --timestamps postgres
docker compose logs --timestamps test
docker compose ps --format json | jq .
- name: Display health check results
if: failure()
run: |
echo postgres
docker inspect --format "{{json .State.Health }}" $(docker compose ps -q postgres) | jq
echo redis
docker inspect --format "{{json .State.Health }}" $(docker compose ps -q redis) | jq
- name: Debug env
if: failure()
run: |
docker compose run test env
# docker compose run test env PGPASSWORD=postgres /usr/bin/psql -w -h postgres -U postgres -d postgres -c "SELECT 1"
# docker compose run test env PGPASSWORD=postgres /usr/lib/postgresql/13/bin/pg_isready -h postgres -p 5432 -d postgres -U postgres
- name: Initialize test database
run: docker compose run test mix do ecto.create, ecto.migrate
- name: Run tests
# run: docker compose run test mix test --cover
# run: docker compose run test env MIX_TEST_PARTITION=${{ matrix.ci_node_index }} mix test --partitions ${{ matrix.ci_node_total }}
run: docker compose run test mix test
- name: Run quality checks
run: docker compose run test mix do format --check-formatted, deps.unlock --check-unused, credo --all, hex.audit, deps.audit, sobelow
- name: Publish unit test results to GitHub
uses: EnricoMi/publish-unit-test-result-action@v2
# Run even if tests fail
if: always()
with:
# Volume mounted from local filesystem into build by docker compose
junit_files: junit-reports/*.xml
# - name: Upload test results to Datadog
# if: always()
# continue-on-error: true
# run: |
# npm install -g @datadog/datadog-ci
# datadog-ci junit upload --service api-graphql junit-reports/
test-dialyzer:
name: Run dialyzer
needs: [build-test]
permissions:
contents: read
# Read from ghcr.io repository
packages: read
# Upload JUnit report files
# https://github.com/EnricoMi/publish-unit-test-result-action#permissions
checks: write
pull-requests: write
issues: read
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [alpine, debian, ubuntu]
elixir: [1.15.5, 1.14.3]
include:
- os: alpine
otp: 24.3.4.6
build_os_ver: '3.15.6'
prod_os_ver: '3.15.6'
- os: alpine
otp: 25.2.3
build_os_ver: '3.17.0'
prod_os_ver: '3.17.0'
- os: debian
build_os_ver: bullseye-20230612-slim
prod_os_ver: bullseye-slim
snapshot_ver: 20230612
otp: 26.0.2
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 25.2.3
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 24.3.4.9
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 25.2.3
env:
DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile
VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }}
steps:
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Check out source
uses: actions/checkout@v3
- name: Pull repos
run: |
docker compose pull --quiet test
# docker images --no-trunc
- name: Run dialyzer
run: docker compose run test mix dialyzer --no-check --format github
test-scan:
name: Security scan code
needs: [build-test]
permissions:
contents: read
# Read from ghcr.io repository
packages: read
# Upload JUnit report files
# https://github.com/EnricoMi/publish-unit-test-result-action#permissions
checks: write
pull-requests: write
issues: read
# Upload SARIF report files
security-events: write
runs-on: ubuntu-latest
steps:
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout source
uses: actions/checkout@v3
- name: Scan code with trivy
uses: aquasecurity/trivy-action@master
# https://github.com/marketplace/actions/aqua-security-trivy
with:
scan-type: 'fs'
scan-ref: '.'
# ignore-unfixed: true
# severity: 'CRITICAL'
# trivy-config: trivy.yaml
# format: 'table'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Display scan results
run: cat trivy-results.sarif | jq .
- name: Upload trivy scan results to GitHub Security tab
if: ${{ env.GITHUB_ADVANCED_SECURITY == 1 }}
uses: github/codeql-action/upload-sarif@v2
if: always()

Check failure on line 495 in .github/workflows/ci-matrix.yml

View workflow run for this annotation

GitHub Actions / CI Matrix

Invalid workflow file

The workflow is not valid. .github/workflows/ci-matrix.yml (Line: 495, Col: 9): 'if' is already defined .github/workflows/ci-matrix.yml (Line: 517, Col: 9): 'if' is already defined
with:
sarif_file: 'trivy-results.sarif'
category: trivy
- name: Scan code with grype
uses: anchore/scan-action@v3
# https://github.com/marketplace/actions/anchore-container-scan
id: scan-grype
with:
path: .
# output-format: table
output-format: 'sarif'
fail-build: false
# severity-cutoff: critical
- name: Display scan results
run: cat ${{ steps.scan-grype.outputs.sarif }} | jq .
- name: Upload grype scan results to GitHub Security tab
if: ${{ env.GITHUB_ADVANCED_SECURITY == 1 }}
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: ${{ steps.scan-grype.outputs.sarif }}
category: grype
build-prod:
name: Build prod image
permissions:
# Interact with GitHub OIDC Token endpoint for AWS
id-token: write
contents: read
# Push to ghcr.io repository
packages: write
# Cancel previous runs
actions: write
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [alpine, chiseled, debian, distroless, ubuntu]
elixir: [1.15.5, 1.14.3]
include:
- os: alpine
otp: 24.3.4.6
build_os_ver: '3.15.6'
prod_os_ver: '3.15.6'
- os: alpine
otp: 25.2.3
build_os_ver: '3.17.0'
prod_os_ver: '3.17.0'
- os: chiseled
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230612-slim
prod_os_ver: bullseye-slim
snapshot_ver: 20230612
otp: 26.0.2
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 25.2.3
- os: distroless
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: ''
otp: 25.2.3
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 24.3.4.9
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 25.2.3
env:
DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile
VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }}
steps:
- name: Cancel previous runs in progress
uses: styfle/[email protected]
with:
access_token: ${{ github.token }}
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Check out source
uses: actions/checkout@v3
- name: Set variables
id: vars
run: |
echo "GITHUB_SHA_SHORT=$(echo $GITHUB_SHA | cut -c 1-7)" >> $GITHUB_ENV
echo "run_id=${GITHUB_RUN_ID}" >> $GITHUB_OUTPUT
echo "run_num=${GITHUB_RUN_NUMBER}" >> $GITHUB_OUTPUT
- name: Get branch name for push
if: github.event_name != 'pull_request'
run: echo "BRANCH=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
- name: Get branch name for pull_request
if: github.event_name == 'pull_request'
run: echo "BRANCH=$(echo $GITHUB_HEAD_REF | tr '//\\' '.' | cut -c -55)" >> $GITHUB_ENV
# - name: Configure ssh keys
# uses: webfactory/[email protected]
# # https://github.com/marketplace/actions/webfactory-ssh-agent
# # https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys
# # ssh-keygen -t ed25519 -m pem -C "[email protected]:reachfh/api-utils.git" -f api-utils
# with:
# ssh-private-key: |
# ${{ secrets.SSH_PRIVATE_KEY }}
- name: Set up Docker buildx
id: buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Build prod image and push to GHCR
uses: docker/build-push-action@v3
with:
file: ${{ env.DOCKER_FILE }}
target: prod
build-args: |
ELIXIR_VER=${{ matrix.elixir }}
OTP_VER=${{ matrix.otp }}
BUILD_OS_VER=${{ matrix.build_os_ver }}
PROD_OS_VER=${{ matrix.prod_os_ver }}
context: .
builder: ${{ steps.buildx.outputs.name }}
push: true
cache-from: type=gha,scope=${{ github.workflow }}-${{ env.VAR }}
cache-to: type=gha,scope=${{ github.workflow }}-${{ env.VAR }},mode=max
# ssh: default
tags: |
ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }}
ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH }}-${{ env.GITHUB_SHA_SHORT }}
# secrets: |
# "access_token=${{ secrets.DEVOPS_ACCESS_TOKEN }}"
# "oban_key_fingerprint=${{ secrets.OBAN_KEY_FINGERPRINT }}"
# "oban_license_key=${{ secrets.OBAN_LICENSE_KEY }}"
- name: Build prod image and push to Docker Hub
if: ${{ env.DEPLOY_DOCKER_HUB == 1 }}
uses: docker/build-push-action@v3
with:
file: ${{ env.DOCKER_FILE }}
target: prod
context: .
builder: ${{ steps.buildx.outputs.name }}
push: true
cache-from: type=gha,scope=${{ github.workflow }}-${{ env.VAR }}
cache-to: type=gha,scope=${{ github.workflow }}-${{ env.VAR }},mode=max
# ssh: default
tags: |
docker.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }}
docker.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH }}-${{ env.GITHUB_SHA_SHORT }}
# secrets: |
# "access_token=${{ secrets.DEVOPS_ACCESS_TOKEN }}"
# "oban_key_fingerprint=${{ secrets.OBAN_KEY_FINGERPRINT }}"
# "oban_license_key=${{ secrets.OBAN_LICENSE_KEY }}"
- name: Configure AWS credentials
if: ${{ env.AWS_ENABLED == 1 }}
uses: aws-actions/configure-aws-credentials@v4
# https://github.com/aws-actions/configure-aws-credentials
# https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services
with:
role-to-assume: ${{ env.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ env.AWS_REGION }}
- name: Log in to Amazon ECR
if: ${{ env.AWS_ENABLED == 1 }}
id: ecr-login
uses: aws-actions/amazon-ecr-login@v1
- name: Set vars
if: ${{ env.AWS_ENABLED == 1 }}
run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> $GITHUB_ENV
# - name: Log in to ECR
# uses: docker/login-action@v2
# with:
# registry: ${{ env.ECR_REGISTRY }}
# username: ${{ secrets.AWS_ACCESS_KEY_ID }}
# password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Build prod image and push to AWS ECR
if: ${{ env.AWS_ENABLED == 1 }}
uses: docker/build-push-action@v3
env:
REGISTRY: "${{ env.ECR_REGISTRY }}/"
IMAGE_OWNER: "${{ env.ECR_IMAGE_OWNER }}/"
with:
file: ${{ env.DOCKER_FILE }}
target: prod
context: .
builder: ${{ steps.buildx.outputs.name }}
push: true
cache-from: type=gha,scope=${{ github.workflow }}-${{ env.VAR }}
cache-to: type=gha,scope=${{ github.workflow }}-${{ env.VAR }},mode=max
# ssh: default
tags: |
${{ env.ECR_REGISTRY }}/${{ env.ECR_IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }}
${{ env.ECR_REGISTRY }}/${{ env.ECR_IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH }}-${{ env.GITHUB_SHA_SHORT }}
# secrets: |
# "access_token=${{ secrets.DEVOPS_ACCESS_TOKEN }}"
# "oban_key_fingerprint=${{ secrets.OBAN_KEY_FINGERPRINT }}"
# "oban_license_key=${{ secrets.OBAN_LICENSE_KEY }}"
# build-newman:
# name: Build newman image
# permissions:
# # Push to ghcr.io repository
# packages: write
# # Cancel previous runs
# actions: write
# runs-on: ubuntu-latest
# steps:
# - name: Cancel previous runs
# uses: styfle/[email protected]
# with:
# access_token: ${{ github.token }}
#
# - name: Check out source
# uses: actions/checkout@v3
#
# - name: Log in to GHCR
# uses: docker/login-action@v2
# with:
# registry: ghcr.io
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }}
#
# # Pull public images without rate limits
# # - name: Log in to Docker Hub
# # uses: docker/login-action@v2
# # with:
# # username: ${{ secrets.DOCKERHUB_USERNAME }}
# # password: ${{ secrets.DOCKERHUB_TOKEN }}
#
# - name: Set up Docker buildx
# id: buildx
# uses: docker/setup-buildx-action@v2
# with:
# driver-opts: network=host
#
# - name: Build newman image
# uses: docker/build-push-action@v3
# with:
# file: deploy/newman.Dockerfile
# context: .
# builder: ${{ steps.buildx.outputs.name }}
# push: true
# cache-from: type=gha,scope=${{ github.workflow }}-newman
# cache-to: type=gha,scope=${{ github.workflow }}-newman,mode=max
# tags: |
# ghcr.io/${{ env.IMAGE_OWNER }}/newman:latest
test-prod:
name: Run external API tests
# needs: [build-prod, build-newman]
needs: [build-prod]
permissions:
# Read from ghcr.io repository
packages: read
# Upload JUnit report files
# https://github.com/EnricoMi/publish-unit-test-result-action#permissions
contents: read
issues: read
checks: write
pull-requests: write
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [alpine, chiseled, debian, distroless, ubuntu]
elixir: [1.15.5, 1.14.3]
include:
- os: alpine
otp: 24.3.4.6
build_os_ver: '3.15.6'
prod_os_ver: '3.15.6'
- os: alpine
otp: 25.2.3
build_os_ver: '3.17.0'
prod_os_ver: '3.17.0'
- os: chiseled
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230612-slim
prod_os_ver: bullseye-slim
snapshot_ver: 20230612
otp: 26.0.2
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 25.2.3
- os: distroless
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: ''
otp: 25.2.3
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 24.3.4.9
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 25.2.3
env:
DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile
VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }}
steps:
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Check out source
uses: actions/checkout@v3
- name: Pull repos
# run: echo "newman postgres router prod" | xargs -n 1 -P 8 docker compose pull --quiet
# docker compose pull --quiet newman
# docker images --no-trunc
run: |
docker compose pull --quiet --include-deps prod
- name: Start services
run: docker compose up --wait --detach prod
- name: Display logs
if: failure()
run: |
docker compose logs --timestamps prod
docker compose logs --timestamps postgres
echo prod
docker inspect --format "{{json .State.Health }}" $(docker compose ps -q prod) | jq
echo postgres
docker inspect --format "{{json .State.Health }}" $(docker compose ps -q postgres) | jq
docker compose ps --format json | jq .
# - name: Debug env
# if: failure()
# run: |
# docker compose run --entrypoint /bin/bash prod env
# docker compose run --entrypoint /bin/bash prod env PGPASSWORD=postgres /usr/bin/psql -w -h postgres -U postgres -d postgres -c "SELECT 1"
# # docker compose run --entrypoint /bin/bash prod pg_isready -h postgres -p 5432 -d postgres -U postgres
- name: Initialize database
run: |
docker compose run prod eval '${{ env.ELIXIR_MODULE }}.Release.create_repos()'
docker compose run prod eval '${{ env.ELIXIR_MODULE }}.Release.migrate()'
docker compose run prod eval '${{ env.ELIXIR_MODULE }}.Release.run_seeds()'
- name: Run health check
run: curl -v http://localhost:${{ env.APP_PORT }}/healthz/liveness
# - name: Run API tests
# env:
# NEWMAN_ARGS: "--verbose -r cli,junitfull --reporter-junitfull-export /junit-reports/newman.xml -e ci.json"
# # NEWMAN_ARGS: "--verbose -r cli,junit --reporter-junit-export /junit-reports/newman.xml -e ci.json"
# # https://hub.docker.com/r/postman/newman/
# run: docker compose run newman run ${{env.NEWMAN_ARGS}} Queries.postman_collection.json
- name: Display logs
if: failure()
run: |
docker compose logs --timestamps prod
# docker compose logs --timestamps newman
- name: Publish unit test results to GitHub
uses: EnricoMi/publish-unit-test-result-action@v2
# Run even if tests fail
if: always()
with:
# Volume mounted from local filesystem into build
junit_files: ./junit-reports/*.xml
check_name: "External API Tests"
scan:
name: Security scan prod image
needs: [build-prod]
permissions:
# Interact with GitHub OIDC Token endpoint for AWS
id-token: write
contents: read
# Read from ghcr.io repository
packages: read
# Upload JUnit report files
# https://github.com/EnricoMi/publish-unit-test-result-action#permissions
checks: write
pull-requests: write
issues: read
# Upload SARIF report files
security-events: write
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [alpine, chiseled, debian, distroless, ubuntu]
elixir: [1.15.5, 1.14.3]
include:
- os: alpine
otp: 24.3.4.6
build_os_ver: '3.15.6'
prod_os_ver: '3.15.6'
- os: alpine
otp: 25.2.3
build_os_ver: '3.17.0'
prod_os_ver: '3.17.0'
- os: chiseled
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230612-slim
prod_os_ver: bullseye-slim
snapshot_ver: 20230612
otp: 26.0.2
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230109-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230109'
otp: 25.2.3
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 24.3.4.9
- os: debian
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: '20230202'
otp: 25.2.3
- os: distroless
build_os_ver: bullseye-20230202-slim
prod_os_ver: bullseye-slim
snapshot_ver: ''
otp: 25.2.3
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 24.3.4.9
- os: ubuntu
build_os_ver: jammy-20221130
prod_os_ver: jammy-20221130
otp: 25.2.3
env:
DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile
VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }}
steps:
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull image
run: docker pull ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{ env.VAR }}${{ env.IMAGE_VER }}
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
# https://github.com/aquasecurity/trivy-action
# https://github.com/marketplace/actions/aqua-security-trivy#inputs
with:
image-ref: ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{ env.VAR }}${{ env.IMAGE_VER }}
# exit-code: '1' # fail build
# ignore-unfixed: true
# vuln-type: 'os,library'
# severity: 'CRITICAL,HIGH'
# cache-dir: /var/cache
format: 'sarif'
output: 'trivy.sarif'
- name: Display scan results
run: cat trivy.sarif | jq .
- name: Upload Trivy scan results to GitHub Security tab
if: ${{ env.GITHUB_ADVANCED_SECURITY == 1 }}
uses: github/codeql-action/upload-sarif@v2
# Requires GitHub Advanced Security
# https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security
# https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning
# https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github
if: always()
with:
sarif_file: 'trivy.sarif'
category: trivy
- name: Scan image with Grype
uses: anchore/scan-action@v3
# https://github.com/marketplace/actions/anchore-container-scan
id: scan-grype
with:
image: ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{ env.VAR }}${{ env.IMAGE_VER }}
# severity-cutoff: critical
fail-build: false
output-format: 'sarif'
# output-format: table
- name: Display scan results
run: cat ${{ steps.scan-grype.outputs.sarif }} | jq .
- name: Upload Grype scan results to GitHub Security tab
if: ${{ env.GITHUB_ADVANCED_SECURITY == 1 }}
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: ${{ steps.scan-grype.outputs.sarif }}
category: grype
# - name: Scan image with snyk
# # if: github.event_name != 'pull_request'
# uses: snyk/actions/docker@master
# continue-on-error: true
# env:
# SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# with:
# command: test
# image: ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }}
# args: --file=${{ env.DOCKER_FILE }} --project-name=api
prod:
name: Push prod images
if: github.event_name != 'pull_request'
# if: contains(github.ref, 'refs/heads/main')
needs: [build-prod, test-prod, test]
permissions:
# Interact with GitHub OIDC Token endpoint for AWS
id-token: write
contents: read
# Push to ghcr.io repository
packages: write
# Upload JUnit report files
# https://github.com/EnricoMi/publish-unit-test-result-action#permissions
checks: write
pull-requests: write
issues: read
runs-on: ubuntu-latest
steps:
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# - name: Tag GHCR release as latest
# run: |
# docker buildx imagetools create \
# --append ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{env.PROD_VAR}}${{env.IMAGE_VER}} \
# --tag ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{env.IMAGE_TAG}}
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Check out source
uses: actions/checkout@v3
- name: Set up Docker buildx
id: buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
# - name: Build final prod image and push to GHCR as latest
# uses: docker/build-push-action@v3
# with:
# file: ${{ env.DOCKER_FILE }}
# target: prod
# context: .
# builder: ${{ steps.buildx.outputs.name }}
# push: true
# cache-from: type=gha,scope=${{ github.workflow }}-${{ env.PROD_VAR }}
# cache-to: type=gha,scope=${{ github.workflow }}-${{ env.PROD_VAR }},mode=max
# tags: |
# ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{ env.IMAGE_VER }}
# ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{ env.IMAGE_TAG }}
- name: Build final prod image and push to Docker Hub
if: ${{ env.DEPLOY_DOCKER_HUB == 1 }}
uses: docker/build-push-action@v3
with:
file: ${{ env.DOCKER_FILE }}
target: prod
context: .
builder: ${{ steps.buildx.outputs.name }}
push: true
cache-from: type=gha,scope=${{ github.workflow }}-${{ env.PROD_VAR }}
cache-to: type=gha,scope=${{ github.workflow }}-${{ env.PROD_VAR }},mode=max
# ssh: default
tags: |
docker.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
docker.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_VER }}
# secrets: |
# "access_token=${{ secrets.DEVOPS_ACCESS_TOKEN }}"
# "oban_key_fingerprint=${{ secrets.OBAN_KEY_FINGERPRINT }}"
# "oban_license_key=${{ secrets.OBAN_LICENSE_KEY }}"
- name: Configure AWS credentials
if: env.AWS_ENABLED
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ env.AWS_REGION }}
- name: Log in to Amazon ECR
if: ${{ env.AWS_ENABLED == 1 }}
id: ecr-login
uses: aws-actions/amazon-ecr-login@v1
- name: Set vars
if: ${{ env.AWS_ENABLED == 1 }}
run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> $GITHUB_ENV
# - name: Log in to ECR
# uses: docker/login-action@v2
# with:
# registry: ${{ env.ECR_REGISTRY }}
# username: ${{ secrets.AWS_ACCESS_KEY_ID }}
# password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# - name: Tag ECR release as latest
# if: ${{ env.AWS_ENABLED == 1 }}
# run: |
# export MANIFEST=$(aws ecr batch-get-image --repository-name "${{env.ECR_IMAGE_OWNER}}/${{env.IMAGE_NAME}}" \
# --image-ids imageTag=${{ env.IMAGE_VER }} --output json | jq --raw-output --join-output '.images[0].imageManifest')
# aws ecr put-image --repository-name "${{env.ECR_IMAGE_OWNER}}/${{env.IMAGE_NAME}}" \
# --image-tag ${{ env.IMAGE_TAG }} --image-manifest "$MANIFEST"
# aws ecr describe-images --repository-name "${{env.ECR_IMAGE_OWNER}}/${{env.IMAGE_NAME}}"
- name: Build final prod image and push to AWS ECR
if: ${{ env.AWS_ENABLED == 1 }}
uses: docker/build-push-action@v3
env:
REGISTRY: "${{ env.ECR_REGISTRY }}/"
IMAGE_OWNER: "${{ env.ECR_IMAGE_OWNER }}/"
VAR: ${{ env.PROD_VAR }}
with:
file: ${{ env.DOCKER_FILE }}
target: prod
context: .
builder: ${{ steps.buildx.outputs.name }}
push: true
cache-from: type=gha,scope=${{ github.workflow }}-${{ env.PROD_VAR }}
cache-to: type=gha,scope=${{ github.workflow }}-${{ env.PROD_VAR }},mode=max
# ssh: default
tags: |
${{env.ECR_REGISTRY}}/${{env.ECR_IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{env.IMAGE_TAG}}
${{env.ECR_REGISTRY}}/${{env.ECR_IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{env.IMAGE_VER}}
# secrets: |
# "access_token=${{ secrets.DEVOPS_ACCESS_TOKEN }}"
# "oban_key_fingerprint=${{ secrets.OBAN_KEY_FINGERPRINT }}"
# "oban_license_key=${{ secrets.OBAN_LICENSE_KEY }}"
deploy:
name: Deploy to ECS
if: ${{ env.AWS_ENABLED == 1 }}
needs: [prod]
permissions:
# Interact with GitHub OIDC Token endpoint for AWS
id-token: write
contents: read
runs-on: ubuntu-latest
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ env.AWS_REGION }}
- name: Log in to Amazon ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v1
- name: Set vars
run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> $GITHUB_ENV
- name: Check out source
uses: actions/checkout@v3
# https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-resources.html
# - name: Generate CodeDeploy appspec.yml
# env:
# CONTAINER_NAME: "iot-app"
# PORT: "4000"
# run: sed -i -e "s!<NAME>!${CONTAINER_NAME}!g" -e "s!<PORT>!${PORT}!g" ecs/appspec.yml
- name: Generate ECS task-defintion.json
env:
AWSLOGS_REGION: ${{ env.AWS_REGION }}
TASK_ROLE_ARN: "arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/iot-app-20230922164312318900000004"
EXECUTION_ROLE_ARN: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/iot-ecs-task-execution-role
HOST: rubegoldberg.io
run: jq --null-input -f ecs/task-definition.json.jq | tee ecs/task-definition.json
- name: Put new image ID in ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.TASKDEF }}
container-name: ${{ env.ECS_CONTAINER }}
image: ${{env.ECR_REGISTRY}}/${{ env.ECR_IMAGE_OWNER }}/${{env.IMAGE_NAME}}:${{env.PROD_VAR}}${{env.IMAGE_VER}}
- name: Deploy to Amazon ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
cluster: ${{ env.ECS_CLUSTER }}
service: ${{ env.ECS_SERVICE }}
wait-for-service-stability: true
codedeploy-appspec: ecs/appspec.yml
codedeploy-application: ${{ env.CODEDEPLOY_APPLICATION }}
codedeploy-deployment-group: ${{ env.CODEDEPLOY_DEPLOYMENT_GROUP }}
# https://github.com/marketplace/actions/slack-notify-build
# https://github.com/marketplace/actions/post-slack-message