Skip to content
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
24 changes: 16 additions & 8 deletions .github/workflows/ci-workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ on:
- 'docker-compose.yml'
- 'infra/**'
- '.github/workflows/ci-workflows.yml'
push:
branches:
- main
paths:
- 'tinyurl/**'
- 'docker-compose.yml'
- 'infra/**'
- '.github/workflows/ci-workflows.yml'
workflow_dispatch:

jobs:
Expand Down Expand Up @@ -51,6 +43,14 @@ jobs:
runs-on: ubuntu-latest
needs: build-test

env:
POSTGRES_USER: tinyurl_ci
POSTGRES_PASSWORD: ci_smoke_postgres_pass
SPRING_DATASOURCE_USERNAME: tinyurl_appuser_ci
SPRING_DATASOURCE_PASSWORD: ci_smoke_appuser_pass
SPRING_FLYWAY_USER: tinyurl_ci
SPRING_FLYWAY_PASSWORD: ci_smoke_postgres_pass

steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -63,6 +63,14 @@ jobs:
runs-on: ubuntu-latest
needs: compose-validate

env:
POSTGRES_USER: tinyurl_ci
POSTGRES_PASSWORD: ci_smoke_postgres_pass
SPRING_DATASOURCE_USERNAME: tinyurl_appuser_ci
SPRING_DATASOURCE_PASSWORD: ci_smoke_appuser_pass
SPRING_FLYWAY_USER: tinyurl_ci
SPRING_FLYWAY_PASSWORD: ci_smoke_postgres_pass

steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down
158 changes: 158 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
name: Deploy API

on:
push:
branches: [main]
paths:
- 'tinyurl/**'
- 'docker-compose.prod.yml'
- 'infra/**'
- '.github/workflows/deploy.yml'
workflow_dispatch:

jobs:
build-test:
name: Build and Test
runs-on: ubuntu-latest

defaults:
run:
working-directory: tinyurl

steps:
- uses: actions/checkout@v4

- name: Set up Java 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
cache: gradle

- name: Make Gradle wrapper executable
run: chmod +x gradlew

- name: Run unit tests
run: ./gradlew --no-daemon clean test

- name: Build executable JAR
run: ./gradlew --no-daemon bootJar

compose-smoke:
name: Compose Smoke Test
needs: build-test
runs-on: ubuntu-latest

env:
POSTGRES_USER: tinyurl_ci
POSTGRES_PASSWORD: ci_smoke_postgres_pass
SPRING_DATASOURCE_USERNAME: tinyurl_appuser_ci
SPRING_DATASOURCE_PASSWORD: ci_smoke_appuser_pass
SPRING_FLYWAY_USER: tinyurl_ci
SPRING_FLYWAY_PASSWORD: ci_smoke_postgres_pass

steps:
- uses: actions/checkout@v4

- name: Build and start stack
run: docker compose up -d --build

- name: Wait for nginx route health
run: |
for i in {1..40}; do
if curl -fsS http://localhost:8080/actuator/health >/dev/null; then
exit 0
fi
sleep 3
done
echo "Service did not become healthy in time"
docker compose logs --no-color
exit 1

- name: Stop stack
if: always()
run: docker compose down -v

deploy:
name: Deploy to EC2 via SSM
needs: [build-test, compose-smoke]
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
contents: read
packages: write # Required for GHCR push

steps:
- uses: actions/checkout@v4

- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1

- name: Fetch parameters from AWS Parameter Store
run: |
EC2_INSTANCE_ID=$(aws ssm get-parameter \
--name "/tinyurl/cicd/ec2-instance-id" \
--query "Parameter.Value" --output text)
RDS_ENDPOINT=$(aws ssm get-parameter \
--name "/tinyurl/cicd/rds-endpoint" \
--query "Parameter.Value" --output text)
echo "EC2_INSTANCE_ID=$EC2_INSTANCE_ID" >> $GITHUB_ENV
echo "RDS_ENDPOINT=$RDS_ENDPOINT" >> $GITHUB_ENV

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push Docker image
run: |
docker build -t ghcr.io/buffden/tinyurl-api:${{ github.sha }} tinyurl/
docker push ghcr.io/buffden/tinyurl-api:${{ github.sha }}

- name: Deploy via SSM RunCommand
run: |
COMMAND_ID=$(aws ssm send-command \
--instance-ids "$EC2_INSTANCE_ID" \
--document-name "AWS-RunShellScript" \
--parameters "commands=[
\"export IMAGE_TAG=${{ github.sha }}\",
\"export RDS_ENDPOINT=$RDS_ENDPOINT\",
\"cd /app\",
\"docker compose -f docker-compose.prod.yml pull\",
\"docker compose -f docker-compose.prod.yml up -d\",
\"sleep 20\",
\"curl -sf http://localhost/actuator/health || exit 1\"
]" \
--output text \
--query "Command.CommandId")

echo "SSM Command ID: $COMMAND_ID"

for i in {1..18}; do
STATUS=$(aws ssm get-command-invocation \
--command-id "$COMMAND_ID" \
--instance-id "$EC2_INSTANCE_ID" \
--query "Status" \
--output text)
echo "Status: $STATUS ($i/18)"
if [ "$STATUS" = "Success" ]; then
echo "Deploy succeeded"
exit 0
elif [ "$STATUS" = "Failed" ] || [ "$STATUS" = "TimedOut" ] || [ "$STATUS" = "Cancelled" ]; then
echo "Deploy failed with status: $STATUS"
aws ssm get-command-invocation \
--command-id "$COMMAND_ID" \
--instance-id "$EC2_INSTANCE_ID" \
--query "StandardErrorContent" \
--output text
exit 1
fi
sleep 10
done
echo "Deploy timed out waiting for SSM"
exit 1
14 changes: 9 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ services:
condition: service_healthy
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/tinyurl
SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME:-tinyurl}
SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD:-tinyurl}
SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME:?SPRING_DATASOURCE_USERNAME is required}
SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD:?SPRING_DATASOURCE_PASSWORD is required}
SPRING_FLYWAY_USER: ${SPRING_FLYWAY_USER:?SPRING_FLYWAY_USER is required}
SPRING_FLYWAY_PASSWORD: ${SPRING_FLYWAY_PASSWORD:?SPRING_FLYWAY_PASSWORD is required}
SERVER_PORT: 8080
TINYURL_BASE_URL: http://localhost:8080
expose:
Expand All @@ -51,8 +53,10 @@ services:
container_name: tinyurl-postgres
environment:
POSTGRES_DB: ${POSTGRES_DB:-tinyurl}
POSTGRES_USER: ${POSTGRES_USER:-tinyurl}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-tinyurl}
POSTGRES_USER: ${POSTGRES_USER:?POSTGRES_USER is required}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME:?SPRING_DATASOURCE_USERNAME is required}
SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD:?SPRING_DATASOURCE_PASSWORD is required}
PGDATA: /var/lib/postgresql/data/pgdata
command:
- postgres
Expand All @@ -66,7 +70,7 @@ services:
networks:
- app_internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \"${POSTGRES_USER:-tinyurl}\" -d \"${POSTGRES_DB:-tinyurl}\""]
test: ["CMD-SHELL", "pg_isready -U \"${POSTGRES_USER}\" -d \"${POSTGRES_DB:-tinyurl}\""]
interval: 10s
timeout: 5s
retries: 5
Expand Down
Loading
Loading