Merge pull request #177 from Vizzuality/staging #293
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
# The workflow uses GH Secrets managed by Terraform: | |
# - TF_GCP_PROJECT_ID | |
# - TF_GCP_REGION | |
# - TF_[PRODUCTION|<UPPER CASE BRANCH NAME>]_GCP_SA_KEY - credentials json for authentication | |
# - TF_[PRODUCTION|<UPPER CASE BRANCH NAME>]_CLIENT_REPOSITORY | |
# - TF_[PRODUCTION|<UPPER CASE BRANCH NAME>]_CLIENT_SERVICE | |
# - TF_[PRODUCTION|<UPPER CASE BRANCH NAME>]_CMS_REPOSITORY | |
# - TF_[PRODUCTION|<UPPER CASE BRANCH NAME>]_CMS_SERVICE | |
# - TF_[PRODUCTION|<UPPER CASE BRANCH NAME>]_ANALYSIS_CF_NAME | |
# | |
# The workflow also processes GH secrets and variables managed by Terraform or manually, which are used to build the .env files for the containers. | |
# These follow the naming convention: | |
# - TF_(PRODUCTION|<UPPER CASE BRANCH NAME>)_[CLIENT_ENV|CMS_ENV]_* - managed by Terraform | |
# - (PRODUCTION|<UPPER CASE BRANCH NAME>)_[CLIENT_ENV|CMS_ENV]_* - managed manually | |
name: Run deploy | |
on: | |
workflow_dispatch: | |
push: | |
branches: | |
- main | |
- staging | |
paths: | |
- 'client/**' | |
- 'cms/**' | |
- '.github/workflows/*' | |
- 'infrastructure/**' | |
jobs: | |
build_client_image: | |
name: Build Client image and push to Amazon ECR | |
runs-on: ubuntu-latest | |
env: | |
APP_ENV_PREFIX: CLIENT_ENV | |
APP_ENV_PATH: client/.env.local | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- uses: dorny/paths-filter@v3 | |
id: client-changes | |
with: | |
filters: | | |
client: | |
- 'client/**' | |
- '.github/workflows/**' | |
- name: Extract branch name | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
run: | | |
{ | |
branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} | |
echo "branch=${branch}" | |
echo "branch_upper=${branch^^}" | |
} >> $GITHUB_OUTPUT | |
id: extract_branch | |
- name: Set environment name | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
run: | | |
{ | |
echo "ENVIRONMENT=${{ steps.extract_branch.outputs.branch == 'main' && 'PRODUCTION' || steps.extract_branch.outputs.branch_upper }}" | |
} >> $GITHUB_ENV | |
- name: Output secrets and vars as JSON | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
# Use GH Actions toJSON function to convert secrets and vars to JSON; in case no values present, output null (otherwise jq will fail) | |
run: | | |
{ | |
echo 'secrets<<EOF' | |
echo '${{ secrets != null && toJSON(secrets) || null }}' | |
echo 'EOF' | |
echo 'vars<<EOF' | |
echo '${{ vars != null && toJSON(vars) || null }}' | |
echo 'EOF' | |
} >> $GITHUB_OUTPUT | |
id: env_json | |
- name: Output secrets and vars as key=value entries | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
# Use jq to convert JSON to key=value entries | |
# 1. to_entries converts JSON to array of key/value pairs | |
# 2. map(.key + "=" + .value) converts each key/value pair to key=value | |
# 3. .[] flattens array to key=value entries | |
run: | | |
{ | |
echo 'entries_all<<EOF' | |
echo '${{ steps.env_json.outputs.secrets }}' '${{ steps.env_json.outputs.vars }}' | jq -r 'to_entries | map(.key + "=" + .value) | .[]' | |
echo 'EOF' | |
} >> $GITHUB_OUTPUT | |
id: env_entries_all | |
- name: Filter secrets and vars for inclusion in .env file by environment and application prefixes | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
# Use grep to filter client secrets & vars and save .env file (names starting with (TF_)((PRODUCTION|STAGING|SOMEBRANCH)_)[CLIENT_ENV|CMS_ENV]_ | |
run: | | |
{ | |
echo 'entries_filtered<<EOF' | |
echo '${{ steps.env_entries_all.outputs.entries_all }}' | grep -E "^(TF_)?(${ENVIRONMENT}_)?${APP_ENV_PREFIX}_" | |
echo 'EOF' | |
} >> $GITHUB_OUTPUT | |
id: env_entries_filtered | |
- name: Strip environment and application prefixes from secret and var names | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
# Use sed to strip environment and application prefixes from secret and var names | |
run: | | |
{ | |
echo 'entries_stripped<<EOF' | |
echo '${{ steps.env_entries_filtered.outputs.entries_filtered }}' | sed -E "s/^(TF_)?("$ENVIRONMENT"_)?"$APP_ENV_PREFIX"_//" | |
echo 'EOF' | |
} >> $GITHUB_OUTPUT | |
id: env_entries_stripped | |
- name: Save .env file | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
run: | | |
echo '${{ steps.env_entries_stripped.outputs.entries_stripped }}' >> $APP_ENV_PATH | |
cat $APP_ENV_PATH | |
- name: Configure AWS credentials | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.TF_PIPELINE_USER_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.TF_PIPELINE_USER_SECRET_ACCESS_KEY }} | |
aws-region: ${{ vars.TF_AWS_REGION }} | |
- name: Login to Amazon ECR | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
id: login-ecr | |
uses: aws-actions/amazon-ecr-login@v2 | |
with: | |
mask-password: 'true' | |
- name: Set up Docker Buildx | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
uses: docker/setup-buildx-action@v3 | |
- name: Build, tag, and push Client image to Amazon ECR | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | |
uses: docker/build-push-action@v5 | |
with: | |
context: ./client | |
cache-from: type=gha | |
cache-to: type=gha,mode=max | |
file: ./client/Dockerfile.prod | |
push: true | |
tags: | | |
${{ steps.login-ecr.outputs.registry }}/${{ secrets.TF_CLIENT_REPOSITORY_NAME }}:${{ github.sha }} | |
${{ steps.login-ecr.outputs.registry }}/${{ secrets.TF_CLIENT_REPOSITORY_NAME }}:${{ steps.extract_branch.outputs.branch == 'main' && 'production' || steps.extract_branch.outputs.branch }} | |
build_cms_image: | |
name: Build CMS image and push to Amazon ECR | |
runs-on: ubuntu-latest | |
env: | |
APP_ENV_PREFIX: CMS_ENV | |
APP_ENV_PATH: cms/.env | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- uses: dorny/paths-filter@v3 | |
id: cms-changes | |
with: | |
filters: | | |
cms: | |
- 'cms/**' | |
- '.github/workflows/**' | |
- name: Extract branch name | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
run: | | |
{ | |
branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} | |
echo "branch=${branch}" | |
echo "branch_upper=${branch^^}" | |
} >> $GITHUB_OUTPUT | |
id: extract_branch | |
- name: Set environment name | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
run: | | |
{ | |
echo "ENVIRONMENT=${{ steps.extract_branch.outputs.branch == 'main' && 'PRODUCTION' || steps.extract_branch.outputs.branch_upper }}" | |
} >> $GITHUB_ENV | |
- name: Output secrets and vars as JSON | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
# Use GH Actions toJSON function to convert secrets and vars to JSON; in case no values present, output null (otherwise jq will fail) | |
run: | | |
{ | |
echo 'secrets<<EOF' | |
echo '${{ secrets != null && toJSON(secrets) || null }}' | |
echo 'EOF' | |
echo 'vars<<EOF' | |
echo '${{ vars != null && toJSON(vars) || null }}' | |
echo 'EOF' | |
} >> $GITHUB_OUTPUT | |
id: env_json | |
- name: Output secrets and vars as key=value entries | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
# Use jq to convert JSON to key=value entries | |
# 1. to_entries converts JSON to array of key/value pairs | |
# 2. map(.key + "=" + .value) converts each key/value pair to key=value | |
# 3. .[] flattens array to key=value entries | |
run: | | |
{ | |
echo 'entries_all<<EOF' | |
echo '${{ steps.env_json.outputs.secrets }}' '${{ steps.env_json.outputs.vars }}' | jq -r 'to_entries | map(.key + "=" + .value) | .[]' | |
echo 'EOF' | |
} >> $GITHUB_OUTPUT | |
id: env_entries_all | |
- name: Filter secrets and vars for inclusion in .env file by environment and application prefixes | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
# Use grep to filter client secrets & vars and save .env file (names starting with (TF_)((PRODUCTION|STAGING|SOMEBRANCH)_)[CLIENT_ENV|CMS_ENV]_ | |
run: | | |
{ | |
echo 'entries_filtered<<EOF' | |
echo '${{ steps.env_entries_all.outputs.entries_all }}' | grep -E "^(TF_)?(${ENVIRONMENT}_)?${APP_ENV_PREFIX}_" | |
echo 'EOF' | |
} >> $GITHUB_OUTPUT | |
id: env_entries_filtered | |
- name: Strip environment and application prefixes from secret and var names | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
# Use sed to strip environment and application prefixes from secret and var names | |
run: | | |
{ | |
echo 'entries_stripped<<EOF' | |
echo '${{ steps.env_entries_filtered.outputs.entries_filtered }}' | sed -E "s/^(TF_)?("$ENVIRONMENT"_)?"$APP_ENV_PREFIX"_//" | |
echo 'EOF' | |
} >> $GITHUB_OUTPUT | |
id: env_entries_stripped | |
- name: Save .env file | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
run: | | |
echo '${{ steps.env_entries_stripped.outputs.entries_stripped }}' >> $APP_ENV_PATH | |
- name: Configure AWS credentials | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.TF_PIPELINE_USER_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.TF_PIPELINE_USER_SECRET_ACCESS_KEY }} | |
aws-region: ${{ vars.TF_AWS_REGION }} | |
- name: Login to Amazon ECR | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
id: login-ecr | |
uses: aws-actions/amazon-ecr-login@v2 | |
with: | |
mask-password: 'true' | |
- name: Set up Docker Buildx | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
uses: docker/setup-buildx-action@v3 | |
- name: Build, tag, and push API image to Amazon ECR | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | |
uses: docker/build-push-action@v5 | |
with: | |
context: ./cms | |
cache-from: type=gha | |
cache-to: type=gha,mode=max | |
file: ./cms/Dockerfile.prod | |
push: true | |
tags: | | |
${{ steps.login-ecr.outputs.registry }}/${{ secrets.TF_CMS_REPOSITORY_NAME }}:${{ github.sha }} | |
${{ steps.login-ecr.outputs.registry }}/${{ secrets.TF_CMS_REPOSITORY_NAME }}:${{ steps.extract_branch.outputs.branch == 'main' && 'production' || steps.extract_branch.outputs.branch }} | |
deploy: | |
name: Deploy Client and CMS to Amazon EB | |
needs: [build_client_image, build_cms_image] | |
runs-on: ubuntu-22.04 | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v3 | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.TF_PIPELINE_USER_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.TF_PIPELINE_USER_SECRET_ACCESS_KEY }} | |
aws-region: ${{ vars.TF_AWS_REGION }} | |
- name: Login to Amazon ECR | |
id: login-ecr | |
uses: aws-actions/amazon-ecr-login@v2 | |
- name: Extract branch name | |
run: | | |
{ | |
branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} | |
echo "branch=${branch}" | |
echo "branch_upper=${branch^^}" | |
} >> $GITHUB_OUTPUT | |
id: extract_branch | |
- name: Generate docker compose file | |
working-directory: infrastructure/source_bundle | |
env: | |
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} | |
ECR_REPOSITORY_CLIENT: ${{ secrets.TF_CLIENT_REPOSITORY_NAME }} | |
ECR_REPOSITORY_CMS: ${{ secrets.TF_CMS_REPOSITORY_NAME }} | |
IMAGE_TAG: ${{ steps.extract_branch.outputs.branch == 'main' && 'production' || steps.extract_branch.outputs.branch }} | |
run: | | |
echo "version: '3.3'" > docker-compose.yml | |
echo "services:" >> docker-compose.yml | |
echo " client:" >> docker-compose.yml | |
echo " image: $ECR_REGISTRY/$ECR_REPOSITORY_CLIENT:$IMAGE_TAG" >> docker-compose.yml | |
echo " restart: always" >> docker-compose.yml | |
echo " ports:" >> docker-compose.yml | |
echo " - 3000:3000" >> docker-compose.yml | |
echo " cms:" >> docker-compose.yml | |
echo " image: $ECR_REGISTRY/$ECR_REPOSITORY_CMS:$IMAGE_TAG" >> docker-compose.yml | |
echo " restart: always" >> docker-compose.yml | |
echo " ports:" >> docker-compose.yml | |
echo " - 1337:1337" >> docker-compose.yml | |
echo " nginx:" >> docker-compose.yml | |
echo " image: nginx" >> docker-compose.yml | |
echo " restart: always" >> docker-compose.yml | |
echo " volumes:" >> docker-compose.yml | |
echo " - ./proxy/conf.d:/etc/nginx/conf.d" >> docker-compose.yml | |
echo " - \"\${EB_LOG_BASE_DIR}/nginx:/var/log/nginx\"" >> docker-compose.yml | |
echo " ports:" >> docker-compose.yml | |
echo " - 80:80" >> docker-compose.yml | |
echo " depends_on:" >> docker-compose.yml | |
echo " - cms" >> docker-compose.yml | |
echo " - client" >> docker-compose.yml | |
- name: Generate zip file | |
working-directory: infrastructure/source_bundle | |
run: | | |
zip -r deploy.zip * .[^.]* | |
- name: Deploy to Amazon EB | |
uses: einaregilsson/beanstalk-deploy@v21 | |
with: | |
aws_access_key: ${{ secrets.TF_PIPELINE_USER_ACCESS_KEY_ID }} | |
aws_secret_key: ${{ secrets.TF_PIPELINE_USER_SECRET_ACCESS_KEY }} | |
application_name: ${{ secrets.TF_PROJECT_NAME}}-${{ steps.extract_branch.outputs.branch == 'main' && 'production' || steps.extract_branch.outputs.branch }} | |
environment_name: ${{ secrets.TF_PROJECT_NAME}}-${{ steps.extract_branch.outputs.branch == 'main' && 'production' || steps.extract_branch.outputs.branch }}-environment | |
region: ${{ vars.TF_AWS_REGION }} | |
version_label: ${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }} | |
deployment_package: infrastructure/source_bundle/deploy.zip |