Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add github actions for deploying AWS ECS envs #853

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .github/workflows/callable-build-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: "Task: Build"
on:
workflow_call:
inputs:
override_sha:
description: 'Optionally force checkout of a specific sha'
default: ''
type: string
push:
description: 'To push or not to push'
required: true
type: boolean
outputs:
container_image_digest:
description: "The sha256 digest of the built container image"
value: ${{ jobs.docker.outputs.digest }}

permissions:
packages: write
contents: read
attestations: write
id-token: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
MattMatt27 marked this conversation as resolved.
Show resolved Hide resolved
cancel-in-progress: true

jobs:
docker:
runs-on: ubuntu-24.04
outputs:
digest: ${{ steps.push.outputs.digest }}
steps:
- uses: actions/checkout@v4
if: ${{ inputs.override_sha != '' }}
with:
fetch-depth: 1
ref: ${{ inputs.override_sha }}

- uses: actions/checkout@v4
if: ${{ inputs.override_sha == '' }}
with:
fetch-depth: 1

- name: Configuration
id: config
run: |
REPOSITORY_OWNER=$(tr "[:upper:]" "[:lower:]" <<< "${{ github.repository_owner }}")
echo "REPOSITORY_OWNER=${REPOSITORY_OWNER}" >> "$GITHUB_OUTPUT"
REPOSITORY_NAME=$(tr "[:upper:]" "[:lower:]" <<< "${{ github.event.repository.name }}")
echo "REPOSITORY_NAME=${REPOSITORY_NAME}" >> "$GITHUB_OUTPUT"
TARGET_IMAGE="ghcr.io/${REPOSITORY_OWNER}/${REPOSITORY_NAME}"
echo "TARGET_IMAGE=${TARGET_IMAGE}" >> "$GITHUB_OUTPUT"
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ steps.config.outputs.REPOSITORY_OWNER }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
context: workflow
images: ${{ steps.config.outputs.TARGET_IMAGE }}
tags: |-
${{ inputs.override_sha == '' && 'type=ref,event=branch' || '' }}
${{ inputs.override_sha == '' && 'type=ref,event=tag' || '' }}
${{ inputs.override_sha == '' && 'type=ref,event=pr' || '' }}
${{ inputs.override_sha == '' && '# skip raw sha' || format('type=raw,value=sha-{0}', inputs.override_sha) }}
labels: |-
${{ inputs.override_sha != '' && 'org.opencontainers.image.version=unknown' }}
${{ inputs.override_sha != '' && format('org.opencontainers.image.revision={0}', inputs.override_sha) }}
flavor: |
latest=false
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: ${{ inputs.push }}
platforms: "linux/amd64"
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Generate artifact attestation
uses: actions/attest-build-provenance@v2
if: ${{ inputs.push }}
MattMatt27 marked this conversation as resolved.
Show resolved Hide resolved
with:
subject-name: ${{ steps.config.outputs.TARGET_IMAGE }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: ${{ inputs.push }}
79 changes: 79 additions & 0 deletions .github/workflows/callable-deploy-ecs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: "Task: Deploy"
on:
workflow_call:
inputs:
env:
description: Target environment of deployment. (stg, prod)
required: true
type: string
container_digest:
description: 'The container sha256 digest to deploy'
required: true
type: string

permissions:
contents: 'read'
id-token: 'write'

concurrency:
group: ${{ inputs.env }}
cancel-in-progress: false
MattMatt27 marked this conversation as resolved.
Show resolved Hide resolved

jobs:
deploy:
name: dashboard
runs-on: ubuntu-24.04
environment: ${{ inputs.env }}
steps:
- uses: actions/checkout@v4

- name: Configuration
id: config
run: |
MattMatt27 marked this conversation as resolved.
Show resolved Hide resolved
REPOSITORY_OWNER=$(tr "[:upper:]" "[:lower:]" <<< "${{ github.repository_owner }}")
echo "REPOSITORY_OWNER=${REPOSITORY_OWNER}" >> "$GITHUB_OUTPUT"
REPOSITORY_NAME=$(tr "[:upper:]" "[:lower:]" <<< "${{ github.event.repository.name }}")
echo "REPOSITORY_NAME=${REPOSITORY_NAME}" >> "$GITHUB_OUTPUT"
TARGET_IMAGE="ghcr.io/${REPOSITORY_OWNER}/${REPOSITORY_NAME}"
echo "TARGET_IMAGE=${TARGET_IMAGE}" >> "$GITHUB_OUTPUT"
TARGET_IMAGE_W_DIGEST="${TARGET_IMAGE}@${{ inputs.container_digest }}"
echo "TARGET_IMAGE_W_DIGEST=${TARGET_IMAGE_W_DIGEST}" >> "$GITHUB_OUTPUT"
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.IAM_ROLE_ARN }}
aws-region: us-east-2

- name: Download task definition
run: |
aws ecs describe-task-definition \
--task-definition ${{ vars.ECS_TASK_DEF_FAMILY }} \
--query taskDefinition \
> task.json
# Remove Ignored Properties
echo "$( jq 'del(.compatibilities)' task.json )" > task.json
echo "$( jq 'del(.taskDefinitionArn)' task.json )" > task.json
echo "$( jq 'del(.requiresAttributes)' task.json )" > task.json
echo "$( jq 'del(.revision)' task.json )" > task.json
echo "$( jq 'del(.status)' task.json )" > task.json
echo "$( jq 'del(.registeredAt)' task.json )" > task.json
echo "$( jq 'del(.registeredBy)' task.json )" > task.json
# Update Image
echo "$( jq --arg image "${{ steps.config.outputs.TARGET_IMAGE_W_DIGEST }}" '.containerDefinitions |= map((select(.name == "dashboard") | .image) |= $image)' task.json )" > task.json
cat task.json
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: task.json
service: ${{ vars.ECS_SERVICE }}
cluster: ${{ vars.ECS_CLUSTER }}
wait-for-service-stability: true
90 changes: 90 additions & 0 deletions .github/workflows/manual-deploy-sha.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: 'Manual Deploy: Git Sha'
on:
workflow_dispatch:
inputs:
sha:
type: string
required: true
description: Git sha (long format) to build and deploy
env:
type: environment
default: stg

concurrency:
group: ${{ github.workflow }}-${{ inputs.sha }}
cancel-in-progress: false

jobs:
# This job builds a new container and deploys it to the target environment. It
# is unsafe to deploy such arbitrary builds directly to production without
# first trialing them in lower environments.

validate:
name: "Validate Inputs"
runs-on: ubuntu-24.04
steps:
- name: Verify not production
if: ${{ inputs.env == 'prod' }}
run: |
cat << EOF
#---------------------------------------------------------------------------------
# ERROR: Cannot deploy arbitrary sha to production
#---------------------------------------------------------------------------------
#
# DETAILS:
#
# This job builds a new container and deploys it to the target environment. It
# is unsafe to deploy such arbitrary builds directly to production without
# first trialing them in lower environments.
#
# WORKAROUND:
#
# If you truely must release a specific sha,
#
# 1) Use this workflow to build and deploy the sha to staging
# 2) Deploy the assigned tag from that deployment using the
# "Manual Deploy: Docker Tag" workflow
# Note: You must cite a DOCKER tag
# Which should look like "sha-9e14d6f3da3c3c3f7ea73b74dec8c931365745e4"
#
#---------------------------------------------------------------------------------
EOF
exit 1
- name: Verify sha is hex
run: |
if [[ ! "${{ inputs.sha }}" =~ ^[0-9A-Fa-f]+$ ]]; then
echo "sha must be hexidecimal"; exit 1
fi
length=$(expr length "${{ inputs.sha }}")
if [ "$length" != "40" ]; then
echo "sha must be all 40 characters"; exit 1
fi
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}

- name: Verify commit exists
run: |
git cat-file commit ${{ inputs.sha }}
build-docker:
name: "Build"
uses: ./.github/workflows/callable-build-docker.yml
secrets: inherit
with:
override_sha: ${{ inputs.sha }}
push: true
needs:
- validate

deploy-ecs:
name: "Deploy container to ECS"
uses: ./.github/workflows/callable-deploy-ecs.yml
secrets: inherit
with:
env: ${{ inputs.env }}
container_digest: ${{ needs.build-docker.outputs.container_image_digest }}
needs:
- build-docker
76 changes: 76 additions & 0 deletions .github/workflows/manual-deploy-tag.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: 'Manual Deploy: Docker Tag'
on:
workflow_dispatch:
inputs:
tag:
type: string
required: true
description: Tag of the DOCKER image to deploy
env:
type: environment
default: stg

concurrency:
group: ${{ github.workflow }}-${{ inputs.env }}
cancel-in-progress: false

jobs:

locate:
name: Find Target Image
runs-on: ubuntu-24.04
outputs:
digest: ${{ steps.inspect.outputs.digest }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Configuration
id: config
run: |
REPOSITORY_OWNER=$(tr "[:upper:]" "[:lower:]" <<< "${{ github.repository_owner }}")
echo "REPOSITORY_OWNER=${REPOSITORY_OWNER}" >> "$GITHUB_OUTPUT"
REPOSITORY_NAME=$(tr "[:upper:]" "[:lower:]" <<< "${{ github.event.repository.name }}")
echo "REPOSITORY_NAME=${REPOSITORY_NAME}" >> "$GITHUB_OUTPUT"
TARGET_IMAGE="ghcr.io/${REPOSITORY_OWNER}/${REPOSITORY_NAME}"
echo "TARGET_IMAGE=${TARGET_IMAGE}" >> "$GITHUB_OUTPUT"
TARGET_IMAGE_W_TAG="${TARGET_IMAGE}:${{ inputs.tag }}"
echo "TARGET_IMAGE_W_TAG=${TARGET_IMAGE_W_TAG}" >> "$GITHUB_OUTPUT"
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Pull target tag & inspect
id: inspect
run: |
set -e
docker pull ${{ steps.config.outputs.TARGET_IMAGE_W_TAG }}
checksum=$(
docker inspect ${{ steps.config.outputs.TARGET_IMAGE_W_TAG }} \
| jq '.[].RepoDigests.[]' \
| tr -d '"' \
| sed 's/^.*@//'
)
echo "digest=${checksum}" >> "$GITHUB_OUTPUT"
deploy:
name: "Deploy"
uses: ./.github/workflows/callable-deploy-ecs.yml
secrets: inherit
with:
env: ${{ inputs.env }}
container_digest: ${{ needs.locate.outputs.digest }}
needs:
- locate
29 changes: 29 additions & 0 deletions .github/workflows/on-merge-to-master.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: On Branch Update (master)
on:
push:
branches:
- master
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}
MattMatt27 marked this conversation as resolved.
Show resolved Hide resolved
cancel-in-progress: false

jobs:

build-docker:
name: "Build"
uses: ./.github/workflows/callable-build-docker.yml
secrets: inherit
with:
push: true

deploy-env-stg:
name: "Deploy Staging"
uses: ./.github/workflows/callable-deploy-ecs.yml
MattMatt27 marked this conversation as resolved.
Show resolved Hide resolved
secrets: inherit
with:
env: stg
container_digest: ${{ needs.build-docker.outputs.container_image_digest }}
needs:
- build-docker
Loading