Skip to content

Make consistent with ci #97

Make consistent with ci

Make consistent with ci #97

Workflow file for this run

---
name: CI
# 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
# Use matrix for multiple versions of Elixir, OTP, and OS
on: push
# on:
# push:
# branches:
# - main
# - qa
# tags: [prod]
# paths-ignore:
# - 'README.md'
# - '.github/**'
# - '.vscode'
# - '.gitignore'
# pull_request:
# # branches: [main]
# types: [opened,synchronize,reopened,labeled,unlabeled]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# Name of image
IMAGE_NAME: foo-app
# Name of org in GHCR Docker repository (must be lowercase)
IMAGE_OWNER: ${{ github.repository_owner }}
# ECR Docker repo org name (may be blank, otherwise must have trailing slash)
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 }}
# 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: 1
# AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
# AWS_ROLE_TO_ASSUME: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/cogini-foo-dev-app-github-action
# AWS_REGION: us-east-1
# S3_BUCKET_ASSETS: cogini-foo-app-dev-app-assets
# CLOUDFRONT_CDN_DISTRIBUTION_ID: XXXX
# S3_BUCKET_CI: cogini-prod-foo-ci
# Health check port for app
APP_PORT: 4000
# Datadog
# DD_API_KEY: ${{ secrets.ACTIONS_DD_API_KEY }}
# DD_ENV: ci
# DD_TAGS: "environment:ci"
# MIX_ENV: foo
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
ECS_SERVICE_WORKER: foo-worker
ECS_CONTAINER_WORKER: foo-worker
TASKDEF_WORKER: ecs/task-definition.worker.json
APPSPEC: ecs/appspec.yml
# AWS SSM Parameter Store name prefix
# AWS_PS_PREFIX: cogini/foo/dev
# Name of environment for resources created by Terraform
# TERRAFORM_ENV: dev
# TASK_ROLE_ARN: "arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/foo-app"
# EXECUTION_ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/foo-ecs-task-execution-role
# GitHub Advanced Security, free for open source, otherwise a paid feature
# 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: 1
DEPLOY_DOCKER_HUB: 0
# Docker
DOCKER_BUILDKIT: '1'
COMPOSE_DOCKER_CLI_BUILD: '1'
COMPOSE_FILE: docker-compose.gha.yml
DOCKER_FILE: deploy/debian.Dockerfile
jobs:
setup-matrix:
name: Set up build matrix
runs-on: ubuntu-latest
outputs:
test-matrix: ${{ steps.common-matrix.outputs.result }}
prod-matrix: ${{ steps.prod-matrix.outputs.result }}
deploy-matrix: ${{ steps.deploy-matrix.outputs.result }}
assets-matrix: ${{ steps.assets-matrix.outputs.result }}
steps:
- uses: actions/github-script@v7
id: common-matrix
# Specify versions of Erlang, Elixir, and base OS
# in a combination supported by https://hub.docker.com/r/hexpm/elixir/tags
with:
script: |
return {
include: [
{
elixir: "1.17.0",
otp: "27.0",
build_os_ver: "bullseye-20240513",
prod_os_ver: "bullseye-slim",
os: "debian"
},
{
elixir: "1.16.3",
otp: "26.2.5",
build_os_ver: "bullseye-20240513",
prod_os_ver: "bullseye-slim",
os: "debian"
}
]
}
# druzan/setup-matrix is slow, taking about 15 seconds to initialize
# - name: Define matrix for test containers
# id: test-matrix
# uses: druzsan/setup-matrix@v2
# with:
# matrix: |
# include:
# - elixir: 1.16.3
# otp: 26.2.5
# build_os_ver: bullseye-20240513
# prod_os_ver: bullseye-slim
# os: debian
- uses: actions/github-script@v7
id: prod-matrix
with:
script: |
return {
include: [
{
elixir: "1.16.3",
otp: "26.2.5",
build_os_ver: "bullseye-20240513",
prod_os_ver: "bullseye-slim",
os: "debian"
},
{
elixir: "1.16.3",
otp: "26.2.5",
build_os_ver: "bullseye-20240513",
prod_os_ver: "bullseye-slim",
os: "distroless"
}
]
}
- uses: actions/github-script@v7
id: deploy-matrix
with:
script: |
return {
include: [
{
elixir: "1.16.3",
otp: "26.2.5",
build_os_ver: "bullseye-20240513",
prod_os_ver: "bullseye-slim",
os: "debian"
}
]
}
- uses: actions/github-script@v7
id: assets-matrix
# Specify versions of Erlang, Elixir, and base OS
# in a combination supported by https://hub.docker.com/r/hexpm/elixir/tags
with:
script: |
return {
include: [
{
elixir: "1.16.3",
otp: "26.2.5",
build_os_ver: "bullseye-20240513",
prod_os_ver: "bullseye-slim",
os: "debian"
}
]
}
create-release:
name: Create GitHub release
runs-on: ubuntu-latest
outputs:
release-tag: build-${{ github.run_number }}
upload-url: ${{ steps.release-view.outputs.upload-url }}
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: build-${{ github.run_number }}
steps:
- name: Check out source
uses: actions/checkout@v4
# - name: Tag repository
# run: |
# echo "$RELEASE_TAG"
# git tag "$RELEASE_TAG"
# git push origin "$RELEASE_TAG"
- name: Create release
run: gh release create "$RELEASE_TAG" -n "Build $RELEASE_TAG"
- name: Get release info
id: release-view
run: |
UPLOAD_URL=$(gh release view "$RELEASE_TAG" --json uploadUrl --jq .uploadUrl | cut -d'{' -f'1')
echo "upload-url=$UPLOAD_URL" >> $GITHUB_OUTPUT
build-test:
name: Build test image
needs: [setup-matrix]
permissions:
# Interact with GitHub OIDC Token endpoint for AWS
id-token: write
contents: read
# Push to ghcr.io repository
packages: write
runs-on: ubuntu-latest
environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.test-matrix) }}
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: Check out source
uses: actions/checkout@v4
- 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@v3
# 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@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# - name: Configure ssh keys
# uses: webfactory/ssh-agent@v0.7.0
# # 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 "git@github.com: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: 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: ${{ secrets.AWS_ROLE_TO_ASSUME }}
# aws-region: ${{ vars.AWS_REGION }}
# - name: Get test data files
# run: |
# mkdir -p test/bert
# aws s3 cp s3://${{ vars.S3_BUCKET_CI }}/test-data.zip test-data.zip
# unzip -o test-data.zip -d test/bert
- name: Set up Docker buildx
id: buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: network=host
- name: Build test image and push to GHCR
uses: docker/build-push-action@v5
# 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
# no-cache: github.run_attempt != '1'
# platforms: linux/amd64,linux/arm64
# 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
needs: [build-test, setup-matrix]
# 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
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.test-matrix) }}
# 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@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Check out source
uses: actions/checkout@v4
- 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, setup-matrix]
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: ${{ fromJson(needs.setup-matrix.outputs.test-matrix) }}
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@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Check out source
uses: actions/checkout@v4
- 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 --quiet-with-result --format github
# run: docker compose run test mix dialyzer --no-check --ignore-exit-status --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@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout source
uses: actions/checkout@v4
# with:
# # Fetch all history for all branches and tags for Gitleaks
# fetch-depth: 0
- 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: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }}
uses: github/codeql-action/upload-sarif@v3
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: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.scan-grype.outputs.sarif }}
category: grype
# - name: Scan with Gitleaks
# uses: gitleaks/gitleaks-action@v2
# env:
# GITHUB_TOKEN: ${{ github.token }}
# GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} # Only required for Organizations, not personal accounts.
# # upload sarif artifact when secrets are detected
# GITLEAKS_ENABLE_UPLOAD_ARTIFACT: true
build-prod:
name: Build prod image
needs: [setup-matrix]
permissions:
# Interact with GitHub OIDC Token endpoint for AWS
id-token: write
contents: read
# Push to ghcr.io repository
packages: write
runs-on: ubuntu-latest
environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.prod-matrix) }}
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: Debug environment
run: |
echo "ref_name: ${{ github.ref_name }}"
echo "environment: ${{ github.event.deployment.environment }}"
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Configure AWS credentials
if: ${{ env.AWS_ENABLED == 1 }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ vars.AWS_REGION }}
- name: Log in to Amazon ECR
if: ${{ env.AWS_ENABLED == 1 }}
id: ecr-login
uses: aws-actions/amazon-ecr-login@v2
- 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@v3
# with:
# registry: ${{ env.ECR_REGISTRY }}
# username: ${{ secrets.AWS_ACCESS_KEY_ID }}
# password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Check out source
uses: actions/checkout@v4
- 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/ssh-agent@v0.7.0
# # 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 "git@github.com: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@v3
with:
driver-opts: network=host
- name: Build prod image and push
uses: docker/build-push-action@v5
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
# no-cache: github.run_attempt != '1'
# 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 }}
${{ 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 }}
# 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 }}"
test-prod:
name: Run external API tests
needs: [build-prod, setup-matrix]
permissions:
# Interact with GitHub OIDC Token endpoint for AWS
id-token: write
# Read from ghcr.io repository
packages: read
contents: read
# Upload JUnit report files
# https://github.com/EnricoMi/publish-unit-test-result-action#permissions
issues: read
checks: write
pull-requests: write
runs-on: ubuntu-latest
environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.prod-matrix) }}
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@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Check out source
uses: actions/checkout@v4
# - 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: ${{ secrets.AWS_ROLE_TO_ASSUME }}
# aws-region: ${{ vars.AWS_REGION }}
# - name: Get test data files
# run: |
# mkdir -p bounce-data
# aws s3 cp s3://${{ vars.S3_BUCKET_CI }}/test-data.zip test-data.zip
# unzip -o test-data.zip -d bounce-data
- 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 prod
- name: Display logs
if: failure()
run: |
echo "postgres logs"
docker compose logs --timestamps postgres
echo "postgres health"
docker inspect --format "{{json .State.Health }}" $(docker compose ps -q postgres) | jq
echo "prod health"
docker inspect --format "{{json .State.Health }}" $(docker compose ps -q prod) | jq
echo "prod logs"
docker compose logs --timestamps prod
echo "ps"
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: |
# # Rundandant with bin/start-docker
# # docker compose run prod eval '${{ env.ELIXIR_MODULE }}.Release.create_repos()'
# # docker compose run prod eval '${{ env.ELIXIR_MODULE }}.Release.migrate()'
# docker compose run --entrypoint bin/prod prod eval '${{ env.ELIXIR_MODULE }}.Release.run_seeds()'
# Basic smoke test to make sure service came up
- name: Run health check
run: for i in 1 2 3 4 5 6 7 8 9 10; do curl -v http://localhost:${{ env.APP_PORT }}/healthz && break || sleep 1; done
- name: Display logs
if: failure()
run: |
echo "postgres logs"
docker compose logs --timestamps postgres
echo "postgres health"
docker inspect --format "{{json .State.Health }}" $(docker compose ps -q postgres) | jq
echo "prod logs"
docker compose logs --timestamps prod
echo "prod health"
docker inspect --format "{{json .State.Health }}" $(docker compose ps -q prod) | jq
echo "ps"
docker compose ps --format json | jq .
# - 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: |
# mkdir -p junit-reports
# docker compose run newman run ${{env.NEWMAN_ARGS}} Queries.postman_collection.json
# - name: Display logs
# if: failure()
# run: |
# echo "postgres logs"
# docker compose logs --timestamps postgres
# echo "postgres health"
# docker inspect --format "{{json .State.Health }}" $(docker compose ps -q postgres) | jq
# echo "prod logs"
# docker compose logs --timestamps prod
# echo "prod health"
# docker inspect --format "{{json .State.Health }}" $(docker compose ps -q prod) | jq
# echo "ps"
# docker compose ps --format json | jq .
# 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, setup-matrix]
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: ${{ fromJson(needs.setup-matrix.outputs.prod-matrix) }}
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@v3
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: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }}
uses: github/codeql-action/upload-sarif@v3
# 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
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: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }}
uses: github/codeql-action/upload-sarif@v3
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
# upload-assets:
# name: Upload assets to CDN
# # Extract assets from container
# if: github.event_name != 'pull_request'
# # if: contains(github.ref, 'refs/heads/main')
# # needs: [build-prod, test-prod, test]
# # needs: [test-prod, test, test-dialyzer]
# needs: [build-prod]
# 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
# environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }}
# strategy:
# fail-fast: false
# matrix: ${{ fromJson(needs.setup-matrix.outputs.assets-matrix) }}
# steps:
# - name: Debug environment
# run: |
# echo "ref_name: ${{ github.ref_name }}"
# echo "environment: ${{ github.event.deployment.environment }}"
#
# - name: Log in to GHCR
# uses: docker/login-action@v3
# with:
# registry: ghcr.io
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }}
#
# - name: Configure AWS credentials
# if: ${{ env.AWS_ENABLED == 1 }}
# uses: aws-actions/configure-aws-credentials@v4
# with:
# role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
# aws-region: ${{ vars.AWS_REGION }}
#
# - name: Check out source
# uses: actions/checkout@v4
#
# - name: Set up Docker buildx
# id: buildx
# uses: docker/setup-buildx-action@v3
# with:
# driver-opts: network=host
#
# - name: Get build artifacts on host
# uses: docker/build-push-action@v5
# with:
# file: ${{ env.DOCKER_FILE }}
# target: artifacts
# 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: false
# cache-from: type=gha,scope=${{ github.workflow }}-${{ env.VAR }}
# outputs: type=local,dest=output
#
# - name: List output dir
# run: find output/static -print
#
# - name: Sync to S3
# run: aws s3 sync output/static/ s3://${{ vars.S3_BUCKET_ASSETS }}
#
# - name: Create CloudFront invalidation
# run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_CDN_DISTRIBUTION_ID }} --paths "/*"
upload-assets:
name: Build and upload assets to CDN
needs: [setup-matrix]
# Build assets in host instead of Docker for better performance.
if: github.event_name != 'pull_request'
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
environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.assets-matrix) }}
env:
MIX_ENV: prod
steps:
- name: Check out source
uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Enable corepack
run: corepack enable
- name: Install Erlang and Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{ matrix.otp }}
elixir-version: ${{ matrix.elixir }}
- name: Cache deps
id: cache-deps
uses: actions/cache@v4
env:
cache-name: cache-elixir-deps
with:
path: deps
key: ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-
- name: Cache compiled build
id: cache-build
uses: actions/cache@v4
env:
cache-name: cache-compiled-build
with:
path: _build
key: ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-
- name: Cache node-modules
id: node-modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: assets/node_modules
key: ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-
- name: Install build tools
run: mix do local.rebar --force, local.hex --force
# Force full recompile on builds that are retried.
- name: Clean deps to avoid flaky incremental builds
if: github.run_attempt != '1'
run: |
mix deps.clean --all
mix clean
shell: sh
- name: Get Elixir deps
run: mix deps.get
- name: Compile
run: mix compile
- name: Install JavaScript libs
# working-directory: assets
# run: npm install && npm run deploy
run: yarn --cwd ./assets install --prod
- name: Deploy assets
run: mix assets.deploy
- name: Configure AWS credentials
if: ${{ env.AWS_ENABLED == 1 }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ vars.AWS_REGION }}
- name: Sync to S3
run: aws s3 sync priv/static s3://${{ vars.S3_BUCKET_ASSETS }}
- name: Create CloudFront invalidation
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_CDN_DISTRIBUTION_ID }} --paths "/*"
deploy-ecs:
name: Deploy to AWS ECS
# if: ${{ env.AWS_ENABLED == 1 }}
needs: [test-prod, test, test-dialyzer, upload-assets, setup-matrix]
permissions:
# Interact with GitHub OIDC Token endpoint for AWS
id-token: write
contents: read
runs-on: ubuntu-latest
environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.deploy-matrix) }}
env:
DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile
VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }}
steps:
- name: Configure AWS credentials
if: ${{ env.AWS_ENABLED == 1 }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ vars.AWS_REGION }}
- name: Log in to Amazon ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v2
- name: Set vars
run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> $GITHUB_ENV
- name: Check out source
uses: actions/checkout@v4
# https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-resources.html
# - name: Generate CodeDeploy appspec.yml
# env:
# CONTAINER_NAME: "foo-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: ${{ vars.AWS_REGION }}
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
AWS_PS_PREFIX: ${{ vars.AWS_PS_PREFIX }}
EXECUTION_ROLE_ARN: ${{ secrets.EXECUTION_ROLE_ARN}}
TASK_ROLE_ARN: ${{ secrets.TASK_ROLE_ARN }}
run: jq --null-input -f "${TASKDEF}.jq" | tee "$TASKDEF"
- 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.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: ${{ env.APPSPEC }}
# codedeploy-application: ${{ env.CODEDEPLOY_APPLICATION }}
# codedeploy-deployment-group: ${{ env.CODEDEPLOY_DEPLOYMENT_GROUP }}
# - name: Create AppSignal revision
# env:
# APPSIGNAL_APP_ENV: ${{ (github.ref_name == 'main' && 'stage') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'prod') }}
# APPSIGNAL_APP_NAME: ${{ env.ECS_SERVICE }}
# APPSIGNAL_PUSH_API_KEY: ${{ secrets.APPSIGNAL_PUSH_API_KEY }}
# REVISION: ${{ env.GITHUB_SHA_SHORT }}
# # REVISION: ${{ env.GITHUB_SHA }}
# run: |
# printf '{"revision":"%s","user":"GitHub"}' "$REVISION" | tee revision.json
# export APPSIGNAL_URL=$(printf 'https://push.appsignal.com/1/markers?api_key=%s&name=%s&environment=%s' "$APPSIGNAL_PUSH_API_KEY" "$APPSIGNAL_APP_NAME" "$APPSIGNAL_APP_ENV")
# # CodeBuild has an insane conflict with YAML syntax and the URL, so put it in a file
# echo "url = \"$APPSIGNAL_URL\"" | tee curl.txt
# curl -g --config curl.txt -d @revision.json
- name: Generate ECS task-defintion.json (worker)
env:
AWSLOGS_REGION: ${{ vars.AWS_REGION }}
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
AWS_PS_PREFIX: ${{ vars.AWS_PS_PREFIX }}
EXECUTION_ROLE_ARN: ${{ secrets.EXECUTION_ROLE_ARN}}
TASK_ROLE_ARN: ${{ secrets.TASK_ROLE_ARN }}
run: jq --null-input -f "${TASKDEF_WORKER}.jq" | tee "$TASKDEF_WORKER"
- name: Put new image ID in ECS task definition (worker)
id: task-def-worker
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.TASKDEF_WORKER }}
container-name: ${{ env.ECS_CONTAINER_WORKER }}
image: ${{env.ECR_REGISTRY}}/${{ env.ECR_IMAGE_OWNER }}${{env.IMAGE_NAME}}:${{env.VAR}}${{env.IMAGE_VER}}
- name: Deploy to Amazon ECS (worker)
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def-worker.outputs.task-definition }}
cluster: ${{ env.ECS_CLUSTER }}
service: ${{ env.ECS_SERVICE_WORKER }}
# - name: Run CodeBuild
# uses: aws-actions/aws-codebuild-run-build@v1
# with:
# project-name: "foo-app"
# # buildspec-override: ecs/buildspec-docker.yml
# env-vars-for-codebuild: |
# custom,
# requester,
# event-name
# env:
# custom: my environment variable
# requester: ${{ github.actor }}
# event-name: ${{ github.event_name }}
tag:
name: Tag prod images as latest
if: github.event_name != 'pull_request'
# if: contains(github.ref, 'refs/heads/main')
needs: [deploy-ecs, setup-matrix]
# needs: [test-prod, test, test-dialyzer]
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
environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.deploy-matrix) }}
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@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure AWS credentials
if: ${{ env.AWS_ENABLED == 1 }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ vars.AWS_REGION }}
- name: Log in to Amazon ECR
if: ${{ env.AWS_ENABLED == 1 }}
id: ecr-login
uses: aws-actions/amazon-ecr-login@v2
- name: Set vars
if: ${{ env.AWS_ENABLED == 1 }}
run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> $GITHUB_ENV
# - name: Check out source
# uses: actions/checkout@v4
- 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.VAR }}${{ 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"
# Requires higher permissions
# aws ecr describe-images --repository-name "${{env.ECR_IMAGE_OWNER}}${{env.IMAGE_NAME}}"
- name: Tag GHCR release as latest
run: |
skopeo copy \
docker://ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{env.VAR}}${{env.IMAGE_VER}} \
docker://ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{env.IMAGE_TAG}}
# skopeo copy --multi-arch=index-only \
# docker://ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{env.VAR}}${{env.IMAGE_VER}} \
# docker://ghcr.io/${{env.IMAGE_OWNER}}/${{env.IMAGE_NAME}}:${{env.IMAGE_TAG}}
# deploy-codedeploy:
# name: Deploy using CodeDeploy
# # if: ${{ env.AWS_ENABLED == 1 }}
# # needs: [test-prod, test, test-dialyzer, upload-assets, setup-matrix]
# needs: [test, test-dialyzer, test-prod, setup-matrix, create-release]
# permissions:
# # Interact with GitHub OIDC Token endpoint for AWS
# id-token: write
# contents: read
# # Push to ghcr.io repository
# packages: write
# runs-on: ubuntu-latest
# environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }}
# strategy:
# fail-fast: false
# matrix: ${{ fromJson(needs.setup-matrix.outputs.deploy-matrix) }}
# env:
# DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile
# VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }}
# GH_TOKEN: ${{ github.token }}
# RELEASE_TAG: ${{ needs.create-release.outputs.release-tag }}
# steps:
# - name: Configure AWS credentials
# if: ${{ env.AWS_ENABLED == 1 }}
# uses: aws-actions/configure-aws-credentials@v4
# with:
# role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
# aws-region: ${{ vars.AWS_REGION }}
# - name: Check out source
# uses: actions/checkout@v4
# - name: Set variables
# id: vars
# run: |
# echo "GITHUB_SHA_SHORT=$(echo $GITHUB_SHA | cut -c 1-7)" >> $GITHUB_ENV
# - name: Download release files
# run: |
# gh release download "${RELEASE_TAG}" -p "codebuild-revision-${VAR}.zip"
# mkdir -p revision
# unzip -o "codebuild-revision-${VAR}.zip" -d revision
# - name: Set variables
# # run: echo "REVISION_FILE=${{env.CODEDEPLOY_APPLICATION}}-$(date +'%Y%m%d%H%M%S')-${{ github.run_number }}-${{env.VAR}}.zip" >> $GITHUB_ENV
# # run: echo "REVISION_FILE=${{env.CODEDEPLOY_APPLICATION}}-${{github.event.head_commit.timestamp}}-${{env.GITHUB_SHA_SHORT}}.zip" >> $GITHUB_ENV
# run: |
# echo "REVISION_FILE=${CODEDEPLOY_APPLICATION}-$(date +'%Y%m%d%H%M%S')-${RELEASE_TAG}-${VAR}.zip" >> $GITHUB_ENV
# - name: Create CodeDeploy revision
# run: |
# aws deploy push --application-name "${CODEDEPLOY_APPLICATION}" \
# --description "Bounce ${RELEASE_TAG} ${GITHUB_SHA_SHORT}" \
# --s3-location s3://${CODEDEPLOY_BUCKET}/${REVISION_FILE} \
# --source revision
# - name: Create CodeDeploy deployment
# run: |
# aws deploy create-deployment --application-name "${CODEDEPLOY_APPLICATION}" \
# --deployment-group-name "${CODEDEPLOY_DEPLOYMENT_GROUP}" \
# --s3-location "bucket=${CODEDEPLOY_BUCKET},key=${REVISION_FILE},bundleType=zip" \
# --description "Bounce ${RELEASE_TAG} ${GITHUB_SHA_SHORT}"
# To see debug logs, create a secret named ACTIONS_STEP_DEBUG with value 'true'