Setup standalone redis (#1638) #2147
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
on: push | |
name: Build | |
env: | |
APP_REPOSITORY_NAME: scfm | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: true | |
jobs: | |
build: | |
name: Build | |
runs-on: ubuntu-latest | |
outputs: | |
matrix: ${{ steps.set-deployment-matrix.outputs.matrix }} | |
matrixLength: ${{ steps.set-deployment-matrix.outputs.matrixLength }} | |
packageMatrix: ${{ steps.set-deployment-matrix.outputs.packageMatrix }} | |
env: | |
PGHOST: localhost | |
PGUSER: postgres | |
RAILS_ENV: test | |
CI: true | |
services: | |
postgres: | |
image: postgres | |
env: | |
POSTGRES_USER: postgres | |
POSTGRES_PASSWORD: "" | |
POSTGRES_HOST_AUTH_METHOD: trust | |
ports: | |
- 5432:5432 | |
# needed because the postgres container does not provide a healthcheck | |
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 | |
steps: | |
- name: Set Deployment Matrix | |
id: set-deployment-matrix | |
run: | | |
branchName=$(echo '${{ github.ref }}' | sed 's,refs/heads/,,g') | |
matrixSource=$(cat << EOF | |
[ | |
{ | |
"identifier": "open-ews-staging", | |
"branch": "develop", | |
"environment": "staging", | |
"friendly_image_tag": "beta", | |
"image_tag": "stag-${{ github.sha }}", | |
"ecs_cluster": "open-ews-staging", | |
"docs_path": "staging/docs/open-ews" | |
}, | |
{ | |
"identifier": "scfm", | |
"branch": "master", | |
"environment": "production", | |
"friendly_image_tag": "latest", | |
"image_tag": "prod-${{ github.sha }}", | |
"ecs_cluster": "scfm", | |
"docs_path": "docs/scfm" | |
} | |
] | |
EOF | |
) | |
matrix=$(echo $matrixSource | jq --arg branchName "$branchName" 'map(. | select((.branch==$branchName)) )') | |
echo "matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT | |
echo "matrixLength=$(echo $matrix | jq length)" >> $GITHUB_OUTPUT | |
echo "packageMatrix={\"platform\":[\"amd64\",\"arm64\"],\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Setup Ruby | |
uses: ruby/setup-ruby@v1 | |
with: | |
bundler-cache: true | |
- name: Setup Node | |
uses: actions/setup-node@v4 | |
with: | |
node-version-file: ".tool-versions" | |
cache: "yarn" | |
- name: Setup DB | |
run: bundle exec rails db:create db:schema:load | |
- name: Run Specs | |
run: | | |
bundle exec rails spec:prepare | |
bundle exec rspec --format RspecApiDocumentation::ApiFormatter | |
- name: Prepare Documentation Source | |
if: steps.set-deployment-matrix.outputs.matrixLength > 0 | |
run: | | |
cp -R doc/slate/source/* doc/api | |
cp app/assets/images/scfm_logo.png doc/api/logo.png | |
- name: Upload Documentation Source | |
if: steps.set-deployment-matrix.outputs.matrixLength > 0 | |
uses: actions/upload-artifact@v4 | |
with: | |
name: documentation_source | |
path: doc/api | |
build_documentation: | |
name: Build Documentation | |
runs-on: ubuntu-latest | |
needs: build | |
if: needs.build.outputs.matrixLength > 0 | |
strategy: | |
matrix: ${{fromJSON(needs.build.outputs.matrix)}} | |
steps: | |
- name: Checkout Slate | |
uses: actions/checkout@v4 | |
with: | |
ref: main | |
repository: slatedocs/slate | |
- name: Setup Ruby | |
uses: ruby/setup-ruby@v1 | |
with: | |
bundler-cache: true | |
ruby-version: "3" | |
- name: Download Documentation Source | |
uses: actions/download-artifact@v4 | |
with: | |
name: documentation_source | |
path: source | |
- name: Prepare Slate | |
run: | | |
echo "@import 'overrides';" >> source/stylesheets/_variables.scss | |
- name: Build API Documentation | |
run: bundle exec middleman build | |
- name: Upload API Documentation | |
uses: actions/upload-artifact@v4 | |
with: | |
name: api_documentation | |
path: build | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
role-skip-session-tagging: true | |
role-duration-seconds: 3600 | |
aws-region: ap-southeast-1 | |
- name: Deploy API Documentation | |
run: aws s3 sync --delete --acl public-read build s3://www.somleng.org/${{ matrix.docs_path }} | |
- name: Invalidate Cache | |
run: aws cloudfront create-invalidation --distribution-id E3962XCJFZ0KB1 --paths /docs/scfm/\* | |
build-packages: | |
name: Build Packages | |
runs-on: ubuntu-latest | |
if: needs.build.outputs.matrixLength > 0 | |
strategy: | |
matrix: ${{fromJSON(needs.build.outputs.packageMatrix)}} | |
fail-fast: false | |
needs: | |
- build | |
steps: | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
role-skip-session-tagging: true | |
role-duration-seconds: 3600 | |
aws-region: ap-southeast-1 | |
- name: Build image | |
uses: aws-actions/aws-codebuild-run-build@v1 | |
with: | |
project-name: scfm-${{ matrix.platform }} | |
buildspec-override: | | |
version: 0.2 | |
phases: | |
build: | |
steps: | |
- name: Build | |
run: | | |
aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS --password-stdin ${{ secrets.ECR_REGISTRY }} | |
export DOCKER_BUILDKIT=1 | |
docker build --cache-from ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --tag ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --push . | |
build-manifest: | |
name: Build Manifest | |
runs-on: ubuntu-latest | |
needs: | |
- build | |
- build-packages | |
strategy: | |
matrix: ${{fromJSON(needs.build.outputs.matrix)}} | |
steps: | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
role-skip-session-tagging: true | |
role-duration-seconds: 3600 | |
aws-region: ap-southeast-1 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Login to ECR | |
uses: docker/login-action@v3 | |
with: | |
registry: ${{ secrets.ECR_REGISTRY }} | |
- name: Build Manifest | |
run: | | |
declare -a platforms=("amd64" "arm64") | |
source_images=$(printf "${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-%s " "${platforms[@]}") | |
docker buildx imagetools create -t ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }} -t ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.image_tag }} $source_images | |
# Do this step in Github Actions because pushing to Github from AWS CodeBuild is slow | |
publish_images: | |
name: Publish Images | |
runs-on: ubuntu-latest | |
needs: | |
- build | |
- build-packages | |
strategy: | |
matrix: ${{fromJSON(needs.build.outputs.matrix)}} | |
steps: | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
role-skip-session-tagging: true | |
role-duration-seconds: 3600 | |
aws-region: ap-southeast-1 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.repository_owner }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Login to ECR | |
uses: docker/login-action@v3 | |
with: | |
registry: ${{ secrets.ECR_REGISTRY }} | |
- name: Publish Images | |
run: | | |
declare -a platforms=("amd64" "arm64") | |
for platform in "${platforms[@]}" | |
do | |
docker image pull ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-$platform | |
docker tag ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-$platform ${{ secrets.GHCR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-$platform | |
docker push ${{ secrets.GHCR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-$platform | |
done | |
source_images=$(printf "${{ secrets.GHCR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-%s " "${platforms[@]}") | |
docker buildx imagetools create -t ${{ secrets.GHCR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }} $source_images | |
deploy: | |
name: Deploy | |
runs-on: ubuntu-latest | |
needs: | |
- build | |
- build-manifest | |
strategy: | |
matrix: ${{fromJSON(needs.build.outputs.matrix)}} | |
steps: | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
role-skip-session-tagging: true | |
role-duration-seconds: 3600 | |
aws-region: ap-southeast-1 | |
- name: Get current webserver task definition | |
run: | | |
aws ecs describe-task-definition --task-definition ${{ matrix.identifier }}-webserver --query 'taskDefinition' > task-definition.json | |
- name: Inject new APP image into webserver task definition | |
id: render-webserver-task-def | |
uses: aws-actions/amazon-ecs-render-task-definition@v1 | |
with: | |
task-definition: task-definition.json | |
container-name: app | |
image: ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.image_tag }} | |
- name: Get current worker task definition | |
run: | | |
aws ecs describe-task-definition --task-definition ${{ matrix.identifier }}-worker --query 'taskDefinition' > task-definition.json | |
- name: Inject new APP image into worker task definition | |
id: render-worker-task-def | |
uses: aws-actions/amazon-ecs-render-task-definition@v1 | |
with: | |
task-definition: task-definition.json | |
container-name: worker | |
image: ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.image_tag }} | |
- name: Get current worker (Fargate) task definition | |
run: | | |
aws ecs describe-task-definition --task-definition ${{ matrix.identifier }}-worker-fargate --query 'taskDefinition' > task-definition.json | |
- name: Inject new APP image into Fargate worker task definition | |
id: render-fargate-worker-task-def | |
uses: aws-actions/amazon-ecs-render-task-definition@v1 | |
with: | |
task-definition: task-definition.json | |
container-name: worker | |
image: ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.image_tag }} | |
- name: Register Fargate task definition | |
uses: aws-actions/amazon-ecs-deploy-task-definition@v2 | |
with: | |
task-definition: ${{ steps.render-fargate-worker-task-def.outputs.task-definition }} | |
- name: Run DB Migrate using Fargate | |
run: | | |
network_configuration=$(aws ecs describe-services --cluster ${{ matrix.ecs_cluster }} --service ${{ matrix.identifier }}-worker --query 'services[0]' | jq 'with_entries(select([.key] | inside(["networkConfiguration"])))') | |
run_task_params=$(echo $network_configuration | jq '.startedBy = "db_migrate_ci" | .cluster = "${{ matrix.ecs_cluster }}" | .launchType = "FARGATE" | .taskDefinition = "${{ matrix.identifier }}-worker-fargate" | .overrides.containerOverrides[0].name = "worker" | .overrides.containerOverrides[0].command = ["bundle", "exec", "rails", "db:migrate"]' | jq -r tostring) | |
aws ecs run-task --cli-input-json $run_task_params | |
- name: Deploy Webserver | |
uses: aws-actions/amazon-ecs-deploy-task-definition@v2 | |
with: | |
task-definition: ${{ steps.render-webserver-task-def.outputs.task-definition }} | |
service: ${{ matrix.identifier }}-webserver | |
cluster: ${{ matrix.ecs_cluster }} | |
wait-for-service-stability: true | |
- name: Deploy Worker | |
uses: aws-actions/amazon-ecs-deploy-task-definition@v2 | |
with: | |
task-definition: ${{ steps.render-worker-task-def.outputs.task-definition }} | |
service: ${{ matrix.identifier }}-worker | |
cluster: ${{ matrix.ecs_cluster }} | |
wait-for-service-stability: true | |
# Need this step for creating Sentry release | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Create Sentry release | |
uses: getsentry/action-release@v1 | |
env: | |
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
SENTRY_ORG: somleng | |
SENTRY_PROJECT: open-ews | |
with: | |
environment: ${{ matrix.environment }} |