Skip to content
Closed
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
6 changes: 6 additions & 0 deletions .config/github-actions-schedule.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"containerDefinitions": [
{
}
]
}
23 changes: 23 additions & 0 deletions .github/workflows/dev-build-upload.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Demo Deployment Workflow

on:
pull_request:
branches:
- main

jobs:
build-push:
uses: ./.github/workflows/docker-build-upload.yml
with:
AWS_DEFAULT_REGION: ${{ vars.AWS_DEFAULT_REGION }}
AWS_ROLE_TO_ASSUME: ${{ vars.AWS_ROLE_TO_ASSUME }}
ECR_REPOSITORY: 'gs-sandbox-github-actions'
NAME_PREFIX: 'gs-sandbox'

DEPLOY_SCHEDULED_TASK: true # Only when you want to deploy scheduled tasks for the app
EXECUTION_NAME: 'github-actions-schedule' # It can be a list separated by ';', only when you want to deploy scheduled tasks

DEPLOY_ECS_SERVICE: true # Only when you want to deploy a ecs service for the app
SERVICE_NAME: 'demo-svc' # Only when you want to deploy a ecs service
ECS_CLUSTER_NAME: 'gs-sandbox-ecs-cluster' # Only when you want to deploy a ecs service
ECS_DEV_DEPLOY: true # Only when you want to deploy a dev ecs service for the app
122 changes: 79 additions & 43 deletions .github/workflows/docker-build-upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ name: Docker Build & Push
# ##### GITHUB REQUIRED VARS #####
# AWS_DEFAULT_REGION: Already available in all /gsoftcolombia projects.
# AWS_ROLE_TO_ASSUME: ARN of the role that aws-actions can assume.
# REPO_NAME: ECR Repository Name
# APP_NAME_PREFIX: Prefix of the task definition in AWS.
# APP_NAME_SUFFIX: Suffix/Postfix of the task definition in AWS.
# ECR_REPOSITORY: ECR Repository Name
# NAME_PREFIX: Prefix of the task definition in AWS.
# EXECUTION_NAME: Suffix/Postfix of the task definition in AWS.
# - If there are multiple task-definitions you can add them separated by ';' e.g. customers;products
# - If the Action is executed from Main, it expects to update a task definition
# with the name {APP_NAME_PREFIX}-prod-{APP_NAME_SUFFIX}
#  otherwise, {APP_NAME_PREFIX}-dev-{APP_NAME_SUFFIX}
# with the name {NAME_PREFIX}-prod-{EXECUTION_NAME}
#  otherwise, {NAME_PREFIX}-dev-{EXECUTION_NAME}

# ##### USAGE TEMPLATE #####
# on:
Expand All @@ -31,10 +31,14 @@ name: Docker Build & Push
# with:
# AWS_DEFAULT_REGION: ${{ vars.AWS_DEFAULT_REGION }}
# AWS_ROLE_TO_ASSUME: ${{ vars.AWS_ROLE_TO_ASSUME }}
# REPO_NAME: ${{ vars.REPO_NAME }}
# APP_NAME_PREFIX: ${{ vars.APP_NAME_PREFIX }} # Required for both ECS Services and Scheduled Tasks
# APP_NAME_SUFFIX: ${{ vars.APP_NAME_SUFFIX }} # Only when you want to deploy scheduled tasks
# ECS_SERVICE_NAME: ${{ vars.ECS_SERVICE_NAME }} # Only when you want to deploy ECS Services
# ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
# NAME_PREFIX: ${{ vars.NAME_PREFIX }} # Required for both ECS Services and Scheduled Tasks
#
# DEPLOY_SCHEDULED_TASK: true # Only when you want to deploy scheduled tasks for the app
# EXECUTION_NAME: ${{ vars.EXECUTION_NAME }} # Only when you want to deploy scheduled tasks
#
# DEPLOY_ECS_SERVICE: true # Only when you want to deploy a ecs service for the app
# SERVICE_NAME: ${{ vars.SERVICE_NAME }} # Only when you want to deploy ECS Services
# ECS_CLUSTER_NAME: ${{ vars.ECS_CLUSTER_NAME }} # Only when you want to deploy ECS Services
# ECS_DEV_DEPLOY: true # Only when you want to deploy a dev ECS Service for the app

Expand All @@ -49,24 +53,32 @@ on:
AWS_ROLE_TO_ASSUME:
required: true
type: string
REPO_NAME:
ECR_REPOSITORY:
required: true
type: string
APP_NAME_PREFIX:
required: false
NAME_PREFIX:
required: true
type: string
APP_NAME_SUFFIX:

DEPLOY_SCHEDULED_TASK:
required: true
type: boolean
EXECUTION_NAME:
required: false
type: string
default: ''
ECS_SERVICE_NAME:
default: 'missing_execution_name' # Only when you want to deploy scheduled tasks, it can be a list separated by ';'

DEPLOY_ECS_SERVICE:
required: true
type: boolean
SERVICE_NAME:
required: false
type: string
default: ''
default: 'missing_service_name' # Only when you want to deploy a ecs service for the app
ECS_CLUSTER_NAME:
required: false # Required if there is ECS_SERVICE_NAME
required: false # Required if there is SERVICE_NAME
type: string
default: ''
default: 'missing_cluster_name' # Only when you want to deploy a ecs service for the app
ECS_DEV_DEPLOY:
required: false
type: boolean
Expand Down Expand Up @@ -110,49 +122,73 @@ jobs:
echo "TASK_ENV=$TASK_ENV" >> $GITHUB_OUTPUT

- name: Build and Push to ECR
continue-on-error: true # for cases when the image is already pushed
id: build
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ inputs.REPO_NAME }}
ECR_REPOSITORY: ${{ inputs.ECR_REPOSITORY }}
run: |
IMAGE_ENV=$([ "$GITHUB_REF" = "refs/heads/main" ] && echo "prod" || echo "dev")
IMAGE_TAG="${IMAGE_ENV}-${{ steps.commit-sha.outputs.COMMIT_SHORT_SHA }}"
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
aws ecr batch-delete-image --repository-name $ECR_REPOSITORY --image-ids imageTag=$IMAGE_TAG 2>/dev/null || true
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "FULL_IMAGE_URI=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

- name: Update ECS Schedule Tasks # It only runs when there are APPs specified (SUFFIX)
if: ${{ inputs.APP_NAME_SUFFIX != '' }}
id: update-schedules
- name: Deploy ECS Schedule Tasks
if: ${{ inputs.DEPLOY_SCHEDULED_TASK == true }}
id: deploy-schedules
run: |
echo "Updating Task Definitions"
export IFS=";"
SUFFIXES="${{ inputs.APP_NAME_SUFFIX }}"
for app in $SUFFIXES; do
list_execution_name="${{ inputs.EXECUTION_NAME }}"
for execution_name in $list_execution_name; do
config_file=".config/${execution_name}.json"
if [ ! -f "$config_file" ]; then
echo "ERROR: Missing scheduled task config file: $config_file"
exit 1
fi

if ! aws ecs describe-task-definition \
--task-definition ${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${app} \
--task-definition ${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${execution_name} \
--region=${{ inputs.AWS_DEFAULT_REGION }} &> /dev/null; then
echo "WARNING: Task definition $TASK_DEFINITION does not exist."
echo "ERROR: Task definition ${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${execution_name} does not exist."
exit 1
else
echo "Updating Task-Definition for: ${app} ..."
TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition ${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${app} --region=${{ inputs.AWS_DEFAULT_REGION }})
NEW_TASK_DEFINITION=$(echo $TASK_DEFINITION | jq --arg IMAGE "${{ steps.build.outputs.FULL_IMAGE_URI }}" '.taskDefinition | .containerDefinitions[0].image = $IMAGE | del(.taskDefinitionArn) | del(.revision) | del(.status) | del(.requiresAttributes) | del(.compatibilities) | del(.registeredAt) | del(.registeredBy)')
echo "Updating Task-Definition for: ${execution_name} ..."
TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition ${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${execution_name} --region=${{ inputs.AWS_DEFAULT_REGION }})
updated_task_definition=$(echo "$TASK_DEFINITION" | jq --slurpfile config ".config/${execution_name}.json" '
.taskDefinition as $base
| ($config[0] // {}) as $updates
| ($updates.containerDefinitions // []) as $container_updates
| ($base * ($updates | del(.containerDefinitions))) as $merged
| if ($container_updates | length) > 0 then
$merged
| .containerDefinitions = [
range(0; ($merged.containerDefinitions | length)) as $index
| (($merged.containerDefinitions[$index] // {}) * ($container_updates[$index] // {}))
]
else
$merged
end
')
NEW_TASK_DEFINITION=$(echo "$updated_task_definition" | jq --arg IMAGE "${{ steps.build.outputs.FULL_IMAGE_URI }}" '.containerDefinitions[0].image = $IMAGE | del(.taskDefinitionArn) | del(.revision) | del(.status) | del(.requiresAttributes) | del(.compatibilities) | del(.registeredAt) | del(.registeredBy)')
aws ecs register-task-definition --region ${{ inputs.AWS_DEFAULT_REGION }} --cli-input-json "$NEW_TASK_DEFINITION"
fi
done

- name: Render Amazon ECS Service Task Definition # It only runs when there is an ECS_SERVICE_NAME specified.
if: ${{ inputs.ECS_SERVICE_NAME != '' }}
- name: Render Amazon ECS Service Task Definition # It only runs when there is an SERVICE_NAME specified.
if: ${{ inputs.DEPLOY_ECS_SERVICE == true }}
id: render-web-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition-family: ${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.ECS_SERVICE_NAME }}
container-name: ${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.ECS_SERVICE_NAME }}
with:
task-definition-family: ${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.SERVICE_NAME }}
container-name: ${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.SERVICE_NAME }}
image: ${{ steps.build.outputs.FULL_IMAGE_URI }}
environment-variables: |
ENVIRONMENT=${{ steps.commit-sha.outputs.TASK_ENV }}

- name: Generate appspec.yaml dynamically
if: ${{ inputs.ECS_SERVICE_NAME != '' }}
if: ${{ inputs.DEPLOY_ECS_SERVICE == true }}
id: generate-appspec
run: |
cat <<EOF > appspec.yaml
Expand All @@ -161,19 +197,19 @@ jobs:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: "${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.ECS_SERVICE_NAME }}"
TaskDefinition: "${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.SERVICE_NAME }}"
LoadBalancerInfo:
ContainerName: "${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.ECS_SERVICE_NAME }}"
ContainerName: "${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.SERVICE_NAME }}"
ContainerPort: 80
EOF

- name: Deploy to Amazon ECS service
if: ${{ inputs.ECS_SERVICE_NAME != '' && (steps.commit-sha.outputs.TASK_ENV == 'prod' || ( steps.commit-sha.outputs.TASK_ENV == 'dev' && inputs.ECS_DEV_DEPLOY == true )) }}
if: ${{ inputs.DEPLOY_ECS_SERVICE == true && (steps.commit-sha.outputs.TASK_ENV == 'prod' || ( steps.commit-sha.outputs.TASK_ENV == 'dev' && inputs.ECS_DEV_DEPLOY == true )) }}
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
service: ${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.ECS_SERVICE_NAME }}
service: ${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.SERVICE_NAME }}
cluster: ${{ inputs.ECS_CLUSTER_NAME }}
wait-for-service-stability: true
codedeploy-application: ${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.ECS_SERVICE_NAME }}
codedeploy-deployment-group: ${{ inputs.APP_NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.ECS_SERVICE_NAME }}
wait-for-service-stability: false
codedeploy-application: ${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.SERVICE_NAME }}
codedeploy-deployment-group: ${{ inputs.NAME_PREFIX }}-${{ steps.commit-sha.outputs.TASK_ENV }}-${{ inputs.SERVICE_NAME }}
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM httpd:latest

COPY ./entrypoint.sh /app/
RUN chmod +x /app/entrypoint.sh

ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["httpd-foreground"]
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,56 @@

This is the main repo where our github actions and also general technical documentation are located.

## Reusable Workflows

### Docker Build & Push

This reusable workflow builds a Dockerfile located in the root of the project, pushes the new Docker image to an ECR Registry, and optionally updates ECS task definitions for services or scheduled tasks.

For scheduled tasks, the caller repository can define runtime overrides under `.config/`:

- Scheduled tasks must provide `.config/<execution-name>.json`.

The file is merged with the current active task definition before the image tag is updated, so the application repository owns the scheduled-task runtime fields without Terraform managing them.

#### Usage

To use this workflow, call it from another workflow file as shown in `.github/workflows/dev-build-upload.yml`:

```yaml
jobs:
build-push:
uses: gsoftcolombia/github-actions/.github/workflows/docker-build-upload.yml@main
with:
AWS_DEFAULT_REGION: ${{ vars.AWS_DEFAULT_REGION }}
AWS_ROLE_TO_ASSUME: ${{ vars.AWS_ROLE_TO_ASSUME }}
ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
NAME_PREFIX: ${{ vars.NAME_PREFIX }}

DEPLOY_SCHEDULED_TASK: true
EXECUTION_NAME: ${{ vars.EXECUTION_NAME }}

DEPLOY_ECS_SERVICE: false
SERVICE_NAME: ${{ vars.SERVICE_NAME }}
ECS_CLUSTER_NAME: ${{ vars.ECS_CLUSTER_NAME }}
ECS_DEV_DEPLOY: false
```

#### Required Variables

- `AWS_DEFAULT_REGION`: AWS region (available in all /gsoftcolombia projects).
- `AWS_ROLE_TO_ASSUME`: ARN of the role for aws-actions to assume.
- `ECR_REPOSITORY`: ECR Repository Name.
- `NAME_PREFIX`: Prefix of the task definition in AWS.

#### Optional Inputs

- `DEPLOY_SCHEDULED_TASK`: Set to true to deploy scheduled tasks.
- `EXECUTION_NAME`: Suffix for scheduled task definitions (can be a list separated by ';').
- `DEPLOY_ECS_SERVICE`: Set to true to deploy an ECS service.
- `SERVICE_NAME`: Name of the ECS service.
- `ECS_CLUSTER_NAME`: Name of the ECS cluster.
- `ECS_DEV_DEPLOY`: Set to true to deploy to dev environment.

For more details, see the workflow file at `.github/workflows/docker-build-upload.yml`.

11 changes: 11 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -e # Exit on error

name_prefix="gs-sandbox"
environment="${environment}"
execution_name="demo"

echo "This is ${name_prefix}-${environment}-${execution_name} and it is ready to start..."

# Run the main application command
exec "$@"
Loading